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 auth_cleanup (FILE *, FILE *, char *); 32 33 krb5_boolean fowner(fp, uid) 34 FILE *fp; 35 uid_t uid; 36 { 37 struct stat sbuf; 38 39 /* 40 * For security reasons, file must be owned either by 41 * the user himself, or by root. Otherwise, don't grant access. 42 */ 43 if (fstat(fileno(fp), &sbuf)) { 44 return(FALSE); 45 } 46 47 if ((sbuf.st_uid != uid) && sbuf.st_uid) { 48 return(FALSE); 49 } 50 51 return(TRUE); 52 } 53 54 /* 55 * Given a Kerberos principal "principal", and a local username "luser", 56 * determine whether user is authorized to login according to the 57 * authorization files ~luser/.k5login" and ~luser/.k5users. Returns TRUE 58 * if authorized, FALSE if not authorized. 59 * 60 */ 61 62 krb5_error_code krb5_authorization(context, principal, luser, 63 cmd, ok, out_fcmd) 64 /* IN */ 65 krb5_context context; 66 krb5_principal principal; 67 const char *luser; 68 char *cmd; 69 /* OUT */ 70 krb5_boolean *ok; 71 char **out_fcmd; 72 { 73 struct passwd *pwd; 74 char *princname; 75 int k5login_flag =0; 76 int k5users_flag =0; 77 krb5_boolean retbool =FALSE; 78 FILE * login_fp = 0, * users_fp = 0; 79 krb5_error_code retval = 0; 80 struct stat st_temp; 81 82 *ok =FALSE; 83 84 /* no account => no access */ 85 if ((pwd = getpwnam(luser)) == NULL) 86 return 0; 87 88 retval = krb5_unparse_name(context, principal, &princname); 89 if (retval) 90 return retval; 91 92 #ifdef DEBUG 93 printf("principal to be authorized %s\n", princname); 94 printf("login file: %s\n", k5login_path); 95 printf("users file: %s\n", k5users_path); 96 #endif 97 98 k5login_flag = stat(k5login_path, &st_temp); 99 k5users_flag = stat(k5users_path, &st_temp); 100 101 /* k5login and k5users must be owned by target user or root */ 102 if (!k5login_flag){ 103 if ((login_fp = fopen(k5login_path, "r")) == NULL) 104 return 0; 105 if ( fowner(login_fp, pwd->pw_uid) == FALSE) { 106 fclose(login_fp); 107 return 0; 108 } 109 } 110 111 if (!k5users_flag){ 112 if ((users_fp = fopen(k5users_path, "r")) == NULL) { 113 return 0; 114 } 115 if ( fowner(users_fp, pwd->pw_uid) == FALSE){ 116 fclose(users_fp); 117 return 0; 118 } 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 auth_cleanup(users_fp, login_fp, princname); 139 return retval; 140 } 141 if (retbool) { 142 if (cmd) 143 *out_fcmd = xstrdup(cmd); 144 } 145 } 146 147 if ((!k5users_flag) && (retbool == FALSE) ){ 148 retval = k5users_lookup (users_fp, princname, 149 cmd, &retbool, out_fcmd); 150 if(retval) { 151 auth_cleanup(users_fp, login_fp, princname); 152 return retval; 153 } 154 } 155 156 if (k5login_flag && k5users_flag){ 157 158 char * kuser = (char *) xcalloc (strlen(princname), sizeof(char)); 159 if (!(krb5_aname_to_localname(context, principal, 160 strlen(princname), kuser)) 161 && (strcmp(kuser, luser) == 0)) { 162 retbool = TRUE; 163 } 164 165 free(kuser); 166 } 167 168 *ok =retbool; 169 auth_cleanup(users_fp, login_fp, princname); 170 return 0; 171 } 172 173 /*********************************************************** 174 k5login_lookup looks for princname in file fp. Spaces 175 before the princaname (in the file ) are not ignored 176 spaces after the princname are ignored. If there are 177 any tokens after the principal name FALSE is returned. 178 179 ***********************************************************/ 180 181 krb5_error_code k5login_lookup (fp, princname, found) 182 FILE *fp; 183 char *princname; 184 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 k5users_lookup (fp, princname, cmd, found, out_fcmd) 244 FILE *fp; 245 char *princname; 246 char *cmd; 247 krb5_boolean *found; 248 char **out_fcmd; 249 { 250 krb5_error_code retval; 251 char * line; 252 char * fprinc, *fcmd; 253 char * lp; 254 char * loc_fcmd = NULL; 255 krb5_boolean loc_found = FALSE; 256 257 retval = get_line(fp, &line); 258 if (retval) 259 return retval; 260 261 while (line){ 262 fprinc = get_first_token (line, &lp); 263 264 if (fprinc && (!strcmp(princname, fprinc))){ 265 fcmd = get_next_token (&lp); 266 267 if ((fcmd) && (!strcmp(fcmd, PERMIT_ALL_COMMANDS))){ 268 if (get_next_token(&lp) == NULL){ 269 loc_fcmd =cmd ? xstrdup(cmd): NULL; 270 loc_found = TRUE; 271 } 272 free (line); 273 break; 274 } 275 276 if (cmd == NULL){ 277 if (fcmd == NULL) 278 loc_found = TRUE; 279 free (line); 280 break; 281 282 }else{ 283 if (fcmd != NULL) { 284 char * temp_rfcmd, *err; 285 krb5_boolean match; 286 do { 287 if(match_commands(fcmd,cmd,&match, 288 &temp_rfcmd, &err)){ 289 if (auth_debug){ 290 fprintf(stderr,"%s",err); 291 } 292 loc_fcmd = err; 293 break; 294 }else{ 295 if (match == TRUE){ 296 loc_fcmd = temp_rfcmd; 297 loc_found = TRUE; 298 break; 299 } 300 } 301 302 }while ((fcmd = get_next_token( &lp))); 303 } 304 free (line); 305 break; 306 } 307 } 308 309 free (line); 310 311 retval = get_line(fp, &line); 312 if (retval) { 313 return retval; 314 } 315 } 316 317 *out_fcmd = loc_fcmd; 318 *found = loc_found; 319 return 0; 320 321 } 322 323 324 /*********************************************** 325 fcmd_resolve - 326 takes a command specified .k5users file and 327 resolves it into a full path name. 328 329 ************************************************/ 330 331 krb5_boolean fcmd_resolve(fcmd, out_fcmd, out_err) 332 char *fcmd; 333 char ***out_fcmd; 334 char **out_err; 335 { 336 char * err; 337 char ** tmp_fcmd; 338 char * path_ptr, *path; 339 char * lp, * tc; 340 int i=0; 341 342 tmp_fcmd = (char **) xcalloc (MAX_CMD, sizeof(char *)); 343 344 if (*fcmd == '/'){ /* must be full path */ 345 tmp_fcmd[0] = xstrdup(fcmd); 346 tmp_fcmd[1] = NULL; 347 *out_fcmd = tmp_fcmd; 348 return TRUE; 349 }else{ 350 /* must be either full path or just the cmd name */ 351 if (strchr(fcmd, '/')){ 352 asprintf(&err, _("Error: bad entry - %s in %s file, must be " 353 "either full path or just the cmd name\n"), 354 fcmd, KRB5_USERS_NAME); 355 *out_err = err; 356 return FALSE; 357 } 358 359 #ifndef CMD_PATH 360 asprintf(&err, _("Error: bad entry - %s in %s file, since %s is just " 361 "the cmd name, CMD_PATH must be defined \n"), 362 fcmd, KRB5_USERS_NAME, fcmd); 363 *out_err = err; 364 return FALSE; 365 #else 366 367 path = xstrdup (CMD_PATH); 368 path_ptr = path; 369 370 while ((*path_ptr == ' ') || (*path_ptr == '\t')) path_ptr ++; 371 372 tc = get_first_token (path_ptr, &lp); 373 374 if (! tc){ 375 asprintf(&err, _("Error: bad entry - %s in %s file, CMD_PATH " 376 "contains no paths \n"), fcmd, KRB5_USERS_NAME); 377 *out_err = err; 378 return FALSE; 379 } 380 381 i=0; 382 do{ 383 if (*tc != '/'){ /* must be full path */ 384 asprintf(&err, _("Error: bad path %s in CMD_PATH for %s must " 385 "start with '/' \n"), tc, KRB5_USERS_NAME ); 386 *out_err = err; 387 return FALSE; 388 } 389 390 tmp_fcmd[i] = xasprintf("%s/%s", tc, fcmd); 391 392 i++; 393 394 } while((tc = get_next_token (&lp))); 395 396 tmp_fcmd[i] = NULL; 397 *out_fcmd = tmp_fcmd; 398 return TRUE; 399 400 #endif /* CMD_PATH */ 401 } 402 } 403 404 /******************************************** 405 cmd_single - checks if cmd consists of a path 406 or a single token 407 408 ********************************************/ 409 410 krb5_boolean cmd_single(cmd) 411 char * cmd; 412 { 413 414 if ( ( strrchr( cmd, '/')) == NULL){ 415 return TRUE; 416 }else{ 417 return FALSE; 418 } 419 } 420 421 /******************************************** 422 cmd_arr_cmp_postfix - compares a command with the postfix 423 of fcmd 424 ********************************************/ 425 426 int cmd_arr_cmp_postfix(fcmd_arr, cmd) 427 char **fcmd_arr; 428 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 cmd_arr_cmp (fcmd_arr, cmd) 461 char **fcmd_arr; 462 char *cmd; 463 { 464 int result =1; 465 int i = 0; 466 467 while(fcmd_arr[i]){ 468 result = strcmp (fcmd_arr[i], cmd); 469 if (result == 0){ 470 break; 471 } 472 i++; 473 } 474 return result; 475 } 476 477 478 krb5_boolean find_first_cmd_that_exists(fcmd_arr, cmd_out, err_out) 479 char **fcmd_arr; 480 char **cmd_out; 481 char **err_out; 482 { 483 struct stat st_temp; 484 int i = 0; 485 krb5_boolean retbool= FALSE; 486 int j =0; 487 struct k5buf buf; 488 489 while(fcmd_arr[i]){ 490 if (!stat (fcmd_arr[i], &st_temp )){ 491 *cmd_out = xstrdup(fcmd_arr[i]); 492 retbool = TRUE; 493 break; 494 } 495 i++; 496 } 497 498 if (retbool == FALSE ){ 499 k5_buf_init_dynamic(&buf); 500 k5_buf_add(&buf, _("Error: not found -> ")); 501 for(j= 0; j < i; j ++) 502 k5_buf_add_fmt(&buf, " %s ", fcmd_arr[j]); 503 k5_buf_add(&buf, "\n"); 504 *err_out = k5_buf_cstring(&buf); 505 if (*err_out == NULL) { 506 perror(prog_name); 507 exit(1); 508 } 509 } 510 511 512 return retbool; 513 } 514 515 /*************************************************************** 516 returns 1 if there is an error, 0 if no error. 517 518 ***************************************************************/ 519 520 int match_commands (fcmd, cmd, match, cmd_out, err_out) 521 char *fcmd; 522 char *cmd; 523 krb5_boolean *match; 524 char **cmd_out; 525 char **err_out; 526 { 527 char ** fcmd_arr; 528 char * err; 529 char * cmd_temp; 530 531 if(fcmd_resolve(fcmd, &fcmd_arr, &err )== FALSE ){ 532 *err_out = err; 533 return 1; 534 } 535 536 if (cmd_single( cmd ) == TRUE){ 537 if (!cmd_arr_cmp_postfix(fcmd_arr, cmd)){ /* found */ 538 539 if(find_first_cmd_that_exists( fcmd_arr,&cmd_temp,&err)== TRUE){ 540 *match = TRUE; 541 *cmd_out = cmd_temp; 542 return 0; 543 }else{ 544 *err_out = err; 545 return 1; 546 } 547 }else{ 548 *match = FALSE; 549 return 0; 550 } 551 }else{ 552 if (!cmd_arr_cmp(fcmd_arr, cmd)){ /* found */ 553 *match = TRUE; 554 *cmd_out = xstrdup(cmd); 555 return 0; 556 } else{ 557 *match = FALSE; 558 return 0; 559 } 560 } 561 562 } 563 564 /********************************************************* 565 get_line - returns a line of any length. out_line 566 is set to null if eof. 567 *********************************************************/ 568 569 krb5_error_code get_line (fp, out_line) 570 /* IN */ 571 FILE *fp; 572 /* OUT */ 573 char **out_line; 574 { 575 char * line, *r, *newline , *line_ptr; 576 int chunk_count = 1; 577 578 line = (char *) xcalloc (BUFSIZ, sizeof (char )); 579 line_ptr = line; 580 line[0] = '\0'; 581 582 while (( r = fgets(line_ptr, BUFSIZ , fp)) != NULL){ 583 newline = strchr(line_ptr, '\n'); 584 if (newline) { 585 *newline = '\0'; 586 break; 587 } 588 else { 589 chunk_count ++; 590 if(!( line = (char *) realloc( line, 591 chunk_count * sizeof(char) * BUFSIZ))){ 592 return ENOMEM; 593 } 594 595 line_ptr = line + (BUFSIZ -1) *( chunk_count -1) ; 596 } 597 } 598 599 if ((r == NULL) && (strlen(line) == 0)) { 600 *out_line = NULL; 601 } 602 else{ 603 *out_line = line; 604 } 605 606 return 0; 607 } 608 609 /******************************************************* 610 get_first_token - 611 Expects a '\0' terminated input line . 612 If there are any spaces before the first token, they 613 will be returned as part of the first token. 614 615 Note: this routine reuses the space pointed to by line 616 ******************************************************/ 617 618 char * get_first_token (line, lnext) 619 char *line; 620 char **lnext; 621 { 622 623 char * lptr, * out_ptr; 624 625 626 out_ptr = line; 627 lptr = line; 628 629 while (( *lptr == ' ') || (*lptr == '\t')) lptr ++; 630 631 if (strlen(lptr) == 0) return NULL; 632 633 while (( *lptr != ' ') && (*lptr != '\t') && (*lptr != '\0')) lptr ++; 634 635 if (*lptr == '\0'){ 636 *lnext = lptr; 637 } else{ 638 *lptr = '\0'; 639 *lnext = lptr + 1; 640 } 641 642 return out_ptr; 643 } 644 /********************************************************** 645 get_next_token - 646 returns the next token pointed to by *lnext. 647 returns NULL if there is no more tokens. 648 Note: that this function modifies the stream 649 pointed to by *lnext and does not allocate 650 space for the returned tocken. It also advances 651 lnext to the next tocken. 652 **********************************************************/ 653 654 char * get_next_token (lnext) 655 char **lnext; 656 { 657 char * lptr, * out_ptr; 658 659 660 lptr = *lnext; 661 662 while (( *lptr == ' ') || (*lptr == '\t')) lptr ++; 663 664 if (strlen(lptr) == 0) return NULL; 665 666 out_ptr = lptr; 667 668 while (( *lptr != ' ') && (*lptr != '\t') && (*lptr != '\0')) lptr ++; 669 670 if (*lptr == '\0'){ 671 *lnext = lptr; 672 } else{ 673 *lptr = '\0'; 674 *lnext = lptr + 1; 675 } 676 677 return out_ptr; 678 } 679 680 static void auth_cleanup(users_fp, login_fp, princname) 681 FILE *users_fp; 682 FILE *login_fp; 683 char *princname; 684 { 685 686 free (princname); 687 if (users_fp) 688 fclose(users_fp); 689 if (login_fp) 690 fclose(login_fp); 691 } 692 693 void init_auth_names(pw_dir) 694 char *pw_dir; 695 { 696 const char *sep; 697 int r1, r2; 698 699 sep = ((strlen(pw_dir) == 1) && (*pw_dir == '/')) ? "" : "/"; 700 r1 = snprintf(k5login_path, sizeof(k5login_path), "%s%s%s", 701 pw_dir, sep, KRB5_LOGIN_NAME); 702 r2 = snprintf(k5users_path, sizeof(k5users_path), "%s%s%s", 703 pw_dir, sep, KRB5_USERS_NAME); 704 if (SNPRINTF_OVERFLOW(r1, sizeof(k5login_path)) || 705 SNPRINTF_OVERFLOW(r2, sizeof(k5users_path))) { 706 fprintf(stderr, _("home directory name `%s' too long, can't search " 707 "for .k5login\n"), pw_dir); 708 exit (1); 709 } 710 } 711