Correct bug introduced by patch 8ee07d7.
[platform/core/security/libprivilege-control.git] / src / privilege-control.c
1 /*
2  * libprivilege control
3  *
4  * Copyright (c) 2000 - 2012 Samsung Electronics Co., Ltd All Rights Reserved
5  *
6  * Contact: Kidong Kim <kd0228.kim@samsung.com>
7  *
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
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
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.
19  *
20  */
21
22 #define _GNU_SOURCE
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28 #include <pwd.h>
29 #include <grp.h>
30 #include <fts.h>
31 #include <errno.h>
32 #include <math.h>
33 #include <ctype.h>
34 #include <fcntl.h>
35 #include <sys/time.h>
36 #include <sys/stat.h>
37 #include <sys/file.h>
38 #include <sys/smack.h>
39 #include <dlog.h>
40 #include <string.h>
41
42 #include "privilege-control.h"
43
44 #define APP_GID 5000
45 #define APP_UID 5000
46 #define DEVELOPER_GID   5100
47 #define DEVELOPER_UID   5100
48
49 #define APP_USER_NAME   "app"
50 #define DEV_USER_NAME   "developer"
51
52 #define APP_HOME_DIR    TOSTRING(HOMEDIR) "/app"
53 #define DEV_HOME_DIR    TOSTRING(HOMEDIR) "/developer"
54
55 #define APP_GROUP_PATH  TOSTRING(SHAREDIR) "/app_group_list"
56 #define DEV_GROUP_PATH  TOSTRING(SHAREDIR) "/dev_group_list"
57
58 #define SMACK_RULES_DIR  "/etc/smack/accesses.d/"
59
60 #define SMACK_APP_LABEL         "~APP~"
61 #define SMACK_WRT_LABEL_PREFIX  "wrt_widget_"
62 #define SMACK_SRC_FILE_SUFFIX   "_src_file"
63 #define SMACK_SRC_DIR_SUFFIX    "_src_dir"
64 #define SMACK_DATA_SUFFIX       "_data"
65 #define WRT_BASE_DEVCAP         "WRT"
66 #define WRT_CLIENT_PATH         "/usr/bin/wrt-client"
67
68 #ifndef SMACK_ENABLED
69 #undef WRT_SMACK_ENABLED
70 #endif
71
72 #ifdef WRT_SMACK_ENABLED
73 static int set_smack_for_wrt(const char* widget_id);
74 #endif // WRT_SMACK_ENABLED
75
76 #ifdef LOG_TAG
77     #undef LOG_TAG
78 #endif // LOG_TAG
79 #ifndef LOG_TAG
80     #define LOG_TAG "PRIVILEGE_CONTROL"
81 #endif // LOG_TAG
82
83 // conditional_log macro for dlogutil (debug)
84 #ifdef DLOG_ENABLED
85 #define C_LOGD(...) LOGD(__VA_ARGS__)
86 #define C_LOGE(...) LOGE(__VA_ARGS__)
87 #else
88 #define C_LOGD(...) do { } while(0)
89 #define C_LOGE(...) do { } while(0)
90 #endif //DLOG_ENABLED
91
92 typedef enum {
93         PKG_TYPE_WGT,
94         PKG_TYPE_OTHER
95 } pkg_type_t;
96
97 typedef struct {
98         char user_name[10];
99         int uid;
100         int gid;
101         char home_dir[64];
102         char group_list[64];
103 } new_user;
104
105 API int control_privilege(void)
106 {
107         C_LOGD("Enter function: %s", __func__);
108         if(getuid() == APP_UID) // current user is 'app'
109                 return PC_OPERATION_SUCCESS;
110
111         if(set_app_privilege("com.samsung.", NULL, NULL) == PC_OPERATION_SUCCESS)
112                 return PC_OPERATION_SUCCESS;
113         else
114                 return PC_ERR_NOT_PERMITTED;
115 }
116
117 static int set_dac(const char* pkg_name)
118 {
119         C_LOGD("Enter function: %s", __func__);
120         FILE* fp_group = NULL;  // /etc/group
121         uid_t t_uid = -1;               // uid of current process
122         gid_t *glist = NULL;    // group list
123         gid_t temp_gid = -1;    // for group list
124         char buf[10] = {0, };           // contents in group_list file
125         int glist_cnt = 0;              // for group list
126         int result;
127         int i;
128         new_user usr;
129
130         /*
131          * initialize user structure
132          */
133         C_LOGD("initialize user structure");
134         memset(usr.user_name, 0x00, 10);
135         memset(usr.home_dir, 0x00, 64);
136         memset(usr.group_list, 0x00, 64);
137         usr.uid = -1;
138         usr.gid = -1;
139
140         t_uid = getuid();
141         C_LOGD("Current uid is %d", t_uid);
142
143         if(t_uid == 0)  // current user is 'root'
144         {
145                 if(!strncmp(pkg_name, "developer", 9))
146                 {
147                         strncpy(usr.user_name, DEV_USER_NAME, sizeof(usr.user_name));
148                         usr.uid = DEVELOPER_UID;
149                         usr.gid = DEVELOPER_GID;
150                         strncpy(usr.home_dir, DEV_HOME_DIR, sizeof(usr.home_dir));
151                         strncpy(usr.group_list, DEV_GROUP_PATH, sizeof(usr.group_list));
152                 }
153                 else
154                 {
155                         strncpy(usr.user_name, APP_USER_NAME, sizeof(usr.user_name));
156                         usr.uid = APP_UID;
157                         usr.gid = APP_GID;
158                         strncpy(usr.home_dir, APP_HOME_DIR, sizeof(usr.home_dir));
159                         strncpy(usr.group_list, APP_GROUP_PATH, sizeof(usr.group_list));
160                 }
161
162                 /*
163                  * get group information
164                  */
165                 C_LOGD("get group information");
166                 if(!(fp_group = fopen(usr.group_list, "r")))
167                 {
168                         C_LOGE("[ERR] file open error: [%s]\n", usr.group_list);
169                         result = PC_ERR_FILE_OPERATION; // return -1
170                         goto error;
171                 }
172
173                 while(fgets(buf, 10, fp_group) != NULL)
174                 {
175                         errno = 0;
176                         temp_gid = strtoul(buf, 0, 10);
177                         if(errno != 0)  // error occured during strtoul()
178                         {
179                                 C_LOGE("[ERR] cannot change string to integer: [%s]", buf);
180                                 result = PC_ERR_INVALID_OPERATION;
181                                 goto error;
182                         }
183
184                         glist = (gid_t*)realloc(glist, sizeof(gid_t) * (glist_cnt + 1));
185                         if(!glist)
186                         {
187                                 result = PC_ERR_MEM_OPERATION;  // return -2
188                                 C_LOGE("Cannot allocate memory");
189                                 goto error;
190                         }
191                         glist[glist_cnt] = temp_gid;
192                         glist_cnt++;
193                 }
194                 fclose(fp_group);
195                 fp_group = NULL;
196
197                 /*
198                  * setgroups()
199                  */
200                 C_LOGD("Adding process to the following groups:");
201                 for(i=0; i<glist_cnt; ++i) {
202                         C_LOGD("glist [ %d ] = %d", i, glist[i]);
203                 }
204                 C_LOGD("setgroups()");
205                 if(setgroups(glist_cnt, glist) != 0)
206                 {
207                         C_LOGE("[ERR] setgrouops fail\n");
208                         result = PC_ERR_NOT_PERMITTED;  // return -3
209                         goto error;
210                 }
211                 if(glist != NULL)
212                 {
213                         free(glist);
214                         glist = NULL;
215                 }
216
217                 /*
218                  * setuid() & setgid()
219                  */
220                 C_LOGD("setgid( %d ) & setuid( %d )", usr.gid, usr.uid);
221                 if(setgid(usr.gid) != 0)        // fail
222                 {
223                         C_LOGE("[ERR] fail to execute setgid().");
224                         result = PC_ERR_INVALID_OPERATION;
225                         goto error;
226                 }
227                 if(setuid(usr.uid) != 0)        // fail
228                 {
229                         C_LOGE("[ERR] fail to execute setuid().");
230                         result = PC_ERR_INVALID_OPERATION;
231                         goto error;
232                 }
233
234                 C_LOGD("setenv(): USER = %s, HOME = %s", usr.user_name, usr.home_dir);
235                 if(setenv("USER", usr.user_name, 1) != 0)       //fail
236                 {
237                         C_LOGE("[ERR] fail to execute setenv() [USER].");
238                         result = PC_ERR_INVALID_OPERATION;
239                         goto error;
240                 }
241                 if(setenv("HOME", usr.home_dir, 1) != 0)        // fail
242                 {
243                         C_LOGE("[ERR] fail to execute setenv() [HOME].");
244                         result = PC_ERR_INVALID_OPERATION;
245                         goto error;
246                 }
247         }
248         else    // current user is not only 'root' but 'app'
249         {
250                 C_LOGE("[ERR] current user is NOT root\n");
251                 result = PC_ERR_NOT_PERMITTED;  // return -3
252                 goto error;
253         }
254
255         result = PC_OPERATION_SUCCESS;
256
257 error:
258     if(fp_group != NULL)
259         fclose(fp_group);
260         if(glist != NULL)
261                 free(glist);
262
263         return result;
264 }
265
266 #ifdef SMACK_ENABLED
267 /**
268  * Set process SMACK label from EXEC label of a file.
269  * This function is emulating EXEC label behaviour of SMACK for programs
270  * run by dlopen/dlsym instead of execv.
271  *
272  * @param path file path to take label from
273  * @return PC_OPERATION_SUCCESS on success, PC_ERR_* on error
274  */
275 static int set_smack_from_binary(const char* path)
276 {
277         C_LOGD("Enter function: %s", __func__);
278         int ret;
279         char* label;
280
281         C_LOGD("Path: %s", path);
282         ret = smack_lgetlabel(path, &label, SMACK_LABEL_EXEC);
283         if (ret != 0) {
284                 C_LOGE("smack_lgetlabel returned PC_ERR_INVALID_OPERATION");
285                 return PC_ERR_INVALID_OPERATION;
286         }
287
288         if (label == NULL) {
289                 /* No label to set, just return with success */
290                 C_LOGD("No label to set, just return with success");
291                 ret = PC_OPERATION_SUCCESS;
292         }
293         else {
294                 ret = smack_set_label_for_self(label);
295                 C_LOGD("label = %s", label);
296                 C_LOGD("smack_set_label_for_self returned %d", ret);
297         }
298
299         free(label);
300         return ret;
301 }
302
303 static int is_widget(const char* path)
304 {
305         C_LOGD("Enter function: %s", __func__);
306         char buf[sizeof(WRT_CLIENT_PATH)];
307         int ret;
308
309         ret = readlink(path, buf, sizeof(WRT_CLIENT_PATH));
310         if (ret == -1)
311                 C_LOGD("readlink(%s) returned error: %s. Assuming that app is not a widget", path, strerror(errno));
312         else if (ret == sizeof(WRT_CLIENT_PATH))
313                 C_LOGD("%s is not a widget", path);
314         if (ret == -1 || ret == sizeof(WRT_CLIENT_PATH))
315                 return 0;
316         buf[ret] = '\0';
317         C_LOGD("buf = %s", buf);
318
319         ret = !strcmp(WRT_CLIENT_PATH, buf);
320         C_LOGD("%s is %s widget", path, ret ? "a" : "not a");
321         return (ret);
322 }
323
324 /**
325  * Partially verify, that the type given for app is correct.
326  * This function will use some heuristics to check whether the app type is right.
327  * It is intended for security hardening to catch privilege setting for the
328  * app type not corresponding to the actual binary.
329  * Beware - when it detects an anomaly, the whole process will be terminated.
330  *
331  * @param type claimed application type
332  * @param path file path to executable
333  * @return return void on success, terminate the process on error
334  */
335 static pkg_type_t verify_app_type(const char* type, const char* path)
336 {
337         C_LOGD("Enter function: %s", __func__);
338         /* TODO: this should actually be treated as error, but until the old
339          * set_privilege API is removed, it must be ignored */
340         if (path == NULL) {
341                 C_LOGD("PKG_TYPE_OTHER");
342                 return PKG_TYPE_OTHER; /* good */
343         }
344
345         if (is_widget(path)) {
346                 if (!strcmp(type, "wgt")) {
347                         C_LOGD("PKG_TYPE_WGT");
348                         return PKG_TYPE_WGT; /* good */
349                 }
350         } else {
351                 if (type == NULL || strcmp(type, "wgt")){
352                         C_LOGD("PKG_TYPE_OTHER");
353                         return PKG_TYPE_OTHER; /* good */
354                 }
355         }
356
357         /* bad */
358         C_LOGE("EXIT_FAILURE");
359         exit(EXIT_FAILURE);
360 }
361
362 static const char* parse_widget_id(const char* path)
363 {
364         C_LOGD("Enter function: %s", __func__);
365         const char* basename = strrchr(path, '/');
366
367         if (basename == NULL)
368                 basename = path;
369         else
370                 ++basename;
371
372         C_LOGD("return widget id: %s", basename);
373         return basename;
374 }
375 #endif // SMACK_ENABLED
376
377 API int set_app_privilege(const char* name, const char* type, const char* path)
378 {
379         C_LOGD("Enter function: %s", __func__);
380         C_LOGD("Function params: name = %s, type = %s, path = %s", name, type, path);
381 #ifdef SMACK_ENABLED
382         const char* widget_id;
383         int ret = PC_OPERATION_SUCCESS;
384
385         switch(verify_app_type(type, path)) {
386         case PKG_TYPE_WGT:
387                 widget_id = parse_widget_id(path);
388                 if (widget_id == NULL) {
389                         C_LOGE("PC_ERR_INVALID_PARAM");
390                         ret = PC_ERR_INVALID_PARAM;
391                 }
392 #ifdef WRT_SMACK_ENABLED
393                 else
394                         ret = set_smack_for_wrt(widget_id);
395 #endif
396                 break;
397         case PKG_TYPE_OTHER:
398                 if (path != NULL)
399                 ret = set_smack_from_binary(path);
400         }
401
402         if (ret != PC_OPERATION_SUCCESS)
403                 return ret;
404 #endif // SMACK_ENABLED
405
406         return set_dac(name);
407 }
408
409 API int set_privilege(const char* pkg_name)
410 {
411         C_LOGD("Enter function: %s", __func__);
412         return set_app_privilege(pkg_name, NULL, NULL);
413 }
414
415 API int wrt_set_privilege(const char* widget_id)
416 {
417         C_LOGD("Enter function: %s", __func__);
418 #ifdef WRT_SMACK_ENABLED
419         int ret;
420
421         ret = set_smack_for_wrt(widget_id);
422         if (ret != PC_OPERATION_SUCCESS) {
423                 C_LOGE("set_smack_for_wrt returned error");
424                 return ret;
425         }
426 #endif // WRT_SMACK_ENABLED
427
428         return set_dac("com.samsung.");
429 }
430
431 #ifdef WRT_SMACK_ENABLED
432 static inline char* wrt_smack_label(const char* widget_id, const char* suffix)
433 {
434         C_LOGD("Enter function: %s", __func__);
435         int ret;
436         char* label;
437
438         ret = asprintf(&label, "%s%s%s", SMACK_WRT_LABEL_PREFIX, widget_id,
439                 (suffix ? suffix : ""));
440
441         if (ret == -1) {
442                 C_LOGE("asprintf failed");
443                 return NULL;
444         }
445
446         if (strlen(label) > SMACK_LABEL_LEN) {
447                 C_LOGE("strlen(label) [%s] > SMACK_LABEL_LEN", label);
448                 free(label);
449                 return NULL;
450         }
451
452         return label;
453 }
454 #endif // WRT_SMACK_ENABLED
455
456 static inline int perm_to_smack(struct smack_accesses* smack, const char* app_label, const char* perm)
457 {
458         C_LOGD("Enter function: %s", __func__);
459         int ret = PC_OPERATION_SUCCESS;
460         char* path = NULL;
461         char* format_string = NULL;
462         FILE* file = NULL;
463         char smack_subject[SMACK_LABEL_LEN + 1];
464         char smack_object[SMACK_LABEL_LEN + 1];
465         char smack_accesses[10];
466
467         if (asprintf(&path, TOSTRING(SHAREDIR) "/%s.smack", perm) == -1) {
468                 C_LOGE("asprintf failed");
469                 ret = PC_ERR_MEM_OPERATION;
470                 goto out;
471         }
472
473         if (asprintf(&format_string,"%%%ds %%%ds %%%ds\n",
474                         SMACK_LABEL_LEN, SMACK_LABEL_LEN, sizeof(smack_accesses)) == -1) {
475                 C_LOGE("asprintf failed");
476                 ret = PC_ERR_MEM_OPERATION;
477                 goto out;
478         }
479
480         file = fopen(path, "r");
481         C_LOGD("path = %s", path);
482         if (file == NULL) {
483                 C_LOGE("fopen failed");
484                 ret = PC_ERR_FILE_OPERATION;
485                 goto out;
486         }
487
488         while (1) {
489                 if (fscanf(file, format_string, smack_subject, smack_object, smack_accesses) != 3) {
490                         C_LOGE("fscanf failed");
491                         goto out;
492                 }
493
494                 if (!strcmp(smack_subject, SMACK_APP_LABEL))
495                         strcpy(smack_subject, app_label);
496
497                 if (!strcmp(smack_object, SMACK_APP_LABEL))
498                         strcpy(smack_object, app_label);
499
500                 C_LOGD("smack_accesses_add_modify (subject: %s, object: %s, access: %s)", smack_subject, smack_object, smack_accesses);
501                 if (smack_accesses_add_modify(smack, smack_subject, smack_object, smack_accesses, "") != 0) {
502                         C_LOGE("smack_accesses_add_modify failed");
503                         ret = PC_ERR_INVALID_OPERATION;
504                         goto out;
505                 }
506         }
507
508 out:
509         free(path);
510         free(format_string);
511         if (file != NULL)
512                 fclose(file);
513         return ret;
514 }
515
516 API int wrt_permissions_reset(const char* widget_id)
517 {
518         C_LOGD("Enter function: %s", __func__);
519         int ret = PC_OPERATION_SUCCESS;
520 #ifdef WRT_SMACK_ENABLED
521         char* label = NULL;
522
523         label = wrt_smack_label(widget_id, NULL);
524         if (label == NULL) {
525                 C_LOGE("wrt_smack_label failed");
526                 return PC_ERR_MEM_OPERATION;
527         }
528
529         if (smack_revoke_subject(label)) {
530                 C_LOGE("smack_revoke_subject failed");
531                 ret = PC_ERR_INVALID_OPERATION;
532         }
533
534         free(label);
535 #endif // WRT_SMACK_ENABLED
536         return ret;
537 }
538
539 API int wrt_permissions_add(const char* widget_id, const char** devcap_list)
540 {
541         C_LOGD("Enter function: %s", __func__);
542         int ret = PC_OPERATION_SUCCESS;
543 #ifdef WRT_SMACK_ENABLED
544         char* widget_label = NULL;
545         struct smack_accesses* smack = NULL;
546         int i;
547
548         widget_label = wrt_smack_label(widget_id, NULL);
549         if (widget_label == NULL) {
550                 C_LOGE("wrt_smack_label failed");
551                 return PC_ERR_MEM_OPERATION;
552         }
553
554         if (smack_accesses_new(&smack)) {
555                 C_LOGE("smack_accesses_new failed");
556                 ret = PC_ERR_MEM_OPERATION;
557                 goto out;
558         }
559
560         for (i = 0; devcap_list[i] != NULL; ++i) {
561                 char* perm = NULL;
562
563                 /* Prepend devcap with "WRT_" */
564                 if (asprintf(&perm, "%s_%s", WRT_BASE_DEVCAP, devcap_list[i]) == -1) {
565                         C_LOGE("asprintf failed");
566                         ret = PC_ERR_MEM_OPERATION;
567                         goto out;
568                 }
569
570                 C_LOGD("Adding permission %s", perm);
571                 ret = perm_to_smack(smack, widget_label, perm);
572                 free(perm);
573                 if (ret != PC_OPERATION_SUCCESS) {
574                         C_LOGE("perm_to_smack failed (%d)", ret);
575                         goto out;
576                 }
577         }
578
579         if (smack_accesses_apply(smack) != 0) {
580                 C_LOGE("smack_accesses_apply failed");
581                 ret = PC_ERR_INVALID_OPERATION;
582                 goto out;
583         }
584
585 out:
586         smack_accesses_free(smack);
587         free(widget_label);
588 #endif // WRT_SMACK_ENABLED
589         return ret;
590 }
591
592 static int dir_set_smack_r(const char *path, const char* label,
593                 enum smack_label_type type, mode_t type_mask)
594 {
595         C_LOGD("Enter function: %s", __func__);
596         int ret;
597         const char* path_argv[] = {path, NULL};
598         FTS *fts = NULL;
599         FTSENT *ftsent;
600
601         ret = PC_ERR_FILE_OPERATION;
602
603         fts = fts_open((char * const *) path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
604         if (fts == NULL) {
605                 C_LOGE("fts_open failed");
606                 goto out;
607         }
608
609         while ((ftsent = fts_read(fts)) != NULL) {
610                 /* Check for error (FTS_ERR) or failed stat(2) (FTS_NS) */
611                 if (ftsent->fts_info == FTS_ERR || ftsent->fts_info == FTS_NS) {
612                         C_LOGE("FTS_ERR error or failed stat(2) (FTS_NS)");
613                         goto out;
614                 }
615
616                 if (ftsent->fts_statp->st_mode & type_mask) {
617                         C_LOGD("smack_lsetlabel (label: %s (type: %d), path: %s)", label, type, ftsent->fts_path);
618                         if (smack_lsetlabel(ftsent->fts_path, label, type) != 0) {
619                                 C_LOGE("smack_lsetlabel failed");
620                                 goto out;
621                         }
622                 }
623         }
624
625         /* If last call to fts_read() set errno, we need to return error. */
626         if (errno == 0)
627                 ret = PC_OPERATION_SUCCESS;
628         else
629                 C_LOGE("Last errno: %s", strerror(errno));
630
631 out:
632         if (fts != NULL)
633                 fts_close(fts);
634         return ret;
635 }
636
637 API int wrt_set_src_dir(const char* widget_id, const char *path)
638 {
639         C_LOGD("Enter function: %s", __func__);
640         C_LOGD("Path: %s", path);
641         int ret = PC_OPERATION_SUCCESS;
642 #ifdef WRT_SMACK_ENABLED
643         char* src_label_dir = NULL;
644         char* src_label_file = NULL;
645
646         ret = PC_ERR_MEM_OPERATION;
647
648         src_label_dir = wrt_smack_label(widget_id, SMACK_SRC_DIR_SUFFIX);
649         if (src_label_dir == NULL) {
650                 C_LOGE("src_label_dir is NULL");
651                 goto out;
652         }
653
654         src_label_file = wrt_smack_label(widget_id, SMACK_SRC_FILE_SUFFIX);
655         if (src_label_file == NULL) {
656                 C_LOGE("src_label_file in NULL");
657                 goto out;
658         }
659
660         /* Set label for directories */
661         ret = dir_set_smack_r(path, src_label_dir, SMACK_LABEL_ACCESS, S_IFDIR);
662         if (ret != PC_OPERATION_SUCCESS) {
663                 C_LOGE("dir_set_smack_r failed");
664                 goto out;
665         }
666
667         /* Set label for non-directories */
668         ret = dir_set_smack_r(path, src_label_file, SMACK_LABEL_ACCESS, ~S_IFDIR);
669
670 out:
671         free(src_label_dir);
672         free(src_label_file);
673 #endif // WRT_SMACK_ENABLED
674         return ret;
675 }
676
677 API int wrt_set_data_dir(const char* widget_id, const char *path)
678 {
679         C_LOGD("Enter function: %s", __func__);
680         C_LOGD("Path: %s", path);
681         int ret = PC_OPERATION_SUCCESS;
682 #ifdef WRT_SMACK_ENABLED
683         char* data_label = NULL;
684         struct stat st;
685
686         ret = PC_ERR_FILE_OPERATION;
687         /* Check whether path exists */
688         if (lstat(path, &st) == 0) {
689                 if (!S_ISDIR(st.st_mode)) {
690                         /* Exists, but it's not a directory? */
691                         C_LOGE("Exists, but it's not a directory?");
692                         goto out;
693                 }
694         } else {
695                 if (errno != ENOENT) {
696                         /* Some other error than "no such file or directory" */
697                         C_LOGE("Other error: %s", strerror(errno));
698                         goto out;
699                 }
700                 if (mkdir(path, S_IRWXU) != 0) {
701                         /* Error while creating the directory */
702                         C_LOGE("Error while creating the directory");
703                         goto out;
704                 }
705                 if (chown(path, APP_UID, APP_GID)) {
706                         /* Error while setting the directory owner */
707                         C_LOGE("Error while setting the directory owner");
708                         int e = errno;
709                         rmdir(path);
710                         errno = e;
711                         goto out;
712                 }
713         }
714
715         ret = PC_ERR_MEM_OPERATION;
716
717         data_label = wrt_smack_label(widget_id, SMACK_DATA_SUFFIX);
718         if (data_label == NULL) {
719                 C_LOGE("data_label is NULL");
720                 goto out;
721         }
722
723         /* Set label for everything inside data path */
724         ret = dir_set_smack_r(path, data_label, SMACK_LABEL_ACCESS, ~0);
725         if (ret != PC_OPERATION_SUCCESS) {
726                 C_LOGE("dir_set_smack_r failed");
727                 goto out;
728         }
729
730         /* Enable transmute on all directories */
731         ret = dir_set_smack_r(path, "1", SMACK_LABEL_TRANSMUTE, S_IFDIR);
732         if (ret != PC_OPERATION_SUCCESS) {
733                 C_LOGE("dir_set_smack_r failed");
734                 goto out;
735         }
736
737 out:
738         free(data_label);
739 #endif // WRT_SMACK_ENABLED
740         return ret;
741 }
742
743 #ifdef WRT_SMACK_ENABLED
744 static int set_smack_for_wrt(const char* widget_id)
745 {
746         C_LOGD("Enter function: %s", __func__);
747         char* widget_label = NULL;
748         char* src_label_file = NULL;
749         char* src_label_dir = NULL;
750         char* data_label = NULL;
751         struct smack_accesses* smack = NULL;
752         int ret;
753
754         ret = PC_ERR_MEM_OPERATION;
755
756         widget_label = wrt_smack_label(widget_id, NULL);
757         C_LOGD("widget id: %s", widget_id);
758         if (widget_label == NULL) {
759                 C_LOGE("widget_label is NULL");
760                 goto out;
761         }
762
763         src_label_file = wrt_smack_label(widget_id, SMACK_SRC_FILE_SUFFIX);
764         if (src_label_file == NULL) {
765                 C_LOGE("src_label_file is NULL");
766                 goto out;
767         }
768
769         src_label_dir = wrt_smack_label(widget_id, SMACK_SRC_DIR_SUFFIX);
770         if (src_label_file == NULL) {
771                 C_LOGE("src_label_file is NULL");
772                 goto out;
773         }
774
775         data_label = wrt_smack_label(widget_id, SMACK_DATA_SUFFIX);
776         if (data_label == NULL) {
777                 C_LOGE("data_label is NULL");
778                 goto out;
779         }
780
781         if (smack_accesses_new(&smack) != 0) {
782                 C_LOGE("smack_accesses_new failed");
783                 goto out;
784         }
785
786         ret = wrt_permissions_reset(widget_id);
787         if (ret != PC_OPERATION_SUCCESS) {
788                 C_LOGE("wrt_permissions_reset failed");
789                 goto out;
790         }
791
792         ret = PC_ERR_INVALID_OPERATION;
793
794         if (smack_set_label_for_self(widget_label) != 0) {
795                 C_LOGE("smack_set_label_for_self failed");
796                 goto out;
797         }
798
799         /* Allow widget to only read and execute it's source directories */
800         C_LOGD("Adding implicit Smack rule: %s %s %s", widget_label, src_label_dir, "rx");
801         if (smack_accesses_add(smack, widget_label, src_label_dir, "rx") != 0) {
802                 C_LOGE("smack_accesses_add failed (rx)");
803                 goto out;
804         }
805
806         /* Allow widget to only read read it's source files */
807         C_LOGD("Adding implicit Smack rule: %s %s %s", widget_label, src_label_file, "r");
808         if (smack_accesses_add(smack, widget_label, src_label_file, "r") != 0) {
809                 C_LOGE("smack_accesses_add failed (r)");
810                 goto out;
811         }
812
813         /* Allow widget to do everything with it's data */
814         /*
815          * FIXME: If a malicious widget finds a way to execute files, it will be
816          * able to execute it's data files, which are fully controlled by the
817          * widget itself. This currently cannot be prevented by SMACK, so other
818          * means must be used.
819          */
820         C_LOGD("Adding implicit Smack rule: %s %s %s", widget_label, data_label, "rwxat");
821         if (smack_accesses_add(smack, widget_label, data_label, "rwxat") != 0) {
822                 C_LOGE("smack_accesses_add failed (rwxat)");
823                 goto out;
824         }
825
826         C_LOGD("Adding Smack rules from pseudo-devcap %s", WRT_BASE_DEVCAP);
827         ret = perm_to_smack(smack, widget_label, WRT_BASE_DEVCAP);
828         if (ret != PC_OPERATION_SUCCESS) {
829                 C_LOGE("perm_to_smack failed");
830                 goto out;
831         }
832
833         if (smack_accesses_apply(smack) != 0) {
834                 C_LOGE("smack_accesses_apply failed");
835                 ret = PC_ERR_INVALID_OPERATION;
836         }
837
838 out:
839         smack_accesses_free(smack);
840         free(widget_label);
841         free(src_label_file);
842         free(src_label_dir);
843         free(data_label);
844
845         return ret;
846 }
847 #endif // WRT_SMACK_ENABLED
848
849 API char* wrt_widget_id_from_socket(int sockfd)
850 {
851         C_LOGD("Enter function: %s", __func__);
852         char* smack_label;
853         char* widget_id;
854         int ret;
855
856         ret = smack_new_label_from_socket(sockfd, &smack_label);
857         if (ret != 0) {
858                 C_LOGE("smack_new_label_from_socket failed");
859                 return NULL;
860         }
861
862         if (strncmp(smack_label, SMACK_WRT_LABEL_PREFIX,
863                     strlen(SMACK_WRT_LABEL_PREFIX))) {
864                 free(smack_label);
865                 return NULL;
866         }
867
868         widget_id = strdup(smack_label + strlen(SMACK_WRT_LABEL_PREFIX));
869         free(smack_label);
870         C_LOGD("widget id: %s", widget_id);
871         return widget_id;
872 }
873
874
875 static int load_smack_from_file(const char* app_id, struct smack_accesses** smack, int *fd)
876 {
877         C_LOGD("Enter function: %s", __func__);
878
879         char* path = NULL;
880         int ret;
881
882         if (asprintf(&path, SMACK_RULES_DIR "/%s", app_id) == -1) {
883                 ret = PC_ERR_MEM_OPERATION;
884                 C_LOGE("asprintf failed");
885                 goto out;
886         }
887
888         if (smack_accesses_new(smack)) {
889                 ret = PC_ERR_MEM_OPERATION;
890                 C_LOGE("smack_accesses_new failed");
891                 goto out;
892         }
893
894         *fd = open(path, O_CREAT|O_RDWR, 0644);
895         if (*fd == -1) {
896                 ret = PC_ERR_FILE_OPERATION;
897                 C_LOGE("file open failed");
898                 goto out;
899         }
900
901         if (flock(*fd, LOCK_EX)) {
902                 ret = PC_ERR_INVALID_OPERATION;
903                 C_LOGE("flock failed");
904                 goto out;
905         }
906
907         if (smack_accesses_add_from_file(*smack, *fd)) {
908                 ret = PC_ERR_INVALID_OPERATION;
909                 C_LOGE("smack_accesses_add_from_file failed");
910                 goto out;
911         }
912
913         /* Rewind the file */
914         if (lseek(*fd, 0, SEEK_SET) == -1) {
915                 ret = PC_ERR_FILE_OPERATION;
916                 C_LOGE("lseek failed");
917                 goto out;
918         }
919
920         ret = PC_OPERATION_SUCCESS;
921
922 out:
923         free(path);
924
925         return ret;
926 }
927
928 static int save_smack_to_file(struct smack_accesses *smack, int fd)
929 {
930         if (smack_accesses_apply(smack)) {
931                 C_LOGE("smack_accesses_apply failed");
932                 return PC_ERR_INVALID_OPERATION;
933         }
934
935         if (smack_accesses_save(smack, fd)) {
936                 C_LOGE("smack_accesses_save failed");
937                 return PC_ERR_INVALID_OPERATION;
938         }
939
940         return PC_OPERATION_SUCCESS;
941 }
942
943
944 API int app_add_permissions(const char* app_id, const char** perm_list)
945 {
946         C_LOGD("Enter function: %s", __func__);
947         char* path = NULL;
948         int i, ret;
949         int fd = -1;
950         struct smack_accesses *smack = NULL;
951
952 #ifdef SMACK_ENABLED
953
954         ret = load_smack_from_file(app_id, &smack, &fd);
955         if (ret != PC_OPERATION_SUCCESS) {
956                 C_LOGE("load_smack_from_file failed");
957                 goto out;
958         }
959         for (i = 0; perm_list[i] != NULL; ++i) {
960                 ret = perm_to_smack(smack, app_id, perm_list[i]);
961                 C_LOGD("perm_to_smack params: app_id: %s, perm_list[%d]: %s", app_id, i, perm_list[i]);
962                 if (ret != PC_OPERATION_SUCCESS){
963                         C_LOGE("perm_to_smack failed");
964                         goto out;
965                 }
966         }
967
968         ret = save_smack_to_file(smack, fd);
969         if(ret != PC_OPERATION_SUCCESS){
970                 C_LOGE("save_smack_to_file failed");
971                 goto out;
972         }
973 #endif
974
975         ret = PC_OPERATION_SUCCESS;
976 out:
977         if (fd != -1)
978                 close(fd);
979         if (smack != NULL)
980                 smack_accesses_free(smack);
981         free(path);
982
983         return ret;
984 }
985
986 API int app_revoke_permissions(const char* app_id)
987 {
988         C_LOGD("Enter function: %s", __func__);
989         char* path = NULL;
990         int ret;
991         int fd = -1;
992         struct smack_accesses *smack = NULL;
993
994 #ifdef SMACK_ENABLED
995         if (asprintf(&path, SMACK_RULES_DIR "/%s", app_id) == -1) {
996                 ret = PC_ERR_MEM_OPERATION;
997                 C_LOGE("asprintf failed");
998                 goto out;
999         }
1000
1001         if (smack_accesses_new(&smack)) {
1002                 ret = PC_ERR_MEM_OPERATION;
1003                 C_LOGE("smack_accesses_new failed");
1004                 goto out;
1005         }
1006
1007         fd = open(path, O_RDONLY);
1008         if (fd == -1) {
1009                 ret = PC_ERR_FILE_OPERATION;
1010                 C_LOGE("file open failed");
1011                 goto out;
1012         }
1013
1014         if (flock(fd, LOCK_EX | LOCK_NB)) {
1015                 /* Non-blocking lock request on a file failed. */
1016                 ret = PC_ERR_INVALID_OPERATION;
1017                 C_LOGE("flock failed");
1018                 goto out;
1019         }
1020
1021         if (unlink(path)) {
1022                 ret = PC_ERR_INVALID_OPERATION;
1023                 C_LOGE("unlink failed");
1024                 goto out;
1025         }
1026
1027         if (smack_accesses_add_from_file(smack, fd)) {
1028                 ret = PC_ERR_INVALID_OPERATION;
1029                 C_LOGE("smack_accesses_add_from_file failed");
1030                 goto out;
1031         }
1032
1033         if (smack_accesses_clear(smack)) {
1034                 ret = PC_ERR_INVALID_OPERATION;
1035                 C_LOGE("smack_accesses_clear failed");
1036                 goto out;
1037         }
1038
1039         if (smack_revoke_subject(app_id)) {
1040                 ret = PC_ERR_INVALID_OPERATION;
1041                 C_LOGE("smack_revoke_subject failed");
1042                 goto out;
1043         }
1044 #endif
1045
1046         ret = PC_OPERATION_SUCCESS;
1047 out:
1048         if (fd != -1)
1049                 close(fd);
1050         if (smack != NULL)
1051                 smack_accesses_free(smack);
1052         free(path);
1053
1054         return ret;
1055 }
1056
1057 API int app_label_dir(const char* label, const char* path)
1058 {
1059         C_LOGD("Enter function: %s", __func__);
1060 #ifdef SMACK_ENABLED
1061
1062         int ret = PC_OPERATION_SUCCESS;
1063
1064         //setting access label on everything in given directory and below
1065         ret = dir_set_smack_r(path, label, SMACK_LABEL_ACCESS, ~0);
1066         if (PC_OPERATION_SUCCESS != ret)
1067                 return ret;
1068
1069         //setting execute label for everything with permission to execute
1070         ret = dir_set_smack_r(path, label, SMACK_LABEL_EXEC, S_IXUSR);
1071         if (PC_OPERATION_SUCCESS != ret)
1072                 return ret;
1073
1074         //removing execute label from directories
1075         ret = dir_set_smack_r(path, "", SMACK_LABEL_EXEC, S_IFMT & ~S_IFREG);
1076
1077         return ret;
1078 #else
1079         return PC_OPERATION_SUCCESS;
1080 #endif
1081 }
1082
1083 API int app_label_shared_dir(const char* app_label, const char* shared_label, const char* path)
1084 {
1085         C_LOGD("Enter function: %s", __func__);
1086 #ifdef SMACK_ENABLED
1087         int ret;
1088         int fd = -1;
1089         struct smack_accesses *smack = NULL;
1090
1091
1092         //setting label on everything in given directory and below
1093         ret = dir_set_smack_r(path, shared_label, SMACK_LABEL_ACCESS, ~0);
1094         if(ret != PC_OPERATION_SUCCESS){
1095                 C_LOGE("dir_set_smakc_r failed");
1096                 goto out;
1097         }
1098
1099         //setting transmute on dir
1100         ret = dir_set_smack_r(path, "1", SMACK_LABEL_TRANSMUTE, S_IFDIR);
1101         if (ret != PC_OPERATION_SUCCESS) {
1102                 C_LOGE("dir_set_smakc_r failed");
1103                 goto out;
1104         }
1105
1106         ret = load_smack_from_file(app_label, &smack, &fd);
1107         if (ret != PC_OPERATION_SUCCESS) {
1108                 C_LOGE("load_smack_from_file failed");
1109                 goto out;
1110         }
1111
1112         //setting access rule for application
1113         if (smack_accesses_add(smack, app_label,shared_label, "wrxat") == -1) {
1114                 C_LOGE("smack_accesses_add failed");
1115                 goto out;
1116         }
1117
1118         ret = save_smack_to_file(smack, fd);
1119         if (ret != PC_OPERATION_SUCCESS) {
1120                 C_LOGE("save_smack_to_file failed");
1121                 goto out;
1122         }
1123
1124         ret = PC_OPERATION_SUCCESS;
1125 out:
1126         if (fd != -1)
1127                 close(fd);
1128         if (smack != NULL)
1129                 smack_accesses_free(smack);
1130         return ret;
1131 #else
1132         return PC_OPERATION_SUCCESS;
1133 #endif
1134 }
1135
1136