4 * Copyright (c) 2000 - 2012 Samsung Electronics Co., Ltd All Rights Reserved
6 * Contact: Kidong Kim <kd0228.kim@samsung.com>
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
26 #include <sys/types.h>
38 #include <sys/smack.h>
40 #include "privilege-control.h"
44 #define DEVELOPER_GID 5100
45 #define DEVELOPER_UID 5100
47 #define APP_USER_NAME "app"
48 #define DEV_USER_NAME "developer"
50 #define APP_HOME_DIR TOSTRING(HOMEDIR) "/app"
51 #define DEV_HOME_DIR TOSTRING(HOMEDIR) "/developer"
53 #define APP_GROUP_PATH TOSTRING(SHAREDIR) "/app_group_list"
54 #define DEV_GROUP_PATH TOSTRING(SHAREDIR) "/dev_group_list"
56 #define SMACK_RULES_DIR "/etc/smack/accesses.d/"
58 #define SMACK_APP_LABEL "~APP~"
59 #define SMACK_WRT_LABEL_PREFIX "wrt_widget_"
60 #define SMACK_SRC_FILE_SUFFIX "_src_file"
61 #define SMACK_SRC_DIR_SUFFIX "_src_dir"
62 #define SMACK_DATA_SUFFIX "_data"
63 #define WRT_BASE_DEVCAP "WRT"
64 #define WRT_CLIENT_PATH "/usr/bin/wrt-client"
67 #undef WRT_SMACK_ENABLED
70 #ifdef WRT_SMACK_ENABLED
71 static int set_smack_for_wrt(const char* widget_id);
72 #endif // WRT_SMACK_ENABLED
87 API int control_privilege(void)
89 if(getuid() == APP_UID) // current user is 'app'
90 return PC_OPERATION_SUCCESS;
92 if(set_app_privilege("com.samsung.", NULL, NULL) == PC_OPERATION_SUCCESS)
93 return PC_OPERATION_SUCCESS;
95 return PC_ERR_NOT_PERMITTED;
98 static int set_dac(const char* pkg_name)
100 FILE* fp_group = NULL; // /etc/group
101 uid_t t_uid = -1; // uid of current process
102 gid_t *glist = NULL; // group list
103 gid_t temp_gid = -1; // for group list
104 char buf[10] = {0, }; // contents in group_list file
105 int glist_cnt = 0; // for group list
110 * initialize user structure
112 memset(usr.user_name, 0x00, 10);
113 memset(usr.home_dir, 0x00, 64);
114 memset(usr.group_list, 0x00, 64);
120 if(t_uid == 0) // current user is 'root'
122 if(!strncmp(pkg_name, "developer", 9))
124 strncpy(usr.user_name, DEV_USER_NAME, sizeof(usr.user_name));
125 usr.uid = DEVELOPER_UID;
126 usr.gid = DEVELOPER_GID;
127 strncpy(usr.home_dir, DEV_HOME_DIR, sizeof(usr.home_dir));
128 strncpy(usr.group_list, DEV_GROUP_PATH, sizeof(usr.group_list));
132 strncpy(usr.user_name, APP_USER_NAME, sizeof(usr.user_name));
135 strncpy(usr.home_dir, APP_HOME_DIR, sizeof(usr.home_dir));
136 strncpy(usr.group_list, APP_GROUP_PATH, sizeof(usr.group_list));
140 * get group information
142 if(!(fp_group = fopen(usr.group_list, "r")))
144 fprintf(stderr, "[ERR] file open error: [%s]\n", usr.group_list);
145 result = PC_ERR_FILE_OPERATION; // return -1
149 while(fgets(buf, 10, fp_group) != NULL)
152 temp_gid = strtoul(buf, 0, 10);
153 if(errno != 0) // error occured during strtoul()
155 fprintf(stderr, "[ERR] cannot change string to integer: [%s]\n", buf);
156 result = PC_ERR_INVALID_OPERATION;
160 glist = (gid_t*)realloc(glist, sizeof(gid_t) * (glist_cnt + 1));
163 result = PC_ERR_MEM_OPERATION; // return -2
166 glist[glist_cnt] = temp_gid;
175 if(setgroups(glist_cnt, glist) != 0)
177 fprintf(stderr, "[ERR] setgrouops fail\n");
178 result = PC_ERR_NOT_PERMITTED; // return -3
188 * setgid() & setgid()
190 if(setgid(usr.gid) != 0) // fail
192 fprintf(stderr, "[ERR] fail to execute setgid().\n");
193 result = PC_ERR_INVALID_OPERATION;
196 if(setuid(usr.uid) != 0) // fail
198 fprintf(stderr, "[ERR] fail to execute setuid().\n");
199 result = PC_ERR_INVALID_OPERATION;
203 if(setenv("USER", usr.user_name, 1) != 0) //fail
205 fprintf(stderr, "[ERR] fail to execute setenv().\n");
206 result = PC_ERR_INVALID_OPERATION;
209 if(setenv("HOME", usr.home_dir, 1) != 0) // fail
211 fprintf(stderr, "[ERR] fail to execute setenv().\n");
212 result = PC_ERR_INVALID_OPERATION;
216 else // current user is not only 'root' but 'app'
218 fprintf(stderr, "[ERR] current user is NOT root\n");
219 result = PC_ERR_NOT_PERMITTED; // return -3
223 result = PC_OPERATION_SUCCESS;
236 * Set process SMACK label from EXEC label of a file.
237 * This function is emulating EXEC label behaviour of SMACK for programs
238 * run by dlopen/dlsym instead of execv.
240 * @param path file path to take label from
241 * @return PC_OPERATION_SUCCESS on success, PC_ERR_* on error
243 static int set_smack_from_binary(const char* path)
248 ret = smack_lgetlabel(path, &label, SMACK_LABEL_EXEC);
250 return PC_ERR_INVALID_OPERATION;
253 /* No label to set, just return with success */
254 ret = PC_OPERATION_SUCCESS;
256 ret = smack_set_label_for_self(label);
262 static int is_widget(const char* path)
264 char buf[sizeof(WRT_CLIENT_PATH)];
267 ret = readlink(path, buf, sizeof(WRT_CLIENT_PATH));
268 if (ret == -1 || ret == sizeof(WRT_CLIENT_PATH))
272 return (!strcmp(WRT_CLIENT_PATH, buf));
276 * Partially verify, that the type given for app is correct.
277 * This function will use some heuristics to check whether the app type is right.
278 * It is intended for security hardening to catch privilege setting for the
279 * app type not corresponding to the actual binary.
280 * Beware - when it detects an anomaly, the whole process will be terminated.
282 * @param type claimed application type
283 * @param path file path to executable
284 * @return return void on success, terminate the process on error
286 static pkg_type_t verify_app_type(const char* type, const char* path)
288 /* TODO: this should actually be treated as error, but until the old
289 * set_privilege API is removed, it must be ignored */
291 return PKG_TYPE_OTHER; /* good */
293 if (is_widget(path)) {
294 if (!strcmp(type, "wgt"))
295 return PKG_TYPE_WGT; /* good */
297 if (type == NULL || strcmp(type, "wgt"))
298 return PKG_TYPE_OTHER; /* good */
305 static const char* parse_widget_id(const char* path)
307 const char* basename = strrchr(path, '/');
309 if (basename == NULL)
316 #endif // SMACK_ENABLED
318 API int set_app_privilege(const char* name, const char* type, const char* path)
321 const char* widget_id;
322 int ret = PC_OPERATION_SUCCESS;
324 switch(verify_app_type(type, path)) {
326 widget_id = parse_widget_id(path);
327 if (widget_id == NULL)
328 ret = PC_ERR_INVALID_PARAM;
329 #ifdef WRT_SMACK_ENABLED
331 ret = set_smack_for_wrt(widget_id);
336 ret = set_smack_from_binary(path);
339 if (ret != PC_OPERATION_SUCCESS)
341 #endif // SMACK_ENABLED
343 return set_dac(name);
346 API int set_privilege(const char* pkg_name)
348 return set_app_privilege(pkg_name, NULL, NULL);
351 API int wrt_set_privilege(const char* widget_id)
353 #ifdef WRT_SMACK_ENABLED
356 ret = set_smack_for_wrt(widget_id);
357 if (ret != PC_OPERATION_SUCCESS)
359 #endif // WRT_SMACK_ENABLED
361 return set_dac("com.samsung.");
364 #ifdef WRT_SMACK_ENABLED
365 static inline char* wrt_smack_label(const char* widget_id, const char* suffix)
370 ret = asprintf(&label, "%s%s%s", SMACK_WRT_LABEL_PREFIX, widget_id,
371 (suffix ? suffix : ""));
376 if (strlen(label) > SMACK_LABEL_LEN) {
383 #endif // WRT_SMACK_ENABLED
385 static inline int perm_to_smack(struct smack_accesses* smack, const char* app_label, const char* perm)
387 int ret = PC_OPERATION_SUCCESS;
389 char* format_string = NULL;
391 char smack_subject[SMACK_LABEL_LEN + 1];
392 char smack_object[SMACK_LABEL_LEN + 1];
393 char smack_accesses[10];
395 if (asprintf(&path, TOSTRING(SHAREDIR) "/%s.smack", perm) == -1) {
396 ret = PC_ERR_MEM_OPERATION;
400 if (asprintf(&format_string,"%%%ds %%%ds %%%ds\n",
401 SMACK_LABEL_LEN, SMACK_LABEL_LEN, sizeof(smack_accesses)) == -1) {
402 ret = PC_ERR_MEM_OPERATION;
406 file = fopen(path, "r");
408 ret = PC_ERR_FILE_OPERATION;
413 if (fscanf(file, format_string, smack_subject, smack_object, smack_accesses) != 1)
416 if (!strcmp(smack_subject, SMACK_APP_LABEL))
417 strcpy(smack_subject, app_label);
419 if (!strcmp(smack_object, SMACK_APP_LABEL))
420 strcpy(smack_object, app_label);
422 if (smack_accesses_add_modify(smack, smack_subject, smack_object, smack_accesses, "") != 0) {
423 ret = PC_ERR_INVALID_OPERATION;
436 API int wrt_permissions_reset(const char* widget_id)
438 int ret = PC_OPERATION_SUCCESS;
439 #ifdef WRT_SMACK_ENABLED
442 label = wrt_smack_label(widget_id, NULL);
444 return PC_ERR_MEM_OPERATION;
446 if (smack_revoke_subject(label))
447 ret = PC_ERR_INVALID_OPERATION;
450 #endif // WRT_SMACK_ENABLED
454 API int wrt_permissions_add(const char* widget_id, const char** devcap_list)
456 int ret = PC_OPERATION_SUCCESS;
457 #ifdef WRT_SMACK_ENABLED
458 char* widget_label = NULL;
459 struct smack_accesses* smack = NULL;
462 widget_label = wrt_smack_label(widget_id, NULL);
463 if (widget_label == NULL)
464 return PC_ERR_MEM_OPERATION;
466 if (smack_accesses_new(&smack)) {
467 ret = PC_ERR_MEM_OPERATION;
471 for (i = 0; devcap_list[i] != NULL; ++i) {
474 /* Prepend devcap with "WRT_" */
475 if (asprintf(&perm, "%s_%s", WRT_BASE_DEVCAP, devcap_list[i]) == -1) {
476 ret = PC_ERR_MEM_OPERATION;
480 ret = perm_to_smack(smack, widget_label, perm);
482 if (ret != PC_OPERATION_SUCCESS)
486 if (smack_accesses_apply(smack) != 0) {
487 ret = PC_ERR_INVALID_OPERATION;
492 smack_accesses_free(smack);
494 #endif // WRT_SMACK_ENABLED
498 static int dir_set_smack_r(const char *path, const char* label,
499 enum smack_label_type type, mode_t type_mask)
502 const char* path_argv[] = {path, NULL};
506 ret = PC_ERR_FILE_OPERATION;
508 fts = fts_open((char * const *) path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
512 while ((ftsent = fts_read(fts)) != NULL) {
513 /* Check for error (FTS_ERR) or failed stat(2) (FTS_NS) */
514 if (ftsent->fts_info == FTS_ERR || ftsent->fts_info == FTS_NS)
517 if (ftsent->fts_statp->st_mode & S_IFMT & type_mask)
518 if (smack_lsetlabel(ftsent->fts_path, label, type) != 0)
522 /* If last call to fts_read() set errno, we need to return error. */
524 ret = PC_OPERATION_SUCCESS;
532 API int wrt_set_src_dir(const char* widget_id, const char *path)
534 int ret = PC_OPERATION_SUCCESS;
535 #ifdef WRT_SMACK_ENABLED
536 char* widget_label = NULL;
537 char* src_label_dir = NULL;
538 char* src_label_file = NULL;
540 ret = PC_ERR_MEM_OPERATION;
542 widget_label = wrt_smack_label(widget_id, NULL);
543 if (widget_label == NULL)
546 src_label_dir = wrt_smack_label(widget_id, SMACK_SRC_DIR_SUFFIX);
547 if (src_label_dir == NULL)
550 src_label_file = wrt_smack_label(widget_id, SMACK_SRC_FILE_SUFFIX);
551 if (src_label_file == NULL)
554 /* Set label for directories */
555 ret = dir_set_smack_r(path, src_label_dir, SMACK_LABEL_ACCESS, S_IFDIR);
556 if (ret != PC_OPERATION_SUCCESS)
559 /* Set label for non-directories */
560 ret = dir_set_smack_r(path, src_label_file, SMACK_LABEL_ACCESS, ~S_IFDIR);
565 free(src_label_file);
566 #endif // WRT_SMACK_ENABLED
570 API int wrt_set_data_dir(const char* widget_id, const char *path)
572 int ret = PC_OPERATION_SUCCESS;
573 #ifdef WRT_SMACK_ENABLED
574 char* widget_label = NULL;
575 char* data_label = NULL;
578 ret = PC_ERR_FILE_OPERATION;
579 /* Check whether path exists */
580 if (lstat(path, &st) == 0) {
581 if (!S_ISDIR(st.st_mode))
582 /* Exists, but it's not a directory? */
586 /* Some other error than "no such file or directory" */
588 if (mkdir(path, S_IRWXU) != 0)
589 /* Error while creating the directory */
591 if (chown(path, APP_UID, APP_GID)) {
592 /* Error while setting the directory owner */
600 ret = PC_ERR_MEM_OPERATION;
602 widget_label = wrt_smack_label(widget_id, NULL);
603 if (widget_label == NULL)
606 data_label = wrt_smack_label(widget_id, SMACK_DATA_SUFFIX);
607 if (data_label == NULL)
610 /* Set label for everything inside data path */
611 ret = dir_set_smack_r(path, data_label, SMACK_LABEL_ACCESS, ~0);
612 if (ret != PC_OPERATION_SUCCESS)
615 /* Enable transmute on all directories */
616 ret = dir_set_smack_r(path, "1", SMACK_LABEL_TRANSMUTE, S_IFDIR);
617 if (ret != PC_OPERATION_SUCCESS)
623 #endif // WRT_SMACK_ENABLED
627 #ifdef WRT_SMACK_ENABLED
628 static int set_smack_for_wrt(const char* widget_id)
630 char* widget_label = NULL;
631 char* src_label_file = NULL;
632 char* src_label_dir = NULL;
633 char* data_label = NULL;
634 struct smack_accesses* smack = NULL;
637 ret = PC_ERR_MEM_OPERATION;
639 widget_label = wrt_smack_label(widget_id, NULL);
640 if (widget_label == NULL)
643 src_label_file = wrt_smack_label(widget_id, SMACK_SRC_FILE_SUFFIX);
644 if (src_label_file == NULL)
647 src_label_dir = wrt_smack_label(widget_id, SMACK_SRC_DIR_SUFFIX);
648 if (src_label_file == NULL)
651 data_label = wrt_smack_label(widget_id, SMACK_DATA_SUFFIX);
652 if (data_label == NULL)
655 if (smack_accesses_new(&smack) != 0)
658 ret = wrt_permissions_reset(widget_id);
659 if (ret != PC_OPERATION_SUCCESS)
662 ret = PC_ERR_INVALID_OPERATION;
664 if (smack_set_label_for_self(widget_label) != 0)
667 /* Allow widget to only read and execute it's source directories */
668 if (smack_accesses_add(smack, widget_label, src_label_dir, "rx") != 0)
671 /* Allow widget to only read read it's source files */
672 if (smack_accesses_add(smack, widget_label, src_label_file, "r") != 0)
675 /* Allow widget to do everything with it's data */
677 * FIXME: If a malicious widget finds a way to execute files, it will be
678 * able to execute it's data files, which are fully controlled by the
679 * widget itself. This currently cannot be prevented by SMACK, so other
680 * means must be used.
682 if (smack_accesses_add(smack, widget_label, data_label, "rwxat") != 0)
685 ret = perm_to_smack(smack, widget_label, WRT_BASE_DEVCAP);
686 if (ret != PC_OPERATION_SUCCESS)
689 if (smack_accesses_apply(smack) != 0)
690 ret = PC_ERR_INVALID_OPERATION;
693 smack_accesses_free(smack);
695 free(src_label_file);
701 #endif // WRT_SMACK_ENABLED
703 API char* wrt_widget_id_from_socket(int sockfd)
709 ret = smack_new_label_from_socket(sockfd, &smack_label);
713 if (strncmp(smack_label, SMACK_WRT_LABEL_PREFIX,
714 strlen(SMACK_WRT_LABEL_PREFIX))) {
719 widget_id = strdup(smack_label + strlen(SMACK_WRT_LABEL_PREFIX));
724 API int app_add_permissions(const char* app_id, const char** perm_list)
729 struct smack_accesses *smack = NULL;
732 if (asprintf(&path, SMACK_RULES_DIR "/%s", app_id) == -1) {
733 ret = PC_ERR_MEM_OPERATION;
737 if (smack_accesses_new(&smack)) {
738 ret = PC_ERR_MEM_OPERATION;
742 fd = open(path, O_CREAT, O_RDWR);
744 ret = PC_ERR_FILE_OPERATION;
748 if (flock(fd, LOCK_EX)) {
749 ret = PC_ERR_INVALID_OPERATION;
753 if (smack_accesses_add_from_file(smack, fd)) {
754 ret = PC_ERR_INVALID_OPERATION;
758 /* Rewind the file */
759 if (lseek(fd, 0, SEEK_SET) == -1) {
760 ret = PC_ERR_FILE_OPERATION;
764 for (i = 0; perm_list[i] != NULL; ++i) {
765 ret = perm_to_smack(smack, app_id, perm_list[i]);
766 if (ret != PC_OPERATION_SUCCESS)
770 if (smack_accesses_apply(smack)) {
771 ret = PC_ERR_INVALID_OPERATION;
775 if (smack_accesses_save(smack, fd)) {
776 ret = PC_ERR_FILE_OPERATION;
781 ret = PC_OPERATION_SUCCESS;
786 smack_accesses_free(smack);
792 API int app_revoke_permissions(const char* app_id)
797 struct smack_accesses *smack = NULL;
800 if (asprintf(&path, SMACK_RULES_DIR "/%s", app_id) == -1) {
801 ret = PC_ERR_MEM_OPERATION;
805 if (smack_accesses_new(&smack)) {
806 ret = PC_ERR_MEM_OPERATION;
810 fd = open(path, O_RDONLY);
812 ret = PC_ERR_FILE_OPERATION;
816 if (flock(fd, LOCK_EX | LOCK_NB)) {
817 /* Non-blocking lock request on a file failed. */
818 ret = PC_ERR_INVALID_OPERATION;
823 ret = PC_ERR_INVALID_OPERATION;
827 if (smack_accesses_add_from_file(smack, fd)) {
828 ret = PC_ERR_INVALID_OPERATION;
832 if (smack_accesses_clear(smack)) {
833 ret = PC_ERR_INVALID_OPERATION;
837 if (smack_revoke_subject(app_id)) {
838 ret = PC_ERR_INVALID_OPERATION;
843 ret = PC_OPERATION_SUCCESS;
848 smack_accesses_free(smack);
854 API int app_label_dir(const char* app_id, const char* path)
857 return dir_set_smack_r(path, app_id, SMACK_LABEL_ACCESS, ~0);
859 return PC_OPERATION_SUCCESS;