As contributed by ISI
authorTheodore Tso <tytso@mit.edu>
Wed, 8 Jun 1994 22:39:10 +0000 (22:39 +0000)
committerTheodore Tso <tytso@mit.edu>
Wed, 8 Jun 1994 22:39:10 +0000 (22:39 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@3681 dc483132-0cff-0310-8789-dd5450dbe970

src/clients/ksu/Imakefile [new file with mode: 0644]
src/clients/ksu/authorization.c [new file with mode: 0644]
src/clients/ksu/ccache.c [new file with mode: 0644]
src/clients/ksu/heuristic.c [new file with mode: 0644]
src/clients/ksu/krb_auth_su.c [new file with mode: 0644]
src/clients/ksu/ksu.1 [new file with mode: 0644]
src/clients/ksu/ksu.h [new file with mode: 0644]
src/clients/ksu/main.c [new file with mode: 0644]

diff --git a/src/clients/ksu/Imakefile b/src/clients/ksu/Imakefile
new file mode 100644 (file)
index 0000000..c74d8e0
--- /dev/null
@@ -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 (file)
index 0000000..fea20c0
--- /dev/null
@@ -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 (file)
index 0000000..38410b9
--- /dev/null
@@ -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 (file)
index 0000000..f6217eb
--- /dev/null
@@ -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 (file)
index 0000000..7783562
--- /dev/null
@@ -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 (file)
index 0000000..eb5e61d
--- /dev/null
@@ -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 (file)
index 0000000..ed31dc2
--- /dev/null
@@ -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 (file)
index 0000000..a76a35f
--- /dev/null
@@ -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;
+
+}
+