Rule tokenizer fix
[platform/core/security/libprivilege-control.git] / src / common.c
1 /*
2  * libprivilege control
3  *
4  * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd All Rights Reserved
5  *
6  * Contact: Rafal Krypa <r.krypa@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 <stdlib.h>
24 #include <stdio.h>
25 #include <errno.h>
26 #include <unistd.h>
27 #include <sys/smack.h>
28 #include <sys/stat.h>
29 #include <sys/file.h>
30 #include <iri.h>
31
32 #include "common.h"
33 #include "privilege-control.h"
34
35 /* TODO: implement such function in libsmack instead */
36 int smack_label_is_valid(const char *smack_label)
37 {
38         SECURE_C_LOGD("Entering function: %s. Params: smack_label=%s",
39                       __func__, smack_label);
40
41         int i;
42
43         if(!smack_label || smack_label[0] == '\0' || smack_label[0] == '-')
44                 goto err;
45
46         for(i = 0; smack_label[i]; ++i) {
47                 if(i >= SMACK_LABEL_LEN)
48                         goto err;
49                 switch(smack_label[i]) {
50                 case '~':
51                 case ' ':
52                 case '/':
53                 case '"':
54                 case '\\':
55                 case '\'':
56                         goto err;
57                 default:
58                         break;
59                 }
60         }
61
62         return 1;
63 err:
64         SECURE_C_LOGE("Invalid SMACK label %s", smack_label);
65         return 0;
66 }
67
68
69 int set_exec_label(const char *label, const char *path)
70 {
71         struct stat st;
72
73         if(stat(path, &st) < 0) {
74                 SECURE_C_LOGE("stat failed for %s (Error = %s)", path, strerror(errno));
75                 return PC_ERR_FILE_OPERATION;
76         }
77
78         // check if it's a link
79         if((st.st_mode & S_IFLNK) != 0) {
80                 SECURE_C_LOGD("%s is a symbolic link", path);
81                 char* target AUTO_FREE;
82                 target = realpath(path, NULL);
83                 if(!target) {
84                         SECURE_C_LOGE("getting link target for %s failed (Error = %s)",
85                                       path, strerror(errno));
86                         return PC_ERR_FILE_OPERATION;
87                 }
88
89                 if(stat(target, &st) < 0) {
90                         SECURE_C_LOGE("stat failed for %s (Error = %s)", target, strerror(errno));
91                         return PC_ERR_FILE_OPERATION;
92                 }
93
94                 if((st.st_mode & (S_IXUSR | S_IFREG)) != (S_IXUSR | S_IFREG)) {
95                         SECURE_C_LOGE("%s is not a regular executable file.", target);
96                         return PC_ERR_FILE_OPERATION;
97                 }
98         } else if((st.st_mode & (S_IXUSR | S_IFREG)) != (S_IXUSR | S_IFREG)) {
99                 SECURE_C_LOGE("%s is not a regular executable file nor a symbolic link.", path);
100                 return PC_ERR_FILE_OPERATION;
101         }
102
103         SECURE_C_LOGD("smack_lsetlabel (label: %s (type: SMACK_LABEL_EXEC), path: %s)",
104                       label, path);
105         if (smack_lsetlabel(path, label, SMACK_LABEL_EXEC) != 0) {
106                 SECURE_C_LOGE("smack_lsetlabel failed.");
107                 return PC_ERR_FILE_OPERATION;
108         }
109         return PC_OPERATION_SUCCESS;
110 }
111
112
113 int tokenize_rule(const char *const s_rule,
114                   char s_subject[],
115                   char s_object[],
116                   char s_access[])
117 {
118         char tmp_s_dump[2] = "\0";
119         int ret = 0;
120
121         ret = sscanf(s_rule, "%" TOSTRING(SMACK_LABEL_LEN) "s%*[ \t\n\r]%" TOSTRING(SMACK_LABEL_LEN)
122                      "s%*[ \t\n\r]%" TOSTRING(ACC_LEN) "s%1s", s_subject, s_object,s_access,
123                      tmp_s_dump);
124
125         if (ret != 3) {
126                 C_LOGE("RDB: Failed to tokenize the rule: <%s>. %d tokens needed, %d found.",
127                        s_rule, 3, ret);
128                 return PC_ERR_INVALID_OPERATION;
129         }
130
131         return PC_OPERATION_SUCCESS;
132 }
133
134
135 bool is_wildcard(const char *const s_label)
136 {
137         return  !strcmp(s_label, "~ALL_APPS~") ||
138                 !strcmp(s_label, "~ALL_APPS_WITH_SAME_PERMISSION~") ||
139                 !strcmp(s_label, "~PUBLIC_PATH~") ||
140                 !strcmp(s_label, "~GROUP_PATH~") ||
141                 !strcmp(s_label, "~SETTINGS_PATH~") ||
142                 !strcmp(s_label, "~NPRUNTIME_PATH~");
143 }
144
145
146 int parse_rule(const char *const s_rule,
147                char s_label[],
148                char s_access[],
149                int *pi_is_reverse)
150 {
151         int ret = PC_OPERATION_SUCCESS;
152         char tmp_s_subject[SMACK_LABEL_LEN + 1];
153         char tmp_s_object[SMACK_LABEL_LEN + 1];
154         char tmp_s_access[ACC_LEN + 1];
155
156         bool b_subject_is_template;
157         bool b_object_is_template;
158
159         // Tokenize
160         ret = tokenize_rule(s_rule, tmp_s_subject, tmp_s_object, tmp_s_access);
161         if(ret != PC_OPERATION_SUCCESS) return ret;
162
163         // Check SMACK_APP_LABEL_TEMPLATE
164         b_subject_is_template = (bool) !strcmp(tmp_s_subject, SMACK_APP_LABEL_TEMPLATE);
165         b_object_is_template = (bool) !strcmp(tmp_s_object, SMACK_APP_LABEL_TEMPLATE);
166         if((b_subject_is_template && b_object_is_template) ||
167             (!b_subject_is_template && !b_object_is_template)) {
168                 C_LOGE("RDB: Incorrect rule format in rule: %s", s_rule);
169                 ret = PC_ERR_INVALID_PARAM;
170                 return ret;
171         }
172
173         // Check label validity and copy rules
174         if(b_subject_is_template) {
175                 // Not reversed
176                 if(!smack_label_is_valid(tmp_s_object) &&
177                     !is_wildcard(tmp_s_object)) {
178                         C_LOGE("RDB: Incorrect subject label: %s", tmp_s_object);
179                         return ret;
180                 }
181                 strcpy(s_label, tmp_s_object);
182                 if(pi_is_reverse != NULL) *pi_is_reverse = 0;
183         } else if(b_object_is_template) {
184                 // Reversed
185                 if(!smack_label_is_valid(tmp_s_subject) &&
186                     !is_wildcard(tmp_s_subject)) {
187                         C_LOGE("RDB: Incorrect subject label: %s", tmp_s_subject);
188                         return ret;
189                 }
190                 strcpy(s_label, tmp_s_subject);
191                 if(pi_is_reverse != NULL) *pi_is_reverse = 1;
192         }
193         strcpy(s_access, tmp_s_access);
194
195         return PC_OPERATION_SUCCESS;
196 }
197
198
199 int validate_all_rules(const char *const *const pp_permissions_list)
200 {
201         int i;
202         char s_label[SMACK_LABEL_LEN + 1];
203         char s_access[ACC_LEN + 1];
204
205         // Parse and check rules.
206         for(i = 0; pp_permissions_list[i] != NULL; ++i) {
207                 // C_LOGE("RDB: Validating rules: %s", pp_permissions_list[i]);
208
209                 // Ignore empty lines
210                 if(strspn(pp_permissions_list[i], " \t\n")
211                     == strlen(pp_permissions_list[i]))
212                         continue;
213
214                 if(parse_rule(pp_permissions_list[i], s_label, s_access, NULL)
215                     != PC_OPERATION_SUCCESS) {
216                         C_LOGE("RDB: Invalid parameter");
217                         return PC_ERR_INVALID_PARAM;
218                 }
219
220                 // Check the other label
221                 if(!is_wildcard(s_label) &&
222                     !smack_label_is_valid(s_label)) {
223                         C_LOGE("RDB: Incorrect object label: %s", s_label);
224                         return PC_ERR_INVALID_PARAM;
225                 }
226         }
227
228         return PC_OPERATION_SUCCESS;
229 }
230
231 /* Auto cleanup stuff */
232 void freep(void *p)
233 {
234         free(*(void **) p);
235 }
236
237 void closep(int *fd)
238 {
239         if(*fd >= 0)
240                 close(*fd);
241 }
242
243 void fclosep(FILE **f)
244 {
245         if(*f)
246                 fclose(*f);
247 }
248
249 void smack_freep(struct smack_accesses **smack)
250 {
251         smack_accesses_free(*smack);
252 }
253
254 void fts_closep(FTS **f)
255 {
256         if(*f)
257                 fts_close(*f);
258
259 }
260
261 static int load_smack_from_file_generic(const char *app_id, struct smack_accesses **smack, int *fd, char **path, bool is_early)
262 {
263         /* Notice that app_id is ignored when flag is_early is set.
264          * It's because all of the "early rules" (for all apps) should
265          * be in one common file: SMACK_STARTUP_RULES_FILE
266          */
267         SECURE_C_LOGD("Entering function: %s. Params: app_id=%s",
268                       __func__, app_id);
269
270         int ret;
271
272         if(is_early) {
273                 if(0 > asprintf(path, "%s", SMACK_STARTUP_RULES_FILE)) {
274                         *path = NULL;
275                         C_LOGE("asprintf failed.");
276                         return PC_ERR_MEM_OPERATION;
277                 }
278         } else {
279                 ret = smack_file_name(app_id, path);
280                 if(ret != PC_OPERATION_SUCCESS)
281                         return ret;
282         }
283
284         if(smack_accesses_new(smack)) {
285                 C_LOGE("smack_accesses_new failed.");
286                 return PC_ERR_MEM_OPERATION;
287         }
288
289         *fd = open(*path, O_CREAT | O_RDWR, 0644);
290         if(*fd == -1) {
291                 C_LOGE("file open failed (error: %s)", strerror(errno));
292                 return PC_ERR_FILE_OPERATION;
293         }
294
295         if(flock(*fd, LOCK_EX)) {
296                 C_LOGE("flock failed");
297                 return PC_ERR_INVALID_OPERATION;
298         }
299
300         if(smack_accesses_add_from_file(*smack, *fd)) {
301                 C_LOGE("smack_accesses_add_from_file failed.");
302                 return PC_ERR_INVALID_OPERATION;
303         }
304
305         /* Rewind the file */
306         if(lseek(*fd, 0, SEEK_SET) == -1) {
307                 C_LOGE("lseek failed.");
308                 return PC_ERR_FILE_OPERATION;
309         }
310
311         return PC_OPERATION_SUCCESS;
312 }
313
314 int load_smack_from_file(const char *app_id, struct smack_accesses **smack, int *fd, char **path)
315 {
316         SECURE_C_LOGD("Entering function: %s. Params: app_id=%s",
317                       __func__, app_id);
318
319         return load_smack_from_file_generic(app_id, smack, fd, path, 0);
320 }
321
322 int load_smack_from_file_early(const char *app_id, struct smack_accesses **smack, int *fd, char **path)
323 {
324         SECURE_C_LOGD("Entering function: %s. Params: app_id=%s",
325                       __func__, app_id);
326
327         return load_smack_from_file_generic(app_id, smack, fd, path, 1);
328 }
329
330 int smack_mark_file_name(const char *app_id, char **path)
331 {
332         SECURE_C_LOGD("Entering function: %s. Params: app_id=%s",
333                       __func__, app_id);
334
335         if(asprintf(path, SMACK_LOADED_APP_RULES "/%s", app_id) == -1) {
336                 C_LOGE("asprintf failed.");
337                 *path = NULL;
338                 return PC_ERR_MEM_OPERATION;
339         }
340
341         return PC_OPERATION_SUCCESS;
342 }
343
344 bool file_exists(const char *path)
345 {
346         SECURE_C_LOGD("Entering function: %s. Params: path=%s",
347                       __func__, path);
348
349         SECURE_C_LOGD("Opening file %s.", path);
350         FILE *file = fopen(path, "r");
351         if(file) {
352                 fclose(file);
353                 return true;
354         }
355         return false;
356 }
357
358 int smack_file_name(const char *app_id, char **path)
359 {
360         SECURE_C_LOGD("Entering function: %s. Params: app_id=%s",
361                       __func__, app_id);
362
363         if(asprintf(path, SMACK_RULES_DIR "/%s", app_id) == -1) {
364                 C_LOGE("asprintf failed.");
365                 *path = NULL;
366                 return PC_ERR_MEM_OPERATION;
367         }
368
369         return PC_OPERATION_SUCCESS;
370 }
371
372 inline int have_smack(void)
373 {
374         SECURE_C_LOGD("Entering function: %s.", __func__);
375
376         static int have_smack = -1;
377
378         if(-1 == have_smack) {
379                 if(NULL == smack_smackfs_path()) {
380                         C_LOGD("Libprivilege-control: no smack found on phone");
381                         have_smack = 0;
382                 } else {
383                         C_LOGD("Libprivilege-control: found smack on phone");
384                         have_smack = 1;
385                 }
386         }
387
388         return have_smack;
389 }
390
391 inline const char* app_type_name(app_type_t app_type)
392 {
393         SECURE_C_LOGD("Entering function: %s. Params: app_type=%d",
394                                 __func__, app_type);
395
396         switch (app_type) {
397         case PERM_APP_TYPE_WRT:
398                 C_LOGD("App type = WRT");
399                 return "WRT";
400         case PERM_APP_TYPE_OSP:
401                 C_LOGD("App type = OSP");
402                 return "OSP";
403         case PERM_APP_TYPE_WRT_PARTNER:
404                 C_LOGD("App type = WRT_partner");
405                 return "WRT_partner";
406         case PERM_APP_TYPE_WRT_PLATFORM:
407                 C_LOGD("App type = WRT_platform");
408                 return "WRT_platform";
409         case PERM_APP_TYPE_OSP_PARTNER:
410                 C_LOGD("App type = OSP_partner");
411                 return "OSP_partner";
412         case PERM_APP_TYPE_OSP_PLATFORM:
413                 C_LOGD("App type = OSP_platform");
414                 return "OSP_platform";
415         case PERM_APP_TYPE_EFL:
416                 C_LOGD("App type = EFL");
417                 return "EFL";
418         default:
419                 C_LOGD("App type = other");
420                 return NULL;
421         }
422 }
423
424 inline const char* app_type_group_name(app_type_t app_type)
425 {
426         SECURE_C_LOGD("Entering function: %s. Params: app_type=%d",
427                                 __func__, app_type);
428
429         switch (app_type) {
430         case PERM_APP_TYPE_WRT:
431         case PERM_APP_TYPE_WRT_PARTNER:
432         case PERM_APP_TYPE_WRT_PLATFORM:
433                 C_LOGD("App type group name = WRT");
434                 return "WRT";
435         case PERM_APP_TYPE_OSP:
436         case PERM_APP_TYPE_OSP_PARTNER:
437         case PERM_APP_TYPE_OSP_PLATFORM:
438                 C_LOGD("App type group name = OST");
439                 return "OSP";
440         case PERM_APP_TYPE_EFL:
441                 C_LOGD("App type = EFL");
442                 return "EFL";
443         default:
444                 return NULL;
445         }
446 }
447
448 const char* app_path_type_name(app_path_type_t app_path_type)
449 {
450         SECURE_C_LOGD("Entering function %s. Params: app_path_type=%d", __func__, app_path_type);
451
452         switch(app_path_type) {
453         case PERM_APP_PATH_GROUP:
454                 return "GROUP_PATH";
455         case PERM_APP_PATH_PUBLIC:
456                 return "PUBLIC_PATH";
457         case PERM_APP_PATH_SETTINGS:
458                 return "SETTINGS_PATH";
459         case PERM_APP_PATH_NPRUNTIME:
460                 return "NPRUNTIME_PATH";
461         case PERM_APP_PATH_PRIVATE:
462         case PERM_APP_PATH_ANY_LABEL:
463         default:
464                 // App path type not stored in the database, return NULL;
465                 return NULL;
466         }
467 }
468
469 /**
470  * This function changes permission URI to basename for file name.
471  * For e.g. from http://tizen.org/privilege/contact.read will be
472  * created basename : org.tizen.privilege.contact.read
473  */
474 int base_name_from_perm(const char *s_perm, char **ps_name)
475 {
476         SECURE_C_LOGD("Entering function: %s. Params: perm=%s",
477                                 __func__, s_perm);
478
479         iri_t *piri_parsed = NULL;
480         char *pc_rest_slash = NULL;
481
482         piri_parsed = iri_parse(s_perm);
483         if (piri_parsed == NULL || piri_parsed->host == NULL) {
484                 SECURE_C_LOGE("Bad permission format : %s", s_perm);
485                 iri_destroy(piri_parsed);
486                 return PC_ERR_INVALID_PARAM;
487         }
488
489         ssize_t i_host_size = strlen(piri_parsed->host);
490         ssize_t i_path_start = 0;
491         char * pc_host_dot = NULL;
492
493         if(piri_parsed->path) {
494                 pc_host_dot = strrchr(piri_parsed->host, '.');
495                 i_path_start = i_host_size;
496         }
497
498         int ret = asprintf(ps_name, "%s%s%.*s%s",
499                            pc_host_dot ? pc_host_dot + 1 : "",
500                            pc_host_dot ? "." : "",
501                            pc_host_dot ? pc_host_dot - piri_parsed->host : i_host_size,
502                            piri_parsed->host,
503                            piri_parsed->path ? piri_parsed->path : "");
504         if (ret == -1) {
505                 C_LOGE("asprintf failed");
506                 iri_destroy(piri_parsed);
507                 return PC_ERR_MEM_OPERATION;
508         }
509
510         pc_rest_slash = *ps_name + i_path_start;
511         while ((pc_rest_slash = strchr(pc_rest_slash, '/'))) {
512                 *pc_rest_slash = '.';
513         }
514
515         iri_destroy(piri_parsed);
516         return PC_OPERATION_SUCCESS;
517 }