/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * Copyright (c) 1994 by the University of Southern California * * EXPORT OF THIS SOFTWARE from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to copy, modify, and distribute * this software and its documentation in source and binary forms is * hereby granted, provided that any documentation or other materials * related to such distribution or use acknowledge that the software * was developed by the University of Southern California. * * DISCLAIMER OF WARRANTY. THIS SOFTWARE IS PROVIDED "AS IS". The * University of Southern California MAKES NO REPRESENTATIONS OR * WARRANTIES, EXPRESS OR IMPLIED. By way of example, but not * limitation, the University of Southern California MAKES NO * REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY * PARTICULAR PURPOSE. The University of Southern * California shall not be held liable for any liability nor for any * direct, indirect, or consequential damages with respect to any * claim by the user or distributor of the ksu software. * * KSU was written by: Ari Medvinsky, ari@isi.edu */ #include "ksu.h" static void auth_cleanup (FILE *, FILE *, char *); krb5_boolean fowner(fp, uid) FILE *fp; uid_t uid; { struct stat sbuf; /* * For security reasons, file must be owned either by * the user himself, or by root. Otherwise, don't grant access. */ if (fstat(fileno(fp), &sbuf)) { return(FALSE); } if ((sbuf.st_uid != uid) && sbuf.st_uid) { return(FALSE); } return(TRUE); } /* * Given a Kerberos principal "principal", and a local username "luser", * determine whether user is authorized to login according to the * authorization files ~luser/.k5login" and ~luser/.k5users. Returns TRUE * if authorized, FALSE if not authorized. * */ krb5_error_code krb5_authorization(context, principal, luser, cmd, ok, out_fcmd) /* IN */ krb5_context context; krb5_principal principal; const char *luser; char *cmd; /* OUT */ krb5_boolean *ok; char **out_fcmd; { struct passwd *pwd; char *princname; int k5login_flag =0; int k5users_flag =0; krb5_boolean retbool =FALSE; FILE * login_fp = 0, * users_fp = 0; krb5_error_code retval = 0; struct stat st_temp; *ok =FALSE; /* no account => no access */ if ((pwd = getpwnam(luser)) == NULL) return 0; retval = krb5_unparse_name(context, principal, &princname); if (retval) return retval; #ifdef DEBUG printf("principal to be authorized %s\n", princname); printf("login file: %s\n", k5login_path); printf("users file: %s\n", k5users_path); #endif k5login_flag = stat(k5login_path, &st_temp); k5users_flag = stat(k5users_path, &st_temp); /* k5login and k5users must be owned by target user or root */ if (!k5login_flag){ if ((login_fp = fopen(k5login_path, "r")) == NULL) return 0; if ( fowner(login_fp, pwd->pw_uid) == FALSE) { fclose(login_fp); return 0; } } if (!k5users_flag){ if ((users_fp = fopen(k5users_path, "r")) == NULL) { return 0; } if ( fowner(users_fp, pwd->pw_uid) == FALSE){ fclose(users_fp); return 0; } } if (auth_debug){ fprintf(stderr, "In krb5_authorization: if auth files exist -> can access\n"); } /* if either file exists, first see if the principal is in the login in file, if it's not there check the k5users file */ if (!k5login_flag){ if (auth_debug) fprintf(stderr, "In krb5_authorization: principal to be authorized %s\n", princname); retval = k5login_lookup(login_fp, princname, &retbool); if (retval) { auth_cleanup(users_fp, login_fp, princname); return retval; } if (retbool) { if (cmd) *out_fcmd = xstrdup(cmd); } } if ((!k5users_flag) && (retbool == FALSE) ){ retval = k5users_lookup (users_fp, princname, cmd, &retbool, out_fcmd); if(retval) { auth_cleanup(users_fp, login_fp, princname); return retval; } } if (k5login_flag && k5users_flag){ char * kuser = (char *) xcalloc (strlen(princname), sizeof(char)); if (!(krb5_aname_to_localname(context, principal, strlen(princname), kuser)) && (strcmp(kuser, luser) == 0)) { retbool = TRUE; } free(kuser); } *ok =retbool; auth_cleanup(users_fp, login_fp, princname); return 0; } /*********************************************************** k5login_lookup looks for princname in file fp. Spaces before the princaname (in the file ) are not ignored spaces after the princname are ignored. If there are any tokens after the principal name FALSE is returned. ***********************************************************/ krb5_error_code k5login_lookup (fp, princname, found) FILE *fp; char *princname; krb5_boolean *found; { krb5_error_code retval; char * line; char * fprinc; char * lp; krb5_boolean loc_found = FALSE; retval = get_line(fp, &line); if (retval) return retval; while (line){ fprinc = get_first_token (line, &lp); if (fprinc && (!strcmp(princname, fprinc))){ if( get_next_token (&lp) ){ free (line); break; /* nothing should follow princname*/ } else{ loc_found = TRUE; free (line); break; } } free (line); retval = get_line(fp, &line); if (retval) return retval; } *found = loc_found; return 0; } /*********************************************************** k5users_lookup looks for princname in file fp. Spaces before the princaname (in the file ) are not ignored spaces after the princname are ignored. authorization alg: if princname is not found return false. if princname is found{ if cmd == NULL then the file entry after principal name must be nothing or * if cmd !=NULL then entry must be matched (* is ok) } ***********************************************************/ krb5_error_code k5users_lookup (fp, princname, cmd, found, out_fcmd) FILE *fp; char *princname; char *cmd; krb5_boolean *found; char **out_fcmd; { krb5_error_code retval; char * line; char * fprinc, *fcmd; char * lp; char * loc_fcmd = NULL; krb5_boolean loc_found = FALSE; retval = get_line(fp, &line); if (retval) return retval; while (line){ fprinc = get_first_token (line, &lp); if (fprinc && (!strcmp(princname, fprinc))){ fcmd = get_next_token (&lp); if ((fcmd) && (!strcmp(fcmd, PERMIT_ALL_COMMANDS))){ if (get_next_token(&lp) == NULL){ loc_fcmd =cmd ? xstrdup(cmd): NULL; loc_found = TRUE; } free (line); break; } if (cmd == NULL){ if (fcmd == NULL) loc_found = TRUE; free (line); break; }else{ if (fcmd != NULL) { char * temp_rfcmd, *err; krb5_boolean match; do { if(match_commands(fcmd,cmd,&match, &temp_rfcmd, &err)){ if (auth_debug){ fprintf(stderr,"%s",err); } loc_fcmd = err; break; }else{ if (match == TRUE){ loc_fcmd = temp_rfcmd; loc_found = TRUE; break; } } }while ((fcmd = get_next_token( &lp))); } free (line); break; } } free (line); retval = get_line(fp, &line); if (retval) { return retval; } } *out_fcmd = loc_fcmd; *found = loc_found; return 0; } /*********************************************** fcmd_resolve - takes a command specified .k5users file and resolves it into a full path name. ************************************************/ krb5_boolean fcmd_resolve(fcmd, out_fcmd, out_err) char *fcmd; char ***out_fcmd; char **out_err; { char * err; char ** tmp_fcmd; char * path_ptr, *path; char * lp, * tc; int i=0; tmp_fcmd = (char **) xcalloc (MAX_CMD, sizeof(char *)); if (*fcmd == '/'){ /* must be full path */ tmp_fcmd[0] = xstrdup(fcmd); tmp_fcmd[1] = NULL; *out_fcmd = tmp_fcmd; return TRUE; }else{ /* must be either full path or just the cmd name */ if (strchr(fcmd, '/')){ asprintf(&err, _("Error: bad entry - %s in %s file, must be " "either full path or just the cmd name\n"), fcmd, KRB5_USERS_NAME); *out_err = err; return FALSE; } #ifndef CMD_PATH asprintf(&err, _("Error: bad entry - %s in %s file, since %s is just " "the cmd name, CMD_PATH must be defined \n"), fcmd, KRB5_USERS_NAME, fcmd); *out_err = err; return FALSE; #else path = xstrdup (CMD_PATH); path_ptr = path; while ((*path_ptr == ' ') || (*path_ptr == '\t')) path_ptr ++; tc = get_first_token (path_ptr, &lp); if (! tc){ asprintf(&err, _("Error: bad entry - %s in %s file, CMD_PATH " "contains no paths \n"), fcmd, KRB5_USERS_NAME); *out_err = err; return FALSE; } i=0; do{ if (*tc != '/'){ /* must be full path */ asprintf(&err, _("Error: bad path %s in CMD_PATH for %s must " "start with '/' \n"), tc, KRB5_USERS_NAME ); *out_err = err; return FALSE; } tmp_fcmd[i] = xasprintf("%s/%s", tc, fcmd); i++; } while((tc = get_next_token (&lp))); tmp_fcmd[i] = NULL; *out_fcmd = tmp_fcmd; return TRUE; #endif /* CMD_PATH */ } } /******************************************** cmd_single - checks if cmd consists of a path or a single token ********************************************/ krb5_boolean cmd_single(cmd) char * cmd; { if ( ( strrchr( cmd, '/')) == NULL){ return TRUE; }else{ return FALSE; } } /******************************************** cmd_arr_cmp_postfix - compares a command with the postfix of fcmd ********************************************/ int cmd_arr_cmp_postfix(fcmd_arr, cmd) char **fcmd_arr; char *cmd; { char * temp_fcmd; char *ptr; int result =1; int i = 0; while(fcmd_arr[i]){ if ( (ptr = strrchr( fcmd_arr[i], '/')) == NULL){ temp_fcmd = fcmd_arr[i]; }else { temp_fcmd = ptr + 1; } result = strcmp (temp_fcmd, cmd); if (result == 0){ break; } i++; } return result; } /********************************************** cmd_arr_cmp - checks if cmd matches any of the fcmd entries. **********************************************/ int cmd_arr_cmp (fcmd_arr, cmd) char **fcmd_arr; char *cmd; { int result =1; int i = 0; while(fcmd_arr[i]){ result = strcmp (fcmd_arr[i], cmd); if (result == 0){ break; } i++; } return result; } krb5_boolean find_first_cmd_that_exists(fcmd_arr, cmd_out, err_out) char **fcmd_arr; char **cmd_out; char **err_out; { struct stat st_temp; int i = 0; krb5_boolean retbool= FALSE; int j =0; struct k5buf buf; while(fcmd_arr[i]){ if (!stat (fcmd_arr[i], &st_temp )){ *cmd_out = xstrdup(fcmd_arr[i]); retbool = TRUE; break; } i++; } if (retbool == FALSE ){ k5_buf_init_dynamic(&buf); k5_buf_add(&buf, _("Error: not found -> ")); for(j= 0; j < i; j ++) k5_buf_add_fmt(&buf, " %s ", fcmd_arr[j]); k5_buf_add(&buf, "\n"); if (k5_buf_status(&buf) != 0) { perror(prog_name); exit(1); } *err_out = buf.data; } return retbool; } /*************************************************************** returns 1 if there is an error, 0 if no error. ***************************************************************/ int match_commands (fcmd, cmd, match, cmd_out, err_out) char *fcmd; char *cmd; krb5_boolean *match; char **cmd_out; char **err_out; { char ** fcmd_arr; char * err; char * cmd_temp; if(fcmd_resolve(fcmd, &fcmd_arr, &err )== FALSE ){ *err_out = err; return 1; } if (cmd_single( cmd ) == TRUE){ if (!cmd_arr_cmp_postfix(fcmd_arr, cmd)){ /* found */ if(find_first_cmd_that_exists( fcmd_arr,&cmd_temp,&err)== TRUE){ *match = TRUE; *cmd_out = cmd_temp; return 0; }else{ *err_out = err; return 1; } }else{ *match = FALSE; return 0; } }else{ if (!cmd_arr_cmp(fcmd_arr, cmd)){ /* found */ *match = TRUE; *cmd_out = xstrdup(cmd); return 0; } else{ *match = FALSE; return 0; } } } /********************************************************* get_line - returns a line of any length. out_line is set to null if eof. *********************************************************/ krb5_error_code get_line (fp, out_line) /* IN */ FILE *fp; /* OUT */ char **out_line; { char * line, *r, *newline , *line_ptr; int chunk_count = 1; line = (char *) xcalloc (BUFSIZ, sizeof (char )); line_ptr = line; line[0] = '\0'; while (( r = fgets(line_ptr, BUFSIZ , fp)) != NULL){ newline = strchr(line_ptr, '\n'); if (newline) { *newline = '\0'; break; } else { chunk_count ++; if(!( line = (char *) realloc( line, chunk_count * sizeof(char) * BUFSIZ))){ return ENOMEM; } line_ptr = line + (BUFSIZ -1) *( chunk_count -1) ; } } if ((r == NULL) && (strlen(line) == 0)) { *out_line = NULL; } else{ *out_line = line; } return 0; } /******************************************************* get_first_token - Expects a '\0' terminated input line . If there are any spaces before the first token, they will be returned as part of the first token. Note: this routine reuses the space pointed to by line ******************************************************/ char * get_first_token (line, lnext) char *line; char **lnext; { char * lptr, * out_ptr; out_ptr = line; lptr = line; while (( *lptr == ' ') || (*lptr == '\t')) lptr ++; if (strlen(lptr) == 0) return NULL; while (( *lptr != ' ') && (*lptr != '\t') && (*lptr != '\0')) lptr ++; if (*lptr == '\0'){ *lnext = lptr; } else{ *lptr = '\0'; *lnext = lptr + 1; } return out_ptr; } /********************************************************** get_next_token - returns the next token pointed to by *lnext. returns NULL if there is no more tokens. Note: that this function modifies the stream pointed to by *lnext and does not allocate space for the returned tocken. It also advances lnext to the next tocken. **********************************************************/ char * get_next_token (lnext) char **lnext; { char * lptr, * out_ptr; lptr = *lnext; while (( *lptr == ' ') || (*lptr == '\t')) lptr ++; if (strlen(lptr) == 0) return NULL; out_ptr = lptr; while (( *lptr != ' ') && (*lptr != '\t') && (*lptr != '\0')) lptr ++; if (*lptr == '\0'){ *lnext = lptr; } else{ *lptr = '\0'; *lnext = lptr + 1; } return out_ptr; } static void auth_cleanup(users_fp, login_fp, princname) FILE *users_fp; FILE *login_fp; char *princname; { free (princname); if (users_fp) fclose(users_fp); if (login_fp) fclose(login_fp); } void init_auth_names(pw_dir) char *pw_dir; { const char *sep; int r1, r2; sep = ((strlen(pw_dir) == 1) && (*pw_dir == '/')) ? "" : "/"; r1 = snprintf(k5login_path, sizeof(k5login_path), "%s%s%s", pw_dir, sep, KRB5_LOGIN_NAME); r2 = snprintf(k5users_path, sizeof(k5users_path), "%s%s%s", pw_dir, sep, KRB5_USERS_NAME); if (SNPRINTF_OVERFLOW(r1, sizeof(k5login_path)) || SNPRINTF_OVERFLOW(r2, sizeof(k5users_path))) { fprintf(stderr, _("home directory name `%s' too long, can't search " "for .k5login\n"), pw_dir); exit (1); } }