From b25f8055ad99cc5a645a5ad363e6681d6e22a8ad Mon Sep 17 00:00:00 2001 From: Theodore Tso Date: Wed, 8 Jun 1994 22:39:10 +0000 Subject: [PATCH] As contributed by ISI git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@3681 dc483132-0cff-0310-8789-dd5450dbe970 --- src/clients/ksu/Imakefile | 28 + src/clients/ksu/authorization.c | 803 +++++++++++++++++++++++++++++ src/clients/ksu/ccache.c | 781 ++++++++++++++++++++++++++++ src/clients/ksu/heuristic.c | 746 +++++++++++++++++++++++++++ src/clients/ksu/krb_auth_su.c | 653 ++++++++++++++++++++++++ src/clients/ksu/ksu.1 | 480 ++++++++++++++++++ src/clients/ksu/ksu.h | 106 ++++ src/clients/ksu/main.c | 871 ++++++++++++++++++++++++++++++++ 8 files changed, 4468 insertions(+) create mode 100644 src/clients/ksu/Imakefile create mode 100644 src/clients/ksu/authorization.c create mode 100644 src/clients/ksu/ccache.c create mode 100644 src/clients/ksu/heuristic.c create mode 100644 src/clients/ksu/krb_auth_su.c create mode 100644 src/clients/ksu/ksu.1 create mode 100644 src/clients/ksu/ksu.h create mode 100644 src/clients/ksu/main.c diff --git a/src/clients/ksu/Imakefile b/src/clients/ksu/Imakefile new file mode 100644 index 000000000..c74d8e003 --- /dev/null +++ b/src/clients/ksu/Imakefile @@ -0,0 +1,28 @@ +# $Source$ +# $Author$ +# $Id$ +# +# Copyright 1990 by the Massachusetts Institute of Technology. +# All Rights Reserved. +# +# 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 use, copy, modify, and +# distribute this software and its documentation for any purpose and +# without fee is hereby granted, provided that the above copyright +# notice appear in all copies and that both that copyright notice and +# this permission notice appear in supporting documentation, and that +# the name of M.I.T. not be used in advertising or publicity pertaining +# to distribution of the software without specific, written prior +# permission. M.I.T. makes no representations about the suitability of +# this software for any purpose. It is provided "as is" without express +# or implied warranty. +# +# + DEPLIBS = $(DEPKLIB) +LOCAL_LIBRARIES = $(KLIB) + +Krb5ClientProgramTarget(ksu) diff --git a/src/clients/ksu/authorization.c b/src/clients/ksu/authorization.c new file mode 100644 index 000000000..fea20c0c5 --- /dev/null +++ b/src/clients/ksu/authorization.c @@ -0,0 +1,803 @@ +/* + * 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 writen by: Ari Medvinsky, ari@isi.edu + */ + +#include "ksu.h" + +static krb5_error_code _dbm_an_to_ln(); +static krb5_error_code _username_an_to_ln(); +static void auth_cleanup(); + +krb5_boolean fowner(FILE * fp, int 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)) { + fclose(fp); + return(FALSE); + } + + if ((sbuf.st_uid != uid) && sbuf.st_uid) { + fclose(fp); + 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( /* IN */ + krb5_principal principal, + const char *luser, + char * local_realm_name, char * cmd, + /* OUT */ + krb5_boolean * ok, char ** out_fcmd) +{ + struct passwd *pwd; + char * kuser; + char *princname; + int k5login_flag =0; + int k5users_flag =0; + krb5_boolean retbool =FALSE; + FILE * login_fp, * users_fp; + krb5_error_code retval = 0; + struct stat statbuf; + struct stat st_temp; + + *ok =FALSE; + + /* no account => no access */ + if ((pwd = getpwnam(luser)) == NULL) { + return 0; + } + + if (retval = krb5_unparse_name(principal, &princname)){ + 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){ + return 0; + } + } + + if (!k5users_flag){ + if ((users_fp = fopen(k5users_path, "r")) == NULL) { + return 0; + } + if ( fowner(users_fp, pwd->pw_uid) == FALSE){ + return 0; + } + } + + if (auth_debug){ + fprintf(stderr, + "In krb5_authorization: if auth files exist -> can access\n"); + } + + if (cmd){ + if(k5users_flag){ + return 0; /* if kusers does not exist -> done */ + }else{ + if(retval = k5users_lookup(users_fp,princname, + cmd,&retbool,out_fcmd)){ + auth_cleanup(k5users_flag,users_fp, + k5login_flag,login_fp, princname); + return retval; + }else{ + *ok =retbool; + return retval; + } + } + } + + /* 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); + } + if (retval = k5login_lookup( login_fp, princname, &retbool)){ + auth_cleanup(k5users_flag,users_fp, + k5login_flag,login_fp, princname); + return retval; + } + + + } + + if ((!k5users_flag) && (retbool == FALSE) ){ + if(retval = k5users_lookup (users_fp,princname, + cmd, &retbool,out_fcmd)){ + auth_cleanup(k5users_flag,users_fp, + k5login_flag,login_fp, princname); + return retval; + } + } + + if ( k5login_flag && k5users_flag){ + + char * kuser = (char *) calloc (strlen(princname), sizeof(char)); + +#ifdef DEBUG + printf("krb5_lname_file %s\n", krb5_lname_file); +#endif + + if (!stat(krb5_lname_file, &statbuf)){ + if ((! _dbm_an_to_ln(principal, strlen(princname), kuser)) && + (strcmp(kuser, luser) == 0)){ + retbool = TRUE; /* found the right one in db */ + } + } + + if (local_realm_name && (retbool == FALSE)){ + char * realm; + int used_def = 0; + + if (!strcmp(local_realm_name, USE_DEFAULT_REALM_NAME)){ + + if (retval = krb5_get_default_realm(&realm)) { + auth_cleanup(k5users_flag,users_fp, + k5login_flag,login_fp, princname); + free(kuser); + return(retval); + } + used_def =1; + } + else{ realm = local_realm_name; } + + if((! _username_an_to_ln(principal,strlen(princname), kuser, + realm)) && (strcmp(kuser,luser) == 0)){ + retbool = TRUE; + } + + if (used_def) free (realm); + } + free(kuser); + } + + *ok =retbool; + auth_cleanup(k5users_flag,users_fp, k5login_flag,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 ( FILE *fp, char * princname, + krb5_boolean * found) { + +krb5_error_code retval; +char * line; +char * fprinc, *cmd; +char * lp; +krb5_boolean loc_found = FALSE; + + + if (retval = get_line(fp, &line )){ + 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); + if (retval = get_line(fp, &line )){ 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 ( 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; + + if (retval = get_line(fp, &line )){ + 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 ? strdup(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); + if (retval = get_line(fp, &line )){ 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(char * fcmd, char *** out_fcmd, char ** out_err){ +char * out_path; +char * err; +char ** tmp_fcmd; +char * path_ptr, *path; +char * lp, * tc; +krb5_boolean found = FALSE; +int i=0; + + tmp_fcmd = (char **) calloc (MAX_CMD, sizeof(char *)); + + if (*fcmd == '/'){ /* must be full path */ + tmp_fcmd[0] = strdup(fcmd); + tmp_fcmd[1] = NULL; + *out_fcmd = tmp_fcmd; + return TRUE; + }else{ + int size; + char * cmd_path_str = ""; + + /* must be either full path or just the cmd name */ + if (strchr(fcmd, '/')){ + err = (char *) calloc((strlen(fcmd) +200) ,sizeof(char)); + sprintf(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 + err = (char *) calloc(2*(strlen(fcmd) +200) ,sizeof(char)); + sprintf(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 = strdup (CMD_PATH); + path_ptr = path; + + while ((*path_ptr == ' ') || (*path_ptr == '\t')) path_ptr ++; + + tc = get_first_token (path_ptr, &lp); + + if (! tc){ + err = (char *) calloc((strlen(fcmd) +200) ,sizeof(char)); + sprintf(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 */ + err = (char *) calloc((strlen(tc) +200) ,sizeof(char)); + sprintf(err,"Error: bad path %s in CMD_PATH for %s must start with '/' \n",tc, KRB5_USERS_NAME ); + *out_err = err; + return FALSE; + } + + out_path = (char *) calloc( MAXPATHLEN, sizeof (char)); + sprintf(out_path,"%s/%s",tc, fcmd ); + + tmp_fcmd[i] = out_path; + + 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( 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(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 (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( char ** fcmd_arr, char ** cmd_out, + char ** err_out){ + +struct stat st_temp; +int i = 0; +krb5_boolean retbool= FALSE; +int j =0; +char * err; +int max_ln=0; +int tln=0; + + while(fcmd_arr[i]){ + tln = strlen(fcmd_arr[i]); + if ( tln > max_ln) max_ln = tln; + if (!stat (fcmd_arr[i], &st_temp )){ + *cmd_out = strdup(fcmd_arr[i]); + retbool = TRUE; + break; + } + i++; + } + +if (retbool == FALSE ){ + err = (char *) calloc((80 +max_ln*i) ,sizeof(char)); + sprintf(err,"Error: not found -> "); + for(j= 0; j < i; j ++){ + sprintf(err,"%s %s ", err, fcmd_arr[j]); + } + sprintf(err,"%s\n", err); + *err_out = err; +} + + +return retbool; +} + +/*************************************************************** +returns 1 if there is an error, 0 if no error. + +***************************************************************/ + +int match_commands ( 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 = strdup(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 ( /* IN */ FILE * fp, + /* OUT */ char ** out_line ){ + +char * line, *r, *newline , *line_ptr; +int chunk_count = 1; + + line = (char *) calloc (BUFSIZ, sizeof (char )); + line_ptr = line; + line[0] = '\0'; + + while (( r = fgets(line_ptr, BUFSIZ , fp)) != NULL){ + if (newline = strchr(line_ptr, '\n')){ + *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 (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 (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; +} + +/******************************************************************** + * Implementation: This version uses a DBM database, indexed by aname, + * to generate a lname. + * + * The entries in the database are normal C strings, and include the trailing + * null in the DBM datum.size. + ********************************************************************/ +static krb5_error_code +_dbm_an_to_ln( krb5_const_principal aname, const int lnsize, char *lname) +{ + DBM *db; + krb5_error_code retval; + datum key, contents; + char *princ_name; + + if (retval = krb5_unparse_name(aname, &princ_name)) + return(retval); + key.dptr = princ_name; + key.dsize = strlen(princ_name)+1; /* need to store the NULL for + decoding */ + + db = dbm_open(krb5_lname_file, O_RDONLY, 0600); + if (!db) { + krb5_xfree(princ_name); + return KRB5_LNAME_CANTOPEN; + } + + contents = dbm_fetch(db, key); + + krb5_xfree(princ_name); + + if (contents.dptr == NULL) { + retval = KRB5_LNAME_NOTRANS; + } else { + strncpy(lname, contents.dptr, lnsize); + if (lnsize < contents.dsize) + retval = KRB5_CONFIG_NOTENUFSPACE; + else if (lname[contents.dsize-1] != '\0') + retval = KRB5_LNAME_BADFORMAT; + else + retval = 0; + } + /* can't close until we copy the contents. */ + (void) dbm_close(db); + return retval; +} + +/***************************************************************** + * Implementation: This version checks the realm to see if it is the + * realm passed in; if so, and there is exactly one non-realm + * component to the name, that name is returned as the lname. + ************************************************************/ + +static krb5_error_code +_username_an_to_ln ( krb5_const_principal aname, const int lnsize, + char *lname, char * realm) +{ + krb5_error_code retval; + int realm_length; + + realm_length = krb5_princ_realm(aname)->length; + + if ((realm_length != strlen(realm)) || + (memcmp(realm, krb5_princ_realm(aname)->data, realm_length))) { + return KRB5_LNAME_NOTRANS; + } + + if (krb5_princ_size(aname) != 1) { + if (krb5_princ_size(aname) == 2 ) { + /* Check to see if 2nd component is the local realm. */ + if ( strncmp(krb5_princ_component(aname,1)->data,realm, + realm_length) || + realm_length != krb5_princ_component(aname,1)->length) + return KRB5_LNAME_NOTRANS; + } + else + /* no components or more than one component to non-realm part of name + --no translation. */ + return KRB5_LNAME_NOTRANS; + } + + strncpy(lname, krb5_princ_component(aname,0)->data, + min(krb5_princ_component(aname,0)->length,lnsize)); + if (lnsize < krb5_princ_component(aname,0)->length ) { + retval = KRB5_CONFIG_NOTENUFSPACE; + } else { + lname[krb5_princ_component(aname,0)->length] = '\0'; + retval = 0; + } + return retval; +} + +void auth_cleanup(int k5users_flag, FILE * users_fp, + int k5login_flag, FILE * login_fp, char *princname){ + + free (princname); + if (!k5users_flag) fclose(users_fp); + if (!k5login_flag) fclose(login_fp); + +} + +void init_auth_names(char *pw_dir){ + + if ((strlen(pw_dir) == 1) && (*pw_dir == '/')){ + sprintf(k5login_path,"%s%s", pw_dir, KRB5_LOGIN_NAME); + sprintf(k5users_path,"%s%s", pw_dir, KRB5_USERS_NAME); + }else{ + sprintf(k5login_path,"%s/%s", pw_dir, KRB5_LOGIN_NAME); + sprintf(k5users_path,"%s/%s", pw_dir, KRB5_USERS_NAME); + } +} diff --git a/src/clients/ksu/ccache.c b/src/clients/ksu/ccache.c new file mode 100644 index 000000000..38410b977 --- /dev/null +++ b/src/clients/ksu/ccache.c @@ -0,0 +1,781 @@ +/* + * 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 writen by: Ari Medvinsky, ari@isi.edu + */ + +#include "ksu.h" + +/****************************************************************** +krb5_cache_copy + +gets rid of any expired tickets in the secondary cache, +copies the default cache into the secondary cache, + +************************************************************************/ + +krb5_boolean compare_creds(); +void show_credential(); + +/* modifies only the cc_other, the algorithm may look a bit funny, + but I had to do it this way, since remove function did not come + with k5 beta 3 release. +*/ + +krb5_error_code krb5_ccache_copy (/* IN */ + krb5_ccache cc_def, char * cc_other_tag, + krb5_principal primary_principal, + /* OUT */ + krb5_ccache * cc_out ){ + +int i=0; +krb5_ccache * cc_other; +char * cc_def_name; +char * cc_other_name; +krb5_error_code retval=0; +krb5_cc_cursor cur; +krb5_creds creds, temp_tktq, temp_tkt; +int code= 0; +krb5_creds ** cc_def_creds_arr = NULL; +krb5_creds ** cc_other_creds_arr = NULL; +uid_t eff_uid, eff_gid; + + cc_other = (krb5_ccache *) calloc(1, sizeof (krb5_ccache)); + + if( retval = krb5_cc_resolve(cc_other_tag, cc_other)){ + com_err (prog_name, retval, "resolving ccache %s", + cc_other_tag); + return retval; + } + + cc_def_name = krb5_cc_get_name(cc_def); + cc_other_name = krb5_cc_get_name(*cc_other); + + if ( ! access(cc_def_name, F_OK)){ + if(retval = krb5_get_nonexp_tkts( cc_def, &cc_def_creds_arr)){ + return retval; + } + } + + + eff_uid = geteuid(); + eff_gid = getegid(); + + if (seteuid(getuid()) < 0) { return errno;} + if (setegid(getgid()) < 0) { return errno;} + + if (retval = krb5_cc_initialize(*cc_other, primary_principal)){ + return retval; + } + + if (seteuid(eff_uid) < 0) {return errno; } + if (setegid(eff_gid) < 0) {return errno; } + + + retval = krb5_store_all_creds(* cc_other, + cc_def_creds_arr, cc_other_creds_arr); + + + + if (cc_def_creds_arr){ + while (cc_def_creds_arr[i]){ + krb5_free_creds(cc_def_creds_arr[i]); + i++; + } + } + + i=0; + + if(cc_other_creds_arr){ + while (cc_other_creds_arr[i]){ + krb5_free_creds(cc_other_creds_arr[i]); + i++; + } + } + + *cc_out = *cc_other; + return retval; +} + + + +krb5_error_code krb5_store_all_creds(krb5_ccache cc, + krb5_creds ** creds_def, krb5_creds ** creds_other ){ + +int i = 0; +int j = 0; +krb5_error_code retval = 0; +krb5_creds ** temp_creds= NULL; +krb5_boolean cmp; + + + if ((creds_def == NULL) && (creds_other == NULL)) + return 0; + + if ((creds_def == NULL) && (creds_other != NULL)) + temp_creds = creds_other; + + if ((creds_def != NULL) && (creds_other == NULL)) + temp_creds = creds_def; + + + if (temp_creds){ + while(temp_creds[i]){ + if (retval= krb5_cc_store_cred(cc, temp_creds[i])){ + return retval; + } + i++; + } + } + else { /* both arrays have elements in them */ + + return KRB5KRB_ERR_GENERIC; + +/************ while(creds_other[i]){ + cmp = FALSE; + j = 0; + while(creds_def[j]){ + cmp = compare_creds(creds_other[i],creds_def[j]); + + if( cmp == TRUE) break; + + j++; + } + if (cmp == FALSE){ + if (retval= krb5_cc_store_cred(cc, + creds_other[i])){ + return retval; + } + } + i ++; + } + + i=0; + while(creds_def[i]){ + if (retval= krb5_cc_store_cred(cc, creds_def[i])){ + return retval; + } + i++; + } + +**************/ + } + return 0; +} + +krb5_boolean compare_creds( krb5_creds * cred1, krb5_creds * cred2){ + +krb5_boolean retval; + + retval = krb5_principal_compare (cred1->client, cred2->client); + + if (retval == TRUE) + retval = krb5_principal_compare (cred1->server, cred2->server); + + return retval; +} + + + + +krb5_error_code krb5_get_nonexp_tkts(krb5_ccache cc, + krb5_creds *** creds_array){ + +krb5_creds creds, temp_tktq, temp_tkt; +krb5_creds **temp_creds; +krb5_error_code retval=0; +krb5_cc_cursor cur; +int count = 0; +int chunk_count = 1; + + if ( ! ( temp_creds = (krb5_creds **) malloc( CHUNK * sizeof(krb5_creds *)))){ + return errno; + } + + + memset((char *) &temp_tktq, 0, sizeof(temp_tktq)); + memset((char *) &temp_tkt, 0, sizeof(temp_tkt)); + memset((char *) &creds, 0, sizeof(creds)); + + /* initialize the cursor */ + if (retval = krb5_cc_start_seq_get(cc, &cur)) { + return retval; + } + + while (!(retval = krb5_cc_next_cred(cc, &cur, &creds))){ + + if(retval = krb5_check_exp(creds.times)){ + if (retval != KRB5KRB_AP_ERR_TKT_EXPIRED){ + return retval; + } + if (auth_debug){ + fprintf(stderr,"krb5_ccache_copy: CREDS EXPIRED:\n"); + fputs(" Valid starting Expires Service principal\n",stdout); + show_credential(&creds, cc); + fprintf(stderr,"\n"); + } + } + else { /* these credentials didn't expire */ + + if (retval = krb5_copy_creds(&creds, &temp_creds[count])){ + return retval; + } + count ++; + + if (count == (chunk_count * CHUNK -1)){ + chunk_count ++; + if (!(temp_creds = (krb5_creds **) realloc(temp_creds, + chunk_count * CHUNK * sizeof(krb5_creds *)))){ + return errno; + } + } + } + + } + + temp_creds[count] = NULL; + *creds_array = temp_creds; + + if (retval == KRB5_CC_END) { + retval = krb5_cc_end_seq_get(cc, &cur); + } + + return retval; + +} + + +extern krb5_deltat krb5_clockskew; + +krb5_error_code krb5_check_exp(krb5_ticket_times tkt_time) +{ +krb5_error_code retval =0; +krb5_timestamp currenttime; + + if (retval = krb5_timeofday (¤ttime)){ + return retval; + } + if (auth_debug){ + fprintf(stderr,"krb5_check_exp: the krb5_clockskew is %d \n", + krb5_clockskew); + + fprintf(stderr,"krb5_check_exp: currenttime - endtime %d \n", + (currenttime - tkt_time.endtime )); + + } + + if (currenttime - tkt_time.endtime > krb5_clockskew){ + retval = KRB5KRB_AP_ERR_TKT_EXPIRED ; + return retval; + } + + return 0; +} + + +char *flags_string( krb5_creds *cred) +{ + static char buf[32]; + int i = 0; + + if (cred->ticket_flags & TKT_FLG_FORWARDABLE) + buf[i++] = 'F'; + if (cred->ticket_flags & TKT_FLG_FORWARDED) + buf[i++] = 'f'; + if (cred->ticket_flags & TKT_FLG_PROXIABLE) + buf[i++] = 'P'; + if (cred->ticket_flags & TKT_FLG_PROXY) + buf[i++] = 'p'; + if (cred->ticket_flags & TKT_FLG_MAY_POSTDATE) + buf[i++] = 'D'; + if (cred->ticket_flags & TKT_FLG_POSTDATED) + buf[i++] = 'd'; + if (cred->ticket_flags & TKT_FLG_INVALID) + buf[i++] = 'i'; + if (cred->ticket_flags & TKT_FLG_RENEWABLE) + buf[i++] = 'R'; + if (cred->ticket_flags & TKT_FLG_INITIAL) + buf[i++] = 'I'; + if (cred->ticket_flags & TKT_FLG_HW_AUTH) + buf[i++] = 'H'; + if (cred->ticket_flags & TKT_FLG_PRE_AUTH) + buf[i++] = 'A'; + buf[i] = '\0'; + return(buf); +} + +static char *Month_names[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + +void printtime( time_t tv) +{ + struct tm *stime; + + stime = localtime((time_t *)&tv); + printf("%2d-%s-%2d %02d:%02d:%02d", + stime->tm_mday, + Month_names[stime->tm_mon], + stime->tm_year, + stime->tm_hour, + stime->tm_min, + stime->tm_sec); +} + + +krb5_error_code +krb5_get_login_princ( const char *luser, char *** princ_list ) +{ + struct stat sbuf; + struct passwd *pwd; + char pbuf[MAXPATHLEN]; + FILE *fp; + char * linebuf; + char *newline; + int gobble; + char ** buf_out; + struct stat st_temp; + int count = 0, chunk_count = 1; + + /* no account => no access */ + + if ((pwd = getpwnam(luser)) == NULL) { + return 0; + } + (void) strcpy(pbuf, pwd->pw_dir); + (void) strcat(pbuf, "/.k5login"); + + if (stat(pbuf, &st_temp)) { /* not accessible */ + return 0; + } + + + /* open ~/.k5login */ + if ((fp = fopen(pbuf, "r")) == NULL) { + return 0; + } + /* + * For security reasons, the .k5login file must be owned either by + * the user himself, or by root. Otherwise, don't grant access. + */ + if (fstat(fileno(fp), &sbuf)) { + fclose(fp); + return 0; + } + if ((sbuf.st_uid != pwd->pw_uid) && sbuf.st_uid) { + fclose(fp); + return 0; + } + + /* check each line */ + + + if( !(linebuf = (char *) calloc (BUFSIZ, sizeof(char)))) return errno; + + if (!(buf_out = (char **) malloc( CHUNK * sizeof(char *)))) return errno; + + while ( fgets(linebuf, BUFSIZ, fp) != NULL) { + /* null-terminate the input string */ + linebuf[BUFSIZ-1] = '\0'; + newline = NULL; + /* nuke the newline if it exists */ + if (newline = strchr(linebuf, '\n')) + *newline = '\0'; + + buf_out[count] = linebuf; + count ++; + + if (count == (chunk_count * CHUNK -1)){ + chunk_count ++; + if (!(buf_out = (char **) realloc(buf_out, + chunk_count * CHUNK * sizeof(char *)))){ + return errno; + } + } + + /* clean up the rest of the line if necessary */ + if (!newline) + while (((gobble = getc(fp)) != EOF) && gobble != '\n'); + + if( !(linebuf = (char *) calloc (BUFSIZ, sizeof(char)))) return errno; + } + + buf_out[count] = NULL; + *princ_list = buf_out; + fclose(fp); + return 0; +} + + + +void +show_credential(krb5_creds * cred, krb5_ccache cc) +{ + krb5_error_code retval; + char *name, *sname, *flags; + int first = 1; + krb5_principal princ; + char * defname; + int show_flags =1; + + retval = krb5_unparse_name(cred->client, &name); + if (retval) { + com_err(prog_name, retval, "while unparsing client name"); + return; + } + retval = krb5_unparse_name(cred->server, &sname); + if (retval) { + com_err(prog_name, retval, "while unparsing server name"); + free(name); + return; + } + + if (retval = krb5_cc_get_principal(cc, &princ)) { + com_err(prog_name, retval, "while retrieving principal name"); + return; + } + if (retval = krb5_unparse_name(princ, &defname)) { + com_err(prog_name, retval, "while unparsing principal name"); + return; + } + + if (!cred->times.starttime) + cred->times.starttime = cred->times.authtime; + + printtime(cred->times.starttime); + putchar(' '); putchar(' '); + printtime(cred->times.endtime); + putchar(' '); putchar(' '); + + printf("%s\n", sname); + + if (strcmp(name, defname)) { + printf("\tfor client %s", name); + first = 0; + } + + if (cred->times.renew_till) { + if (first) + fputs("\t",stdout); + else + fputs(", ",stdout); + fputs("renew until ", stdout); + printtime(cred->times.renew_till); + } + if (show_flags) { + flags = flags_string(cred); + if (flags && *flags) { + if (first) + fputs("\t",stdout); + else + fputs(", ",stdout); + printf("Flags: %s", flags); + first = 0; + } + } + putchar('\n'); + free(name); + free(sname); +} + +int gen_sim(){ + static int i = 0; + i ++; + return i; +} + +krb5_error_code krb5_ccache_overwrite( krb5_ccache ccs, krb5_ccache cct, + krb5_principal primary_principal){ +char * cct_name; +char * ccs_name; +krb5_error_code retval=0; +krb5_principal temp_principal; +krb5_creds ** ccs_creds_arr = NULL; +int i=0; +uid_t eff_uid, eff_gid; + + ccs_name = krb5_cc_get_name(ccs); + cct_name = krb5_cc_get_name(cct); + + if ( ! access(ccs_name, F_OK)){ + if(retval = krb5_get_nonexp_tkts( ccs, &ccs_creds_arr)){ + return retval; + } + } + + if ( ! access(cct_name, F_OK)){ + if (retval = krb5_cc_get_principal(cct, &temp_principal)){ + return retval; + } + }else{ + temp_principal = primary_principal; + } + + eff_uid = geteuid(); + eff_gid = getegid(); + + if (seteuid(getuid()) < 0) { return errno;} + if (setegid(getgid()) < 0) { return errno;} + + if (retval = krb5_cc_initialize(cct, temp_principal)){ + return retval; + } + if (seteuid(eff_uid) < 0) {return errno; } + if (setegid(eff_gid) < 0) {return errno; } + + retval = krb5_store_all_creds(cct, + ccs_creds_arr, NULL); + + if (ccs_creds_arr){ + while (ccs_creds_arr[i]){ + krb5_free_creds(ccs_creds_arr[i]); + i++; + } + } + + return retval; +} + +krb5_error_code krb5_store_some_creds(krb5_ccache cc, krb5_creds ** creds_def, + krb5_creds ** creds_other, + krb5_principal prst,krb5_boolean *stored){ + +int i = 0; +int j = 0; +krb5_error_code retval = 0; +krb5_creds ** temp_creds= NULL; +krb5_boolean cmp; +krb5_boolean temp_stored = FALSE; + + + if ((creds_def == NULL) && (creds_other == NULL)) + return 0; + + if ((creds_def == NULL) && (creds_other != NULL)) + temp_creds = creds_other; + + if ((creds_def != NULL) && (creds_other == NULL)) + temp_creds = creds_def; + + + if (temp_creds){ + while(temp_creds[i]){ + if (krb5_principal_compare( temp_creds[i]->client, + prst)== TRUE){ + + if(retval=krb5_cc_store_cred(cc,temp_creds[i])){ + return retval; + } + temp_stored = TRUE; + } + + i++; + } + } + else { /* both arrays have elements in them */ + return KRB5KRB_ERR_GENERIC; + } + +*stored = temp_stored; +return 0; +} +/****************************************************************** +krb5_cache_copy_restricted + +gets rid of any expired tickets in the secondary cache, +copies the default cache into the secondary cache, +only credentials that are for prst are copied. + +the algorithm may look a bit funny, +but I had to do it this way, since cc_remove function did not come +with k5 beta 3 release. +************************************************************************/ + +krb5_error_code krb5_ccache_copy_restricted (/* IN */ + krb5_ccache cc_def, char * cc_other_tag, + krb5_principal prst, + /* OUT */ + krb5_ccache * cc_out, krb5_boolean *stored ){ + +int i=0; +krb5_ccache * cc_other; +char * cc_def_name; +char * cc_other_name; +krb5_error_code retval=0; +krb5_cc_cursor cur; +krb5_creds creds, temp_tktq, temp_tkt; +int code= 0; +krb5_creds ** cc_def_creds_arr = NULL; +krb5_creds ** cc_other_creds_arr = NULL; +uid_t eff_uid, eff_gid; + + cc_other = (krb5_ccache *) calloc(1, sizeof (krb5_ccache)); + + if( retval = krb5_cc_resolve(cc_other_tag, cc_other)){ + com_err (prog_name, retval, "resolving ccache %s", + cc_other_tag); + return retval; + } + + cc_def_name = krb5_cc_get_name(cc_def); + cc_other_name = krb5_cc_get_name(*cc_other); + + if ( ! access(cc_def_name, F_OK)){ + if(retval = krb5_get_nonexp_tkts( cc_def, &cc_def_creds_arr)){ + return retval; + } + + } + + eff_uid = geteuid(); + eff_gid = getegid(); + + if (seteuid(getuid()) < 0) { return errno;} + if (setegid(getgid()) < 0) { return errno;} + + if (retval = krb5_cc_initialize(*cc_other, prst)){ + return retval; + } + + if (seteuid(eff_uid) < 0) {return errno; } + if (setegid(eff_gid) < 0) {return errno; } + + + retval = krb5_store_some_creds(* cc_other, + cc_def_creds_arr, cc_other_creds_arr, prst, stored); + + + + if (cc_def_creds_arr){ + while (cc_def_creds_arr[i]){ + krb5_free_creds(cc_def_creds_arr[i]); + i++; + } + } + + i=0; + + if(cc_other_creds_arr){ + while (cc_other_creds_arr[i]){ + krb5_free_creds(cc_other_creds_arr[i]); + i++; + } + } + + *cc_out = *cc_other; + return retval; +} + +/************************************************************ +krb5_ccache_refresh - gets rid of all the expired tickets in +a cache. The alg. may look a bit funny, --> cc_remove was +not available with beta3 release. + +************************************************************/ + +krb5_error_code krb5_ccache_refresh ( krb5_ccache cc ){ + +int i=0; +krb5_error_code retval=0; +krb5_principal temp_principal; +krb5_creds ** cc_creds_arr = NULL; +char * cc_name; + + cc_name = krb5_cc_get_name(cc); + + if ( ! access(cc_name, F_OK)){ + + if (auth_debug) { + fprintf(stderr,"Refreshing cache %s\n", cc_name); + } + + if(retval = krb5_get_nonexp_tkts( cc, &cc_creds_arr)){ + return retval; + } + + if (retval = krb5_cc_get_principal(cc, &temp_principal)){ + return retval; + } + + if (retval = krb5_cc_initialize(cc, temp_principal)){ + return retval; + } + + if (retval = krb5_store_all_creds(cc, + cc_creds_arr, NULL)){ + return retval; + } + + if (cc_creds_arr){ + while (cc_creds_arr[i]){ + krb5_free_creds(cc_creds_arr[i]); + i++; + } + } + } + return 0; +} + +krb5_error_code krb5_ccache_filter ( krb5_ccache cc, krb5_principal prst){ + +int i=0; +krb5_error_code retval=0; +krb5_principal temp_principal; +krb5_creds ** cc_creds_arr = NULL; +char * cc_name; +krb5_boolean stored; + + cc_name = krb5_cc_get_name(cc); + + if ( ! access(cc_name, F_OK)){ + + if (auth_debug) { + fprintf(stderr,"puting cache %s through a filter for -z option\n", cc_name); + } + + if(retval = krb5_get_nonexp_tkts( cc, &cc_creds_arr)){ + return retval; + } + + if (retval = krb5_cc_get_principal(cc, &temp_principal)){ + return retval; + } + + if (retval = krb5_cc_initialize(cc, temp_principal)){ + return retval; + } + + if (retval = krb5_store_some_creds(cc,cc_creds_arr,NULL,prst,&stored)){ + return retval; + } + + if (cc_creds_arr){ + while (cc_creds_arr[i]){ + krb5_free_creds(cc_creds_arr[i]); + i++; + } + } + } + return 0; +} + diff --git a/src/clients/ksu/heuristic.c b/src/clients/ksu/heuristic.c new file mode 100644 index 000000000..f6217ebca --- /dev/null +++ b/src/clients/ksu/heuristic.c @@ -0,0 +1,746 @@ +/* + * 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 writen by: Ari Medvinsky, ari@isi.edu + */ + +#include "ksu.h" + +/******************************************************************* +get_all_princ_from_file - retrieves all principal names + from file pointed to by fp. + +*******************************************************************/ +void close_time(); +krb5_boolean find_str_in_list(); + +krb5_error_code get_all_princ_from_file ( FILE * fp, char *** plist){ + + krb5_error_code retval; + char * line, * fprinc, * lp, ** temp_list = NULL; + int count = 0, chunk_count = 1; + int i =0; + + if (!(temp_list = (char **) malloc( CHUNK * sizeof(char *)))){ + return errno; + } + + if (retval = get_line(fp, &line )){ + return retval; + } + + + while (line){ + fprinc = get_first_token (line, &lp); + + + if (fprinc ){ + temp_list[count] = strdup(fprinc); + count ++; + } + + if(count == (chunk_count * CHUNK -1)){ + chunk_count ++; + if (!(temp_list = (char **) realloc(temp_list, + chunk_count * CHUNK * sizeof(char *)))){ + return errno; + } + } + + + free (line); + if (retval = get_line(fp, &line )){ return retval;} + } + + temp_list[count] = NULL; + + + *plist = temp_list; + return 0; +} + +/************************************************************* +list_union - combines list1 and list2 into combined_list. + the space for list1 and list2 is either freed + or used by combined_list. +**************************************************************/ + +krb5_error_code list_union(char ** list1,char ** list2, + char *** combined_list){ + +int c1 =0, c2 = 0, i=0, j=0; +char ** tlist; + + if (! list1){ + *combined_list = list2; + return 0; + } + + if (! list2){ + *combined_list = list1; + return 0; + } + + while (list1[c1]) c1++; + while (list2[c2]) c2++; + + if (!(tlist = (char **) calloc( c1 + c2 + 1, sizeof ( char *)))){ + return errno; + } + + i = 0; + while(list1[i]){ + tlist[i] = list1[i]; + i++; + } + j = 0; + while(list2[j]){ + if(find_str_in_list(list1, list2[j])==FALSE){ + tlist[i] = list2[j]; + i++; + } + j++; + } + + free (list1); + free (list2); + + tlist[i]= NULL; + + + *combined_list = tlist; + return 0; +} + +krb5_error_code +filter(FILE *fp,char *cmd, char ** k5users_list,char *** k5users_filt_list){ + +krb5_error_code retval =0; +krb5_boolean found = FALSE; +char * out_cmd = NULL; +int i=0, j=0, found_count = 0, k=0; +char ** temp_filt_list; + + *k5users_filt_list = NULL; + + if (! k5users_list){ + return 0; + } + + while(k5users_list[i]){ + + if (retval= k5users_lookup(fp, k5users_list[i], + cmd, &found, &out_cmd)){ + return retval; + } + + if (found == FALSE){ + free (k5users_list[i]); + k5users_list[i] = NULL; + if (out_cmd) gb_err = out_cmd; + }else{ + found_count ++; + } + i++; + } + + if (! (temp_filt_list = (char **) calloc ( found_count +1, + sizeof (char*)))){ + return errno; + } + + for(j= 0, k=0; j < i; j ++ ){ + if (k5users_list[j]){ + temp_filt_list[k] = k5users_list[j]; + k++; + } + } + + temp_filt_list[k] = NULL; + + free (k5users_list); + + *k5users_filt_list = temp_filt_list; + return 0; +} + +krb5_error_code +get_authorized_princ_names( const char *luser, char *cmd, char *** princ_list) +{ + + struct passwd *pwd; + FILE *fp; + int count = 0, chunk_count = 1; + int k5login_flag =0, i=0; + int k5users_flag =0; + FILE * login_fp, * users_fp; + char ** k5login_list = NULL, ** k5users_list = NULL; + char ** k5users_filt_list = NULL; + char ** combined_list = NULL; + struct stat tb; + krb5_error_code retval; + + *princ_list = NULL; + + /* no account => no access */ + + if ((pwd = getpwnam(luser)) == NULL) { + return 0; + } + + k5login_flag = stat(k5login_path, &tb); + k5users_flag = stat(k5users_path, &tb); + + if (!k5login_flag){ + if ((login_fp = fopen(k5login_path, "r")) == NULL) { + return 0; + } + if ( fowner(login_fp, pwd->pw_uid) == FALSE){ + close_time(k5users_flag,users_fp, k5login_flag,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){ + close_time(k5users_flag,users_fp, k5login_flag,login_fp); + return 0; + } + + if(retval = get_all_princ_from_file (users_fp, &k5users_list)){ + close_time(k5users_flag,users_fp, k5login_flag,login_fp); + return retval; + } + + rewind(users_fp); + + if(retval = filter(users_fp,cmd, k5users_list, &k5users_filt_list)){ + close_time(k5users_flag,users_fp, k5login_flag, login_fp); + return retval; + } + + } + + if (cmd){ + *princ_list = k5users_filt_list; + close_time(k5users_flag,users_fp, k5login_flag, login_fp); + return 0; + } + + if (!k5login_flag){ + if(retval = get_all_princ_from_file (login_fp, &k5login_list)){ + close_time(k5users_flag,users_fp, k5login_flag,login_fp); + return retval; + } + } + + if(retval = list_union(k5login_list, k5users_filt_list, & combined_list)){ + close_time(k5users_flag,users_fp, k5login_flag,login_fp); + return retval; + } + + *princ_list = combined_list ; + close_time(k5users_flag,users_fp, k5login_flag,login_fp); + return 0; +} + + +void close_time(int k5users_flag, FILE * users_fp, + int k5login_flag, FILE * login_fp){ + + if (!k5users_flag) fclose(users_fp); + if (!k5login_flag) fclose(login_fp); + +} + +krb5_boolean find_str_in_list( char ** list , char * elm){ + +int i=0; +krb5_boolean found = FALSE; + +if (!list) return found; + +while (list[i] ){ + if (!strcmp(list[i], elm)){ + found = TRUE; + break; + } + i++; +} + +return found; + +} + +/********************************************************************** +returns the principal that is closes to client (can be the the client +himself). plist contains +a principal list obtained from .k5login and .k5users file. +A principal is picked that has the best chance of getting in. + +**********************************************************************/ + + +krb5_error_code get_closest_principal( char ** plist, krb5_principal * client, + krb5_boolean * found ) +{ +krb5_error_code retval =0; +char * client_name; +krb5_principal temp_client, best_client = NULL; +int i = 0, j=0, cnelem, pnelem; +krb5_boolean got_one; + + *found = FALSE; + + if (! plist ) return 0; + + cnelem = krb5_princ_size(*client); + + + while(plist[i]){ + + if (retval = krb5_parse_name(plist[i], &temp_client)){ + return retval; + } + + pnelem = krb5_princ_size(temp_client); + + if ( cnelem > pnelem){ + i++; + continue; + } + + if (krb5_princ_realm(*client)->length == + krb5_princ_realm(temp_client)->length + && (!memcmp (krb5_princ_realm(*client)->data, + krb5_princ_realm(temp_client)->data, + krb5_princ_realm(temp_client)->length))){ + + got_one = TRUE; + for(j =0; j < cnelem; j ++){ + + krb5_data *p1 = + krb5_princ_component(*client, j); + krb5_data *p2 = + krb5_princ_component(temp_client, j); + + if ((p1->length != p2->length) || + memcmp(p1->data,p2->data,p1->length)){ + got_one = FALSE; + break; + } + } + if (got_one == TRUE){ + if(best_client){ + if(krb5_princ_size(best_client) > + krb5_princ_size(temp_client)){ + best_client = temp_client; + } + }else{ + best_client = temp_client; + } + } + } + i++; + } + + if (best_client) { + *found = TRUE; + *client = best_client; + } + + return 0; +} + +/**************************************************************** +find_either_ticket checks to see whether there is a ticket for the + end server or tgt, if neither is there the return FALSE, +*****************************************************************/ + +krb5_error_code find_either_ticket (krb5_ccache cc, krb5_principal client, +krb5_principal end_server, krb5_boolean * found) { + +krb5_principal kdc_server; +krb5_creds tgt, tgtq; +krb5_ticket * target_tkt; +krb5_error_code retval; +char * client_name; +krb5_boolean temp_found = FALSE; +char * cc_source_name; + +cc_source_name = krb5_cc_get_name(cc); + +if ( ! access(cc_source_name, F_OK)){ + + if (retval = find_ticket (cc, client, end_server, &temp_found)) { + return retval; + } + + if (temp_found == FALSE){ + + if (retval = krb5_tgtname( krb5_princ_realm (client), + krb5_princ_realm(client), &kdc_server)){ + return retval ; + } + + if(retval = find_ticket (cc,client, kdc_server, &temp_found)) { + return retval; + } + } + else { + if (auth_debug) + printf("find_either_ticket: found end server ticket\n"); + } + +} + + *found = temp_found; + + return 0; +} + + +krb5_error_code find_ticket (krb5_ccache cc, krb5_principal client, +krb5_principal server, krb5_boolean * found) { + +krb5_principal kdc_server; +krb5_creds tgt, tgtq; +krb5_ticket * target_tkt; +krb5_error_code retval; +char * client_name; + + *found = FALSE; + + memset((char *) &tgtq, 0, sizeof(tgtq)); + memset((char *) &tgt, 0, sizeof(tgt)); + + if (retval= krb5_copy_principal( client, &tgtq.client)){ + return retval; + } + + if (retval= krb5_copy_principal( server, &tgtq.server)){ + return retval ; + } + + retval = krb5_cc_retrieve_cred(cc, KRB5_TC_MATCH_SRV_NAMEONLY, + &tgtq, &tgt); + + if (! retval) retval = krb5_check_exp(tgt.times); + + if (retval){ + if ((retval != KRB5_CC_NOTFOUND) && + (retval != KRB5KRB_AP_ERR_TKT_EXPIRED)){ + return retval ; + } + } else{ + *found = TRUE; + return 0; + } + + free(tgtq.server); + free(tgtq.client); + + return 0; +} + + + +krb5_error_code find_princ_in_list (krb5_principal princ, + char **plist , krb5_boolean * found ){ + +int i=0; +char * princname; +krb5_error_code retval; + +*found = FALSE; + +if (!plist) return 0; + +if (retval = krb5_unparse_name(princ, &princname)){ + return retval; +} + +while (plist[i] ){ + if (!strcmp(plist[i], princname)){ + *found = TRUE; + break; + } + i++; +} + +return 0; + +} + +typedef struct princ_info { + krb5_principal p; + krb5_boolean found; +}princ_info; + +/********************************************************************** +get_best_princ_for_target - + +sets the client name, path_out gets set, if authorization is not possible +path_out gets set to ... + +***********************************************************************/ + +krb5_error_code get_best_princ_for_target(int source_uid, int target_uid, + char * source_user, char * target_user, + krb5_ccache cc_source, opt_info *options, + char * cmd, char *hostname, + krb5_principal * client, int * path_out){ + +princ_info princ_trials[10]; +char * cc_source_name; +krb5_principal cc_def_princ = NULL; +krb5_principal temp_client; +krb5_principal target_client; +krb5_principal source_client; +krb5_principal end_server; +krb5_error_code retval; +char ** aplist =NULL; +krb5_boolean found = FALSE; +struct stat tb; +int count =0; +int i; + +*path_out = 0; + +/* -n option was specified client is set we are done */ +if (options->princ){ + return 0; +} + +cc_source_name = krb5_cc_get_name(cc_source); + +if ( ! access(cc_source_name, F_OK)){ + if (retval = krb5_cc_get_principal(cc_source, &cc_def_princ)){ + return retval; + } +} + +if (retval=krb5_parse_name(target_user, &target_client)){ + return retval; +} + +if (retval=krb5_parse_name(source_user, &source_client)){ + return retval; +} + + + +if (source_uid == 0){ + if (target_uid != 0){ + *client = target_client; /* this will be used to restrict + the cache copty */ + }else{ + if(cc_def_princ){ + *client = cc_def_princ; + }else{ + *client = target_client; + } + } + + if (auth_debug){ + printf(" GET_best_princ_for_target: via source_uid == 0\n"); + } + + return 0; +} + +/* from here on, the code is for source_uid != 0 */ + + /* if .k5users and .k5login do not exist */ +if ( stat(k5login_path, &tb) && stat(k5users_path, &tb) ){ + *client = target_client; + + if ( cmd){ + *path_out = NOT_AUTHORIZED; + } + + if (auth_debug){ + printf(" GET_best_princ_for_target: via no auth files path\n"); + } + + return 0; +}else{ + if (retval = get_authorized_princ_names(target_user, cmd, & aplist)){ + return retval; + } + + /* .k5users or .k5login exist, but no authorization */ + if ((!aplist) || (!aplist[0])){ + *path_out = NOT_AUTHORIZED; + if (auth_debug){ + printf( + "GET_best_princ_for_target: via empty auth files path\n"); + } + return 0; + } +} + +if (retval = krb5_sname_to_principal(hostname, NULL, + KRB5_NT_SRV_HST, &end_server)){ + return retval; +} + + +/* first see if default principal of the source cache + can get us in, then the target_user@realm, then the + source_user@realm. If all of them fail, try any + other ticket in the cache. +*/ + +if (cc_def_princ){ + princ_trials[count ++].p = cc_def_princ; +}else{ + princ_trials[count ++].p = NULL; +} +princ_trials[count ++].p = target_client; +princ_trials[count ++].p = source_client; + +for (i= 0; i < count; i ++){ + princ_trials[i].found = FALSE; +} + +for (i= 0; i < count; i ++){ + if(princ_trials[i].p){ + if (retval= find_princ_in_list(princ_trials[i].p, aplist, &found)){ + return retval; + } + + if ( found == TRUE){ + princ_trials[i].found = TRUE; + + if (retval = find_either_ticket (cc_source, princ_trials[i].p, + end_server, &found)){ + return retval; + } + if (found == TRUE){ + *client = princ_trials[i].p; + if (auth_debug){ + printf("GET_best_princ_for_target: via ticket file, choice #%d\n", i); + } + return 0; + } + } + } +} + +/* out of preferred principals, see if there is any ticket that will + get us in */ + +i=0; +while (aplist[i]){ + + if (retval = krb5_parse_name(aplist[i], &temp_client)){ + return retval; + } + + if (retval = find_either_ticket (cc_source, temp_client, + end_server, &found)){ + return retval; + } + if (found == TRUE){ + if (auth_debug){ + printf("GET_best_princ_for_target: via ticket file, choice: any ok ticket \n" ); + } + *client = temp_client; + return 0; + } + + krb5_free_principal(temp_client); + + i++; +} + +/* no tickets qualified, select a principal, that may be used + for password promting */ + + +if (princ_trials[0].found == TRUE){ + *client = princ_trials[0].p; + + if (auth_debug){ + printf( + "GET_best_princ_for_target: via prompt passwd list choice: default cache principal\n"); + } + return 0; +} + + +#ifdef PRINC_LOOK_AHEAD + +if (princ_trials[0].p){ + if (retval= krb5_copy_principal(princ_trials[0].p, &temp_client)){ + return retval; + } + + /* get the client name that is the closest + to default principal of source cache */ + + if (retval = get_closest_principal(aplist, &temp_client, & found)){ + return retval; + } + + if (found == TRUE){ + *client = temp_client; + if (auth_debug){ + printf( + "GET_best_princ_for_target: via prompt passwd list choice: approximation of default cache principal\n"); + } + return 0; + } +} +#endif /* PRINC_LOOK_AHEAD */ + +for (i=1; i < count; i ++){ + if (princ_trials[i].found == TRUE){ + *client = princ_trials[i].p; + if (auth_debug){ + printf( + "GET_best_princ_for_target: via prompt passwd list choice #%d \n",i); + } + return 0; + } +} + +/* ok looks like we are out of luck, so just take the firs name + in the list and return */ + +if (retval = krb5_parse_name(aplist[0], &temp_client)){ + return retval; +} + +if(auth_debug){ + printf( "GET_best_princ_for_target: out of luck choice\n"); +} + +*client = temp_client; +return 0; + +} diff --git a/src/clients/ksu/krb_auth_su.c b/src/clients/ksu/krb_auth_su.c new file mode 100644 index 000000000..7783562af --- /dev/null +++ b/src/clients/ksu/krb_auth_su.c @@ -0,0 +1,653 @@ +/* + * 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 writen by: Ari Medvinsky, ari@isi.edu + */ + +#include "ksu.h" + +void plain_dump_principal (); + +krb5_data tgtname = { + KRB5_TGS_NAME_SIZE, + KRB5_TGS_NAME +}; + +/* + * Try no preauthentication first; then try the encrypted timestamp + */ +int preauth_search_list[] = { + 0, + KRB5_PADATA_ENC_TIMESTAMP, + -1 + }; + + + +krb5_boolean krb5_auth_check(krb5_principal client_pname, + char * hostname, opt_info * options, + char * target_user, krb5_ccache cc, + int * path_passwd) +{ +krb5_principal client, server, temp_client; +krb5_creds tgt, tgtq, cred; +krb5_creds **tgts = NULL; /* list of ticket granting tickets */ + +krb5_ticket * target_tkt; /* decrypted ticket for server */ +int default_client = 1 ; +struct passwd *pw = NULL; +krb5_error_code retval =0; +char ** k5login_plist = NULL; +int got_it = 0; +char * client_name; + + + *path_passwd = 0; + memset((char *) &tgtq, 0, sizeof(tgtq)); + memset((char *) &tgt, 0, sizeof(tgt)); + memset((char *) &cred, 0, sizeof(cred)); + + + if (retval= krb5_copy_principal( client_pname, &client)){ + com_err(prog_name, retval,"while copying client principal"); + return (FALSE) ; + } + + if (auth_debug) + { dump_principal("krb5_auth_check: Client principal name", client); } + + if ( retval = krb5_sname_to_principal(hostname, NULL, + KRB5_NT_SRV_HST, &server)){ + com_err(prog_name, retval, + "while creating server %s principal name", hostname); + krb5_free_principal(client); + return (FALSE) ; + } + + if (auth_debug) + { dump_principal("krb5_auth_check: Server principal name", server); } + + + + /* check if ticket is already in the cache, if it is + then use it. + */ + if( krb5_fast_auth(client, server, target_user, cc) == TRUE){ + if (auth_debug ){ + fprintf (stderr,"Athenticated via fast_auth \n"); + } + return TRUE; + } + + /* check to see if the local tgt is in the cache */ + + if (retval= krb5_copy_principal( client, &tgtq.client)){ + com_err(prog_name, retval,"while copying client principal"); + return (FALSE) ; + } + + if (retval = krb5_tgtname( krb5_princ_realm (client), krb5_princ_realm(client), + &tgtq.server)){ + com_err(prog_name, retval, "while creating tgt for local realm"); + krb5_free_principal(client); + krb5_free_principal(server); + return (FALSE) ; + } + + if (auth_debug){ dump_principal("local tgt principal name", tgtq.server ); } + retval = krb5_cc_retrieve_cred(cc, KRB5_TC_MATCH_SRV_NAMEONLY, + &tgtq, &tgt); + + if (! retval) retval = krb5_check_exp(tgt.times); + + if (retval){ + if ((retval != KRB5_CC_NOTFOUND) && + (retval != KRB5KRB_AP_ERR_TKT_EXPIRED)){ + com_err(prog_name, retval, + "while retrieving creds from cache"); + return (FALSE) ; + } + } else{ + got_it = 1; + } + + if (! got_it){ + +#ifdef GET_TGT_VIA_PASSWD + + fprintf(stderr,"WARNING: Your password may get exposed if you are logged in remotely \n"); + fprintf(stderr," and don't have a secure channel. \n"); + + /*get the ticket granting ticket, via passwd(promt for passwd)*/ + if (krb5_get_tkt_via_passwd (&cc, client, tgtq.server, + options) == FALSE){ + return FALSE; + } + *path_passwd = 1; +#else + plain_dump_principal (client); + fprintf(stderr,"does not have any appropriate tickets in the cache.\n"); + return FALSE; + +#endif /* GET_TGT_VIA_PASSWD */ + + } + + if (retval= krb5_copy_principal( client, &cred.client)){ + com_err(prog_name, retval,"while copying client principal"); + return (FALSE) ; + } + + if (retval= krb5_copy_principal( server, &cred.server)){ + com_err(prog_name, retval,"while copying client principal"); + return (FALSE) ; + } + + if (retval = krb5_get_cred_from_kdc(cc, &cred, &tgts)){ + com_err(prog_name, retval, "while geting credentials from kdc"); + return (FALSE); + } + + + if (auth_debug){ + fprintf(stderr,"krb5_auth_check: got ticket for end server \n"); + dump_principal("cred.server", cred.server ); + } + + + if (tgts){ + register int i =0; + + if (auth_debug){ + fprintf(stderr, "krb5_auth_check: went via multiple realms"); + } + while (tgts[i]){ + if (retval = krb5_cc_store_cred( cc, tgts[i])){ + com_err(prog_name, retval, + "while storing credentials from cross-realm walk"); + return (FALSE); + } + i++; + } + krb5_free_tgt_creds(tgts); + } + + if (retval = krb5_verify_tkt_def(client, server, + &cred.ticket, &target_tkt)){ + com_err(prog_name, retval, "while verifing ticket for server"); + return (FALSE); + } + + if (retval = krb5_cc_store_cred( cc, &cred)){ + com_err(prog_name, retval, + "While storing credentials"); + return (FALSE); + } + + return (TRUE); +} + +/* krb5_fast_auth checks if ticket for the end server is already in + the cache, if it is, we don't need a tgt */ + +krb5_boolean krb5_fast_auth(krb5_principal client, krb5_principal server, + char * target_user, krb5_ccache cc){ + +krb5_creds tgt, tgtq; +krb5_ticket * target_tkt; +krb5_error_code retval; +char * client_name; + + memset((char *) &tgtq, 0, sizeof(tgtq)); + memset((char *) &tgt, 0, sizeof(tgt)); + + if (retval= krb5_copy_principal( client, &tgtq.client)){ + com_err(prog_name, retval,"while copying client principal"); + return (FALSE) ; + } + + if (retval= krb5_copy_principal( server, &tgtq.server)){ + com_err(prog_name, retval,"while copying client principal"); + return (FALSE) ; + } + + if (retval = krb5_cc_retrieve_cred(cc, KRB5_TC_MATCH_SRV_NAMEONLY, + &tgtq, &tgt)){ + if (auth_debug) + com_err(prog_name, retval,"While Retrieving credentials"); + return (FALSE) ; + + } + + if (retval = krb5_verify_tkt_def(client, server, + &tgt.ticket, &target_tkt)){ + com_err(prog_name, retval, "while verifing ticket for server"); + return (FALSE); + } + + return TRUE; +} + + + +krb5_error_code krb5_verify_tkt_def( /* IN */ + krb5_principal client, + krb5_principal server, krb5_data * scr_ticket, + /* OUT */ + krb5_ticket ** clear_ticket) +{ +krb5_keytab keytabid; +krb5_keytab_entry ktentry; +krb5_keyblock *tkt_key = NULL; +krb5_ticket * tkt = NULL; +krb5_error_code retval =0; + + if (retval = decode_krb5_ticket(scr_ticket, &tkt)){ + return retval; + } + + if (server && !krb5_principal_compare(server, tkt->server)){ + return KRB5KRB_AP_WRONG_PRINC; + } + + if (auth_debug){ + fprintf(stderr,"krb5_verify_tkt_def: verified target server\n"); + dump_principal("server", server); + dump_principal("tkt->server", tkt->server); + } + + /* get the default keytab */ + if( retval = krb5_kt_default(&keytabid)){ + krb5_free_ticket(tkt); + return retval; + } + + if (retval = krb5_kt_get_entry(keytabid, server, + tkt->enc_part.kvno, &ktentry)){ + krb5_free_ticket(tkt); + return retval; + } + + krb5_kt_close(keytabid); + + if ( retval = krb5_copy_keyblock(&ktentry.key, &tkt_key)){ + krb5_free_ticket(tkt); + krb5_kt_free_entry(&ktentry); + return retval; + } + + /* decrypt the ticket */ + if (retval = krb5_decrypt_tkt_part(tkt_key, tkt)) { + krb5_free_ticket(tkt); + krb5_kt_free_entry(&ktentry); + krb5_free_keyblock(tkt_key); + return(retval); + } + + if (!krb5_principal_compare(client, tkt->enc_part2->client)) { + retval = KRB5KRB_AP_ERR_BADMATCH; + } + + if (auth_debug){ + fprintf(stderr, + "krb5_verify_tkt_def: verified client's identity\n"); + dump_principal("client", client); + dump_principal("tkt->enc_part2->client",tkt->enc_part2->client); + } + + *clear_ticket = tkt; + krb5_kt_free_entry(&ktentry); + krb5_free_keyblock(tkt_key); + return retval; + +} + + +krb5_boolean krb5_get_tkt_via_passwd (krb5_ccache * ccache, krb5_principal client, + krb5_principal server, opt_info * options) { + krb5_address **my_addresses; + krb5_error_code code; + krb5_creds my_creds; + krb5_timestamp now; + int preauth_type = -1; + int pwsize; + int i; + char password[255], *client_name, prompt[255]; + + if (code = krb5_unparse_name(client, &client_name)) { + com_err (prog_name, code, "when unparsing name"); + return (FALSE); + } + + memset((char *)&my_creds, 0, sizeof(my_creds)); + + if (code = krb5_copy_principal(client, &my_creds.client)){ + com_err (prog_name, code, "while copying principal"); + return (FALSE); + } + + if (code = krb5_copy_principal(server, &my_creds.server)){ + com_err (prog_name, code, "while copying principal"); + return (FALSE); + } + + code = krb5_os_localaddr(&my_addresses); + + if (code != 0) { + com_err (prog_name, code, "when getting my address"); + return (FALSE); + } + + if (code = krb5_timeofday(&now)) { + com_err(prog_name, code, "while getting time of day"); + return (FALSE); + } + + my_creds.times.starttime = 0; /* start timer when request + gets to KDC */ + + my_creds.times.endtime = now + options->lifetime; + if (options->opt & KDC_OPT_RENEWABLE) { + my_creds.times.renew_till = now + options->rlife; + } else + my_creds.times.renew_till = 0; + + + (void) sprintf(prompt,"Kerberos password for %s: ", (char *) client_name); + + pwsize = sizeof(password); + + code = krb5_read_password(prompt, 0, password, &pwsize); + if (code || pwsize == 0) { + fprintf(stderr, "Error while reading password for '%s'\n", + client_name); + memset(password, 0, sizeof(password)); + krb5_free_addresses(my_addresses); + return (FALSE); + } + + if (preauth_type > 0) { + code = krb5_get_in_tkt_with_password(options->opt, my_addresses, + preauth_type, + ETYPE_DES_CBC_CRC, + KEYTYPE_DES, + password, + *ccache, + &my_creds, 0); + } else { + for (i=0; preauth_search_list[i] >= 0; i++) { + code = krb5_get_in_tkt_with_password(options->opt, my_addresses, + preauth_search_list[i], + ETYPE_DES_CBC_CRC, + KEYTYPE_DES, + password, + *ccache, + &my_creds, 0); + if (code != KRB5KDC_PREAUTH_FAILED && + code != KRB5KRB_ERR_GENERIC) + break; + } + } + memset(password, 0, sizeof(password)); + + + krb5_free_addresses(my_addresses); + + if (code) { + if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) + fprintf (stderr, "%s: Password incorrect\n", prog_name); + else + com_err (prog_name, code, "while getting initial credentials"); + return (FALSE); + } + return (TRUE); +} + + +void dump_principal (char * str, krb5_principal p) +{ +char * stname; +krb5_error_code retval; + + if (retval = krb5_unparse_name(p, &stname)){ + fprintf(stderr," %s while unparsing name \n", + error_message(retval)); + } + fprintf(stderr, " %s: %s\n", str, stname ); +} + +void plain_dump_principal ( krb5_principal p) +{ +char * stname; +krb5_error_code retval; + + if (retval = krb5_unparse_name(p, &stname)){ + fprintf(stderr," %s while unparsing name \n", + error_message(retval)); + } + fprintf(stderr, "%s ", stname ); +} + + +static time_t convtime(); + +krb5_error_code +krb5_parse_lifetime (time, len) + char *time; + long *len; +{ + *len = convtime(time); + return 0; +} + + +/* + * this next function was lifted from the source to sendmail, which is: + * + * Copyright (c) 1983 Eric P. Allman + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include /* for isdigit */ + +static time_t +convtime(p) + char *p; +{ + register time_t t, r; + register char c; + + r = 0; + while (*p != '\0') + { + t = 0; + while (isdigit(c = *p++)) + t = t * 10 + (c - '0'); + if (c == '\0') + p--; + switch (c) + { + case 'w': /* weeks */ + t *= 7; + + case 'd': /* days */ + t *= 24; + + case 'h': /* hours */ + default: + t *= 60; + + case 'm': /* minutes */ + t *= 60; + + case 's': /* seconds */ + break; + } + r += t; + } + + return (r); +} + +krb5_error_code get_tgt_via_login_list(krb5_principal server, krb5_ccache cc, + char ** k5login_plist, krb5_principal * client, int * got_it){ + +krb5_creds tgt, tgtq; +int i =0; +krb5_principal temp_client; +krb5_error_code retval =0; + *got_it=0; + + if (! k5login_plist ) return 0; + + memset((char *) &tgtq, 0, sizeof(tgtq)); + memset((char *) &tgt, 0, sizeof(tgt)); + + while(k5login_plist[i]){ + if (retval = krb5_parse_name(k5login_plist[i], + &temp_client)){ + return retval; + } + + + if (retval= krb5_copy_principal( temp_client, &tgtq.client)){ + return retval ; + } + + /* check to see if the local tgt is in the cache */ + + if (retval = krb5_tgtname( krb5_princ_realm (temp_client), + krb5_princ_realm(temp_client), &tgtq.server)){ + return retval ; + } + + retval = krb5_cc_retrieve_cred(cc, KRB5_TC_MATCH_SRV_NAMEONLY, + &tgtq, &tgt); + + if (! retval) retval = krb5_check_exp(tgt.times); + + if (retval){ + if ((retval != KRB5_CC_NOTFOUND) && + (retval != KRB5KRB_AP_ERR_TKT_EXPIRED)){ + return retval ; + } + } else{ + if (auth_debug){ + fprintf(stderr,"Auth via k5login name %s\n", + k5login_plist[i]); + } + + *got_it = 1; + *client = temp_client; + break; + } + + i++; + } + + return 0; +} + +/********************************************************************** +returns the principal that is closes to client. plist contains +a principal list obtained from .k5login and parhaps .k5users file. +This routine gets called before getting the password for a tgt. +A principal is picked that has the best chance of getting in. + +**********************************************************************/ + + +krb5_error_code get_best_principal( char ** plist, krb5_principal * client) +{ +krb5_error_code retval =0; +char * client_name; +krb5_principal temp_client, best_client = NULL; + +int i = 0, nelem; + + if (! plist ) return 0; + + nelem = krb5_princ_size(*client); + + while(plist[i]){ + + if (retval = krb5_parse_name(plist[i], &temp_client)){ + return retval; + } + + if (krb5_princ_realm(*client)->length == + krb5_princ_realm(temp_client)->length + && (!memcmp (krb5_princ_realm(*client)->data, + krb5_princ_realm(temp_client)->data, + krb5_princ_realm(temp_client)->length))){ + + + if(nelem){ + krb5_data *p1 = + krb5_princ_component(*client, 0); + krb5_data *p2 = + krb5_princ_component(temp_client, 0); + + if ((p1->length == p2->length) && + (!memcmp(p1->data,p2->data,p1->length))){ + + if (auth_debug){ + fprintf(stderr, + "get_best_principal: compare with %s\n", + plist[i]); + } + + if(best_client){ + if(krb5_princ_size(best_client) > + krb5_princ_size(temp_client)){ + best_client = temp_client; + } + }else{ + best_client = temp_client; + } + } + } + + } + i++; + } + + if (best_client) *client = best_client; + return 0; +} diff --git a/src/clients/ksu/ksu.1 b/src/clients/ksu/ksu.1 new file mode 100644 index 000000000..eb5e61dc6 --- /dev/null +++ b/src/clients/ksu/ksu.1 @@ -0,0 +1,480 @@ +.\" 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 writen by: Ari Medvinsky, ari@isi.edu +.TH KSU 1 "Kerberos Version 5.3" +.SH NAME +ksu \- Kerberized super-user +.SH SYNOPSIS +.B ksu +[ +.I target_user +] [ +.B \-n +.I target_principal_name +] [ +.B \-c +.I source_cache_name +] [ +.B \-C +.I target_cache_name +] [ +.B \-k +] [ +.B \-D +] [ +.B \-r +.I time +] [ +.B \-pf +] [ +.B \-l +.I lifetime +] [ +.B \-zZ +] [ +.B \-e +.I command +[ +.I args ... +] ] [ +.B \-a +[ +.I args ... +] ] +.br +.SH REQUIREMENTS +Must have Kerberos version 5 installed, to compile ksu. +Must have a Kerberos version 5 server running to use ksu. +.br +.SH DESCRIPTION +.I ksu +is a Kerberized version of the su program that has two missions: +one is to securely change the real and effective user ID to that +of the target user, the other is to create a new security context. +For the sake of clarity all references to, and attributes of +the user invoking the program will start with 'source' (e.g. +source user, source cache, etc.). Likewise all references +to and attributes of the target account, will start with 'target'. +.br +.SH AUTHENTICATION +To fulfill the first mission, ksu operates in two phases: authentication +and authorization. Resolving the target principal name is the +first step in authentication. The user +can either specify his principal name with the +.B \-n +option +(e.g. +.B \-n +jqpublic@USC.EDU) or a default principal name will be assigned +using a heuristic described in the OPTIONS section (see +.B \-n +option). +The target user name must be the first argument to ksu, if not specified +root is the default. If the source user is root no authentication +or authorization takes place. Otherwise, ksu looks for an appropriate +Kerberos ticket in the source cache. +.PP +The ticket can either be for +the end-server +or a ticket granting ticket (TGT) for the target principal's realm. If the +ticket for the end server is already in the cache, it's, decrypted and +verified. If it's not in the cache but the TGT is, TGT is used to +obtain the ticket for the end-server. The end-server ticket is then +verified. If neither ticket is in the cache, but ksu is compiled +with the GET_TGT_VIA_PASSWD define, the user will be prompted +for a Kerberos password which will then be used to get a TGT. +If the user is logged in remotely and +does not have a secure channel, the password may be exposed. +If neither ticket is in the cache and GET_TGT_VIA_PASSWD is not defined, +authentication fails. +.br +.SH AUTHORIZATION +This section describes authorization of the source user when ksu +is invoked without the +.B \-e +option. +For a description of the +.B \-e +option, see the OPTIONS section. +.PP +Upon successful authentication, ksu checks whether the target principal +is authorized to access the target account. +In the target user's home directory, ksu attempts to access +two authorization files: .k5login and .k5users. In the .k5login +file each line contains the name of a +principal that is authorized to access the account. +.TP 12 +For example: +jqpublic@USC.EDU +.br +jqpublic/secure@USC.EDU +.br +jqpublic/admin@USC.EDU +.PP +The format of .k5users is the same, accept the +principal name may be followed by a list of commands that +the principal is authorized to execute. (see the +.B \-e +option in the OPTIONS section for details). +.PP +Thus if the target principal +name is found in the .k5login file the source user is authorized to access +the target account. Otherwise ksu looks in the .k5users file. +If the target principal name is found without any trailing commands +or followed only by '*' then the source user is authorized. +If either .k5login or .k5users exist but an appropriate entry for the target +principal does not exist then access is denied. If neither +file exists then a database of local principal names is +consulted.(the name of this database is defined in Kerberos osconf.h +file by DEFAULT_LNAME_FILENAME macro). If the target principal name is +found then the source user is authorized to access the account. +If it's not found, and ksu was compiled with LOCAL_REALM macro undefined, +authorization fails. If LOCAL_REALM is defined, and it matches +the target principal's realm and the first component of the +target principal name translates to the target account name then +authorization is successful. Otherwise, authorization fails. +.br +.SH EXECUTION OF THE TARGET SHELL +Upon successful authentication and authorization, ksu +proceeds in a similar fashion to su. The environment +is unmodified with the exception of USER, HOME and SHELL variables. +If the target user is not root, USER gets set to the target user +name. Otherwise USER remains unchanged. Both HOME and SHELL are +set to the target login's default values. +In addition, the environment variable KRB5CCNAME gets set to the +name of the target cache. +The real and effective user ID are changed to that of the +target user. The target user's shell is then invoked +(the shell name is specified in the password file). +Upon termination of the shell, ksu deletes the target cache (unless +ksu is invoked with +.B \-k + or ' +.B \-C .' options). +This is implemented by first doing a fork and then an exec, instead +of just exec, as done by su. +.br +.SH CREATING A NEW SECURITY CONTEXT +.PP +Ksu can be used to create a new security context for the +target program (either the target +shell, or command specified via the -e option). +The target program inherits a set +of credentials from the source user. +By default, this set includes all of the credentials +in the source cache plus any +additional credentials obtained during authentication. +The source user is able to limit the credentials in this set +by using -z or -Z option. +-z restricts the copy of tickets from the source cache +to the target cache to only the tickets where client == +the target principal name. The -Z option +provides the target user with a fresh target cache +(no creds in the cache). Note that for security reasons, +when the source user is root and target user is non-root, +-z option is the default mode of operation. In this +case if -n is specified and no credentials can be copied +to the target cache, the source user is prompted for +a Kerberos password (unless -Z specified or GET_TGT_VIA_PASSWD is +undefined). If successful, a TGT is obtained +from the Kerberos server and stored in the target cache. +Otherwise, ksu continues in a normal mode of operation, but +the destination cache will remain empty. +.PP +\fISide Note:\fP during authentication, only the tickets that could be +obtained without providing a password are cached in +in the source cache. +.SH OPTIONS +.TP 10 +\fB\-n \fItarget_principal_name +Specify a Kerberos target principal name. +Used in authentication and authorization +phases of ksu. + +If ksu is invoked without +.B \-n, +a default principal name is +assigned via the following heuristic: + +\fICase 1:\fP source user is non-root. +.br +If neither ~/target_user/.k5users +nor ~/target_user/.k5login exist then +the default principal name is +target_user_login_name@local_realm. Otherwise, +starting with the first principal listed below, +ksu checks if the principal is authorized +to access the target account and whether +there is a legitimate ticket for that principal +in the source cache. If both conditions are met +that principal becomes the default target principal, +otherwise go to the next principal. + +a) default principal of the source cache +.br +b) target_user@local_realm +.br +c) source_user@local_realm + +If a-c fails try any principal for which there is +a ticket in the source cache and that is +authorized to access the target account. +If that fails select the first principal that +is authorized to access the target account from +the following list: + +a) default principal of the source cache +.br +b) if ksu is configured with PRINC_LOOK_AHEAD +.br + turned on, the principal with the same +.br + realm name as a) and has the first part +.br + of the principal name equal to prefix of a). +.br +c) target_user@local_realm +.br +d) source_user@local_realm + +If all fails select the first authorized principal +(from .k5login, .k5users file). + +\fICase 2:\fP source user is root. +.br +If the target user is non-root then the +default principal name is target_user@local_realm. +Else, if the source cache exists the default +principal name is set to the default principal +of the source cache. If the source cache does not +exist, default principal name is set to +root@local_realm. +.TP 10 +\fB\-c \fIsource_cache_name +Specify source cache name (e.g. +.B \-c +FILE:/tmp/my_cache). +If +.B \-c +option is not used then the +name is obtained from KRB5CCNAME environment variable. +If KRB5CCNAME is not defined the source cache name +is set to krb5cc_. +.TP 10 +\fB\-C \fItarget_cache_name +Specify the target cache name (e.g. +.B \-C +FILE:/tmp/target_cache). +If '.' is specified (e.g. ksu +\-C .) ksu uses the source +cache and does not create a new target cache. Note: +this case requires both source and target user +to have read and write permissions for the source cache. +If +.B \-C +option is not used, the default target cache name is +set to krb5cc_.(gen_sym()), +where gen_sim generates a new number such that +the resulting cache does not already exist. +.br +For example: krb5cc_1984.2 +.TP 10 +\fB\-k +Do not delete the target cache upon termination of the +target shell or a command ( +.B \-e +command). +Without +.B \-k, +ksu deletes the target cache upon termination +of the source cache unless the '-C .' option was used. +.TP 10 +\fB\-D +turn on debug mode. +.TP 10 +\fITicket granting ticket options: -l lifetime -r time -pf\fP +The ticket granting ticket options only apply to the +case where there are no appropriate tickets in +the cache to authenticate the source user. In this case +if ksu is configured to prompt users for a +Kerberos password (GET_TGT_VIA_PASSWD is defined), +the ticket granting +ticket options that are specified will be used +when getting a ticket granting ticket from the Kerberos +server. +.TP 10 +\fB\-l \fIlifetime +option specifies the lifetime (in hours) to be +requested for the ticket; if this option is not +specified, the default ticket lifetime +(configured by each site) is used instead. +.TP 10 +\fB\-r \fItime +option specifies that the RENEWABLE option +should be requested for the ticket, and specifies +(in hours) the desired total lifetime of the ticket. +.TP 10 +\fB\-p +option specifies that the PROXIABLE option should be +requested for the ticket. +.TP 10 +\fB\-f +option specifies that the FORWARDABLE option should +be requested for the ticket. +.TP 10 +\fB\-z +restrict the copy of tickets from the source cache +to the target cache to only the tickets where client == +the target principal name. Use the +.B \-n +option +if you want the tickets for other then the default +principal. Note that the +.B \-z +option is mutually +exclusive with '-C .' and -Z options. +.TP 10 +\fB\-Z +Don't copy any tickets from the source cache to the +target cache. Just create a fresh target cache, +where the default principal name of the cache is +initialized to the target principal name. Note that +.B \-Z +option is mutually +exclusive with '-C .' and -z options. +.TP 10 +\fB\-e \fIcommand [args ...] +ksu proceeds exactly the same as if it was invoked without the +.B \-e +option, +except instead of executing the target shell, ksu executes the +specified command (Example of usage: ksu bob +.B \-e +ls +.B \-lag). + +\fIThe authorization algorithm for -e is as follows:\fP + +If the source user is root, no authorization takes place and +the command is executed. If source user id != 0, and .k5users +file does not exist, authorization fails. +Otherwise, .k5users file must have an +appropriate entry for target principal +to get authorized. + +\fIThe .k5users file format:\fP + +A single principal entry on each line +that may be followed by a list of commands that +the principal is authorized to execute. +A principal name followed by a '*' means +that the user is authorized to execute +any command. Thus, in the following example: + +jqpublic@USC.EDU ls mail /local/kerberos/klist +.br +jqpublic/secure@USC.EDU * +.br +jqpublic/admin@USC.EDU + +jqpublic@USC.EDU is only authorized to execute ls, mail +and klist commands. jqpublic/secure@USC.EDU is authorized +to execute any command. jqpublic/admin@USC.EDU is not +authorized to execute any command. Note, that +jqpublic/admin@USC.EDU is authorized to execute +the target shell (regular ksu, without the +.B \-e +option) but jqpublic@USC.EDU is not. + +The commands listed after the principal name must +be either a full path names or just the program name. +In the second case, CMD_PATH specifying the location +of authorized programs, must be defined at the +compilation time of ksu. + +\fIWhich command gets executed ?\fP + +If the source user is root or the user +is authorized to execute any command ('*' entry) +then command can be either a full or a relative +path leading to the target program. +Otherwise, the user must specify either a full +path or just the program name. +.TP 10 +\fB\-a \fIargs +specify arguments to be passed to the target shell. +Note: that all flags and parameters following -a +will be passed to the shell, thus all options +intended for ksu must precede +.B \-a. +.B \-a +option can be used to simulate the +.B \-e +option if used as follows: +.B \-a +.B \-c +[command [arguments]]. +.B \-c +is interpreted by the c-shell to execute the command. +.PP +.SH INSTALLATION INSTRUCTIONS +ksu can be compiled with the following flags (see the makefile): +.TP 10 +\fILOCAL_REALM\fP +possible values: the name of the local realm +or '.' in which case krb.conf is used to get +up the local realm name. +.TP 10 +\fIGET_TGT_VIA_PASSWD\fP +in case no appropriate tickets are found in the source +cache, the user will be prompted for a Kerberos +password. The password is then used to get a +ticket granting ticket from the Kerberos server. +The danger of configuring ksu with this macro is +if the source user is loged in remotely and does not +have a secure channel, the password may get exposed. +.TP 10 +\fIPRINC_LOOK_AHEAD\fP +during the resolution of the default principal name, +PRINC_LOOK_AHEAD enables ksu to find principal names +in the .k5users file that have a common prefix with +the default principal of the source cache. +.TP 10 +\fICMD_PATH\fP +specifies a list of directories containing programs +that users are authorized to execute (via .k5users file). +.TP 10 +SAMPLE CONFIGURATION: +KSU_OPTS = -DLOCAL_REALM='"ISI.EDU"' -DGET_TGT_VIA_PASSWD +-DPRINC_LOOK_AHEAD -DCMD_PATH='"/bin /usr/ucb /local/bin" +.TP 10 +PERMISSIONS FOR KSU +ksu should be owned by root and have the set user id bit turned on. +.TP 10 +END-SERVER ENTRY +ksu attempts to get a ticket for the end server just as +Kerberized telnet and rlogin. Thus, there must be +an entry for the server in the Kerberos database +(e.g. host/nii.isi.edu@ISI.EDU). k5srvtab must be in +an appropriate location. diff --git a/src/clients/ksu/ksu.h b/src/clients/ksu/ksu.h new file mode 100644 index 000000000..ed31dc278 --- /dev/null +++ b/src/clients/ksu/ksu.h @@ -0,0 +1,106 @@ +/* + * 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 writen by: Ari Medvinsky, ari@isi.edu + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NO_TARGET_FILE '.' + +#define KRB5_DEFAULT_OPTIONS 0 +#define KRB5_DEFAULT_TKT_LIFE 60*60*8 /* 8 hours */ + +#define KRB5_SECONDARY_CACHE "FILE:/tmp/krb5cc_" + +#define KRB5_LOGIN_NAME ".k5login" +#define KRB5_USERS_NAME ".k5users" +#define USE_DEFAULT_REALM_NAME "." +#define PERMIT_ALL_COMMANDS "*" +#define KRB5_SEC_BUFFSIZE 80 +#define NOT_AUTHORIZED 1 + +#define CHUNK 3 +#define CACHE_MODE 0600 +#define MAX_CMD 2048 /* this is temp, should use realloc instead, + as done in most of the code */ + + +extern int optind; +extern char * optarg; + +/* globals */ +extern char * prog_name; +extern int auth_debug; +extern char k5login_path[MAXPATHLEN]; +extern char k5users_path[MAXPATHLEN]; +extern char * gb_err; +/***********/ + +typedef struct opt_info{ + int opt; + long lifetime; + long rlife; + int princ; +}opt_info; + +extern krb5_boolean krb5_auth_check(); +extern krb5_error_code get_best_principal(); +extern void dump_principal (); +extern krb5_error_code krb5_verify_tkt_def(); +extern krb5_boolean krb5_fast_auth(); +extern krb5_boolean krb5_get_tkt_via_passwd (); +extern int gen_sim(); +extern krb5_error_code krb5_authorization(); +extern krb5_error_code k5login_lookup (); +extern krb5_error_code k5users_lookup (); +extern krb5_error_code get_line (); +extern char * get_first_token (); +extern char * get_next_token (); +extern krb5_boolean fowner(); + +#ifndef min +#define min(a,b) ((a) > (b) ? (b) : (a)) +#endif /* min */ + + +extern char *krb5_lname_file; /* Note: print this out just be sure + that it gets set */ + + diff --git a/src/clients/ksu/main.c b/src/clients/ksu/main.c new file mode 100644 index 000000000..a76a35fac --- /dev/null +++ b/src/clients/ksu/main.c @@ -0,0 +1,871 @@ +/* + * 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 writen by: Ari Medvinsky, ari@isi.edu + */ + +#include "ksu.h" + +/* globals */ +char * prog_name; +int auth_debug =0; +char k5login_path[MAXPATHLEN]; +char k5users_path[MAXPATHLEN]; +char * gb_err = NULL; +/***********/ + +#define _DEF_CSH "/bin/csh" +int set_env_var(); +void sweep_up(); +char * ontty(); +void init_auth_names(); + +/* Note -e and -a options are mutually exclusive */ +/* insure the proper specification of target user as well as catching + ill specified arguments to commands */ + +void usage (){ + fprintf(stderr, "Usage: %s [target user] [-n principal] [-c source cachename] [-C target cachename] [-k] [-D] [-r time] [-pf] [-l lifetime] [-zZ] [-e command [args... ] ] [-a [args... ] ] \n", prog_name); + +} + +#define DEBUG + +main (argc, argv) + int argc; + char ** argv; +{ +int hp =0; +int some_rest_copy = 0; +int all_rest_copy = 0; +char localhostname [MAXHOSTNAMELEN]; +opt_info options; +int option=0; +int statusp=0; +int use_source_cache = 0; +krb5_error_code retval = 0; +krb5_principal client = NULL; +krb5_ccache cc_target = NULL; +char * cc_target_tag = NULL; +char * target_user = NULL; +char * source_user, *p; +char * local_realm_name = NULL; + +krb5_ccache cc_source = NULL; +char * cc_source_tag = NULL; +uid_t source_uid, target_uid; +uid_t source_gid, target_gid; +char * cc_source_tag_tmp = NULL; +char * cc_target_tag_tmp=NULL; +char * cmd = NULL, * exec_cmd = NULL; +int errflg = 0; +uid_t eff_uid, eff_gid; +krb5_boolean auth_val; +krb5_boolean authorization_val = FALSE; +int path_passwd = 0; +int done =0,i,j; +uid_t ruid; +struct passwd *pwd=NULL, *target_pwd ; +char * shell; +char ** params; +int keep_target_cache = 0; +int child_pid, ret_pid; +extern char * getpass(), *crypt(); +int pargc; +char ** pargv; +struct stat st_temp; +int temp_debug; + + options.opt = KRB5_DEFAULT_OPTIONS; + options.lifetime = KRB5_DEFAULT_TKT_LIFE; + options.rlife =0; + options.princ =0; + + params = (char **) calloc (2, sizeof (char *)); + params[1] = NULL; + + + krb5_init_ets(); /* initialize kerberos error tables */ + +#ifdef LOCAL_REALM + local_realm_name = LOCAL_REALM ; +#endif + + + if (strrchr(argv[0], '/')) + argv[0] = strrchr(argv[0], '/')+1; + prog_name = argv[0]; + + +#ifndef LOG_NDELAY +#define LOG_NDELAY 0 +#endif + +#ifndef LOG_AUTH /* 4.2 syslog */ + openlog(prog_name, LOG_PID|LOG_NDELAY); +#else + openlog(prog_name, LOG_PID | LOG_AUTH | LOG_NDELAY, LOG_AUTH); +#endif /* 4.2 syslog */ + + + if (( argc == 1) || (argv[1][0] == '-')){ + target_user = strdup("root"); + pargc = argc; + pargv = argv; + } else { + target_user = strdup(argv[1]); + pargc = argc -1; + + if ((pargv =(char **) calloc(pargc +1,sizeof(char *)))==NULL){ + com_err(prog_name, errno, "while allocating memory"); + exit(1); + } + + pargv[pargc] = NULL; + pargv[0] = argv[0]; + + for(i =1; i< pargc; i ++){ + pargv[i] = argv[i + 1]; + } + } + + + while(!done && ((option = getopt(pargc, pargv,"n:c:C:r:a:zZDfpkl:e:")) != EOF)){ + switch (option) { + case 'r': + options.opt |= KDC_OPT_RENEWABLE; + retval = krb5_parse_lifetime(optarg, &options.rlife); + if (retval != 0 || options.rlife == 0) { + fprintf(stderr, "Bad lifetime value (%s hours?)\n", optarg); + errflg++; + } + break; + case 'a': + /* when integrating this remember to pass in pargc, pargv and + take care of params argument */ + optind --; + if (auth_debug){printf("Before get_params optind=%d \n", optind);} + + if ( retval = get_params( & optind, pargc, pargv, ¶ms)){ + com_err(prog_name, retval, "when gathering parameters"); + errflg++; + } + if(auth_debug){ printf("After get_params optind=%d \n", optind);} + done = 1; + break; + case 'p': + options.opt |= KDC_OPT_PROXIABLE; + break; + case 'f': + options.opt |= KDC_OPT_FORWARDABLE; + break; + case 'k': + keep_target_cache =1; + break; + case 'l': + retval = krb5_parse_lifetime(optarg, &options.lifetime); + if (retval != 0 || options.lifetime == 0) { + fprintf(stderr, "Bad lifetime value (%s hours?)\n", optarg); + errflg++; + } + break; + case 'n': + if (retval = krb5_parse_name(optarg, &client)){ + com_err(prog_name, retval, "when parsing name %s", optarg); + errflg++; + } + + options.princ = 1; + + break; + case 'D': + auth_debug = 1; + break; + case 'z': + some_rest_copy = 1; + if(all_rest_copy || use_source_cache){ + fprintf(stderr, + "-z option is mutually exclusive with -Z and -C . \n"); + errflg++; + } + break; + case 'Z': + all_rest_copy = 1; + if(some_rest_copy || use_source_cache){ + fprintf(stderr, + "-Z option is mutually exclusive with -z and -C . \n"); + errflg++; + } + break; + case 'C': + if (cc_target_tag == NULL) { + cc_target_tag = strdup(optarg); + + if ((strlen(cc_target_tag) == 1) && + (*cc_target_tag == NO_TARGET_FILE)){ + use_source_cache = 1; + if(some_rest_copy || all_rest_copy){ + fprintf(stderr, + "-C . option is mutually exclusive with -z and -Z\n"); + errflg++; + } + } + else { + fprintf(stderr,"In -C option \n"); + if ( strchr(cc_target_tag, ':')){ + cc_target_tag_tmp=strchr(cc_target_tag,':') + 1; + if(!stat(cc_target_tag_tmp, &st_temp )){ + fprintf(stderr,"File %s exists \n", + cc_target_tag_tmp); + errflg++; + } + } + else { + fprintf(stderr, + "malformed credential cache name %s\n", + cc_target_tag); + errflg++; + } + } + } else { + fprintf(stderr, "Only one -C option allowed\n"); + errflg++; + } + break; + case 'c': + if (cc_source_tag == NULL) { + cc_source_tag = strdup(optarg); + if ( strchr(cc_source_tag, ':')){ + cc_source_tag_tmp = strchr(cc_source_tag, ':') + 1; + + if( stat( cc_source_tag_tmp, &st_temp)){ + fprintf(stderr,"File %s does not exist \n", + cc_source_tag_tmp); + errflg++; + + } + } + else { + fprintf(stderr,"malformed credential cache name %s\n", + cc_source_tag); + errflg++; + } + + } else { + fprintf(stderr, "Only one -c option allowed\n"); + errflg++; + } + break; + case 'e': + cmd = strdup(optarg); + if(auth_debug){printf("Before get_params optind=%d \n", optind);} + if ( retval = get_params( & optind, pargc, pargv, ¶ms)){ + com_err(prog_name, retval, "when gathering parameters"); + errflg++; + } + if(auth_debug){printf("After get_params optind=%d \n", optind);} + done = 1; + + if (auth_debug){ + fprintf(stderr,"Command to be executed: %s\n", cmd); + } + break; + case '?': + default: + errflg++; + break; + } + } + + if (errflg) { + usage(); + exit(2); + } + + if (optind != pargc ){ + usage(); + exit(2); + } + + if (auth_debug){ + for(j=1; params[j] != NULL; j++){ + fprintf (stderr,"params[%d]= %s\n", j,params[j]); + } + } + + /***********************************/ + ruid = getuid(); + source_user = getlogin(); /*checks for the the login name in /etc/utmp*/ + + /* verify that that the user exists and get his passwd structure */ + + if (source_user == NULL ||(pwd = getpwnam(source_user)) == NULL || + pwd->pw_uid != ruid){ + pwd = getpwuid(ruid); + } + + if (pwd == NULL) { + fprintf(stderr, "ksu: who are you?\n"); + exit(1); + } + + /* allocate space and copy the usernamane there */ + source_user = strdup(pwd->pw_name); + source_uid = pwd->pw_uid; + source_gid = pwd->pw_gid; + + if ((target_pwd = getpwnam(target_user)) == NULL){ + fprintf(stderr, "ksu: unknown login %s\n", target_user); + exit(1); + } + target_uid = target_pwd->pw_uid; + target_gid = target_pwd->pw_gid; + + init_auth_names(target_pwd->pw_dir); + + if (gethostname (localhostname, MAXHOSTNAMELEN)){ + fprintf (stderr, " failed to get localhostname\n"); + } + + /***********************************/ + + if (cc_source_tag == NULL){ + cc_source_tag = krb5_cc_default_name(); + cc_source_tag_tmp = strchr(cc_source_tag, ':') + 1; + } + + /* get a handle for the cache */ + if ( retval = krb5_cc_resolve(cc_source_tag, &cc_source)){ + com_err(prog_name, retval,"while getting source cache"); + exit(0); + } + + + if (! stat(cc_source_tag_tmp, &st_temp)){ + + + if (access(cc_source_tag_tmp, R_OK | W_OK )){ + fprintf(stderr, + "%s does not have correct permissions for %s\n", + source_user, cc_source_tag); + exit(0); + } + + + if (retval= krb5_ccache_refresh(cc_source)){ + com_err(prog_name, retval, + "while refreshing %s (source cache)", cc_source_tag); + exit(0); + } + + } + + + if (retval = get_best_princ_for_target(source_uid,target_uid, source_user, + target_user, cc_source, &options, cmd, + localhostname, &client, &hp)){ + com_err(prog_name, retval, "while selecting the best principal"); + exit(0); + } + + if (auth_debug){ + if (hp){ + fprintf(stderr, + "GET_best_princ_for_target result: NOT AUTHORIZED\n"); + }else{ + fprintf(stderr, + "GET_best_princ_for_target result-best principal "); + plain_dump_principal (client); + fprintf(stderr,"\n"); + } + } + + if (hp){ + if (gb_err) fprintf(stderr, "%s", gb_err); + fprintf(stderr, "Not authorized\n"); + exit(0); + } + + + if ( stat(cc_source_tag_tmp, &st_temp)){ + if (use_source_cache){ + + eff_uid = geteuid(); + eff_gid = getegid(); + + if (seteuid(source_uid) < 0) + { perror("ksu: seteuid"); exit(1);} + + if (setegid(source_gid) < 0) + { perror("ksu: setegid"); exit(1);} + + if (retval = krb5_cc_initialize(cc_source, client)){ + com_err(prog_name, retval, + "while initializing source cache"); + exit(0); + } + + if (seteuid(eff_uid) < 0) + { perror("ksu: seteuid"); exit(1); } + + if (setegid(eff_gid) < 0) + { perror("ksu: setegid"); exit(1); } + + } + } + + + if (cc_target_tag == NULL) { + + cc_target_tag = (char *)calloc(KRB5_SEC_BUFFSIZE ,sizeof(char)); + do { + sprintf(cc_target_tag, "%s%d.%d", KRB5_SECONDARY_CACHE, + target_uid, gen_sim()); + cc_target_tag_tmp = strchr(cc_target_tag, ':') + 1; + + }while ( !stat ( cc_target_tag_tmp, &st_temp)); + /* make sure that the new ticket file does not already exist */ + } + + + if (auth_debug){ + fprintf(stderr, " source cache = %s\n", cc_source_tag); + fprintf(stderr, " target cache = %s\n", cc_target_tag); + } + + + + /* Make sure that the resulting cache file is owned by the + source user. Only when proper authentication and authorization + takes place, the target user becomes the owner of the cache. + */ + + if (! use_source_cache){ + + /* if root ksu's to a regular user, then + then only the credentials for that particular user + should be copied */ + + if ((source_uid == 0) && (target_uid != 0)) { + krb5_principal kdc_server ; + krb5_boolean stored = FALSE; + + if (retval =krb5_ccache_copy_restricted( cc_source, + cc_target_tag,client,&cc_target, &stored)){ + com_err (prog_name, retval, + "while copying cache %s to %s", + krb5_cc_get_name(cc_source),cc_target_tag); + exit(0); + } + + if (retval = krb5_tgtname( krb5_princ_realm (client), + krb5_princ_realm(client), + &kdc_server)){ + com_err(prog_name, retval, + "while creating tgt for local realm") ; + sweep_up(use_source_cache, cc_target); + exit(0); + } + + +#ifdef GET_TGT_VIA_PASSWD + if ((!all_rest_copy) && options.princ && (stored == FALSE)){ + fprintf(stderr,"WARNING: Your password may get exposed if you are logged in remotely \n"); + fprintf(stderr," and don't have a secure channel. \n"); + if (krb5_get_tkt_via_passwd (&cc_target, client, + kdc_server, &options) == FALSE){ + fprintf(stderr, + "could not get a tgt for "); + plain_dump_principal (client); + fprintf(stderr, "\n"); + + } + } +#endif /* GET_TGT_VIA_PASSWD */ + } else{ + if (retval = krb5_ccache_copy(cc_source, cc_target_tag, + client,&cc_target)){ + com_err (prog_name, retval, + "while copying cache %s to %s", + krb5_cc_get_name(cc_source), + cc_target_tag); + exit(0); + } + } + + } + else{ + + cc_target = cc_source; + cc_target_tag = cc_source_tag; + cc_target_tag_tmp = cc_source_tag_tmp; + + } + + /* if the user is root then authentication is not neccesary, + root gets in automatically */ + + if (source_uid) { + char * client_name; + + auth_val = krb5_auth_check(client, localhostname, &options, + target_user,cc_target, &path_passwd); + /* cache the tickets if possible in the source cache */ + if (!path_passwd && !use_source_cache){ + + if (retval = krb5_ccache_overwrite(cc_target, cc_source, + client)){ + com_err (prog_name, retval, + "while copying cache %s to %s", + krb5_cc_get_name(cc_target), + krb5_cc_get_name(cc_source)); + sweep_up(use_source_cache, cc_target); + exit(0); + } + + } + + /* if kerbereros authentication failed then exit */ + if (auth_val ==FALSE){ + fprintf(stderr, "Authentication failed.\n"); + syslog(LOG_WARNING, + "'%s %s' authentication failed for %s%s", + prog_name,target_user,source_user,ontty()); + sweep_up(use_source_cache, cc_target); + exit(0); + } + + if (retval = krb5_unparse_name(client, &client_name)) { + com_err (prog_name, retval, "When unparsing name"); + sweep_up(use_source_cache, cc_target); + exit(0); + } + + fprintf(stderr,"Authenticated %s\n", client_name); + syslog(LOG_NOTICE,"'%s %s' authenticated %s for %s%s", + prog_name,target_user,client_name, + source_user,ontty()); + + if(retval = krb5_authorization(client,target_user, + local_realm_name, cmd, &authorization_val, &exec_cmd)){ + com_err(prog_name,retval,"while checking authorization"); + sweep_up(use_source_cache, cc_target); + exit(0); + } + + if (authorization_val == TRUE){ + + if (cmd) { + fprintf(stderr, + "Account %s: authorization for %s for execution of\n", + target_user, client_name); + fprintf(stderr, " %s successful\n",exec_cmd); + syslog(LOG_NOTICE, + "Account %s: authorization for %s for execution of %s successful", + target_user, client_name, exec_cmd); + + }else{ + fprintf(stderr, + "Account %s: authorization for %s successful\n", + target_user, client_name); + syslog(LOG_NOTICE, + "Account %s: authorization for %s successful", + target_user, client_name); + } + }else { + if (cmd){ + if (exec_cmd){ /* was used to pass back the error msg */ + fprintf(stderr, "%s", exec_cmd ); + syslog(LOG_WARNING, "%s",exec_cmd); + } + fprintf(stderr, + "Account %s: authorization for %s for execution of %s failed \n", + target_user, client_name, cmd ); + syslog(LOG_WARNING, + "Account %s: authorization for %s for execution of %s failed \n", + target_user, client_name, cmd ); + + }else{ + fprintf(stderr, + "Account %s: authorization of %s failed \n", + target_user, client_name); + syslog(LOG_WARNING, + "Account %s: authorization of %s failed \n", + target_user, client_name); + + } + + sweep_up(use_source_cache, cc_target); + exit(0); + } + } + + if( some_rest_copy){ + if (retval = krb5_ccache_filter(cc_target, client)){ + com_err(prog_name,retval,"while calling cc_filter"); + sweep_up(use_source_cache, cc_target); + exit(0); + } + } + + if (all_rest_copy){ + if (retval = krb5_cc_initialize(cc_target, client)){ + com_err(prog_name, retval, + "while erasing target cache"); + exit(0); + } + + } + + /* get the shell of the user, this will be the shell used by su */ + target_pwd = getpwnam(target_user); + + if (target_pwd->pw_shell) + shell = strdup(target_pwd->pw_shell); + else { + shell = _DEF_CSH; /* default is cshell */ + } + + /* insist that the target login uses a standard shell (root is omited) */ + + if (!standard_shell(target_pwd->pw_shell) && source_uid) { + fprintf(stderr, "ksu: permission denied (shell).\n"); + sweep_up(use_source_cache, cc_target); + exit(1); + } + + /* want to check the scoop with USER for the real ksu , MOD */ + + if (target_pwd->pw_uid){ + + if(set_env_var("USER", target_pwd->pw_name)){ + fprintf(stderr,"ksu: couldn't set environment variable USER\n"); + sweep_up(use_source_cache, cc_target); + exit(1); + } + } + + if(set_env_var( "HOME", target_pwd->pw_dir)){ + fprintf(stderr,"ksu: couldn't set environment variable USER\n"); + sweep_up(use_source_cache, cc_target); + exit(1); + } + + if(set_env_var( "SHELL", shell)){ + fprintf(stderr,"ksu: couldn't set environment variable USER\n"); + sweep_up(use_source_cache, cc_target); + exit(1); + } + + /* set the cc env name to target */ + + if(set_env_var( KRB5_CC_NAME, cc_target_tag)){ + fprintf(stderr,"ksu: couldn't set environment variable %s \n", + KRB5_CC_NAME); + sweep_up(use_source_cache, cc_target); + exit(1); + } + + + if (!use_source_cache){ + + /* set up ownership on cache for target user */ + + if (chown(cc_target_tag_tmp, target_uid, target_gid)){ + com_err(prog_name, errno, "while changing owner for %s", + cc_target_tag_tmp); + sweep_up(use_source_cache, cc_target); + exit(1); + } + + } + + /* set permissions */ + if (setgid(target_pwd->pw_gid) < 0) { + perror("ksu: setgid"); + sweep_up(use_source_cache, cc_target); + exit(1); + } + + + if (initgroups(target_user, target_pwd->pw_gid)) { + fprintf(stderr, "ksu: initgroups failed.\n"); + sweep_up(use_source_cache, cc_target); + exit(1); + } + + fprintf(stderr,"Changing uid to %d\n", target_pwd->pw_uid); + if (setuid(target_pwd->pw_uid) < 0) { + perror("ksu: setuid"); + sweep_up(use_source_cache, cc_target); + exit(1); + } + + /* verify that the target user can read and write + to the new cache */ + + if (access( cc_target_tag_tmp, R_OK | W_OK )){ + com_err(prog_name, errno, + "%s does not have correct permissions for %s, %s aborted", + target_user, cc_target_tag_tmp, prog_name); + exit(1); + } + + + if (cmd){ + if (source_uid == 0){ + exec_cmd = cmd; + } + + if( !exec_cmd){ + fprintf(stderr, + "Internal error: command %s did not get resolved\n",cmd); + exit(1); + } + + params[0] = exec_cmd; + } + else{ + params[0] = shell; + } + + if (auth_debug){ + fprintf(stderr, "program to be execed %s\n",params[0]); + } + + if( keep_target_cache || use_source_cache ) { + execv(params[0], params); + com_err(prog_name, errno, "while trying to execv %s", + params[0]); + sweep_up(use_source_cache, cc_target); + exit(1); + }else{ + if (child_pid = fork()){ + if (auth_debug){ + printf(" The childs pid is %d \n", child_pid); + printf(" The parents pid is %d \n", getpid()); + } + ret_pid = waitpid(child_pid, &statusp, 0); + if (ret_pid == -1){ + com_err(prog_name, errno, "while calling waitpid"); + exit(1); + } + sweep_up(use_source_cache, cc_target); + }else{ + execv(params[0], params); + com_err(prog_name, errno, "while trying to execv %s", + params[0]); + exit(1); + + } + } +} + + +int standard_shell(sh) +char *sh; +{ +register char *cp; +char *getusershell(); + + while ((cp = getusershell()) != NULL) + if (!strcmp(cp, sh)) + return (1); + return (0); +} + + +/* Modify this later , (clean it up) , MOD */ + +char * ontty() +{ +char *p, *ttyname(); +static char buf[MAXPATHLEN + 4]; + + buf[0] = 0; + if (p = ttyname(STDERR_FILENO)) + sprintf(buf, " on %s", p); + return (buf); +} + + +int set_env_var( char * name, char * value){ +char * env_var_buf; + + /* allocate extra two spaces, one for the = and one for the \0 */ + env_var_buf = (char *) calloc(2 + strlen(name) + strlen(value), + sizeof(char)); + + sprintf(env_var_buf,"%s=%s",name, value); + return putenv(env_var_buf); + +} + +void sweep_up(int use_source_cache, krb5_ccache cc){ +krb5_error_code retval; +char * cc_name; +struct stat st_temp; + + if (! use_source_cache){ + cc_name = krb5_cc_get_name(cc); + if ( ! stat(cc_name, &st_temp)){ + if (retval = krb5_cc_destroy(cc)){ + com_err(prog_name, retval, + "while destroying cache"); + } + } + } +} +/***************************************************************** +get_params is to be called for the -a option or -e option to + collect all params passed in for the shell or for + cmd. An aray is returned containing all params. + optind is incremented accordingly and the first + element in the returned array is reserved for the + name of the command to be executed or the name of the + shell. +*****************************************************************/ + +krb5_error_code +get_params( int * optind, int pargc, char ** pargv, char *** params ) { + +int i,j; +char ** ret_params; +int size = pargc - *optind + 2; + + if ((ret_params = (char **) calloc(size, sizeof (char *)))== NULL ){ + return errno; + } + + for (i = *optind, j=1; i < pargc; i++,j++){ + ret_params[j] = pargv[i]; + *optind = *optind + 1; + } + + ret_params[size-1] = NULL; + *params = ret_params; +return 0; + +} + -- 2.26.2