Tizen 2.1 base
[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
40 #include "privilege-control.h"
41
42 #define APP_GID 5000
43 #define APP_UID 5000
44 #define DEVELOPER_GID   5100
45 #define DEVELOPER_UID   5100
46
47 #define APP_USER_NAME   "app"
48 #define DEV_USER_NAME   "developer"
49
50 #define APP_HOME_DIR    TOSTRING(HOMEDIR) "/app"
51 #define DEV_HOME_DIR    TOSTRING(HOMEDIR) "/developer"
52
53 #define APP_GROUP_PATH  TOSTRING(SHAREDIR) "/app_group_list"
54 #define DEV_GROUP_PATH  TOSTRING(SHAREDIR) "/dev_group_list"
55
56 #define SMACK_RULES_DIR  "/etc/smack/accesses.d/"
57
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"
65
66 #ifndef SMACK_ENABLED
67 #undef WRT_SMACK_ENABLED
68 #endif
69
70 #ifdef WRT_SMACK_ENABLED
71 static int set_smack_for_wrt(const char* widget_id);
72 #endif // WRT_SMACK_ENABLED
73
74 typedef enum {
75         PKG_TYPE_WGT,
76         PKG_TYPE_OTHER
77 } pkg_type_t;
78
79 typedef struct {
80         char user_name[10];
81         int uid;
82         int gid;
83         char home_dir[64];
84         char group_list[64];
85 } new_user;
86
87 API int control_privilege(void)
88 {
89         if(getuid() == APP_UID) // current user is 'app'
90                 return PC_OPERATION_SUCCESS;
91
92         if(set_app_privilege("com.samsung.", NULL, NULL) == PC_OPERATION_SUCCESS)
93                 return PC_OPERATION_SUCCESS;
94         else
95                 return PC_ERR_NOT_PERMITTED;
96 }
97
98 static int set_dac(const char* pkg_name)
99 {
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
106         int result;
107         new_user usr;
108
109         /*
110          * initialize user structure
111          */
112         memset(usr.user_name, 0x00, 10);
113         memset(usr.home_dir, 0x00, 64);
114         memset(usr.group_list, 0x00, 64);
115         usr.uid = -1;
116         usr.gid = -1;
117
118         t_uid = getuid();
119
120         if(t_uid == 0)  // current user is 'root'
121         {
122                 if(!strncmp(pkg_name, "developer", 9))
123                 {
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));
129                 }
130                 else
131                 {
132                         strncpy(usr.user_name, APP_USER_NAME, sizeof(usr.user_name));
133                         usr.uid = APP_UID;
134                         usr.gid = APP_GID;
135                         strncpy(usr.home_dir, APP_HOME_DIR, sizeof(usr.home_dir));
136                         strncpy(usr.group_list, APP_GROUP_PATH, sizeof(usr.group_list));
137                 }
138
139                 /*
140                  * get group information
141                  */
142                 if(!(fp_group = fopen(usr.group_list, "r")))
143                 {
144                         fprintf(stderr, "[ERR] file open error: [%s]\n", usr.group_list);
145                         result = PC_ERR_FILE_OPERATION; // return -1
146                         goto error;
147                 }
148
149                 while(fgets(buf, 10, fp_group) != NULL)
150                 {
151                         errno = 0;
152                         temp_gid = strtoul(buf, 0, 10);
153                         if(errno != 0)  // error occured during strtoul()
154                         {
155                                 fprintf(stderr, "[ERR] cannot change string to integer: [%s]\n", buf);
156                                 result = PC_ERR_INVALID_OPERATION;
157                                 goto error;
158                         }
159
160                         glist = (gid_t*)realloc(glist, sizeof(gid_t) * (glist_cnt + 1));
161                         if(!glist)
162                         {
163                                 result = PC_ERR_MEM_OPERATION;  // return -2
164                                 goto error;
165                         }
166                         glist[glist_cnt] = temp_gid;
167                         glist_cnt++;
168                 }
169                 fclose(fp_group);
170                 fp_group = NULL;
171
172                 /*
173                  * setgroups()
174                  */
175                 if(setgroups(glist_cnt, glist) != 0)
176                 {
177                         fprintf(stderr, "[ERR] setgrouops fail\n");
178                         result = PC_ERR_NOT_PERMITTED;  // return -3
179                         goto error;
180                 }
181                 if(glist != NULL)
182                 {
183                         free(glist);
184                         glist = NULL;
185                 }
186
187                 /*
188                  * setgid() & setgid()
189                  */
190                 if(setgid(usr.gid) != 0)        // fail
191                 {
192                         fprintf(stderr, "[ERR] fail to execute setgid().\n");
193                         result = PC_ERR_INVALID_OPERATION;
194                         goto error;
195                 }
196                 if(setuid(usr.uid) != 0)        // fail
197                 {
198                         fprintf(stderr, "[ERR] fail to execute setuid().\n");
199                         result = PC_ERR_INVALID_OPERATION;
200                         goto error;
201                 }
202
203                 if(setenv("USER", usr.user_name, 1) != 0)       //fail
204                 {
205                         fprintf(stderr, "[ERR] fail to execute setenv().\n");
206                         result = PC_ERR_INVALID_OPERATION;
207                         goto error;
208                 }
209                 if(setenv("HOME", usr.home_dir, 1) != 0)        // fail
210                 {
211                         fprintf(stderr, "[ERR] fail to execute setenv().\n");
212                         result = PC_ERR_INVALID_OPERATION;
213                         goto error;
214                 }
215         }
216         else    // current user is not only 'root' but 'app'
217         {
218                 fprintf(stderr, "[ERR] current user is NOT root\n");
219                 result = PC_ERR_NOT_PERMITTED;  // return -3
220                 goto error;
221         }
222
223         result = PC_OPERATION_SUCCESS;
224
225 error:
226     if(fp_group != NULL)
227         fclose(fp_group);
228         if(glist != NULL)
229                 free(glist);
230
231         return result;
232 }
233
234 #ifdef SMACK_ENABLED
235 /**
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.
239  *
240  * @param path file path to take label from
241  * @return PC_OPERATION_SUCCESS on success, PC_ERR_* on error
242  */
243 static int set_smack_from_binary(const char* path)
244 {
245         int ret;
246         char* label;
247
248         ret = smack_lgetlabel(path, &label, SMACK_LABEL_EXEC);
249         if (ret != 0)
250                 return PC_ERR_INVALID_OPERATION;
251
252         if (label == NULL)
253                 /* No label to set, just return with success */
254                 ret = PC_OPERATION_SUCCESS;
255         else
256                 ret = smack_set_label_for_self(label);
257
258         free(label);
259         return ret;
260 }
261
262 static int is_widget(const char* path)
263 {
264         char buf[sizeof(WRT_CLIENT_PATH)];
265         int ret;
266
267         ret = readlink(path, buf, sizeof(WRT_CLIENT_PATH));
268         if (ret == -1 || ret == sizeof(WRT_CLIENT_PATH))
269                 return 0;
270         buf[ret] = '\0';
271
272         return (!strcmp(WRT_CLIENT_PATH, buf));
273 }
274
275 /**
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.
281  *
282  * @param type claimed application type
283  * @param path file path to executable
284  * @return return void on success, terminate the process on error
285  */
286 static pkg_type_t verify_app_type(const char* type, const char* path)
287 {
288         /* TODO: this should actually be treated as error, but until the old
289          * set_privilege API is removed, it must be ignored */
290         if (path == NULL)
291                 return PKG_TYPE_OTHER; /* good */
292
293         if (is_widget(path)) {
294                 if (!strcmp(type, "wgt"))
295                         return PKG_TYPE_WGT; /* good */
296         } else {
297                 if (type == NULL || strcmp(type, "wgt"))
298                         return PKG_TYPE_OTHER; /* good */
299         }
300
301         /* bad */
302         exit(EXIT_FAILURE);
303 }
304
305 static const char* parse_widget_id(const char* path)
306 {
307         const char* basename = strrchr(path, '/');
308
309         if (basename == NULL)
310                 basename = path;
311         else
312                 ++basename;
313
314         return basename;
315 }
316 #endif // SMACK_ENABLED
317
318 API int set_app_privilege(const char* name, const char* type, const char* path)
319 {
320 #ifdef SMACK_ENABLED
321         const char* widget_id;
322         int ret = PC_OPERATION_SUCCESS;
323
324         switch(verify_app_type(type, path)) {
325         case PKG_TYPE_WGT:
326                 widget_id = parse_widget_id(path);
327                 if (widget_id == NULL)
328                         ret = PC_ERR_INVALID_PARAM;
329 #ifdef WRT_SMACK_ENABLED
330                 else
331                         ret = set_smack_for_wrt(widget_id);
332 #endif
333                 break;
334         case PKG_TYPE_OTHER:
335                 if (path != NULL)
336                 ret = set_smack_from_binary(path);
337         }
338
339         if (ret != PC_OPERATION_SUCCESS)
340                 return ret;
341 #endif // SMACK_ENABLED
342
343         return set_dac(name);
344 }
345
346 API int set_privilege(const char* pkg_name)
347 {
348         return set_app_privilege(pkg_name, NULL, NULL);
349 }
350
351 API int wrt_set_privilege(const char* widget_id)
352 {
353 #ifdef WRT_SMACK_ENABLED
354         int ret;
355
356         ret = set_smack_for_wrt(widget_id);
357         if (ret != PC_OPERATION_SUCCESS)
358                 return ret;
359 #endif // WRT_SMACK_ENABLED
360
361         return set_dac("com.samsung.");
362 }
363
364 #ifdef WRT_SMACK_ENABLED
365 static inline char* wrt_smack_label(const char* widget_id, const char* suffix)
366 {
367         int ret;
368         char* label;
369
370         ret = asprintf(&label, "%s%s%s", SMACK_WRT_LABEL_PREFIX, widget_id,
371                 (suffix ? suffix : ""));
372
373         if (ret == -1)
374                 return NULL;
375
376         if (strlen(label) > SMACK_LABEL_LEN) {
377                 free(label);
378                 return NULL;
379         }
380
381         return label;
382 }
383 #endif // WRT_SMACK_ENABLED
384
385 static inline int perm_to_smack(struct smack_accesses* smack, const char* app_label, const char* perm)
386 {
387         int ret = PC_OPERATION_SUCCESS;
388         char* path = NULL;
389         char* format_string = NULL;
390         FILE* file = NULL;
391         char smack_subject[SMACK_LABEL_LEN + 1];
392         char smack_object[SMACK_LABEL_LEN + 1];
393         char smack_accesses[10];
394
395         if (asprintf(&path, TOSTRING(SHAREDIR) "/%s.smack", perm) == -1) {
396                 ret = PC_ERR_MEM_OPERATION;
397                 goto out;
398         }
399
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;
403                 goto out;
404         }
405
406         file = fopen(path, "r");
407         if (file == NULL) {
408                 ret = PC_ERR_FILE_OPERATION;
409                 goto out;
410         }
411
412         while (1) {
413                 if (fscanf(file, format_string, smack_subject, smack_object, smack_accesses) != 1)
414                         goto out;
415
416                 if (!strcmp(smack_subject, SMACK_APP_LABEL))
417                         strcpy(smack_subject, app_label);
418
419                 if (!strcmp(smack_object, SMACK_APP_LABEL))
420                         strcpy(smack_object, app_label);
421
422                 if (smack_accesses_add_modify(smack, smack_subject, smack_object, smack_accesses, "") != 0) {
423                         ret = PC_ERR_INVALID_OPERATION;
424                         goto out;
425                 }
426         }
427
428 out:
429         free(path);
430         free(format_string);
431         if (file != NULL)
432                 fclose(file);
433         return ret;
434 }
435
436 API int wrt_permissions_reset(const char* widget_id)
437 {
438         int ret = PC_OPERATION_SUCCESS;
439 #ifdef WRT_SMACK_ENABLED
440         char* label = NULL;
441
442         label = wrt_smack_label(widget_id, NULL);
443         if (label == NULL)
444                 return PC_ERR_MEM_OPERATION;
445
446         if (smack_revoke_subject(label))
447                 ret = PC_ERR_INVALID_OPERATION;
448
449         free(label);
450 #endif // WRT_SMACK_ENABLED
451         return ret;
452 }
453
454 API int wrt_permissions_add(const char* widget_id, const char** devcap_list)
455 {
456         int ret = PC_OPERATION_SUCCESS;
457 #ifdef WRT_SMACK_ENABLED
458         char* widget_label = NULL;
459         struct smack_accesses* smack = NULL;
460         int i;
461
462         widget_label = wrt_smack_label(widget_id, NULL);
463         if (widget_label == NULL)
464                 return PC_ERR_MEM_OPERATION;
465
466         if (smack_accesses_new(&smack)) {
467                 ret = PC_ERR_MEM_OPERATION;
468                 goto out;
469         }
470
471         for (i = 0; devcap_list[i] != NULL; ++i) {
472                 char* perm = NULL;
473
474                 /* Prepend devcap with "WRT_" */
475                 if (asprintf(&perm, "%s_%s", WRT_BASE_DEVCAP, devcap_list[i]) == -1) {
476                         ret = PC_ERR_MEM_OPERATION;
477                         goto out;
478                 }
479
480                 ret = perm_to_smack(smack, widget_label, perm);
481                 free(perm);
482                 if (ret != PC_OPERATION_SUCCESS)
483                         goto out;
484         }
485
486         if (smack_accesses_apply(smack) != 0) {
487                 ret = PC_ERR_INVALID_OPERATION;
488                 goto out;
489         }
490
491 out:
492         smack_accesses_free(smack);
493         free(widget_label);
494 #endif // WRT_SMACK_ENABLED
495         return ret;
496 }
497
498 static int dir_set_smack_r(const char *path, const char* label,
499                 enum smack_label_type type, mode_t type_mask)
500 {
501         int ret;
502         const char* path_argv[] = {path, NULL};
503         FTS *fts = NULL;
504         FTSENT *ftsent;
505
506         ret = PC_ERR_FILE_OPERATION;
507
508         fts = fts_open((char * const *) path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
509         if (fts == NULL)
510                 goto out;
511
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)
515                         goto out;
516
517                 if (ftsent->fts_statp->st_mode & S_IFMT & type_mask)
518                         if (smack_lsetlabel(ftsent->fts_path, label, type) != 0)
519                                 goto out;
520         }
521
522         /* If last call to fts_read() set errno, we need to return error. */
523         if (errno == 0)
524                 ret = PC_OPERATION_SUCCESS;
525
526 out:
527         if (fts != NULL)
528                 fts_close(fts);
529         return ret;
530 }
531
532 API int wrt_set_src_dir(const char* widget_id, const char *path)
533 {
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;
539
540         ret = PC_ERR_MEM_OPERATION;
541
542         widget_label = wrt_smack_label(widget_id, NULL);
543         if (widget_label == NULL)
544                 goto out;
545
546         src_label_dir = wrt_smack_label(widget_id, SMACK_SRC_DIR_SUFFIX);
547         if (src_label_dir == NULL)
548                 goto out;
549
550         src_label_file = wrt_smack_label(widget_id, SMACK_SRC_FILE_SUFFIX);
551         if (src_label_file == NULL)
552                 goto out;
553
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)
557                 goto out;
558
559         /* Set label for non-directories */
560         ret = dir_set_smack_r(path, src_label_file, SMACK_LABEL_ACCESS, ~S_IFDIR);
561
562 out:
563         free(widget_label);
564         free(src_label_dir);
565         free(src_label_file);
566 #endif // WRT_SMACK_ENABLED
567         return ret;
568 }
569
570 API int wrt_set_data_dir(const char* widget_id, const char *path)
571 {
572         int ret = PC_OPERATION_SUCCESS;
573 #ifdef WRT_SMACK_ENABLED
574         char* widget_label = NULL;
575         char* data_label = NULL;
576         struct stat st;
577
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? */
583                         goto out;
584         } else {
585                 if (errno != ENOENT)
586                         /* Some other error than "no such file or directory" */
587                         goto out;
588                 if (mkdir(path, S_IRWXU) != 0)
589                         /* Error while creating the directory */
590                         goto out;
591                 if (chown(path, APP_UID, APP_GID)) {
592                         /* Error while setting the directory owner */
593                         int e = errno;
594                         rmdir(path);
595                         errno = e;
596                         goto out;
597                 }
598         }
599
600         ret = PC_ERR_MEM_OPERATION;
601
602         widget_label = wrt_smack_label(widget_id, NULL);
603         if (widget_label == NULL)
604                 goto out;
605
606         data_label = wrt_smack_label(widget_id, SMACK_DATA_SUFFIX);
607         if (data_label == NULL)
608                 goto out;
609
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)
613                 goto out;
614
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)
618                 goto out;
619
620 out:
621         free(widget_label);
622         free(data_label);
623 #endif // WRT_SMACK_ENABLED
624         return ret;
625 }
626
627 #ifdef WRT_SMACK_ENABLED
628 static int set_smack_for_wrt(const char* widget_id)
629 {
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;
635         int ret;
636
637         ret = PC_ERR_MEM_OPERATION;
638
639         widget_label = wrt_smack_label(widget_id, NULL);
640         if (widget_label == NULL)
641                 goto out;
642
643         src_label_file = wrt_smack_label(widget_id, SMACK_SRC_FILE_SUFFIX);
644         if (src_label_file == NULL)
645                 goto out;
646
647         src_label_dir = wrt_smack_label(widget_id, SMACK_SRC_DIR_SUFFIX);
648         if (src_label_file == NULL)
649                 goto out;
650
651         data_label = wrt_smack_label(widget_id, SMACK_DATA_SUFFIX);
652         if (data_label == NULL)
653                 goto out;
654
655         if (smack_accesses_new(&smack) != 0)
656                 goto out;
657
658         ret = wrt_permissions_reset(widget_id);
659         if (ret != PC_OPERATION_SUCCESS)
660                 goto out;
661
662         ret = PC_ERR_INVALID_OPERATION;
663
664         if (smack_set_label_for_self(widget_label) != 0)
665                 goto out;
666
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)
669                 goto out;
670
671         /* Allow widget to only read read it's source files */
672         if (smack_accesses_add(smack, widget_label, src_label_file, "r") != 0)
673                 goto out;
674
675         /* Allow widget to do everything with it's data */
676         /*
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.
681          */
682         if (smack_accesses_add(smack, widget_label, data_label, "rwxat") != 0)
683                 goto out;
684
685         ret = perm_to_smack(smack, widget_label, WRT_BASE_DEVCAP);
686         if (ret != PC_OPERATION_SUCCESS)
687                 goto out;
688
689         if (smack_accesses_apply(smack) != 0)
690                 ret = PC_ERR_INVALID_OPERATION;
691
692 out:
693         smack_accesses_free(smack);
694         free(widget_label);
695         free(src_label_file);
696         free(src_label_dir);
697         free(data_label);
698
699         return ret;
700 }
701 #endif // WRT_SMACK_ENABLED
702
703 API char* wrt_widget_id_from_socket(int sockfd)
704 {
705         char* smack_label;
706         char* widget_id;
707         int ret;
708
709         ret = smack_new_label_from_socket(sockfd, &smack_label);
710         if (ret != 0)
711                 return NULL;
712
713         if (strncmp(smack_label, SMACK_WRT_LABEL_PREFIX,
714                     strlen(SMACK_WRT_LABEL_PREFIX))) {
715                 free(smack_label);
716                 return NULL;
717         }
718
719         widget_id = strdup(smack_label + strlen(SMACK_WRT_LABEL_PREFIX));
720         free(smack_label);
721         return widget_id;
722 }
723
724 API int app_add_permissions(const char* app_id, const char** perm_list)
725 {
726         char* path = NULL;
727         int i, ret;
728         int fd = -1;
729         struct smack_accesses *smack = NULL;
730
731 #ifdef SMACK_ENABLED
732         if (asprintf(&path, SMACK_RULES_DIR "/%s", app_id) == -1) {
733                 ret = PC_ERR_MEM_OPERATION;
734                 goto out;
735         }
736
737         if (smack_accesses_new(&smack)) {
738                 ret = PC_ERR_MEM_OPERATION;
739                 goto out;
740         }
741
742         fd = open(path, O_CREAT, O_RDWR);
743         if (fd == -1) {
744                 ret = PC_ERR_FILE_OPERATION;
745                 goto out;
746         }
747
748         if (flock(fd, LOCK_EX)) {
749                 ret = PC_ERR_INVALID_OPERATION;
750                 goto out;
751         }
752
753         if (smack_accesses_add_from_file(smack, fd)) {
754                 ret = PC_ERR_INVALID_OPERATION;
755                 goto out;
756         }
757
758         /* Rewind the file */
759         if (lseek(fd, 0, SEEK_SET) == -1) {
760                 ret = PC_ERR_FILE_OPERATION;
761                 goto out;
762         }
763
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)
767                         goto out;
768         }
769
770         if (smack_accesses_apply(smack)) {
771                 ret = PC_ERR_INVALID_OPERATION;
772                 goto out;
773         }
774
775         if (smack_accesses_save(smack, fd)) {
776                 ret = PC_ERR_FILE_OPERATION;
777                 goto out;
778         }
779 #endif
780
781         ret = PC_OPERATION_SUCCESS;
782 out:
783         if (fd != -1)
784                 close(fd);
785         if (smack != NULL)
786                 smack_accesses_free(smack);
787         free(path);
788
789         return ret;
790 }
791
792 API int app_revoke_permissions(const char* app_id)
793 {
794         char* path = NULL;
795         int ret;
796         int fd = -1;
797         struct smack_accesses *smack = NULL;
798
799 #ifdef SMACK_ENABLED
800         if (asprintf(&path, SMACK_RULES_DIR "/%s", app_id) == -1) {
801                 ret = PC_ERR_MEM_OPERATION;
802                 goto out;
803         }
804
805         if (smack_accesses_new(&smack)) {
806                 ret = PC_ERR_MEM_OPERATION;
807                 goto out;
808         }
809
810         fd = open(path, O_RDONLY);
811         if (fd == -1) {
812                 ret = PC_ERR_FILE_OPERATION;
813                 goto out;
814         }
815
816         if (flock(fd, LOCK_EX | LOCK_NB)) {
817                 /* Non-blocking lock request on a file failed. */
818                 ret = PC_ERR_INVALID_OPERATION;
819                 goto out;
820         }
821
822         if (unlink(path)) {
823                 ret = PC_ERR_INVALID_OPERATION;
824                 goto out;
825         }
826
827         if (smack_accesses_add_from_file(smack, fd)) {
828                 ret = PC_ERR_INVALID_OPERATION;
829                 goto out;
830         }
831
832         if (smack_accesses_clear(smack)) {
833                 ret = PC_ERR_INVALID_OPERATION;
834                 goto out;
835         }
836
837         if (smack_revoke_subject(app_id)) {
838                 ret = PC_ERR_INVALID_OPERATION;
839                 goto out;
840         }
841 #endif
842
843         ret = PC_OPERATION_SUCCESS;
844 out:
845         if (fd != -1)
846                 close(fd);
847         if (smack != NULL)
848                 smack_accesses_free(smack);
849         free(path);
850
851         return ret;
852 }
853
854 API int app_label_dir(const char* app_id, const char* path)
855 {
856 #ifdef SMACK_ENABLED
857         return dir_set_smack_r(path, app_id, SMACK_LABEL_ACCESS, ~0);
858 #else
859         return PC_OPERATION_SUCCESS;
860 #endif
861 }