• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 // A simple file permissions checker. See associated README.
18 
19 #define _GNU_SOURCE
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stdarg.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <sys/types.h>
27 #include <dirent.h>
28 #include <errno.h>
29 
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <time.h>
33 
34 #include <pwd.h>
35 #include <grp.h>
36 
37 #include <linux/kdev_t.h>
38 
39 #define DEFAULT_CONFIG_FILE "/data/local/perm_checker.conf"
40 
41 #define PERMS(M) (M & ~S_IFMT)
42 #define MAX_NAME_LEN 4096
43 #define MAX_UID_LEN 256
44 #define MAX_GID_LEN MAX_UID_LEN
45 
46 static char *config_file;
47 static char *executable_file;
48 
49 enum perm_rule_type {EXACT_FILE = 0, EXACT_DIR, WILDCARD, RECURSIVE,
50     NUM_PR_TYPES};
51 
52 struct perm_rule {
53     char *rule_text;
54     int rule_line;
55     char *spec;
56     mode_t min_mode;
57     mode_t max_mode;
58     uid_t min_uid;
59     uid_t max_uid;
60     gid_t min_gid;
61     gid_t max_gid;
62     enum perm_rule_type type;
63     struct perm_rule *next;
64 };
65 
66 typedef struct perm_rule perm_rule_t;
67 
68 static perm_rule_t *rules[NUM_PR_TYPES];
69 
str2uid(char * str,int line_num)70 static uid_t str2uid(char *str, int line_num)
71 {
72     struct passwd *pw;
73 
74     if (isdigit(str[0]))
75         return (uid_t) atol(str);
76 
77     if (!(pw = getpwnam(str))) {
78         printf("# ERROR # Invalid uid '%s' reading line %d\n", str, line_num);
79         exit(255);
80     }
81     return pw->pw_uid;
82 }
83 
str2gid(char * str,int line_num)84 static gid_t str2gid(char *str, int line_num)
85 {
86     struct group *gr;
87 
88     if (isdigit(str[0]))
89         return (uid_t) atol(str);
90 
91     if (!(gr = getgrnam(str))) {
92         printf("# ERROR # Invalid gid '%s' reading line %d\n", str, line_num);
93         exit(255);
94     }
95     return gr->gr_gid;
96 }
97 
add_rule(int line_num,char * spec,unsigned long min_mode,unsigned long max_mode,char * min_uid_buf,char * max_uid_buf,char * min_gid_buf,char * max_gid_buf)98 static void add_rule(int line_num, char *spec,
99                      unsigned long min_mode, unsigned long max_mode,
100                      char *min_uid_buf, char *max_uid_buf,
101                      char *min_gid_buf, char *max_gid_buf) {
102 
103     char rule_text_buf[MAX_NAME_LEN + 2*MAX_UID_LEN + 2*MAX_GID_LEN + 9];
104     perm_rule_t *pr = malloc(sizeof(perm_rule_t));
105     if (!pr) {
106         printf("Out of memory.\n");
107         exit(255);
108     }
109     if (snprintf(rule_text_buf, sizeof(rule_text_buf),
110                  "%s %lo %lo %s %s %s %s", spec, min_mode, max_mode,
111                  min_uid_buf, max_uid_buf, min_gid_buf, max_gid_buf)
112                  >= (long int) sizeof(rule_text_buf)) {
113         // This should never happen, but just in case...
114         printf("# ERROR # Maximum length limits exceeded on line %d\n",
115                line_num);
116         exit(255);
117     }
118     pr->rule_text = strndup(rule_text_buf, sizeof(rule_text_buf));
119     pr->rule_line = line_num;
120     if (strstr(spec, "/...")) {
121         pr->spec = strndup(spec, strlen(spec) - 3);
122         pr->type = RECURSIVE;
123     } else if (spec[strlen(spec) - 1] == '*') {
124         pr->spec = strndup(spec, strlen(spec) - 1);
125         pr->type = WILDCARD;
126     } else if (spec[strlen(spec) - 1] == '/') {
127         pr->spec = strdup(spec);
128         pr->type = EXACT_DIR;
129     } else {
130         pr->spec = strdup(spec);
131         pr->type = EXACT_FILE;
132     }
133     if ((pr->spec == NULL) || (pr->rule_text == NULL)) {
134         printf("Out of memory.\n");
135         exit(255);
136     }
137     pr->min_mode = min_mode;
138     pr->max_mode = max_mode;
139     pr->min_uid = str2uid(min_uid_buf, line_num);
140     pr->max_uid = str2uid(max_uid_buf, line_num);
141     pr->min_gid = str2gid(min_gid_buf, line_num);
142     pr->max_gid = str2gid(max_gid_buf, line_num);
143 
144     // Add the rule to the appropriate set
145     pr->next = rules[pr->type];
146     rules[pr->type] = pr;
147 #if 0  // Useful for debugging
148     printf("rule #%d: type = %d spec = %s min_mode = %o max_mode = %o "
149            "min_uid = %d max_uid = %d min_gid = %d max_gid = %d\n",
150            num_rules, pr->type, pr->spec, pr->min_mode, pr->max_mode,
151            pr->min_uid, pr->max_uid, pr->min_gid, pr->max_gid);
152 #endif
153 }
154 
read_rules(FILE * fp)155 static int read_rules(FILE *fp)
156 {
157     char spec[MAX_NAME_LEN + 5];  // Allows for "/..." suffix + terminator
158     char min_uid_buf[MAX_UID_LEN + 1], max_uid_buf[MAX_UID_LEN + 1];
159     char min_gid_buf[MAX_GID_LEN + 1], max_gid_buf[MAX_GID_LEN + 1];
160     unsigned long min_mode, max_mode;
161     int res;
162     int num_rules = 0, num_lines = 0;
163 
164     // Note: Use of an unsafe C function here is OK, since this is a test
165     while ((res = fscanf(fp, "%s %lo %lo %s %s %s %s\n", spec,
166                          &min_mode, &max_mode, min_uid_buf, max_uid_buf,
167                          min_gid_buf, max_gid_buf)) != EOF) {
168         num_lines++;
169         if (res < 7) {
170             printf("# WARNING # Invalid rule on line number %d\n", num_lines);
171             continue;
172         }
173         add_rule(num_lines, spec,
174                  min_mode, max_mode,
175                  min_uid_buf, max_uid_buf,
176                  min_gid_buf, max_gid_buf);
177         num_rules++;
178     }
179 
180     // Automatically add a rule to match this executable itself
181     add_rule(-1, executable_file,
182              000, 0777,
183              "root", "shell",
184              "root", "shell");
185 
186     // Automatically add a rule to match the configuration file
187     add_rule(-1, config_file,
188              000, 0777,
189              "root", "shell",
190              "root", "shell");
191 
192     return num_lines - num_rules;
193 }
194 
print_failed_rule(const perm_rule_t * pr)195 static void print_failed_rule(const perm_rule_t *pr)
196 {
197     printf("# INFO # Failed rule #%d: %s\n", pr->rule_line, pr->rule_text);
198 }
199 
print_new_rule(const char * name,mode_t mode,uid_t uid,gid_t gid)200 static void print_new_rule(const char *name, mode_t mode, uid_t uid, gid_t gid)
201 {
202     struct passwd *pw;
203     struct group *gr;
204     gr = getgrgid(gid);
205     pw = getpwuid(uid);
206     printf("%s %4o %4o %s %d %s %d\n", name, mode, mode, pw->pw_name, uid,
207            gr->gr_name, gid);
208 }
209 
210 // Returns 1 if the rule passes, prints the failure and returns 0 if not
pass_rule(const perm_rule_t * pr,mode_t mode,uid_t uid,gid_t gid)211 static int pass_rule(const perm_rule_t *pr, mode_t mode, uid_t uid, gid_t gid)
212 {
213     if (((pr->min_mode & mode) == pr->min_mode) &&
214             ((pr->max_mode | mode) == pr->max_mode) &&
215             (pr->min_gid <= gid) && (pr->max_gid >= gid) &&
216             (pr->min_uid <= uid) && (pr->max_uid >= uid))
217         return 1;
218     print_failed_rule(pr);
219     return 0;
220 }
221 
222 // Returns 0 on success
validate_file(const char * name,mode_t mode,uid_t uid,gid_t gid)223 static int validate_file(const char *name, mode_t mode, uid_t uid, gid_t gid)
224 {
225     perm_rule_t *pr;
226     int rules_matched = 0;
227     int retval = 0;
228 
229     pr = rules[EXACT_FILE];
230     while (pr != NULL) {
231         if (strcmp(name, pr->spec) == 0) {
232             if (!pass_rule(pr, mode, uid, gid))
233                 retval++;
234             else
235                 rules_matched++;  // Exact match found
236         }
237         pr = pr->next;
238     }
239 
240     if ((retval + rules_matched) > 1)
241         printf("# WARNING # Multiple exact rules for file: %s\n", name);
242 
243     // If any exact rule matched or failed, we are done with this file
244     if (retval)
245         print_new_rule(name, mode, uid, gid);
246     if (rules_matched || retval)
247         return retval;
248 
249     pr = rules[WILDCARD];
250     while (pr != NULL) {
251         // Check if the spec is a prefix of the filename, and that the file
252         // is actually in the same directory as the wildcard.
253         if ((strstr(name, pr->spec) == name) &&
254                 (!strchr(name + strlen(pr->spec), '/'))) {
255             if (!pass_rule(pr, mode, uid, gid))
256                 retval++;
257             else
258                 rules_matched++;
259         }
260         pr = pr->next;
261     }
262 
263     pr = rules[RECURSIVE];
264     while (pr != NULL) {
265         if (strstr(name, pr->spec) == name) {
266             if (!pass_rule(pr, mode, uid, gid))
267                 retval++;
268             else
269                 rules_matched++;
270         }
271         pr = pr->next;
272     }
273 
274     if (!rules_matched)
275         retval++;  // In case no rules either matched or failed, be sure to fail
276 
277     if (retval)
278         print_new_rule(name, mode, uid, gid);
279 
280     return retval;
281 }
282 
283 // Returns 0 on success
validate_link(const char * name,mode_t mode,uid_t uid,gid_t gid)284 static int validate_link(const char *name, mode_t mode, uid_t uid, gid_t gid)
285 {
286     perm_rule_t *pr;
287     int rules_matched = 0;
288     int retval = 0;
289 
290     // For now, we match links against "exact" file rules only
291     pr = rules[EXACT_FILE];
292     while (pr != NULL) {
293         if (strcmp(name, pr->spec) == 0) {
294             if (!pass_rule(pr, mode, uid, gid))
295                 retval++;
296             else
297                 rules_matched++;  // Exact match found
298         }
299         pr = pr->next;
300     }
301 
302     if ((retval + rules_matched) > 1)
303         printf("# WARNING # Multiple exact rules for link: %s\n", name);
304     if (retval)
305         print_new_rule(name, mode, uid, gid);
306 
307     // Note: Unlike files, if no rules matches for links, retval = 0 (success).
308     return retval;
309 }
310 
311 // Returns 0 on success
validate_dir(const char * name,mode_t mode,uid_t uid,gid_t gid)312 static int validate_dir(const char *name, mode_t mode, uid_t uid, gid_t gid)
313 {
314     perm_rule_t *pr;
315     int rules_matched = 0;
316     int retval = 0;
317 
318     pr = rules[EXACT_DIR];
319     while (pr != NULL) {
320         if (strcmp(name, pr->spec) == 0) {
321             if (!pass_rule(pr, mode, uid, gid))
322                 retval++;
323             else
324                 rules_matched++;  // Exact match found
325         }
326         pr = pr->next;
327     }
328 
329     if ((retval + rules_matched) > 1)
330         printf("# WARNING # Multiple exact rules for directory: %s\n", name);
331 
332     // If any exact rule matched or failed, we are done with this directory
333     if (retval)
334         print_new_rule(name, mode, uid, gid);
335     if (rules_matched || retval)
336         return retval;
337 
338     pr = rules[RECURSIVE];
339     while (pr != NULL) {
340         if (strstr(name, pr->spec) == name) {
341             if (!pass_rule(pr, mode, uid, gid))
342                 retval++;
343             else
344                 rules_matched++;
345         }
346         pr = pr->next;
347     }
348 
349     if (!rules_matched)
350         retval++;  // In case no rules either matched or failed, be sure to fail
351 
352     if (retval)
353         print_new_rule(name, mode, uid, gid);
354 
355     return retval;
356 }
357 
358 // Returns 0 on success
check_path(const char * name)359 static int check_path(const char *name)
360 {
361     char namebuf[MAX_NAME_LEN + 1];
362     char tmp[MAX_NAME_LEN + 1];
363     DIR *d;
364     struct dirent *de;
365     struct stat s;
366     int err;
367     int retval = 0;
368 
369     err = lstat(name, &s);
370     if (err < 0) {
371         if (errno != ENOENT)
372         {
373             perror(name);
374             return 1;
375         }
376         return 0;  // File doesn't exist anymore
377     }
378 
379     if (S_ISDIR(s.st_mode)) {
380         if (name[strlen(name) - 1] != '/')
381             snprintf(namebuf, sizeof(namebuf), "%s/", name);
382         else
383             snprintf(namebuf, sizeof(namebuf), "%s", name);
384 
385         retval |= validate_dir(namebuf, PERMS(s.st_mode), s.st_uid, s.st_gid);
386         d = opendir(namebuf);
387         if(d == 0) {
388             printf("%s : opendir failed: %s\n", namebuf, strerror(errno));
389             return 1;
390         }
391 
392         while ((de = readdir(d)) != 0) {
393             if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
394                 continue;
395             snprintf(tmp, sizeof(tmp), "%s%s", namebuf, de->d_name);
396             retval |= check_path(tmp);
397         }
398         closedir(d);
399         return retval;
400     } else if (S_ISLNK(s.st_mode)) {
401         return validate_link(name, PERMS(s.st_mode), s.st_uid, s.st_gid);
402     } else {
403         return validate_file(name, PERMS(s.st_mode), s.st_uid, s.st_gid);
404     }
405 }
406 
main(int argc,char ** argv)407 int main(int argc, char **argv)
408 {
409     FILE *fp;
410     int i;
411 
412     if (argc > 2) {
413       printf("\nSyntax: %s [configfilename]\n", argv[0]);
414     }
415     config_file = (argc == 2) ? argv[1] : DEFAULT_CONFIG_FILE;
416     executable_file = argv[0];
417 
418     // Initialize ruleset pointers
419     for (i = 0; i < NUM_PR_TYPES; i++)
420         rules[i] = NULL;
421 
422     if (!(fp = fopen(config_file, "r"))) {
423         printf("Error opening %s\n", config_file);
424         exit(255);
425     }
426     read_rules(fp);
427     fclose(fp);
428 
429     if (check_path("/"))
430         return 255;
431 
432     printf("Passed.\n");
433     return 0;
434 }
435