1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* 3 * Copyright (c) 1994 by the University of Southern California 4 * 5 * EXPORT OF THIS SOFTWARE from the United States of America may 6 * require a specific license from the United States Government. 7 * It is the responsibility of any person or organization contemplating 8 * export to obtain such a license before exporting. 9 * 10 * WITHIN THAT CONSTRAINT, permission to copy, modify, and distribute 11 * this software and its documentation in source and binary forms is 12 * hereby granted, provided that any documentation or other materials 13 * related to such distribution or use acknowledge that the software 14 * was developed by the University of Southern California. 15 * 16 * DISCLAIMER OF WARRANTY. THIS SOFTWARE IS PROVIDED "AS IS". The 17 * University of Southern California MAKES NO REPRESENTATIONS OR 18 * WARRANTIES, EXPRESS OR IMPLIED. By way of example, but not 19 * limitation, the University of Southern California MAKES NO 20 * REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY 21 * PARTICULAR PURPOSE. The University of Southern 22 * California shall not be held liable for any liability nor for any 23 * direct, indirect, or consequential damages with respect to any 24 * claim by the user or distributor of the ksu software. 25 * 26 * KSU was written by: Ari Medvinsky, ari@isi.edu 27 */ 28 29 #include "ksu.h" 30 31 static void 32 free_fcmd_list(char **list) 33 { 34 size_t i; 35 36 if (list == NULL) 37 return; 38 for (i = 0; i < MAX_CMD && list[i] != NULL; i++) 39 free(list[i]); 40 free(list); 41 } 42 43 krb5_boolean 44 fowner(FILE *fp, uid_t uid) 45 { 46 struct stat sbuf; 47 48 /* 49 * For security reasons, file must be owned either by 50 * the user himself, or by root. Otherwise, don't grant access. 51 */ 52 if (fstat(fileno(fp), &sbuf)) { 53 return(FALSE); 54 } 55 56 if ((sbuf.st_uid != uid) && sbuf.st_uid) { 57 return(FALSE); 58 } 59 60 return(TRUE); 61 } 62 63 /* 64 * Given a Kerberos principal "principal", and a local username "luser", 65 * determine whether user is authorized to login according to the authorization 66 * files ~luser/.k5login" and ~luser/.k5users. Set *ok to TRUE if authorized, 67 * FALSE if not authorized. Return 0 if the authorization check succeeded 68 * (regardless of its result), non-zero if it encountered an error. 69 */ 70 71 krb5_error_code 72 krb5_authorization(krb5_context context, krb5_principal principal, 73 const char *luser, char *cmd, krb5_boolean *ok, 74 char **out_fcmd) 75 { 76 struct passwd *pwd; 77 char *princname = NULL; 78 int k5login_flag =0; 79 int k5users_flag =0; 80 krb5_boolean retbool =FALSE; 81 FILE * login_fp = 0, * users_fp = 0; 82 krb5_error_code retval = 0; 83 struct stat st_temp; 84 85 *ok =FALSE; 86 87 /* no account => no access */ 88 if ((pwd = getpwnam(luser)) == NULL) 89 goto cleanup; 90 91 retval = krb5_unparse_name(context, principal, &princname); 92 if (retval) 93 return retval; 94 95 #ifdef DEBUG 96 printf("principal to be authorized %s\n", princname); 97 printf("login file: %s\n", k5login_path); 98 printf("users file: %s\n", k5users_path); 99 #endif 100 101 k5login_flag = stat(k5login_path, &st_temp); 102 k5users_flag = stat(k5users_path, &st_temp); 103 104 /* k5login and k5users must be owned by target user or root */ 105 if (!k5login_flag){ 106 login_fp = fopen(k5login_path, "r"); 107 if (login_fp == NULL) 108 goto cleanup; 109 if (fowner(login_fp, pwd->pw_uid) == FALSE) 110 goto cleanup; 111 } 112 113 if (!k5users_flag){ 114 users_fp = fopen(k5users_path, "r"); 115 if (users_fp == NULL) 116 goto cleanup; 117 if (fowner(users_fp, pwd->pw_uid) == FALSE) 118 goto cleanup; 119 } 120 121 if (auth_debug){ 122 fprintf(stderr, 123 "In krb5_authorization: if auth files exist -> can access\n"); 124 } 125 126 /* if either file exists, 127 first see if the principal is in the login in file, 128 if it's not there check the k5users file */ 129 130 if (!k5login_flag){ 131 if (auth_debug) 132 fprintf(stderr, 133 "In krb5_authorization: principal to be authorized %s\n", 134 princname); 135 136 retval = k5login_lookup(login_fp, princname, &retbool); 137 if (retval) 138 goto cleanup; 139 if (retbool) { 140 if (cmd) 141 *out_fcmd = xstrdup(cmd); 142 } 143 } 144 145 if ((!k5users_flag) && (retbool == FALSE) ){ 146 retval = k5users_lookup (users_fp, princname, 147 cmd, &retbool, out_fcmd); 148 if (retval) 149 goto cleanup; 150 } 151 152 if (k5login_flag && k5users_flag){ 153 154 char * kuser = (char *) xcalloc (strlen(princname), sizeof(char)); 155 if (!(krb5_aname_to_localname(context, principal, 156 strlen(princname), kuser)) 157 && (strcmp(kuser, luser) == 0)) { 158 retbool = TRUE; 159 } 160 161 free(kuser); 162 } 163 164 *ok =retbool; 165 166 cleanup: 167 if (users_fp != NULL) 168 fclose(users_fp); 169 if (login_fp != NULL) 170 fclose(login_fp); 171 free(princname); 172 return retval; 173 } 174 175 /*********************************************************** 176 k5login_lookup looks for princname in file fp. Spaces 177 before the princaname (in the file ) are not ignored 178 spaces after the princname are ignored. If there are 179 any tokens after the principal name FALSE is returned. 180 181 ***********************************************************/ 182 183 krb5_error_code 184 k5login_lookup(FILE *fp, char *princname, krb5_boolean *found) 185 { 186 187 krb5_error_code retval; 188 char * line; 189 char * fprinc; 190 char * lp; 191 krb5_boolean loc_found = FALSE; 192 193 retval = get_line(fp, &line); 194 if (retval) 195 return retval; 196 197 while (line){ 198 fprinc = get_first_token (line, &lp); 199 200 if (fprinc && (!strcmp(princname, fprinc))){ 201 if( get_next_token (&lp) ){ 202 free (line); 203 break; /* nothing should follow princname*/ 204 } 205 else{ 206 loc_found = TRUE; 207 free (line); 208 break; 209 } 210 } 211 212 free (line); 213 214 retval = get_line(fp, &line); 215 if (retval) 216 return retval; 217 } 218 219 220 *found = loc_found; 221 return 0; 222 223 } 224 225 /*********************************************************** 226 k5users_lookup looks for princname in file fp. Spaces 227 before the princaname (in the file ) are not ignored 228 spaces after the princname are ignored. 229 230 authorization alg: 231 232 if princname is not found return false. 233 234 if princname is found{ 235 if cmd == NULL then the file entry after principal 236 name must be nothing or * 237 238 if cmd !=NULL then entry must be matched (* is ok) 239 } 240 241 242 ***********************************************************/ 243 krb5_error_code 244 k5users_lookup(FILE *fp, char *princname, char *cmd, 245 krb5_boolean *found, char **out_fcmd) 246 { 247 krb5_error_code retval; 248 char * line; 249 char * fprinc, *fcmd; 250 char * lp; 251 char * loc_fcmd = NULL; 252 krb5_boolean loc_found = FALSE; 253 254 retval = get_line(fp, &line); 255 if (retval) 256 return retval; 257 258 while (line){ 259 fprinc = get_first_token (line, &lp); 260 261 if (fprinc && (!strcmp(princname, fprinc))){ 262 fcmd = get_next_token (&lp); 263 264 if ((fcmd) && (!strcmp(fcmd, PERMIT_ALL_COMMANDS))){ 265 if (get_next_token(&lp) == NULL){ 266 loc_fcmd =cmd ? xstrdup(cmd): NULL; 267 loc_found = TRUE; 268 } 269 free (line); 270 break; 271 } 272 273 if (cmd == NULL){ 274 if (fcmd == NULL) 275 loc_found = TRUE; 276 free (line); 277 break; 278 279 }else{ 280 if (fcmd != NULL) { 281 char * temp_rfcmd, *err; 282 krb5_boolean match; 283 do { 284 if(match_commands(fcmd,cmd,&match, 285 &temp_rfcmd, &err)){ 286 if (auth_debug){ 287 fprintf(stderr,"%s",err); 288 } 289 loc_fcmd = err; 290 break; 291 }else{ 292 if (match == TRUE){ 293 loc_fcmd = temp_rfcmd; 294 loc_found = TRUE; 295 break; 296 } 297 } 298 299 }while ((fcmd = get_next_token( &lp))); 300 } 301 free (line); 302 break; 303 } 304 } 305 306 free (line); 307 308 retval = get_line(fp, &line); 309 if (retval) { 310 return retval; 311 } 312 } 313 314 *out_fcmd = loc_fcmd; 315 *found = loc_found; 316 return 0; 317 318 } 319 320 321 /*********************************************** 322 fcmd_resolve - 323 takes a command specified .k5users file and 324 resolves it into a full path name. 325 326 ************************************************/ 327 328 krb5_boolean 329 fcmd_resolve(char *fcmd, char ***out_fcmd, char **out_err) 330 { 331 char * err; 332 char ** tmp_fcmd = NULL; 333 char * path_ptr, *path; 334 char * lp, * tc; 335 int i=0; 336 krb5_boolean ok = FALSE; 337 338 tmp_fcmd = (char **) xcalloc (MAX_CMD, sizeof(char *)); 339 340 if (*fcmd == '/'){ /* must be full path */ 341 tmp_fcmd[0] = xstrdup(fcmd); 342 tmp_fcmd[1] = NULL; 343 *out_fcmd = tmp_fcmd; 344 tmp_fcmd = NULL; 345 }else{ 346 /* must be either full path or just the cmd name */ 347 if (strchr(fcmd, '/')){ 348 asprintf(&err, _("Error: bad entry - %s in %s file, must be " 349 "either full path or just the cmd name\n"), 350 fcmd, KRB5_USERS_NAME); 351 *out_err = err; 352 goto cleanup; 353 } 354 355 #ifndef CMD_PATH 356 asprintf(&err, _("Error: bad entry - %s in %s file, since %s is just " 357 "the cmd name, CMD_PATH must be defined \n"), 358 fcmd, KRB5_USERS_NAME, fcmd); 359 *out_err = err; 360 goto cleanup; 361 #else 362 363 path = xstrdup (CMD_PATH); 364 path_ptr = path; 365 366 while ((*path_ptr == ' ') || (*path_ptr == '\t')) path_ptr ++; 367 368 tc = get_first_token (path_ptr, &lp); 369 370 if (! tc){ 371 asprintf(&err, _("Error: bad entry - %s in %s file, CMD_PATH " 372 "contains no paths \n"), fcmd, KRB5_USERS_NAME); 373 *out_err = err; 374 goto cleanup; 375 } 376 377 i=0; 378 do{ 379 if (*tc != '/'){ /* must be full path */ 380 asprintf(&err, _("Error: bad path %s in CMD_PATH for %s must " 381 "start with '/' \n"), tc, KRB5_USERS_NAME ); 382 *out_err = err; 383 goto cleanup; 384 } 385 386 tmp_fcmd[i] = xasprintf("%s/%s", tc, fcmd); 387 388 i++; 389 390 } while((tc = get_next_token (&lp))); 391 392 tmp_fcmd[i] = NULL; 393 *out_fcmd = tmp_fcmd; 394 tmp_fcmd = NULL; 395 #endif /* CMD_PATH */ 396 } 397 398 ok = TRUE; 399 400 cleanup: 401 free_fcmd_list(tmp_fcmd); 402 return ok; 403 } 404 405 /******************************************** 406 cmd_single - checks if cmd consists of a path 407 or a single token 408 409 ********************************************/ 410 411 krb5_boolean 412 cmd_single(char *cmd) 413 { 414 415 if ( ( strrchr( cmd, '/')) == NULL){ 416 return TRUE; 417 }else{ 418 return FALSE; 419 } 420 } 421 422 /******************************************** 423 cmd_arr_cmp_postfix - compares a command with the postfix 424 of fcmd 425 ********************************************/ 426 427 int 428 cmd_arr_cmp_postfix(char **fcmd_arr, char *cmd) 429 { 430 char * temp_fcmd; 431 char *ptr; 432 int result =1; 433 int i = 0; 434 435 while(fcmd_arr[i]){ 436 if ( (ptr = strrchr( fcmd_arr[i], '/')) == NULL){ 437 temp_fcmd = fcmd_arr[i]; 438 }else { 439 temp_fcmd = ptr + 1; 440 } 441 442 result = strcmp (temp_fcmd, cmd); 443 if (result == 0){ 444 break; 445 } 446 i++; 447 } 448 449 return result; 450 451 452 } 453 454 /********************************************** 455 cmd_arr_cmp - checks if cmd matches any 456 of the fcmd entries. 457 458 **********************************************/ 459 460 int 461 cmd_arr_cmp(char **fcmd_arr, char *cmd) 462 { 463 int result =1; 464 int i = 0; 465 466 while(fcmd_arr[i]){ 467 result = strcmp (fcmd_arr[i], cmd); 468 if (result == 0){ 469 break; 470 } 471 i++; 472 } 473 return result; 474 } 475 476 477 krb5_boolean 478 find_first_cmd_that_exists(char **fcmd_arr, char **cmd_out, char **err_out) 479 { 480 struct stat st_temp; 481 int i = 0; 482 krb5_boolean retbool= FALSE; 483 int j =0; 484 struct k5buf buf; 485 486 while(fcmd_arr[i]){ 487 if (!stat (fcmd_arr[i], &st_temp )){ 488 *cmd_out = xstrdup(fcmd_arr[i]); 489 retbool = TRUE; 490 break; 491 } 492 i++; 493 } 494 495 if (retbool == FALSE ){ 496 k5_buf_init_dynamic(&buf); 497 k5_buf_add(&buf, _("Error: not found -> ")); 498 for(j= 0; j < i; j ++) 499 k5_buf_add_fmt(&buf, " %s ", fcmd_arr[j]); 500 k5_buf_add(&buf, "\n"); 501 *err_out = k5_buf_cstring(&buf); 502 if (*err_out == NULL) { 503 perror(prog_name); 504 exit(1); 505 } 506 } 507 508 509 return retbool; 510 } 511 512 /*************************************************************** 513 returns 1 if there is an error, 0 if no error. 514 515 ***************************************************************/ 516 517 int 518 match_commands(char *fcmd, char *cmd, krb5_boolean *match, 519 char **cmd_out, char **err_out) 520 { 521 char ** fcmd_arr = NULL; 522 char * err; 523 char * cmd_temp; 524 int result = 1; 525 526 if(fcmd_resolve(fcmd, &fcmd_arr, &err )== FALSE ){ 527 *err_out = err; 528 goto cleanup; 529 } 530 531 if (cmd_single( cmd ) == TRUE){ 532 if (!cmd_arr_cmp_postfix(fcmd_arr, cmd)){ /* found */ 533 if (!find_first_cmd_that_exists(fcmd_arr, &cmd_temp, &err)) { 534 *err_out = err; 535 goto cleanup; 536 } 537 538 *match = TRUE; 539 *cmd_out = cmd_temp; 540 } else { 541 *match = FALSE; 542 } 543 }else{ 544 if (!cmd_arr_cmp(fcmd_arr, cmd)){ /* found */ 545 *match = TRUE; 546 *cmd_out = xstrdup(cmd); 547 } else{ 548 *match = FALSE; 549 } 550 } 551 552 result = 0; 553 554 cleanup: 555 free_fcmd_list(fcmd_arr); 556 return result; 557 } 558 559 /********************************************************* 560 get_line - returns a line of any length. out_line 561 is set to null if eof. 562 *********************************************************/ 563 564 krb5_error_code 565 get_line(FILE *fp, char **out_line) 566 { 567 char * line, *r, *newline , *line_ptr; 568 int chunk_count = 1; 569 570 line = (char *) xcalloc (BUFSIZ, sizeof (char )); 571 line_ptr = line; 572 line[0] = '\0'; 573 574 while (( r = fgets(line_ptr, BUFSIZ , fp)) != NULL){ 575 newline = strchr(line_ptr, '\n'); 576 if (newline) { 577 *newline = '\0'; 578 break; 579 } 580 else { 581 chunk_count ++; 582 line = xrealloc(line, chunk_count * BUFSIZ); 583 584 line_ptr = line + (BUFSIZ -1) *( chunk_count -1) ; 585 } 586 } 587 588 if ((r == NULL) && (strlen(line) == 0)) { 589 *out_line = NULL; 590 } 591 else{ 592 *out_line = line; 593 } 594 595 return 0; 596 } 597 598 /******************************************************* 599 get_first_token - 600 Expects a '\0' terminated input line . 601 If there are any spaces before the first token, they 602 will be returned as part of the first token. 603 604 Note: this routine reuses the space pointed to by line 605 ******************************************************/ 606 607 char * 608 get_first_token(char *line, char **lnext) 609 { 610 611 char * lptr, * out_ptr; 612 613 614 out_ptr = line; 615 lptr = line; 616 617 while (( *lptr == ' ') || (*lptr == '\t')) lptr ++; 618 619 if (strlen(lptr) == 0) return NULL; 620 621 while (( *lptr != ' ') && (*lptr != '\t') && (*lptr != '\0')) lptr ++; 622 623 if (*lptr == '\0'){ 624 *lnext = lptr; 625 } else{ 626 *lptr = '\0'; 627 *lnext = lptr + 1; 628 } 629 630 return out_ptr; 631 } 632 /********************************************************** 633 get_next_token - 634 returns the next token pointed to by *lnext. 635 returns NULL if there is no more tokens. 636 Note: that this function modifies the stream 637 pointed to by *lnext and does not allocate 638 space for the returned tocken. It also advances 639 lnext to the next tocken. 640 **********************************************************/ 641 642 char * 643 get_next_token (char **lnext) 644 { 645 char * lptr, * out_ptr; 646 647 648 lptr = *lnext; 649 650 while (( *lptr == ' ') || (*lptr == '\t')) lptr ++; 651 652 if (strlen(lptr) == 0) return NULL; 653 654 out_ptr = lptr; 655 656 while (( *lptr != ' ') && (*lptr != '\t') && (*lptr != '\0')) lptr ++; 657 658 if (*lptr == '\0'){ 659 *lnext = lptr; 660 } else{ 661 *lptr = '\0'; 662 *lnext = lptr + 1; 663 } 664 665 return out_ptr; 666 } 667 668 void 669 init_auth_names(char *pw_dir) 670 { 671 const char *sep; 672 int r1, r2; 673 674 sep = ((strlen(pw_dir) == 1) && (*pw_dir == '/')) ? "" : "/"; 675 r1 = snprintf(k5login_path, sizeof(k5login_path), "%s%s%s", 676 pw_dir, sep, KRB5_LOGIN_NAME); 677 r2 = snprintf(k5users_path, sizeof(k5users_path), "%s%s%s", 678 pw_dir, sep, KRB5_USERS_NAME); 679 if (SNPRINTF_OVERFLOW(r1, sizeof(k5login_path)) || 680 SNPRINTF_OVERFLOW(r2, sizeof(k5users_path))) { 681 fprintf(stderr, _("home directory name `%s' too long, can't search " 682 "for .k5login\n"), pw_dir); 683 exit (1); 684 } 685 } 686