[132] | 1 | /* |
---|
[1939] | 2 | * |
---|
| 3 | * Copyright (C) 1988, 1989 by the Massachusetts Institute of Technology |
---|
| 4 | * Developed by the MIT Student Information Processing Board (SIPB). |
---|
| 5 | * For copying information, see the file mit-copyright.h in this release. |
---|
| 6 | * |
---|
| 7 | */ |
---|
| 8 | /* |
---|
[22404] | 9 | * $Id: acl.c,v 1.18 2006-03-10 07:11:39 ghudson Exp $ |
---|
[132] | 10 | * |
---|
[148] | 11 | * Routines for the manipulation of access control lists in core, |
---|
| 12 | * along with routines to move them to and from files. |
---|
| 13 | * |
---|
[132] | 14 | */ |
---|
| 15 | |
---|
[1476] | 16 | #ifndef __STDC__ |
---|
| 17 | #define const |
---|
| 18 | #endif |
---|
| 19 | |
---|
[132] | 20 | #ifndef lint |
---|
[1476] | 21 | static const char rcsid_acl_c[] = |
---|
[22404] | 22 | "$Id: acl.c,v 1.18 2006-03-10 07:11:39 ghudson Exp $"; |
---|
[12459] | 23 | #endif /* lint */ |
---|
[132] | 24 | |
---|
| 25 | #include <stdio.h> |
---|
[22404] | 26 | #include <stdlib.h> |
---|
[8855] | 27 | #include <string.h> |
---|
[818] | 28 | #include <ctype.h> |
---|
[1476] | 29 | #include <discuss/discuss.h> |
---|
[1624] | 30 | #include "internal.h" |
---|
[132] | 31 | |
---|
| 32 | char *acl_union(), *acl_intersection(), *acl_subtract(); |
---|
[1476] | 33 | static void panic (); |
---|
[132] | 34 | |
---|
[166] | 35 | bool acl_check(list, principal, modes) |
---|
[1476] | 36 | dsc_acl *list; |
---|
| 37 | const char *principal; |
---|
| 38 | const char *modes; |
---|
[132] | 39 | { |
---|
[1476] | 40 | register dsc_acl_entry *ae; |
---|
[132] | 41 | register int n; |
---|
| 42 | for (ae = list->acl_entries, n=list->acl_length; |
---|
| 43 | n; |
---|
| 44 | ae++, n--) { |
---|
| 45 | if ((strcmp(principal, ae->principal) == 0) || |
---|
| 46 | (strcmp("*", ae->principal) == 0)) |
---|
| 47 | return(acl_is_subset(modes, ae->modes)); |
---|
| 48 | } |
---|
| 49 | return (FALSE); |
---|
| 50 | } |
---|
| 51 | |
---|
[1476] | 52 | dsc_acl *acl_read(fd) |
---|
| 53 | int fd; |
---|
[132] | 54 | { |
---|
| 55 | static char buf[128]; |
---|
[628] | 56 | FILE *f=fdopen(dup(fd), "r"); |
---|
[132] | 57 | register char *cp; |
---|
| 58 | register int n; |
---|
[1476] | 59 | register dsc_acl_entry *ae; |
---|
| 60 | register dsc_acl *list; |
---|
[818] | 61 | |
---|
| 62 | if (!f) return NULL; /* oops. */ |
---|
| 63 | |
---|
[1476] | 64 | list = (dsc_acl *) malloc(sizeof(dsc_acl)); |
---|
[823] | 65 | if (!list) goto punt; |
---|
[1476] | 66 | list->acl_entries = (dsc_acl_entry *)NULL; |
---|
[823] | 67 | list->acl_length = 0; |
---|
| 68 | |
---|
[818] | 69 | if (fgets(buf, 128, f) == NULL) goto punt; |
---|
[823] | 70 | if (!isdigit((unsigned)buf[0])) goto punt; |
---|
[818] | 71 | |
---|
| 72 | n=atoi(buf); |
---|
[1476] | 73 | list->acl_entries = |
---|
| 74 | (dsc_acl_entry *)malloc((unsigned)(n * sizeof(dsc_acl_entry))); |
---|
[818] | 75 | if (!list->acl_entries) goto punt; |
---|
| 76 | |
---|
| 77 | list->acl_length = 0; |
---|
| 78 | for (ae=list->acl_entries; n; --n) { |
---|
[132] | 79 | buf[0]=0; |
---|
[818] | 80 | if (fgets(buf, 128, f) == NULL) goto punt; |
---|
[7182] | 81 | if(cp=strchr(buf, '\n')) *cp='\0'; |
---|
| 82 | if(cp=strchr(buf, ':')) { |
---|
[132] | 83 | *cp='\0'; |
---|
[818] | 84 | list->acl_length++; |
---|
| 85 | ae->principal = NULL; |
---|
[166] | 86 | ae->modes = malloc((unsigned)(strlen(buf)+1)); |
---|
[818] | 87 | if (!ae->modes) goto punt; |
---|
[166] | 88 | (void) strcpy(ae->modes, buf); |
---|
| 89 | ae->principal = malloc((unsigned)(strlen(cp+1)+1)); |
---|
[818] | 90 | if (!ae->principal) goto punt; |
---|
[166] | 91 | (void) strcpy(ae->principal, cp+1); |
---|
[818] | 92 | ae++; |
---|
[132] | 93 | } else { /* skip line */ |
---|
| 94 | } |
---|
| 95 | } |
---|
[628] | 96 | (void) fclose(f); |
---|
[132] | 97 | return(list); |
---|
[818] | 98 | punt: |
---|
| 99 | fclose(f); |
---|
| 100 | if (list) acl_destroy(list); |
---|
| 101 | return NULL; |
---|
[132] | 102 | } |
---|
[628] | 103 | /* |
---|
| 104 | * Attempt to write an access control list to fd. |
---|
| 105 | * Return FALSE on failure with reason in errno. |
---|
| 106 | */ |
---|
[132] | 107 | |
---|
[166] | 108 | bool acl_write(fd, list) |
---|
[132] | 109 | int fd; |
---|
[1476] | 110 | dsc_acl *list; |
---|
[132] | 111 | { |
---|
[628] | 112 | FILE *f=fdopen(dup(fd), "w"); |
---|
[132] | 113 | register int n; |
---|
[1476] | 114 | register dsc_acl_entry *ae; |
---|
[628] | 115 | char buf[BUFSIZ]; |
---|
| 116 | int len; |
---|
| 117 | |
---|
| 118 | (void) sprintf(buf, "%d\n", list->acl_length); |
---|
| 119 | len = strlen(buf); |
---|
| 120 | if (fwrite(buf, 1, len, f) != len) goto punt; |
---|
| 121 | |
---|
| 122 | for (ae=list->acl_entries, n=list->acl_length; n; --n, ++ae) { |
---|
[15700] | 123 | (void) sprintf(buf, "%s:%.100s\n", ae->modes, ae->principal); |
---|
[628] | 124 | len = strlen(buf); |
---|
| 125 | if (fwrite(buf, 1, len, f) != len) goto punt; |
---|
| 126 | } |
---|
| 127 | if (fflush(f) == EOF) goto punt; |
---|
| 128 | if (fsync(fileno(f)) < 0) goto punt; |
---|
| 129 | if (fclose(f) == EOF) return FALSE; |
---|
[148] | 130 | return(TRUE); |
---|
[628] | 131 | punt: |
---|
| 132 | fclose(f); |
---|
| 133 | return(FALSE); |
---|
[132] | 134 | } |
---|
| 135 | |
---|
| 136 | acl_add_access(list, principal, modes) |
---|
[1476] | 137 | dsc_acl *list; |
---|
| 138 | const char *principal; |
---|
| 139 | const char *modes; |
---|
[132] | 140 | { |
---|
| 141 | register int n; |
---|
[1476] | 142 | register dsc_acl_entry *ae; |
---|
[132] | 143 | char *new_modes; |
---|
| 144 | for(ae=list->acl_entries, n=list->acl_length; n; --n, ++ae) { |
---|
| 145 | if (!strcmp(ae->principal, principal)) { |
---|
| 146 | new_modes = acl_union(modes, ae->modes); |
---|
[166] | 147 | (void) free(ae->modes); |
---|
[132] | 148 | ae->modes=new_modes; |
---|
| 149 | return; |
---|
| 150 | } |
---|
| 151 | } |
---|
| 152 | /* |
---|
| 153 | * Whoops.. fell through. |
---|
| 154 | * Realloc the world - or at least the vector. |
---|
| 155 | */ |
---|
| 156 | list->acl_length++; |
---|
[1476] | 157 | list->acl_entries = |
---|
| 158 | (dsc_acl_entry *) realloc((char *)(list->acl_entries), |
---|
| 159 | (unsigned)(list->acl_length |
---|
| 160 | * sizeof(dsc_acl_entry))); |
---|
[132] | 161 | ae = list->acl_entries + list->acl_length - 2; |
---|
| 162 | /* |
---|
| 163 | * Is the last entry "*"? If so, push it back one. |
---|
| 164 | */ |
---|
[180] | 165 | if (list -> acl_length > 1) { /* non-empty acl */ |
---|
| 166 | if (!strcmp(ae->principal, "*")) { |
---|
| 167 | if(!strcmp(principal, "*")) |
---|
[258] | 168 | panic("acl broke"); |
---|
[180] | 169 | *(ae+1) = *ae; |
---|
| 170 | --ae; |
---|
| 171 | } |
---|
[132] | 172 | } |
---|
| 173 | ae++; |
---|
[166] | 174 | ae->principal = malloc((unsigned)(strlen(principal)+1)); |
---|
| 175 | (void) strcpy(ae->principal, principal); |
---|
| 176 | ae->modes = malloc((unsigned)(strlen(modes)+1)); |
---|
| 177 | (void) strcpy(ae->modes, modes); |
---|
[132] | 178 | } |
---|
| 179 | |
---|
[148] | 180 | #ifdef notdef |
---|
| 181 | acl_delete_modes(list, principal, modes) |
---|
[1476] | 182 | dsc_acl *list; |
---|
[132] | 183 | char *principal; |
---|
| 184 | char *modes; |
---|
| 185 | { |
---|
[1476] | 186 | register dsc_acl_entry *ae, *ae1; |
---|
[132] | 187 | register int n; |
---|
| 188 | char *new_modes; |
---|
| 189 | for(ae=list->acl_entries, n=list->acl_length; n; --n, ++ae) { |
---|
| 190 | if (!strcmp(ae->principal, principal)) { |
---|
| 191 | new_modes = acl_subtract(modes, ae->modes); |
---|
[166] | 192 | (void) free(ae->modes); |
---|
[132] | 193 | ae->modes=new_modes; |
---|
| 194 | goto cleanup; |
---|
| 195 | } |
---|
| 196 | } |
---|
| 197 | return; |
---|
| 198 | cleanup: |
---|
| 199 | ae1 = list->acl_entries + list->acl_length - 1; |
---|
| 200 | if ((strcmp(ae1->principal, "*")!= 0 && strcmp(ae->modes, "")) || |
---|
| 201 | (!strcmp(ae1->principal, "*") && !strcmp(ae->modes, ae1->modes))) { |
---|
[166] | 202 | (void) free(ae->principal); |
---|
| 203 | (void) free(ae->modes); |
---|
[132] | 204 | for (; ae <ae1; ae++) *ae= *(ae+1); |
---|
| 205 | list->acl_length--; |
---|
| 206 | list->acl_entries = (acl_entry *) realloc(list->acl_entries, |
---|
[1476] | 207 | list->acl_length * sizeof(dsc_acl_entry)); |
---|
[132] | 208 | } |
---|
| 209 | |
---|
| 210 | } |
---|
[12459] | 211 | #endif /* notdef */ |
---|
[132] | 212 | acl_replace_access(list, principal, modes) |
---|
[1476] | 213 | dsc_acl *list; |
---|
| 214 | const char *principal; |
---|
| 215 | const char *modes; |
---|
[132] | 216 | { |
---|
[1476] | 217 | register dsc_acl_entry *ae; |
---|
[148] | 218 | register int n; |
---|
| 219 | for(ae=list->acl_entries, n=list->acl_length; n; --n, ++ae) { |
---|
| 220 | if (!strcmp(ae->principal, principal)) { |
---|
[166] | 221 | (void) free(ae->modes); |
---|
| 222 | ae->modes = malloc((unsigned)(strlen(modes)+1)); |
---|
| 223 | (void) strcpy(ae->modes, modes); |
---|
[148] | 224 | return; |
---|
| 225 | } |
---|
| 226 | } |
---|
| 227 | /* Didn't find it. Insert it.. the easy way. */ |
---|
| 228 | acl_add_access(list, principal, modes); |
---|
| 229 | } |
---|
[132] | 230 | |
---|
[166] | 231 | bool |
---|
[148] | 232 | acl_delete_access(list, principal) |
---|
[1476] | 233 | dsc_acl *list; |
---|
| 234 | const char *principal; |
---|
[148] | 235 | { |
---|
[1476] | 236 | register dsc_acl_entry *ae; |
---|
[148] | 237 | register int n; |
---|
| 238 | for(ae=list->acl_entries, n=list->acl_length; n; --n, ++ae) { |
---|
| 239 | if (!strcmp(ae->principal, principal)) { |
---|
| 240 | /* gotcha! */ |
---|
| 241 | list->acl_length--; |
---|
| 242 | while (--n) { |
---|
| 243 | *ae= *(ae+1); |
---|
| 244 | ae++; |
---|
| 245 | } |
---|
| 246 | return TRUE; |
---|
| 247 | } |
---|
| 248 | } |
---|
| 249 | return FALSE; |
---|
[132] | 250 | } |
---|
| 251 | /* |
---|
| 252 | * Return empty ACL |
---|
| 253 | */ |
---|
| 254 | |
---|
[1476] | 255 | dsc_acl *acl_create() |
---|
[132] | 256 | { |
---|
[1476] | 257 | register dsc_acl *result = (dsc_acl *) malloc(sizeof(dsc_acl)); |
---|
[132] | 258 | result->acl_length=0; |
---|
[1476] | 259 | result->acl_entries=(dsc_acl_entry *) malloc(sizeof(dsc_acl_entry)); |
---|
[132] | 260 | return(result); |
---|
| 261 | } |
---|
| 262 | |
---|
[1476] | 263 | dsc_acl *acl_copy (old_acl) |
---|
| 264 | dsc_acl *old_acl; |
---|
[258] | 265 | { |
---|
[1476] | 266 | register dsc_acl *new_acl = (dsc_acl *) malloc (sizeof(dsc_acl)); |
---|
| 267 | register dsc_acl_entry *old_ae,*new_ae; |
---|
[258] | 268 | register int n; |
---|
| 269 | |
---|
| 270 | new_acl -> acl_length = old_acl -> acl_length; |
---|
[1476] | 271 | new_acl -> acl_entries = (dsc_acl_entry *) malloc ((unsigned)(new_acl -> acl_length * sizeof(dsc_acl_entry))); |
---|
[258] | 272 | |
---|
| 273 | for (old_ae = old_acl->acl_entries, new_ae = new_acl->acl_entries, n=new_acl->acl_length; |
---|
| 274 | n; |
---|
| 275 | --n, ++old_ae, ++new_ae) { |
---|
| 276 | new_ae->principal = malloc (strlen(old_ae->principal)+1); |
---|
| 277 | strcpy (new_ae->principal,old_ae->principal); |
---|
| 278 | new_ae->modes = malloc (strlen(old_ae->modes)+1); |
---|
| 279 | strcpy (new_ae->modes,old_ae->modes); |
---|
| 280 | } |
---|
| 281 | |
---|
| 282 | return (new_acl); |
---|
| 283 | } |
---|
| 284 | |
---|
[1476] | 285 | const char *acl_get_access(list, principal) |
---|
| 286 | dsc_acl *list; |
---|
| 287 | const char *principal; |
---|
[132] | 288 | { |
---|
[1476] | 289 | register dsc_acl_entry *ae; |
---|
[132] | 290 | register int n; |
---|
| 291 | |
---|
| 292 | for(ae=list->acl_entries, n=list->acl_length; n; --n, ++ae) { |
---|
| 293 | if (!strcmp(ae->principal, principal) || |
---|
| 294 | !strcmp(ae->principal, "*")) { |
---|
| 295 | return(ae->modes); |
---|
| 296 | } |
---|
| 297 | } |
---|
| 298 | return(""); |
---|
| 299 | } |
---|
| 300 | /* |
---|
| 301 | * Destroy an ACL. |
---|
| 302 | */ |
---|
| 303 | |
---|
| 304 | acl_destroy(list) |
---|
[1476] | 305 | dsc_acl *list; |
---|
[132] | 306 | { |
---|
[1476] | 307 | register dsc_acl_entry *ae; |
---|
[132] | 308 | register int n; |
---|
| 309 | if(!list) return; |
---|
| 310 | for (ae=list->acl_entries, n=list->acl_length; |
---|
[818] | 311 | ae && n; |
---|
[132] | 312 | --n, ++ae) { |
---|
[818] | 313 | if (ae->principal) (void) free(ae->principal); |
---|
| 314 | if (ae->modes) (void) free(ae->modes); |
---|
[132] | 315 | } |
---|
[818] | 316 | if (ae) (void) free((char *)(list->acl_entries)); |
---|
[166] | 317 | (void) free((char *)list); |
---|
[132] | 318 | } |
---|
| 319 | |
---|
| 320 | /* |
---|
| 321 | * Returns true if every character of s1 occurs in s2. |
---|
| 322 | */ |
---|
[166] | 323 | bool acl_is_subset(s1, s2) |
---|
[1476] | 324 | register const char *s1, *s2; |
---|
[132] | 325 | { |
---|
| 326 | register char *last; |
---|
[7182] | 327 | while(*s1 && (last = strchr(s2, *s1))) s1++; |
---|
[132] | 328 | |
---|
| 329 | return(last != NULL); |
---|
| 330 | } |
---|
| 331 | |
---|
| 332 | /* |
---|
| 333 | * Returns the intersection of the two strings s1 and s2. |
---|
| 334 | * This function allocates a new string, which should be freed |
---|
| 335 | * when not needed. |
---|
| 336 | */ |
---|
| 337 | char *acl_intersection(s1, s2) |
---|
[1476] | 338 | register const char *s1, *s2; |
---|
[132] | 339 | { |
---|
| 340 | register char *result=malloc(1); |
---|
| 341 | register int resp=0; |
---|
| 342 | |
---|
| 343 | while(*s1) { |
---|
[7182] | 344 | if(strchr(s2, *s1)) { |
---|
[132] | 345 | result[resp++] = *s1; |
---|
[166] | 346 | result=realloc(result, (unsigned)(resp+1)); |
---|
[132] | 347 | } |
---|
| 348 | s1++; |
---|
| 349 | } |
---|
| 350 | result[resp] = '\0'; |
---|
| 351 | return(result); |
---|
| 352 | } |
---|
| 353 | |
---|
| 354 | char *acl_union(s1, s2) |
---|
[1476] | 355 | register const char *s1, *s2; |
---|
[132] | 356 | { |
---|
| 357 | register int resp=strlen(s2); |
---|
[166] | 358 | register char *result=malloc((unsigned)(resp+1)); |
---|
[132] | 359 | strcpy(result, s2); |
---|
| 360 | while(*s1) { |
---|
[7182] | 361 | if (!strchr(result, *s1)) { |
---|
[132] | 362 | result[resp++]= *s1; |
---|
[166] | 363 | result=realloc(result, (unsigned)(resp+1)); |
---|
[132] | 364 | } |
---|
| 365 | s1++; |
---|
| 366 | } |
---|
| 367 | result[resp]='\0'; |
---|
| 368 | return(result); |
---|
| 369 | } |
---|
| 370 | |
---|
| 371 | char *acl_subtract(s1, s2) |
---|
[1476] | 372 | register const char *s1, *s2; |
---|
[132] | 373 | { |
---|
| 374 | register char *result = malloc(1); |
---|
| 375 | register int len=0; |
---|
| 376 | for (; *s2; s2++) { |
---|
[7182] | 377 | if (!strchr(s1, *s2)) { |
---|
[166] | 378 | result = realloc(result, (unsigned)(len+2)); |
---|
[132] | 379 | result[len++]= *s2; |
---|
| 380 | } |
---|
| 381 | } |
---|
| 382 | result[len]='\0'; |
---|
| 383 | return(result); |
---|
| 384 | } |
---|
| 385 | |
---|
[148] | 386 | /* |
---|
| 387 | * Canonicalize an acl string; sort the characters of s1 into |
---|
| 388 | * the order found in s2, removing duplicates, and returning an error code if |
---|
| 389 | * any of them are not in s2. |
---|
| 390 | * This is a sucky algorithm, but who really gives? |
---|
| 391 | */ |
---|
[132] | 392 | |
---|
[148] | 393 | char *acl_canon(s1, s2, code) |
---|
[1476] | 394 | register const char *s1, *s2; |
---|
[148] | 395 | int *code; |
---|
| 396 | { |
---|
[1476] | 397 | register const char *cp; |
---|
[148] | 398 | register char *out; |
---|
[197] | 399 | register int len, maxlen; |
---|
[148] | 400 | |
---|
| 401 | *code = 0; |
---|
| 402 | for (cp = s1; *cp; cp++) { |
---|
[7182] | 403 | if (*cp != ' ' && !strchr(s2, *cp)) |
---|
[148] | 404 | *code = BAD_MODES; |
---|
| 405 | } |
---|
[197] | 406 | maxlen = strlen(s2); |
---|
| 407 | out = malloc(maxlen + 1); len = 0; |
---|
[148] | 408 | for (cp = s2; *cp; cp++) { |
---|
[197] | 409 | len++; |
---|
| 410 | if (len > maxlen) /* shouldn't happen, but.. */ |
---|
| 411 | out = realloc(out, (unsigned)len + 1); |
---|
| 412 | |
---|
[7182] | 413 | out[len-1] = (strchr(s1, *cp) ? *cp : ' '); |
---|
[148] | 414 | } |
---|
| 415 | out[len]='\0'; |
---|
| 416 | return(out); |
---|
| 417 | } |
---|
| 418 | |
---|
| 419 | |
---|
| 420 | |
---|
| 421 | |
---|
[132] | 422 | #ifdef TESTS |
---|
| 423 | #include <sys/file.h> |
---|
| 424 | main() |
---|
| 425 | { |
---|
| 426 | int fd; |
---|
[1476] | 427 | dsc_acl *a; |
---|
[132] | 428 | |
---|
| 429 | printf("%s * %s = %s\n", "eabce", "abcd", |
---|
| 430 | acl_intersection("eabce", "abcd")); |
---|
| 431 | printf("%s + %s = %s\n", "abc", "cbade", acl_union("abc", "cbade")); |
---|
| 432 | printf("%s < %s = %d\n", "ab", "bcde", acl_is_subset("ab", "bcde")); |
---|
| 433 | printf("%s < %s = %d\n", "ab", "abcde", acl_is_subset("ab", "abcde")); |
---|
| 434 | |
---|
| 435 | fd = open ("foo.acl", O_RDONLY); |
---|
| 436 | a=acl_read(fd); |
---|
[166] | 437 | (void) close(fd); |
---|
[132] | 438 | printf("a for wesommer: %d\n", acl_check(a, "wesommer", "a")); |
---|
| 439 | printf("a for foobar: %d\n", acl_check(a, "foobar", "a")); |
---|
| 440 | printf("a for spook: %d\n", acl_check(a, "spook", "a")); |
---|
| 441 | printf("g for foobar: %d\n", acl_check(a, "foobar", "g")); |
---|
| 442 | printf("g for wesommer: %d\n", acl_check(a, "wesommer", "g")); |
---|
| 443 | printf("g for spook: %d\n", acl_check(a, "spook", "g")); |
---|
| 444 | printf("d for spook: %d\n", acl_check(a, "spook", "d")); |
---|
| 445 | printf("d for wesommer: %d\n", acl_check(a, "wesommer", "d")); |
---|
| 446 | acl_add_access(a, "zot", "qna"); |
---|
| 447 | acl_add_access(a, "spook", "d"); |
---|
| 448 | acl_add_access(a, "*", "w"); |
---|
| 449 | fd = open("bar.acl", O_WRONLY+O_CREAT+O_TRUNC); |
---|
| 450 | acl_write(fd, a); |
---|
[166] | 451 | (void) close(fd); |
---|
[132] | 452 | } |
---|
[12459] | 453 | #endif /* TESTS */ |
---|
[258] | 454 | |
---|
[1476] | 455 | static void panic(s) |
---|
[132] | 456 | char *s; |
---|
| 457 | { |
---|
[25469] | 458 | printf("%s\n", s); |
---|
[132] | 459 | fflush(stdout); |
---|
| 460 | abort(); |
---|
| 461 | } |
---|