a9a6a99f3347a9be9f5f8dcb8a70c64a047e14f7
[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                 C_LOGD("smack_lsetlabel (label: %s (type: %d), path: %s)", label, type, ftsent->fts_path);
617                 if (ftsent->fts_statp->st_mode & S_IFMT & type_mask)
618                         if (smack_lsetlabel(ftsent->fts_path, label, type) != 0) {
619                                 C_LOGE("smack_lsetlabel failed");
620                                 goto out;
621                         }
622         }
623
624         /* If last call to fts_read() set errno, we need to return error. */
625         if (errno == 0)
626                 ret = PC_OPERATION_SUCCESS;
627         else
628                 C_LOGE("Last errno: %s", strerror(errno));
629
630 out:
631         if (fts != NULL)
632                 fts_close(fts);
633         return ret;
634 }
635
636 API int wrt_set_src_dir(const char* widget_id, const char *path)
637 {
638         C_LOGD("Enter function: %s", __func__);
639         C_LOGD("Path: %s", path);
640         int ret = PC_OPERATION_SUCCESS;
641 #ifdef WRT_SMACK_ENABLED
642         char* src_label_dir = NULL;
643         char* src_label_file = NULL;
644
645         ret = PC_ERR_MEM_OPERATION;
646
647         src_label_dir = wrt_smack_label(widget_id, SMACK_SRC_DIR_SUFFIX);
648         if (src_label_dir == NULL) {
649                 C_LOGE("src_label_dir is NULL");
650                 goto out;
651         }
652
653         src_label_file = wrt_smack_label(widget_id, SMACK_SRC_FILE_SUFFIX);
654         if (src_label_file == NULL) {
655                 C_LOGE("src_label_file in NULL");
656                 goto out;
657         }
658
659         /* Set label for directories */
660         ret = dir_set_smack_r(path, src_label_dir, SMACK_LABEL_ACCESS, S_IFDIR);
661         if (ret != PC_OPERATION_SUCCESS) {
662                 C_LOGE("dir_set_smack_r failed");
663                 goto out;
664         }
665
666         /* Set label for non-directories */
667         ret = dir_set_smack_r(path, src_label_file, SMACK_LABEL_ACCESS, ~S_IFDIR);
668
669 out:
670         free(src_label_dir);
671         free(src_label_file);
672 #endif // WRT_SMACK_ENABLED
673         return ret;
674 }
675
676 API int wrt_set_data_dir(const char* widget_id, const char *path)
677 {
678         C_LOGD("Enter function: %s", __func__);
679         C_LOGD("Path: %s", path);
680         int ret = PC_OPERATION_SUCCESS;
681 #ifdef WRT_SMACK_ENABLED
682         char* data_label = NULL;
683         struct stat st;
684
685         ret = PC_ERR_FILE_OPERATION;
686         /* Check whether path exists */
687         if (lstat(path, &st) == 0) {
688                 if (!S_ISDIR(st.st_mode)) {
689                         /* Exists, but it's not a directory? */
690                         C_LOGE("Exists, but it's not a directory?");
691                         goto out;
692                 }
693         } else {
694                 if (errno != ENOENT) {
695                         /* Some other error than "no such file or directory" */
696                         C_LOGE("Other error: %s", strerror(errno));
697                         goto out;
698                 }
699                 if (mkdir(path, S_IRWXU) != 0) {
700                         /* Error while creating the directory */
701                         C_LOGE("Error while creating the directory");
702                         goto out;
703                 }
704                 if (chown(path, APP_UID, APP_GID)) {
705                         /* Error while setting the directory owner */
706                         C_LOGE("Error while setting the directory owner");
707                         int e = errno;
708                         rmdir(path);
709                         errno = e;
710                         goto out;
711                 }
712         }
713
714         ret = PC_ERR_MEM_OPERATION;
715
716         data_label = wrt_smack_label(widget_id, SMACK_DATA_SUFFIX);
717         if (data_label == NULL) {
718                 C_LOGE("data_label is NULL");
719                 goto out;
720         }
721
722         /* Set label for everything inside data path */
723         ret = dir_set_smack_r(path, data_label, SMACK_LABEL_ACCESS, ~0);
724         if (ret != PC_OPERATION_SUCCESS) {
725                 C_LOGE("dir_set_smack_r failed");
726                 goto out;
727         }
728
729         /* Enable transmute on all directories */
730         ret = dir_set_smack_r(path, "1", SMACK_LABEL_TRANSMUTE, S_IFDIR);
731         if (ret != PC_OPERATION_SUCCESS) {
732                 C_LOGE("dir_set_smack_r failed");
733                 goto out;
734         }
735
736 out:
737         free(data_label);
738 #endif // WRT_SMACK_ENABLED
739         return ret;
740 }
741
742 #ifdef WRT_SMACK_ENABLED
743 static int set_smack_for_wrt(const char* widget_id)
744 {
745         C_LOGD("Enter function: %s", __func__);
746         char* widget_label = NULL;
747         char* src_label_file = NULL;
748         char* src_label_dir = NULL;
749         char* data_label = NULL;
750         struct smack_accesses* smack = NULL;
751         int ret;
752
753         ret = PC_ERR_MEM_OPERATION;
754
755         widget_label = wrt_smack_label(widget_id, NULL);
756         C_LOGD("widget id: %s", widget_id);
757         if (widget_label == NULL) {
758                 C_LOGE("widget_label is NULL");
759                 goto out;
760         }
761
762         src_label_file = wrt_smack_label(widget_id, SMACK_SRC_FILE_SUFFIX);
763         if (src_label_file == NULL) {
764                 C_LOGE("src_label_file is NULL");
765                 goto out;
766         }
767
768         src_label_dir = wrt_smack_label(widget_id, SMACK_SRC_DIR_SUFFIX);
769         if (src_label_file == NULL) {
770                 C_LOGE("src_label_file is NULL");
771                 goto out;
772         }
773
774         data_label = wrt_smack_label(widget_id, SMACK_DATA_SUFFIX);
775         if (data_label == NULL) {
776                 C_LOGE("data_label is NULL");
777                 goto out;
778         }
779
780         if (smack_accesses_new(&smack) != 0) {
781                 C_LOGE("smack_accesses_new failed");
782                 goto out;
783         }
784
785         ret = wrt_permissions_reset(widget_id);
786         if (ret != PC_OPERATION_SUCCESS) {
787                 C_LOGE("wrt_permissions_reset failed");
788                 goto out;
789         }
790
791         ret = PC_ERR_INVALID_OPERATION;
792
793         if (smack_set_label_for_self(widget_label) != 0) {
794                 C_LOGE("smack_set_label_for_self failed");
795                 goto out;
796         }
797
798         /* Allow widget to only read and execute it's source directories */
799         C_LOGD("Adding implicit Smack rule: %s %s %s", widget_label, src_label_dir, "rx");
800         if (smack_accesses_add(smack, widget_label, src_label_dir, "rx") != 0) {
801                 C_LOGE("smack_accesses_add failed (rx)");
802                 goto out;
803         }
804
805         /* Allow widget to only read read it's source files */
806         C_LOGD("Adding implicit Smack rule: %s %s %s", widget_label, src_label_file, "r");
807         if (smack_accesses_add(smack, widget_label, src_label_file, "r") != 0) {
808                 C_LOGE("smack_accesses_add failed (r)");
809                 goto out;
810         }
811
812         /* Allow widget to do everything with it's data */
813         /*
814          * FIXME: If a malicious widget finds a way to execute files, it will be
815          * able to execute it's data files, which are fully controlled by the
816          * widget itself. This currently cannot be prevented by SMACK, so other
817          * means must be used.
818          */
819         C_LOGD("Adding implicit Smack rule: %s %s %s", widget_label, data_label, "rwxat");
820         if (smack_accesses_add(smack, widget_label, data_label, "rwxat") != 0) {
821                 C_LOGE("smack_accesses_add failed (rwxat)");
822                 goto out;
823         }
824
825         C_LOGD("Adding Smack rules from pseudo-devcap %s", WRT_BASE_DEVCAP);
826         ret = perm_to_smack(smack, widget_label, WRT_BASE_DEVCAP);
827         if (ret != PC_OPERATION_SUCCESS) {
828                 C_LOGE("perm_to_smack failed");
829                 goto out;
830         }
831
832         if (smack_accesses_apply(smack) != 0) {
833                 C_LOGE("smack_accesses_apply failed");
834                 ret = PC_ERR_INVALID_OPERATION;
835         }
836
837 out:
838         smack_accesses_free(smack);
839         free(widget_label);
840         free(src_label_file);
841         free(src_label_dir);
842         free(data_label);
843
844         return ret;
845 }
846 #endif // WRT_SMACK_ENABLED
847
848 API char* wrt_widget_id_from_socket(int sockfd)
849 {
850         C_LOGD("Enter function: %s", __func__);
851         char* smack_label;
852         char* widget_id;
853         int ret;
854
855         ret = smack_new_label_from_socket(sockfd, &smack_label);
856         if (ret != 0) {
857                 C_LOGE("smack_new_label_from_socket failed");
858                 return NULL;
859         }
860
861         if (strncmp(smack_label, SMACK_WRT_LABEL_PREFIX,
862                     strlen(SMACK_WRT_LABEL_PREFIX))) {
863                 free(smack_label);
864                 return NULL;
865         }
866
867         widget_id = strdup(smack_label + strlen(SMACK_WRT_LABEL_PREFIX));
868         free(smack_label);
869         C_LOGD("widget id: %s", widget_id);
870         return widget_id;
871 }
872
873
874 static int load_smack_from_file(const char* app_id, struct smack_accesses** smack, int *fd)
875 {
876         C_LOGD("Enter function: %s", __func__);
877
878         char* path = NULL;
879         int ret;
880
881         if (asprintf(&path, SMACK_RULES_DIR "/%s", app_id) == -1) {
882                 ret = PC_ERR_MEM_OPERATION;
883                 C_LOGE("asprintf failed");
884                 goto out;
885         }
886
887         if (smack_accesses_new(smack)) {
888                 ret = PC_ERR_MEM_OPERATION;
889                 C_LOGE("smack_accesses_new failed");
890                 goto out;
891         }
892
893         *fd = open(path, O_CREAT|O_RDWR, 0644);
894         if (*fd == -1) {
895                 ret = PC_ERR_FILE_OPERATION;
896                 C_LOGE("file open failed");
897                 goto out;
898         }
899
900         if (flock(*fd, LOCK_EX)) {
901                 ret = PC_ERR_INVALID_OPERATION;
902                 C_LOGE("flock failed");
903                 goto out;
904         }
905
906         if (smack_accesses_add_from_file(*smack, *fd)) {
907                 ret = PC_ERR_INVALID_OPERATION;
908                 C_LOGE("smack_accesses_add_from_file failed");
909                 goto out;
910         }
911
912         /* Rewind the file */
913         if (lseek(*fd, 0, SEEK_SET) == -1) {
914                 ret = PC_ERR_FILE_OPERATION;
915                 C_LOGE("lseek failed");
916                 goto out;
917         }
918
919         ret = PC_OPERATION_SUCCESS;
920
921 out:
922         free(path);
923
924         return ret;
925 }
926
927 static int save_smack_to_file(struct smack_accesses *smack, int fd)
928 {
929         if (smack_accesses_apply(smack)) {
930                 C_LOGE("smack_accesses_apply failed");
931                 return PC_ERR_INVALID_OPERATION;
932         }
933
934         if (smack_accesses_save(smack, fd)) {
935                 C_LOGE("smack_accesses_save failed");
936                 return PC_ERR_INVALID_OPERATION;
937         }
938
939         return PC_OPERATION_SUCCESS;
940 }
941
942
943 API int app_add_permissions(const char* app_id, const char** perm_list)
944 {
945         C_LOGD("Enter function: %s", __func__);
946         char* path = NULL;
947         int i, ret;
948         int fd = -1;
949         struct smack_accesses *smack = NULL;
950
951 #ifdef SMACK_ENABLED
952
953         ret = load_smack_from_file(app_id, &smack, &fd);
954         if (ret != PC_OPERATION_SUCCESS) {
955                 C_LOGE("load_smack_from_file failed");
956                 goto out;
957         }
958         for (i = 0; perm_list[i] != NULL; ++i) {
959                 ret = perm_to_smack(smack, app_id, perm_list[i]);
960                 C_LOGD("perm_to_smack params: app_id: %s, perm_list[%d]: %s", app_id, i, perm_list[i]);
961                 if (ret != PC_OPERATION_SUCCESS){
962                         C_LOGE("perm_to_smack failed");
963                         goto out;
964                 }
965         }
966
967         ret = save_smack_to_file(smack, fd);
968         if(ret != PC_OPERATION_SUCCESS){
969                 C_LOGE("save_smack_to_file failed");
970                 goto out;
971         }
972 #endif
973
974         ret = PC_OPERATION_SUCCESS;
975 out:
976         if (fd != -1)
977                 close(fd);
978         if (smack != NULL)
979                 smack_accesses_free(smack);
980         free(path);
981
982         return ret;
983 }
984
985 API int app_revoke_permissions(const char* app_id)
986 {
987         C_LOGD("Enter function: %s", __func__);
988         char* path = NULL;
989         int ret;
990         int fd = -1;
991         struct smack_accesses *smack = NULL;
992
993 #ifdef SMACK_ENABLED
994         if (asprintf(&path, SMACK_RULES_DIR "/%s", app_id) == -1) {
995                 ret = PC_ERR_MEM_OPERATION;
996                 C_LOGE("asprintf failed");
997                 goto out;
998         }
999
1000         if (smack_accesses_new(&smack)) {
1001                 ret = PC_ERR_MEM_OPERATION;
1002                 C_LOGE("smack_accesses_new failed");
1003                 goto out;
1004         }
1005
1006         fd = open(path, O_RDONLY);
1007         if (fd == -1) {
1008                 ret = PC_ERR_FILE_OPERATION;
1009                 C_LOGE("file open failed");
1010                 goto out;
1011         }
1012
1013         if (flock(fd, LOCK_EX | LOCK_NB)) {
1014                 /* Non-blocking lock request on a file failed. */
1015                 ret = PC_ERR_INVALID_OPERATION;
1016                 C_LOGE("flock failed");
1017                 goto out;
1018         }
1019
1020         if (unlink(path)) {
1021                 ret = PC_ERR_INVALID_OPERATION;
1022                 C_LOGE("unlink failed");
1023                 goto out;
1024         }
1025
1026         if (smack_accesses_add_from_file(smack, fd)) {
1027                 ret = PC_ERR_INVALID_OPERATION;
1028                 C_LOGE("smack_accesses_add_from_file failed");
1029                 goto out;
1030         }
1031
1032         if (smack_accesses_clear(smack)) {
1033                 ret = PC_ERR_INVALID_OPERATION;
1034                 C_LOGE("smack_accesses_clear failed");
1035                 goto out;
1036         }
1037
1038         if (smack_revoke_subject(app_id)) {
1039                 ret = PC_ERR_INVALID_OPERATION;
1040                 C_LOGE("smack_revoke_subject failed");
1041                 goto out;
1042         }
1043 #endif
1044
1045         ret = PC_OPERATION_SUCCESS;
1046 out:
1047         if (fd != -1)
1048                 close(fd);
1049         if (smack != NULL)
1050                 smack_accesses_free(smack);
1051         free(path);
1052
1053         return ret;
1054 }
1055
1056 API int app_label_dir(const char* label, const char* path)
1057 {
1058         C_LOGD("Enter function: %s", __func__);
1059 #ifdef SMACK_ENABLED
1060
1061         int ret = PC_OPERATION_SUCCESS;
1062
1063         //setting label on everything in given directory and below
1064         ret = dir_set_smack_r(path, label, SMACK_LABEL_ACCESS, ~0);
1065         if (PC_OPERATION_SUCCESS != ret)
1066                 return ret;
1067
1068         //setting execute label for executable files
1069         ret = dir_set_smack_r(path, label, SMACK_LABEL_EXEC, S_IFREG | S_IXUSR);
1070
1071         return ret;
1072 #else
1073         return PC_OPERATION_SUCCESS;
1074 #endif
1075 }
1076
1077 API int app_label_shared_dir(const char* app_label, const char* shared_label, const char* path)
1078 {
1079         C_LOGD("Enter function: %s", __func__);
1080 #ifdef SMACK_ENABLED
1081         int ret;
1082         int fd = -1;
1083         struct smack_accesses *smack = NULL;
1084
1085
1086         //setting label on everything in given directory and below
1087         ret = dir_set_smack_r(path, shared_label, SMACK_LABEL_ACCESS, ~0);
1088         if(ret != PC_OPERATION_SUCCESS){
1089                 C_LOGE("dir_set_smakc_r failed");
1090                 goto out;
1091         }
1092
1093         //setting transmute on dir
1094         ret = dir_set_smack_r(path, "1", SMACK_LABEL_TRANSMUTE, S_IFDIR);
1095         if (ret != PC_OPERATION_SUCCESS) {
1096                 C_LOGE("dir_set_smakc_r failed");
1097                 goto out;
1098         }
1099
1100         ret = load_smack_from_file(app_label, &smack, &fd);
1101         if (ret != PC_OPERATION_SUCCESS) {
1102                 C_LOGE("load_smack_from_file failed");
1103                 goto out;
1104         }
1105
1106         //setting access rule for application
1107         if (smack_accesses_add(smack, app_label,shared_label, "wrxat") == -1) {
1108                 C_LOGE("smack_accesses_add failed");
1109                 goto out;
1110         }
1111
1112         ret = save_smack_to_file(smack, fd);
1113         if (ret != PC_OPERATION_SUCCESS) {
1114                 C_LOGE("save_smack_to_file failed");
1115                 goto out;
1116         }
1117
1118         ret = PC_OPERATION_SUCCESS;
1119 out:
1120         if (fd != -1)
1121                 close(fd);
1122         if (smack != NULL)
1123                 smack_accesses_free(smack);
1124         return ret;
1125 #else
1126         return PC_OPERATION_SUCCESS;
1127 #endif
1128 }
1129
1130