From 3983a7a6514083e7c9487997dd3eaf86c321041e Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Sun, 17 Jan 1999 22:08:55 +0000 Subject: [PATCH] kerb5 support from fcusack@iconnet.net --- INSTALL | 8 +-- check.c | 163 ++++++++++++++++++++++++++++++++++++++++++++++++++- config.h.in | 5 -- configure | 95 ++++-------------------------- configure.in | 24 ++++---- logging.c | 6 ++ sudo.c | 72 ++++++++++++++++++++++- sudo.h | 3 + sudo.pod | 9 ++- 9 files changed, 278 insertions(+), 107 deletions(-) diff --git a/INSTALL b/INSTALL index e92f5c9ce..f99ba3551 100644 --- a/INSTALL +++ b/INSTALL @@ -169,10 +169,10 @@ Special features/options: authentication but does not use the kerberos cookie scheme. --with-kerb5 - Enable kerberos v5 support. This enables with kerberos v4 support - and links with the standard kerberos v5 libraries as well as the v4 - compatibility libraries. This uses kerberos passphrases for - authentication but does not use the kerberos cookie scheme. + Enable kerberos v5 support. Tested against MIT Kerberos V, + release 1.1, although also expected to work against CNS. This + This uses kerberos passphrases for authentication but does not + use the kerberos cookie scheme. --with-authenticate Enable support for the AIX 4.x general authentication function. diff --git a/check.c b/check.c index 2cdb8f205..26623d51e 100644 --- a/check.c +++ b/check.c @@ -62,6 +62,9 @@ #ifdef HAVE_KERB4 # include #endif /* HAVE_KERB4 */ +#ifdef HAVE_KERB5 +# include +#endif /* HAVE_KERB5 */ #ifdef HAVE_PAM # include # include @@ -109,6 +112,10 @@ static char *expand_prompt __P((char *, char *, char *)); #ifdef HAVE_KERB4 static int sudo_krb_validate_user __P((struct passwd *, char *)); #endif /* HAVE_KERB4 */ +#ifdef HAVE_KERB5 +static int sudo_krb5_validate_user __P((struct passwd *, char *)); +static int verify_krb_v5_tgt __P((krb5_ccache)); +#endif /* HAVE_KERB5 */ #ifdef HAVE_PAM static void pam_attempt_auth __P((void)); #endif /* HAVE_PAM */ @@ -134,6 +141,11 @@ struct skey skey; #ifdef HAVE_OPIE struct opie opie; #endif +#ifdef HAVE_KERB5 +extern krb5_context sudo_context; +extern char *realm; +extern int xrealm; +#endif /* HAVE_KERB5 */ @@ -519,7 +531,7 @@ static void check_passwd() reenter = 1; if (authenticate(user_name, pass, &reenter, &message) == 0) return; /* valid password */ -#else +#else /* HAVE_AUTHENTICATE */ # ifdef HAVE_SKEY /* rewrite the prompt if using s/key since the challenge can change */ set_perms(PERM_ROOT, 0); @@ -599,6 +611,11 @@ static void check_passwd() return; # endif /* HAVE_KERB4 */ +# ifdef HAVE_KERB5 + if (sudo_krb5_validate_user(user_pw_ent, pass) == 0) + return; +# endif /* HAVE_KERB5 */ + # ifdef HAVE_AFS if (ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION, user_name, /* name */ @@ -690,6 +707,150 @@ static int sudo_krb_validate_user(pw, pass) } #endif /* HAVE_KERB4 */ + +#ifdef HAVE_KERB5 +/******************************************************************** + * + * sudo_krb5_validate_user() + * + * Validate a user via Kerberos 5. We may lose a bit of memory, but it's + * OK since we're a short lived program. I'd rather do that than contort + * the code to handle the cleanup. + */ +static int sudo_krb5_validate_user(pw, pass) + struct passwd *pw; + char *pass; +{ + krb5_error_code retval; + krb5_principal princ; + krb5_creds creds; + krb5_ccache ccache; + char cache_name[64]; + char *princ_name; + krb5_get_init_creds_opt opts; + + /* Initialize */ + if (!sudo_context) + return -1; + krb5_get_init_creds_opt_init(&opts); + + princ_name = malloc(strlen(pw->pw_name) + strlen(realm) + 2); + if (!princ_name) { + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + + sprintf(princ_name, "%s@%s", pw->pw_name, realm); + if (retval = krb5_parse_name(sudo_context, princ_name, &princ)) + return retval; + + /* Set the ticket file to be in /tmp so we don't need to change perms. */ + (void) sprintf(cache_name, "FILE:/tmp/sudocc_%ld", getpid()); + if (retval = krb5_cc_resolve(sudo_context, cache_name, &ccache)) + return retval; + + if (retval = krb5_get_init_creds_password(sudo_context, &creds, princ, + pass, krb5_prompter_posix, NULL, + 0, NULL, &opts)) + return retval; + + /* Stash the TGT so we can verify it. */ + if (retval = krb5_cc_initialize(sudo_context, ccache, princ)) + return retval; + if (retval = krb5_cc_store_cred(sudo_context, ccache, &creds)) { + (void) krb5_cc_destroy(sudo_context, ccache); + return retval; + } + + retval = verify_krb_v5_tgt(ccache); + (void) krb5_cc_destroy(sudo_context, ccache); + return (retval == -1); +} + + +/* + * This routine with some modification is from the MIT V5B6 appl/bsd/login.c + * + * Verify the Kerberos ticket-granting ticket just retrieved for the + * user. If the Kerberos server doesn't respond, assume the user is + * trying to fake us out (since we DID just get a TGT from what is + * supposedly our KDC). If the host/ service is unknown (i.e., + * the local keytab doesn't have it), let her in. + * + * Returns 1 for confirmation, -1 for failure, 0 for uncertainty. + */ +static int verify_krb_v5_tgt(ccache) + krb5_ccache ccache; +{ + char phost[BUFSIZ]; + krb5_error_code retval; + krb5_principal princ; + krb5_keyblock * keyblock = 0; + krb5_data packet; + krb5_auth_context auth_context = NULL; + + packet.data = 0; + + /* + * Get the server principal for the local host. + * (Use defaults of "host" and canonicalized local name.) + */ + if (krb5_sname_to_principal(sudo_context, NULL, NULL, + KRB5_NT_SRV_HST, &princ)) + return -1; + + /* Extract the name directly. */ + strncpy(phost, krb5_princ_component(c, princ, 1)->data, BUFSIZ); + phost[BUFSIZ - 1] = '\0'; + + /* + * Do we have host/ keys? + * (use default keytab, kvno IGNORE_VNO to get the first match, + * and enctype is currently ignored anyhow.) + */ + if (retval = krb5_kt_read_service_key(sudo_context, NULL, princ, 0, + ENCTYPE_DES_CBC_MD5, &keyblock)) { + /* Keytab or service key does not exist */ + if (xrealm) + retval = -1; + else + retval = 0; + goto cleanup; + } + if (keyblock) + krb5_free_keyblock(sudo_context, keyblock); + + /* Talk to the kdc and construct the ticket. */ + retval = krb5_mk_req(sudo_context, &auth_context, 0, "host", phost, + NULL, ccache, &packet); + if (auth_context) { + krb5_auth_con_free(sudo_context, auth_context); + auth_context = NULL; /* setup for rd_req */ + } + if (retval) { + retval = -1; + goto cleanup; + } + + /* Try to use the ticket. */ + retval = krb5_rd_req(sudo_context, &auth_context, &packet, princ, + NULL, NULL, NULL); + if (retval) { + retval = -1; + } else { + retval = 1; + } + +cleanup: + if (packet.data) + krb5_free_data_contents(sudo_context, &packet); + krb5_free_principal(sudo_context, princ); + return retval; + +} +#endif /* HAVE_KERB5 */ + + #ifdef HAVE_PAM /******************************************************************** * pam_attempt_auth() diff --git a/config.h.in b/config.h.in index 217876210..977036eba 100644 --- a/config.h.in +++ b/config.h.in @@ -139,11 +139,6 @@ /* Define if you use Kerberos. */ #undef HAVE_KERB5 -/* Keberos v5 has v4 compatibility */ -#ifdef HAVE_KERB5 -# define HAVE_KERB4 -#endif /* HAVE_KERB5 */ - /* Define if you use SIA. */ #undef HAVE_SIA diff --git a/configure b/configure index 5508e8e0e..1156549d9 100755 --- a/configure +++ b/configure @@ -6815,7 +6815,7 @@ else fi fi -if test "$with_kerb4" = "yes" -o "$with_kerb5" = "yes"; then +if test "$with_kerb4" = "yes"; then if test -f "/usr/include/kerberosIV/krb.h"; then CPPFLAGS="${CPPFLAGS} -I/usr/include/kerberosIV" elif test -f "/usr/local/include/kerberosIV/krb.h"; then @@ -6825,7 +6825,7 @@ if test "$with_kerb4" = "yes" -o "$with_kerb5" = "yes"; then elif test -f "/usr/local/kerberos/include/krb.h"; then CPPFLAGS="${CPPFLAGS} -I/usr/local/kerberos/include" else - echo 'Unable to locate kerberos include files, you will have to edit the Makefile and add -I/path/to/krb/includes to CPPFLAGS' + echo 'Unable to locate kerberos 4 include files, you will have to edit the Makefile and add -I/path/to/krb/includes to CPPFLAGS' fi if test -d "/usr/kerberos/lib"; then @@ -6835,97 +6835,25 @@ if test "$with_kerb4" = "yes" -o "$with_kerb5" = "yes"; then elif test -f "/usr/local/lib/libkrb.a"; then SUDO_LDFLAGS="${SUDO_LDFLAGS} -L/usr/local/lib" elif test ! -f "/usr/lib/libkrb.a"; then - echo 'Unable to locate kerberos libraries, you will have to edit the Makefile and add -L/path/to/krb/libs to SUDO_LDFLAGS' + echo 'Unable to locate kerberos 4 libraries, you will have to edit the Makefile and add -L/path/to/krb/libs to SUDO_LDFLAGS' fi - if test "$with_kerb5" = "yes"; then - echo $ac_n "checking for -lkrb4""... $ac_c" 1>&6 -echo "configure:6844: checking for -lkrb4" >&5 -if eval "test \"`echo '$''{'ac_cv_lib_krb4'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - ac_save_LIBS="$LIBS" -LIBS="-lkrb4 $LIBS" -cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then - rm -rf conftest* - ac_cv_lib_krb4=yes -else - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - ac_cv_lib_krb4=no -fi -rm -f conftest* -LIBS="$ac_save_LIBS" - -fi -echo "$ac_t""$ac_cv_lib_krb4" 1>&6 -if test "$ac_cv_lib_krb4" = yes; then - SUDO_LIBS="${SUDO_LIBS} -lkrb4" -else - SUDO_LIBS="${SUDO_LIBS} -lkrb" -fi - - echo $ac_n "checking for -ldes""... $ac_c" 1>&6 -echo "configure:6879: checking for -ldes" >&5 -if eval "test \"`echo '$''{'ac_cv_lib_des'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - ac_save_LIBS="$LIBS" -LIBS="-ldes $LIBS" -cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then - rm -rf conftest* - ac_cv_lib_des=yes -else - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - ac_cv_lib_des=no -fi -rm -f conftest* -LIBS="$ac_save_LIBS" - -fi -echo "$ac_t""$ac_cv_lib_des" 1>&6 -if test "$ac_cv_lib_des" = yes; then - SUDO_LIBS="${SUDO_LIBS} -ldes" -fi - - SUDO_LIBS="${SUDO_LIBS} -ldes425 -lkrb5 -lcrypto -lcom_err" - else - # kerb4 - echo $ac_n "checking for -ldes""... $ac_c" 1>&6 -echo "configure:6915: checking for -ldes" >&5 + echo $ac_n "checking for -ldes""... $ac_c" 1>&6 +echo "configure:6843: checking for -ldes" >&5 if eval "test \"`echo '$''{'ac_cv_lib_des'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-ldes $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then +if { (eval echo configure:6857: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* ac_cv_lib_des=yes else @@ -6945,7 +6873,10 @@ else SUDO_LIBS="${SUDO_LIBS} -lkrb" fi - fi +fi + +if test "$with_kerb5" = "yes"; then + SUDO_LIBS="${SUDO_LIBS} -lkrb5 -lk5crypto -lcom_err" fi if test "$with_pam" = "yes"; then @@ -7036,7 +6967,7 @@ if test "$with_authenticate" = "yes"; then fi echo $ac_n "checking for log file location""... $ac_c" 1>&6 -echo "configure:7040: checking for log file location" >&5 +echo "configure:6971: checking for log file location" >&5 if test -n "$with_logpath"; then echo "$ac_t""$with_logpath" 1>&6 cat >> confdefs.h <&6 -echo "configure:7070: checking for timestamp file location" >&5 +echo "configure:7001: checking for timestamp file location" >&5 if test -n "$with_timedir"; then echo "$ac_t""$with_timedir" 1>&6 cat >> confdefs.h < #endif /* HAVE_DCE */ +#ifdef HAVE_KERB5 +#include +#endif /* HAVE_KERB5 */ #include "sudo.h" #include "version.h" @@ -145,6 +148,12 @@ static char *runas_homedir = NULL; extern struct interface *interfaces; extern int num_interfaces; extern int printmatches; +int arg_prompt = 0; /* was -p used? */ +#ifdef HAVE_KERB5 +krb5_context sudo_context = NULL; +char *realm = NULL; +int xrealm = 0; +#endif /* HAVE_KERB5 */ /* * Table of "bad" envariables to remove and len for strncmp() @@ -161,7 +170,10 @@ struct env_table badenv_table[] = { #endif /* _AIX */ #ifdef HAVE_KERB4 { "KRB_CONF", 8 }, -#endif +#endif /* HAVE_KERB4 */ +#ifdef HAVE_KERB5 + { "KRB5_CONFIG", 11 }, +#endif /* HAVE_KERB5 */ { "ENV=", 4 }, { "BASH_ENV=", 9 }, { (char *) NULL, 0 } @@ -416,6 +428,10 @@ static void load_globals(sudo_mode) #ifdef FQDN struct hostent *h_ent; #endif /* FQDN */ +#ifdef HAVE_KERB5 + krb5_error_code retval; + char *lrealm; +#endif /* HAVE_KERB5 */ /* * Get a local copy of the user's struct passwd with the shadow password @@ -439,6 +455,38 @@ static void load_globals(sudo_mode) exit(1); } +#ifdef HAVE_KERB5 + if (retval = krb5_init_context(&sudo_context)) { + log_error(GLOBAL_KRB5_INIT_ERR); + inform_user(GLOBAL_KRB5_INIT_ERR); + exit(1); + } + krb5_init_ets(sudo_context); + + if (retval = krb5_get_default_realm(sudo_context, &lrealm)) { + log_error(GLOBAL_KRB5_INIT_ERR); + inform_user(GLOBAL_KRB5_INIT_ERR); + exit(1); + } + + if (realm) { + if (strcmp(realm, lrealm) != 0) + xrealm = 1; /* User supplied realm is not the system default */ + free(lrealm); + } else + realm = lrealm; + + if (!arg_prompt) { + p = malloc(strlen(user_name) + strlen(realm) + 17); + if (p == NULL) { + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + sprintf(p, "Password for %s@%s: ", user_name, realm); + prompt = p; + } +#endif /* HAVE_KERB5 */ + /* Set euid == user and ruid == root */ set_perms(PERM_ROOT, sudo_mode); set_perms(PERM_USER, sudo_mode); @@ -562,12 +610,26 @@ static int parse_args() usage(1); /* only one -? option allowed */ switch (NewArgv[0][1]) { +#ifdef HAVE_KERB5 + case 'r': + /* must have an associated realm */ + if (NewArgv[1] == NULL) + usage(1); + + realm = NewArgv[1]; + + /* shift Argv over and adjust Argc */ + NewArgc--; + NewArgv++; + break; +#endif /* HAVE_KERB5 */ case 'p': /* must have an associated prompt */ if (NewArgv[1] == NULL) usage(1); prompt = NewArgv[1]; + arg_prompt = 1; /* shift Argv over and adjust Argc */ NewArgc--; @@ -656,7 +718,13 @@ static int parse_args() static void usage(exit_val) int exit_val; { - (void) fprintf(stderr, "usage: %s -V | -h | -l | -v | -k | -H | [-b] [-p prompt] [-u username/#uid] -s | \n", Argv[0]); + (void) fprintf(stderr, + "usage: %s -V | -h | -l | -v | -k | -H | [-b] [-p prompt] ", + Argv[0]); +#ifdef HAVE_KERB5 + (void) fprintf(stderr, "[-r realm] "); +#endif /* HAVE_KERB5 */ + (void) fprintf(stderr, "[-u username/#uid] -s | \n"); exit(exit_val); } diff --git a/sudo.h b/sudo.h index 1d1ebce03..b636b957e 100644 --- a/sudo.h +++ b/sudo.h @@ -163,6 +163,9 @@ struct generic_alias { #define BAD_STAMPDIR 0x0E #define BAD_STAMPFILE 0x0F #define BAD_ALLOCATION 0x10 +#ifdef HAVE_KERB5 +#define GLOBAL_KRB5_INIT_ERR ( 0x11 | GLOBAL_PROBLEM ) +#endif /* HAVE_KERB5 */ /* * Boolean values diff --git a/sudo.pod b/sudo.pod index 0aa727d4b..723ff8136 100644 --- a/sudo.pod +++ b/sudo.pod @@ -9,7 +9,7 @@ sudo - execute a command as the superuser =head1 SYNOPSIS B B<-V> | B<-h> | B<-l> | B<-v> | B<-k> | B<-s> | B<-H> | -[ B<-b> ] | [ B<-p> prompt ] [ B<-u> username/#uid] I +[ B<-b> ] | [ B<-r> realm ] | [ B<-p> prompt ] [ B<-u> username/#uid] I =head1 DESCRIPTION @@ -72,6 +72,13 @@ The C<-b> (I) option tells B to run the given command in the background. Note that if you use the C<-b> option you cannot use shell job control to manipulate the command. +=item -r + +The C<-r> (I) option is only available if B was configured +with B version 5 support. It allows the user to specify a +B realm other than the system default to use when authenticating +the user via B. + =item -p The C<-p> (I) option allows you to override the default -- 2.40.0