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