perm_checker.c revision 7341494707810f709855ea85ce03a8ec3ac8dbaf
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 PERMS(M) (M & ~S_IFMT) 40#define MAX_NAME_LEN 4096 41#define MAX_UID_LEN 256 42#define MAX_GID_LEN MAX_UID_LEN 43 44enum perm_rule_type {EXACT_FILE = 0, EXACT_DIR, WILDCARD, RECURSIVE, 45 NUM_PR_TYPES}; 46 47struct perm_rule { 48 char *rule_text; 49 int rule_line; 50 char *spec; 51 mode_t min_mode; 52 mode_t max_mode; 53 uid_t min_uid; 54 uid_t max_uid; 55 gid_t min_gid; 56 gid_t max_gid; 57 enum perm_rule_type type; 58 struct perm_rule *next; 59}; 60 61typedef struct perm_rule perm_rule_t; 62 63static perm_rule_t *rules[NUM_PR_TYPES]; 64 65static uid_t str2uid(char *str, int line_num) 66{ 67 struct passwd *pw; 68 69 if (isdigit(str[0])) 70 return (uid_t) atol(str); 71 72 if (!(pw = getpwnam(str))) { 73 printf("# ERROR # Invalid uid '%s' reading line %d\n", str, line_num); 74 exit(255); 75 } 76 return pw->pw_uid; 77} 78 79static gid_t str2gid(char *str, int line_num) 80{ 81 struct group *gr; 82 83 if (isdigit(str[0])) 84 return (uid_t) atol(str); 85 86 if (!(gr = getgrnam(str))) { 87 printf("# ERROR # Invalid gid '%s' reading line %d\n", str, line_num); 88 exit(255); 89 } 90 return gr->gr_gid; 91} 92 93static int read_rules(FILE *fp) 94{ 95 char spec[MAX_NAME_LEN + 5]; // Allows for "/..." suffix + terminator 96 char min_uid_buf[MAX_UID_LEN + 1], max_uid_buf[MAX_UID_LEN + 1]; 97 char min_gid_buf[MAX_GID_LEN + 1], max_gid_buf[MAX_GID_LEN + 1]; 98 char rule_text_buf[MAX_NAME_LEN + 2*MAX_UID_LEN + 2*MAX_GID_LEN + 9]; 99 unsigned long min_mode, max_mode; 100 perm_rule_t *pr; 101 int res; 102 int num_rules = 0, num_lines = 0; 103 104 // Note: Use of an unsafe C function here is OK, since this is a test 105 while ((res = fscanf(fp, "%s %lo %lo %s %s %s %s\n", spec, 106 &min_mode, &max_mode, min_uid_buf, max_uid_buf, 107 min_gid_buf, max_gid_buf)) != EOF) { 108 num_lines++; 109 if (res < 7) { 110 printf("# WARNING # Invalid rule on line number %d\n", num_lines); 111 continue; 112 } 113 if (!(pr = malloc(sizeof(perm_rule_t)))) { 114 printf("Out of memory.\n"); 115 exit(255); 116 } 117 if (snprintf(rule_text_buf, sizeof(rule_text_buf), 118 "%s %lo %lo %s %s %s %s", spec, min_mode, max_mode, 119 min_uid_buf, max_uid_buf, min_gid_buf, max_gid_buf) 120 >= (long int) sizeof(rule_text_buf)) { 121 // This should never happen, but just in case... 122 printf("# ERROR # Maximum length limits exceeded on line %d\n", 123 num_lines); 124 exit(255); 125 } 126 pr->rule_text = strndup(rule_text_buf, sizeof(rule_text_buf)); 127 pr->rule_line = num_lines; 128 if (strstr(spec, "/...")) { 129 pr->spec = strndup(spec, strlen(spec) - 3); 130 pr->type = RECURSIVE; 131 } else if (spec[strlen(spec) - 1] == '*') { 132 pr->spec = strndup(spec, strlen(spec) - 1); 133 pr->type = WILDCARD; 134 } else if (spec[strlen(spec) - 1] == '/') { 135 pr->spec = strdup(spec); 136 pr->type = EXACT_DIR; 137 } else { 138 pr->spec = strdup(spec); 139 pr->type = EXACT_FILE; 140 } 141 if ((pr->spec == NULL) || (pr->rule_text == NULL)) { 142 printf("Out of memory.\n"); 143 exit(255); 144 } 145 pr->min_mode = min_mode; 146 pr->max_mode = max_mode; 147 pr->min_uid = str2uid(min_uid_buf, num_lines); 148 pr->max_uid = str2uid(max_uid_buf, num_lines); 149 pr->min_gid = str2gid(min_gid_buf, num_lines); 150 pr->max_gid = str2gid(max_gid_buf, num_lines); 151 152 // Add the rule to the appropriate set 153 pr->next = rules[pr->type]; 154 rules[pr->type] = pr; 155 num_rules++; 156#if 0 // Useful for debugging 157 printf("rule #%d: type = %d spec = %s min_mode = %o max_mode = %o " 158 "min_uid = %d max_uid = %d min_gid = %d max_gid = %d\n", 159 num_rules, pr->type, pr->spec, pr->min_mode, pr->max_mode, 160 pr->min_uid, pr->max_uid, pr->min_gid, pr->max_gid); 161#endif 162 } 163 return num_lines - num_rules; 164} 165 166static void print_failed_rule(const perm_rule_t *pr) 167{ 168 printf("# INFO # Failed rule #%d: %s\n", pr->rule_line, pr->rule_text); 169} 170 171static void print_new_rule(const char *name, mode_t mode, uid_t uid, gid_t gid) 172{ 173 struct passwd *pw; 174 struct group *gr; 175 gr = getgrgid(gid); 176 pw = getpwuid(uid); 177 printf("%s %4o %4o %s %d %s %d\n", name, mode, mode, pw->pw_name, uid, 178 gr->gr_name, gid); 179} 180 181// Returns 1 if the rule passes, prints the failure and returns 0 if not 182static int pass_rule(const perm_rule_t *pr, mode_t mode, uid_t uid, gid_t gid) 183{ 184 if (((pr->min_mode & mode) == pr->min_mode) && 185 ((pr->max_mode | mode) == pr->max_mode) && 186 (pr->min_gid <= gid) && (pr->max_gid >= gid) && 187 (pr->min_uid <= uid) && (pr->max_uid >= uid)) 188 return 1; 189 print_failed_rule(pr); 190 return 0; 191} 192 193// Returns 0 on success 194static int validate_file(const char *name, mode_t mode, uid_t uid, gid_t gid) 195{ 196 perm_rule_t *pr; 197 int rules_matched = 0; 198 int retval = 0; 199 200 pr = rules[EXACT_FILE]; 201 while (pr != NULL) { 202 if (strcmp(name, pr->spec) == 0) { 203 if (!pass_rule(pr, mode, uid, gid)) 204 retval++; 205 else 206 rules_matched++; // Exact match found 207 } 208 pr = pr->next; 209 } 210 211 if ((retval + rules_matched) > 1) 212 printf("# WARNING # Multiple exact rules for file: %s\n", name); 213 214 // If any exact rule matched or failed, we are done with this file 215 if (retval) 216 print_new_rule(name, mode, uid, gid); 217 if (rules_matched || retval) 218 return retval; 219 220 pr = rules[WILDCARD]; 221 while (pr != NULL) { 222 // Check if the spec is a prefix of the filename, and that the file 223 // is actually in the same directory as the wildcard. 224 if ((strstr(name, pr->spec) == name) && 225 (!strchr(name + strlen(pr->spec), '/'))) { 226 if (!pass_rule(pr, mode, uid, gid)) 227 retval++; 228 else 229 rules_matched++; 230 } 231 pr = pr->next; 232 } 233 234 pr = rules[RECURSIVE]; 235 while (pr != NULL) { 236 if (strstr(name, pr->spec) == name) { 237 if (!pass_rule(pr, mode, uid, gid)) 238 retval++; 239 else 240 rules_matched++; 241 } 242 pr = pr->next; 243 } 244 245 if (!rules_matched) 246 retval++; // In case no rules either matched or failed, be sure to fail 247 248 if (retval) 249 print_new_rule(name, mode, uid, gid); 250 251 return retval; 252} 253 254// Returns 0 on success 255static int validate_link(const char *name, mode_t mode, uid_t uid, gid_t gid) 256{ 257 perm_rule_t *pr; 258 int rules_matched = 0; 259 int retval = 0; 260 261 // For now, we match links against "exact" file rules only 262 pr = rules[EXACT_FILE]; 263 while (pr != NULL) { 264 if (strcmp(name, pr->spec) == 0) { 265 if (!pass_rule(pr, mode, uid, gid)) 266 retval++; 267 else 268 rules_matched++; // Exact match found 269 } 270 pr = pr->next; 271 } 272 273 if ((retval + rules_matched) > 1) 274 printf("# WARNING # Multiple exact rules for link: %s\n", name); 275 if (retval) 276 print_new_rule(name, mode, uid, gid); 277 278 // Note: Unlike files, if no rules matches for links, retval = 0 (success). 279 return retval; 280} 281 282// Returns 0 on success 283static int validate_dir(const char *name, mode_t mode, uid_t uid, gid_t gid) 284{ 285 perm_rule_t *pr; 286 int rules_matched = 0; 287 int retval = 0; 288 289 pr = rules[EXACT_DIR]; 290 while (pr != NULL) { 291 if (strcmp(name, pr->spec) == 0) { 292 if (!pass_rule(pr, mode, uid, gid)) 293 retval++; 294 else 295 rules_matched++; // Exact match found 296 } 297 pr = pr->next; 298 } 299 300 if ((retval + rules_matched) > 1) 301 printf("# WARNING # Multiple exact rules for directory: %s\n", name); 302 303 // If any exact rule matched or failed, we are done with this directory 304 if (retval) 305 print_new_rule(name, mode, uid, gid); 306 if (rules_matched || retval) 307 return retval; 308 309 pr = rules[RECURSIVE]; 310 while (pr != NULL) { 311 if (strstr(name, pr->spec) == name) { 312 if (!pass_rule(pr, mode, uid, gid)) 313 retval++; 314 else 315 rules_matched++; 316 } 317 pr = pr->next; 318 } 319 320 if (!rules_matched) 321 retval++; // In case no rules either matched or failed, be sure to fail 322 323 if (retval) 324 print_new_rule(name, mode, uid, gid); 325 326 return retval; 327} 328 329// Returns 0 on success 330static int check_path(const char *name) 331{ 332 char namebuf[MAX_NAME_LEN + 1]; 333 char tmp[MAX_NAME_LEN + 1]; 334 DIR *d; 335 struct dirent *de; 336 struct stat s; 337 int err; 338 int retval = 0; 339 340 err = lstat(name, &s); 341 if (err < 0) { 342 if (errno != ENOENT) 343 { 344 perror(name); 345 return 1; 346 } 347 return 0; // File doesn't exist anymore 348 } 349 350 if (S_ISDIR(s.st_mode)) { 351 if (name[strlen(name) - 1] != '/') 352 snprintf(namebuf, sizeof(namebuf), "%s/", name); 353 else 354 snprintf(namebuf, sizeof(namebuf), "%s", name); 355 356 retval |= validate_dir(namebuf, PERMS(s.st_mode), s.st_uid, s.st_gid); 357 d = opendir(namebuf); 358 if(d == 0) { 359 printf("%s : opendir failed: %s\n", namebuf, strerror(errno)); 360 return 1; 361 } 362 363 while ((de = readdir(d)) != 0) { 364 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) 365 continue; 366 snprintf(tmp, sizeof(tmp), "%s%s", namebuf, de->d_name); 367 retval |= check_path(tmp); 368 } 369 closedir(d); 370 return retval; 371 } else if (S_ISLNK(s.st_mode)) { 372 validate_link(name, PERMS(s.st_mode), s.st_uid, s.st_gid); 373 } else { 374 return validate_file(name, PERMS(s.st_mode), s.st_uid, s.st_gid); 375 } 376} 377 378int main(int argc, char **argv) 379{ 380 FILE *fp; 381 int i; 382 383 // Initialize ruleset pointers 384 for (i = 0; i < NUM_PR_TYPES; i++) 385 rules[i] = NULL; 386 387 if (!(fp = fopen("/etc/perm_checker.conf", "r"))) { 388 printf("Error opening /etc/perm_checker.conf\n"); 389 exit(255); 390 } 391 read_rules(fp); 392 fclose(fp); 393 394 if (check_path("/")) 395 return 255; 396 397 printf("Passed.\n"); 398 return 0; 399} 400