# or implied warranty.
#
#
- DEPLIBS = $(DEPKLIB)
+
+DEPLIBS = $(DEPKLIB)
LOCAL_LIBRARIES = $(KLIB)
+DEFINES = -DLOCAL_REALM='"."' -DGET_TGT_VIA_PASSWD -DPRINC_LOOK_AHEAD -DCMD_PATH='"/bin /local/bin"'
+
OBJS = krb_auth_su.o ccache.o authorization.o main.o heuristic.o
SRCS = krb_auth_su.c ccache.c authorization.c main.c heuristic.c
-all:: ksu
+all:: ksu
NormalProgramTarget(ksu,$(OBJS),$(DEPLIBS),$(LOCAL_LIBRARIES),)
-Krb5InstallClientProgram(ksu)
-DependTarget()
+InstallProgramWithFlags(ksu,$(CLIENT_BINDIR),$(INSTUIDFLAGS))
+Krb5InstallManPage(ksu,$(CLIENT_MANDIR),$(CLIENT_MANSUFFIX))
+
+DependTarget()
krb5_ccache cc_def, char * cc_other_tag,
krb5_principal primary_principal,
/* OUT */
- krb5_ccache * cc_out ){
+ krb5_ccache * cc_out, krb5_boolean * stored){
int i=0;
krb5_ccache * cc_other;
krb5_creds ** cc_def_creds_arr = NULL;
krb5_creds ** cc_other_creds_arr = NULL;
uid_t eff_uid, eff_gid;
+struct stat st_temp;
cc_other = (krb5_ccache *) calloc(1, sizeof (krb5_ccache));
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 ( ! stat(cc_def_name, &st_temp)){
if(retval = krb5_get_nonexp_tkts( cc_def, &cc_def_creds_arr)){
return retval;
}
}
+ *stored = krb5_find_princ_in_cred_list(cc_def_creds_arr,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(*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]);
free(sname);
}
-int gen_sim(){
+int gen_sym(){
static int i = 0;
i ++;
return i;
krb5_creds ** ccs_creds_arr = NULL;
int i=0;
uid_t eff_uid, eff_gid;
+struct stat st_temp;
ccs_name = krb5_cc_get_name(ccs);
cct_name = krb5_cc_get_name(cct);
- if ( ! access(ccs_name, F_OK)){
+ if ( ! stat(ccs_name, &st_temp)){
if(retval = krb5_get_nonexp_tkts( ccs, &ccs_creds_arr)){
return retval;
}
}
- if ( ! access(cct_name, F_OK)){
+ if ( ! stat(cct_name, &st_temp)){
if (retval = krb5_cc_get_principal(cct, &temp_principal)){
return retval;
}
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);
krb5_creds ** cc_def_creds_arr = NULL;
krb5_creds ** cc_other_creds_arr = NULL;
uid_t eff_uid, eff_gid;
+struct stat st_temp;
cc_other = (krb5_ccache *) calloc(1, sizeof (krb5_ccache));
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 ( ! stat(cc_def_name, &st_temp)){
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);
krb5_principal temp_principal;
krb5_creds ** cc_creds_arr = NULL;
char * cc_name;
+struct stat st_temp;
cc_name = krb5_cc_get_name(cc);
- if ( ! access(cc_name, F_OK)){
+ if ( ! stat(cc_name, &st_temp)){
if (auth_debug) {
fprintf(stderr,"Refreshing cache %s\n", cc_name);
krb5_creds ** cc_creds_arr = NULL;
char * cc_name;
krb5_boolean stored;
+struct stat st_temp;
cc_name = krb5_cc_get_name(cc);
- if ( ! access(cc_name, F_OK)){
+ if ( ! stat(cc_name, &st_temp)){
if (auth_debug) {
fprintf(stderr,"puting cache %s through a filter for -z option\n", cc_name);
return 0;
}
+krb5_boolean krb5_find_princ_in_cred_list ( krb5_creds ** creds_list,
+ krb5_principal princ){
+
+int i = 0;
+krb5_boolean temp_stored = FALSE;
+
+ if (creds_list){
+ while(creds_list[i]){
+ if (krb5_principal_compare( creds_list[i]->client,
+ princ)== TRUE){
+ temp_stored = TRUE;
+ break;
+ }
+
+ i++;
+ }
+ }
+
+return temp_stored;
+}
+
+krb5_error_code krb5_find_princ_in_cache ( krb5_ccache cc,
+ krb5_principal princ,
+ krb5_boolean * found ){
+krb5_error_code retval;
+krb5_creds ** creds_list = NULL;
+char * cc_name;
+struct stat st_temp;
+
+ cc_name = krb5_cc_get_name(cc);
+
+ if ( ! stat(cc_name, &st_temp)){
+ if(retval = krb5_get_nonexp_tkts( cc, &creds_list)){
+ return retval;
+ }
+ }
+
+ *found = krb5_find_princ_in_cred_list(creds_list, princ);
+return 0;
+}
return 0;
}
-
void close_time(int k5users_flag, FILE * users_fp,
int k5login_flag, FILE * login_fp){
char * client_name;
krb5_boolean temp_found = FALSE;
char * cc_source_name;
+struct stat st_temp;
cc_source_name = krb5_cc_get_name(cc);
-if ( ! access(cc_source_name, F_OK)){
+if ( ! stat(cc_source_name, &st_temp)){
if (retval = find_ticket (cc, client, end_server, &temp_found)) {
return retval;
struct stat tb;
int count =0;
int i;
+struct stat st_temp;
*path_out = 0;
cc_source_name = krb5_cc_get_name(cc_source);
-if ( ! access(cc_source_name, F_OK)){
+if ( ! stat(cc_source_name, &st_temp)){
if (retval = krb5_cc_get_principal(cc_source, &cc_def_princ)){
return retval;
}
/* from here on, the code is for source_uid != 0 */
+if (source_uid && (source_uid == target_uid)){
+ if(cc_def_princ){
+ *client = cc_def_princ;
+ }else{
+ *client = target_client;
+ }
+ if (auth_debug){
+ printf("GET_best_princ_for_target: via source_uid == target_uid\n");
+ }
+
+ return 0;
+}
+
/* if .k5users and .k5login do not exist */
if ( stat(k5login_path, &tb) && stat(k5users_path, &tb) ){
*client = target_client;
for password promting */
-if (princ_trials[0].found == TRUE){
- *client = princ_trials[0].p;
+for (i=0; 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: default cache principal\n");
+ if (auth_debug){
+ printf(
+ "GET_best_princ_for_target: via prompt passwd list choice #%d \n",i);
+ }
+ return 0;
}
- 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 */
+for (i=0; i < count; i ++){
+ if (princ_trials[i].p){
+ if(retval=krb5_copy_principal(princ_trials[i].p, &temp_client)){
+ return retval;
+ }
- if (retval = get_closest_principal(aplist, &temp_client, & found)){
- return retval;
- }
+ /* get the client name that is the closest
+ to the three princ in trials */
- 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");
+ if(retval=get_closest_principal(aplist, &temp_client, & found)){
+ return retval;
}
- 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);
+ if (found == TRUE){
+ *client = temp_client;
+ if (auth_debug){
+ printf(
+ "GET_best_princ_for_target: via prompt passwd list choice: approximation of princ in trials # %d \n",i);
+ }
+ return 0;
}
- return 0;
+ krb5_free_principal(temp_client);
}
}
-/* 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;
-}
+#endif /* PRINC_LOOK_AHEAD */
+
+
if(auth_debug){
- printf( "GET_best_princ_for_target: out of luck choice\n");
+ printf( "GET_best_princ_for_target: out of luck, can't get appropriate default principal\n");
}
-*client = temp_client;
+*path_out = NOT_AUTHORIZED;
return 0;
}
char ** k5login_plist = NULL;
int got_it = 0;
char * client_name;
-
+krb5_boolean zero_password;
*path_passwd = 0;
memset((char *) &tgtq, 0, sizeof(tgtq));
#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");
+ fprintf(stderr,"WARNING: Your password may be exposed if you enter it here and are logged \n");
+ fprintf(stderr," in remotely using an unsecure (non-encrypted) channel. \n");
/*get the ticket granting ticket, via passwd(promt for passwd)*/
if (krb5_get_tkt_via_passwd (&cc, client, tgtq.server,
- options) == FALSE){
+ options, & zero_password) == FALSE){
return FALSE;
}
*path_passwd = 1;
krb5_free_tgt_creds(tgts);
}
- if (retval = krb5_verify_tkt_def(client, server,
+ if (retval = krb5_verify_tkt_def(client, server, &cred.keyblock,
&cred.ticket, &target_tkt)){
com_err(prog_name, retval, "while verifing ticket for server");
return (FALSE);
}
- if (retval = krb5_verify_tkt_def(client, server,
+ if (retval = krb5_verify_tkt_def(client, server, &tgt.keyblock,
&tgt.ticket, &target_tkt)){
com_err(prog_name, retval, "while verifing ticket for server");
return (FALSE);
krb5_error_code krb5_verify_tkt_def( /* IN */
krb5_principal client,
- krb5_principal server, krb5_data * scr_ticket,
+ krb5_principal server,
+ krb5_keyblock * cred_ses_key,
+ krb5_data * scr_ticket,
/* OUT */
krb5_ticket ** clear_ticket)
{
krb5_keyblock *tkt_key = NULL;
krb5_ticket * tkt = NULL;
krb5_error_code retval =0;
+krb5_keyblock * tkt_ses_key;
if (retval = decode_krb5_ticket(scr_ticket, &tkt)){
return retval;
return(retval);
}
+
+
if (!krb5_principal_compare(client, tkt->enc_part2->client)) {
- retval = KRB5KRB_AP_ERR_BADMATCH;
+ krb5_free_ticket(tkt);
+ krb5_kt_free_entry(&ktentry);
+ krb5_free_keyblock(tkt_key);
+ return KRB5KRB_AP_ERR_BADMATCH;
}
if (auth_debug){
dump_principal("tkt->enc_part2->client",tkt->enc_part2->client);
}
+ tkt_ses_key = tkt->enc_part2->session;
+
+ if (cred_ses_key->keytype != tkt_ses_key->keytype ||
+ cred_ses_key->length != tkt_ses_key->length ||
+ memcmp((char *)cred_ses_key->contents,
+ (char *)tkt_ses_key->contents, cred_ses_key->length)) {
+
+ krb5_free_ticket(tkt);
+ krb5_kt_free_entry(&ktentry);
+ krb5_free_keyblock(tkt_key);
+ return KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ }
+
+ if (auth_debug){
+ fprintf(stderr,
+ "krb5_verify_tkt_def: session keys match \n");
+ }
+
*clear_ticket = tkt;
krb5_kt_free_entry(&ktentry);
krb5_free_keyblock(tkt_key);
- return retval;
+ return 0;
}
krb5_boolean krb5_get_tkt_via_passwd (krb5_ccache * ccache, krb5_principal client,
- krb5_principal server, opt_info * options) {
+ krb5_principal server, opt_info * options,
+ krb5_boolean * zero_password) {
krb5_address **my_addresses;
krb5_error_code code;
krb5_creds my_creds;
int i;
char password[255], *client_name, prompt[255];
+
+ *zero_password = FALSE;
+
if (code = krb5_unparse_name(client, &client_name)) {
com_err (prog_name, code, "when unparsing name");
return (FALSE);
pwsize = sizeof(password);
code = krb5_read_password(prompt, 0, password, &pwsize);
- if (code || pwsize == 0) {
- fprintf(stderr, "Error while reading password for '%s'\n",
+ if (code ) {
+ com_err(prog_name, code, "while reading password for '%s'\n",
client_name);
memset(password, 0, sizeof(password));
krb5_free_addresses(my_addresses);
return (FALSE);
}
+ if ( pwsize == 0) {
+ fprintf(stderr, "No password given\n");
+ *zero_password = TRUE;
+ 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,
--- /dev/null
+.\" 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.4"
+.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 \-q
+] [
+.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 '.' is specified then the target user will be
+the source user (e.g. ksu .).
+If the source user is root or the target user is the source user, 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.
+
+While no authentication takes place if the source user
+is root or is the same as the target user, additional
+tickets can still be obtained for the target cache.
+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,
+if a password is not provided (user hit return)
+ksu continues in a
+normal mode of operation (the target cache will
+not contain the desired TGT).
+If the wrong password is typed in, ksu fails.
+.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 the target user is the source user the default principal name
+is set to the default principal of the source cache. If the
+cache does not exist then the default principal name is set to
+target_user@local_realm.
+If the source and target users are different and
+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 above list.
+If none are authorized and ksu is configured with PRINC_LOOK_AHEAD
+turned on, select the default principal as follows:
+
+For each candidate in the above list,
+select an authorized principal that has
+the same realm name and first part
+of the principal name equal to the prefix of the candidate.
+For example if candidate a) is jqpublic@ISI.EDU and jqpublic/secure@ISI.EDU
+is authorized to access the target account then the default principal
+is set to jqpublic/secure@ISI.EDU.
+
+\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_sym 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
+(unless ksu was invoked with '-C .' option).
+.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 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
+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\-q
+suppress the printing of status messages.
+.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 or source user == target user,
+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 target user is the source user 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 5 flags (see the Imakefile):
+.TP 10
+\fILOCAL_REALM\fP
+possible values: the name of the local realm
+or '.' in which case krb.conf is used to look
+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 as described in the OPTIONS section
+(see -n option).
+.TP 10
+\fICMD_PATH\fP
+specifies a list of directories containing programs
+that users are authorized to execute (via .k5users file).
+.TP 10
+\fIHAS_GETUSERSHELL\fP
+If the source user is non-root, ksu insists that
+the target user's shell to be invoked
+is a "legal shell". getusershell(3) is called to obtain
+the names of "legal shells". Note that the target user's
+shell is obtained from the passwd 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.
+.SH SIDE EFFECTS
+ksu deletes all expired tickets from the source cache.
+.SH AUTHOR OF KSU: GENNADY (ARI) MEDVINSKY
#include <unistd.h>
#include <string.h>
#include <syslog.h>
+#include <stdarg.h>
#define NO_TARGET_FILE '.'
+#define SOURCE_USER_LOGIN "."
#define KRB5_DEFAULT_OPTIONS 0
-#define KRB5_DEFAULT_TKT_LIFE 60*60*8 /* 8 hours */
+#define KRB5_DEFAULT_TKT_LIFE 60*60*12 /* 12 hours */
#define KRB5_SECONDARY_CACHE "FILE:/tmp/krb5cc_"
/* globals */
extern char * prog_name;
extern int auth_debug;
+extern int quiet;
extern char k5login_path[MAXPATHLEN];
extern char k5users_path[MAXPATHLEN];
extern char * gb_err;
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 int gen_sym();
extern krb5_error_code krb5_authorization();
extern krb5_error_code k5login_lookup ();
extern krb5_error_code k5users_lookup ();
extern char * get_first_token ();
extern char * get_next_token ();
extern krb5_boolean fowner();
+extern krb5_boolean krb5_find_princ_in_cred_list();
+extern krb5_error_code krb5_find_princ_in_cache();
#ifndef min
#define min(a,b) ((a) > (b) ? (b) : (a))
char k5login_path[MAXPATHLEN];
char k5users_path[MAXPATHLEN];
char * gb_err = NULL;
+int quiet = 0;
/***********/
#define _DEF_CSH "/bin/csh"
void sweep_up();
char * ontty();
void init_auth_names();
+void print_status( const char *fmt, ...);
+char * get_dir_of_file();
/* 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);
+ fprintf(stderr, "Usage: %s [target user] [-n principal] [-c source cachename] [-C target cachename] [-k] [-D] [-r time] [-pf] [-l lifetime] [-zZ] [-q] [-e command [args... ] ] [-a [args... ] ] \n", prog_name);
}
char ** pargv;
struct stat st_temp;
int temp_debug;
+krb5_boolean stored = FALSE;
+krb5_principal kdc_server;
+krb5_boolean zero_password;
+char * dir_of_cc_target;
+char * dir_of_cc_source;
options.opt = KRB5_DEFAULT_OPTIONS;
options.lifetime = KRB5_DEFAULT_TKT_LIFE;
}
- while(!done && ((option = getopt(pargc, pargv,"n:c:C:r:a:zZDfpkl:e:")) != EOF)){
+ while(!done && ((option = getopt(pargc, pargv,"n:c:C:r:a:zZDfpkql:e:")) != EOF)){
switch (option) {
case 'r':
options.opt |= KDC_OPT_RENEWABLE;
case 'k':
keep_target_cache =1;
break;
+ case 'q':
+ quiet =1;
+ break;
case 'l':
retval = krb5_parse_lifetime(optarg, &options.lifetime);
if (retval != 0 || options.lifetime == 0) {
}
}
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 )){
source_uid = pwd->pw_uid;
source_gid = pwd->pw_gid;
+
+ if (!strcmp(SOURCE_USER_LOGIN, target_user)){
+ target_user = strdup (source_user);
+ }
+
if ((target_pwd = getpwnam(target_user)) == NULL){
fprintf(stderr, "ksu: unknown login %s\n", target_user);
exit(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);
+ exit(1);
}
fprintf(stderr,
"%s does not have correct permissions for %s\n",
source_user, cc_source_tag);
- exit(0);
+ exit(1);
}
if (retval= krb5_ccache_refresh(cc_source)){
com_err(prog_name, retval,
"while refreshing %s (source cache)", cc_source_tag);
- exit(0);
+ exit(1);
}
}
target_user, cc_source, &options, cmd,
localhostname, &client, &hp)){
com_err(prog_name, retval, "while selecting the best principal");
- exit(0);
+ exit(1);
}
if (auth_debug){
if (hp){
if (gb_err) fprintf(stderr, "%s", gb_err);
- fprintf(stderr, "Not authorized\n");
- exit(0);
+ fprintf(stderr,"account %s: authorization failed\n",target_user);
+ exit(1);
}
-
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);}
+ dir_of_cc_source = get_dir_of_file(cc_source_tag_tmp);
- if (setegid(source_gid) < 0)
- { perror("ksu: setegid"); exit(1);}
+
+ if (access(dir_of_cc_source, R_OK | W_OK )){
+ fprintf(stderr,
+ "%s does not have correct permissions for %s\n",
+ source_user, cc_source_tag);
+ exit(1);
+ }
if (retval = krb5_cc_initialize(cc_source, client)){
com_err(prog_name, retval,
"while initializing source cache");
- exit(0);
+ exit(1);
+ }
+ if (chown(cc_source_tag_tmp, source_uid, source_gid)){
+ com_err(prog_name, errno,
+ "while changing owner for %s",
+ cc_source_tag_tmp);
+ exit(1);
}
-
- if (seteuid(eff_uid) < 0)
- { perror("ksu: seteuid"); exit(1); }
-
- if (setegid(eff_gid) < 0)
- { perror("ksu: setegid"); exit(1); }
-
}
}
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());
+ target_uid, gen_sym());
cc_target_tag_tmp = strchr(cc_target_tag, ':') + 1;
}while ( !stat ( cc_target_tag_tmp, &st_temp));
}
+ dir_of_cc_target = get_dir_of_file( use_source_cache ?
+ cc_source_tag_tmp: cc_target_tag_tmp);
+
+ if (access(dir_of_cc_target, R_OK | W_OK )){
+ fprintf(stderr,
+ "%s does not have correct permissions for %s\n",
+ source_user, cc_target_tag);
+ exit(1);
+ }
+
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
+ /*
+ Only when proper authentication and authorization
takes place, the target user becomes the owner of the cache.
*/
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);
+ exit(1);
}
- 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)){
+ 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);
+ exit(1);
}
+
}
}
else{
-
cc_target = cc_source;
cc_target_tag = cc_source_tag;
cc_target_tag_tmp = cc_source_tag_tmp;
+ if(retval=krb5_find_princ_in_cache(cc_target,client, &stored)){
+ com_err (prog_name, retval,
+ "while searching for client in source ccache");
+ exit(1);
+ }
+ }
+
+ if ((source_uid == 0) || (target_uid == source_uid)){
+ #ifdef GET_TGT_VIA_PASSWD
+ if ((!all_rest_copy) && options.princ && (stored == FALSE)){
+ 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(1);
+ }
+
+ fprintf(stderr,"WARNING: Your password may be exposed if you enter it here and are logged \n");
+ fprintf(stderr," in remotely using an unsecure (non-encrypted) channel.\n");
+ if (krb5_get_tkt_via_passwd (&cc_target, client,
+ kdc_server, &options,
+ &zero_password) == FALSE){
+
+ if (zero_password == FALSE){
+ fprintf(stderr,"Goodbye\n");
+ sweep_up(use_source_cache,
+ cc_target);
+ exit(1);
+ }
+
+ fprintf(stderr,
+ "Could not get a tgt for ");
+ plain_dump_principal (client);
+ fprintf(stderr, "\n");
+
+ }
+ }
+ #endif /* GET_TGT_VIA_PASSWD */
}
- /* if the user is root then authentication is not neccesary,
+ /* if the user is root or same uid then authentication is not neccesary,
root gets in automatically */
- if (source_uid) {
+ if (source_uid && (source_uid != target_uid)) {
char * client_name;
auth_val = krb5_auth_check(client, localhostname, &options,
target_user,cc_target, &path_passwd);
+
+
+ /* 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(1);
+ }
+
/* cache the tickets if possible in the source cache */
if (!path_passwd && !use_source_cache){
krb5_cc_get_name(cc_target),
krb5_cc_get_name(cc_source));
sweep_up(use_source_cache, cc_target);
- exit(0);
+ exit(1);
+ }
+ if (chown(cc_source_tag_tmp, source_uid, source_gid)){
+ com_err(prog_name, errno,
+ "while changing owner for %s",
+ cc_source_tag_tmp);
+ exit(1);
}
-
- }
-
- /* 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);
+ exit(1);
}
- fprintf(stderr,"Authenticated %s\n", client_name);
+ print_status("Authenticated %s\n", client_name);
syslog(LOG_NOTICE,"'%s %s' authenticated %s for %s%s",
prog_name,target_user,client_name,
source_user,ontty());
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);
+ exit(1);
}
if (authorization_val == TRUE){
if (cmd) {
- fprintf(stderr,
+ print_status(
"Account %s: authorization for %s for execution of\n",
target_user, client_name);
- fprintf(stderr, " %s successful\n",exec_cmd);
+ print_status(" %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,
+ print_status(
"Account %s: authorization for %s successful\n",
target_user, client_name);
syslog(LOG_NOTICE,
}
sweep_up(use_source_cache, cc_target);
- exit(0);
+ exit(1);
}
}
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);
+ exit(1);
}
}
if (retval = krb5_cc_initialize(cc_target, client)){
com_err(prog_name, retval,
"while erasing target cache");
- exit(0);
+ exit(1);
}
}
shell = _DEF_CSH; /* default is cshell */
}
- /* insist that the target login uses a standard shell (root is omited) */
#ifdef HAS_GETUSERSHELL
+
+ /* 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);
}
#endif /* HAS_GETUSERSHELL */
-
- /* want to check the scoop with USER for the real ksu , MOD */
if (target_pwd->pw_uid){
/* set the cc env name to target */
- if(set_env_var(KRB5_ENV_CCNAME, cc_target_tag)){
+ if(set_env_var( KRB5_ENV_CCNAME, cc_target_tag)){
fprintf(stderr,"ksu: couldn't set environment variable %s \n",
KRB5_ENV_CCNAME);
sweep_up(use_source_cache, cc_target);
exit(1);
}
- fprintf(stderr,"Changing uid to %d\n", target_pwd->pw_uid);
+ if ( ! strcmp(target_user, source_user)){
+ print_status("Leaving uid as %s (%d)\n",
+ target_user, target_pwd->pw_uid);
+ }else{
+ print_status("Changing uid to %s (%d)\n",
+ target_user, 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 (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){
+ if ((source_uid == 0) || (source_uid == target_uid )){
exec_cmd = cmd;
}
exit(1);
}
sweep_up(use_source_cache, cc_target);
+
+ if (auth_debug){
+ printf("The exit status of the child is %d\n",
+ statusp);
+ }
+
+ exit (statusp);
}else{
execv(params[0], params);
com_err(prog_name, errno, "while trying to execv %s",
params[0]);
- exit(1);
-
+ exit (1);
}
}
}
-
#ifdef HAS_GETUSERSHELL
+
int standard_shell(sh)
char *sh;
{
return (1);
return (0);
}
-#endif
-
-/* Modify this later , (clean it up) , MOD */
+#endif /* HAS_GETUSERSHELL */
char * ontty()
{
}
+void print_status( const char *fmt, ...)
+{
+va_list ap;
+ if (! quiet){
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+}
+
+
+char * get_dir_of_file( char * path){
+
+char * temp_path;
+char * ptr;
+
+temp_path = strdup(path);
+
+if (ptr = strrchr( temp_path, '/')){
+ *ptr = '\0';
+}else{
+ free (temp_path);
+ temp_path = (char *) calloc(MAXPATHLEN, sizeof(char));
+ temp_path = (char *) getwd(temp_path);
+}
+
+return temp_path;
+}