summaryrefslogtreecommitdiffstats
path: root/src/clients
diff options
context:
space:
mode:
authorTheodore Tso <tytso@mit.edu>1994-06-08 22:39:10 +0000
committerTheodore Tso <tytso@mit.edu>1994-06-08 22:39:10 +0000
commitb25f8055ad99cc5a645a5ad363e6681d6e22a8ad (patch)
treedeb8e240720fb0d8c1914319b6f825c6088c314a /src/clients
parent81b1a9fbc14f973ff80a4bc67f237c17099fb34a (diff)
downloadkrb5-b25f8055ad99cc5a645a5ad363e6681d6e22a8ad.tar.gz
krb5-b25f8055ad99cc5a645a5ad363e6681d6e22a8ad.tar.xz
krb5-b25f8055ad99cc5a645a5ad363e6681d6e22a8ad.zip
As contributed by ISI
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@3681 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/clients')
-rw-r--r--src/clients/ksu/Imakefile28
-rw-r--r--src/clients/ksu/authorization.c803
-rw-r--r--src/clients/ksu/ccache.c781
-rw-r--r--src/clients/ksu/heuristic.c746
-rw-r--r--src/clients/ksu/krb_auth_su.c653
-rw-r--r--src/clients/ksu/ksu.1480
-rw-r--r--src/clients/ksu/ksu.h106
-rw-r--r--src/clients/ksu/main.c871
8 files changed, 4468 insertions, 0 deletions
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 (&currenttime)){
+ 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 <ctype.h> /* 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_<source uid>.
+.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_<target uid>.(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 <krb5/krb5.h>
+#include <krb5/asn1.h>
+#include <krb5/ext-proto.h>
+#include <krb5/los-proto.h>
+#include <krb5/dbm.h>
+
+#include <krb5/osconf.h>
+#include <krb5/sysincl.h>
+#include <stdio.h>
+#include <com_err.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+
+#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, &params)){
+ 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, &params)){
+ 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;
+
+}
+