From: Todd C. Miller Date: Wed, 13 Oct 1999 02:34:55 +0000 (+0000) Subject: New krb5 code from Frank Cusack . X-Git-Tag: SUDO_1_6_0~33 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3acdd5b02ff59fbbc891b52021a00d80d758862c;p=sudo New krb5 code from Frank Cusack . --- diff --git a/auth/kerb5.c b/auth/kerb5.c index 130a6c54d..db8e0bff1 100644 --- a/auth/kerb5.c +++ b/auth/kerb5.c @@ -3,7 +3,7 @@ * All rights reserved. * * This code is derived from software contributed by Frank Cusack - * . + * . * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -62,11 +62,15 @@ static const char rcsid[] = "$Sudo$"; #endif /* lint */ -char *realm = NULL; -static int xrealm = 0; -static krb5_context sudo_context = NULL; +static int verify_krb_v5_tgt __P((krb5_context, krb5_ccache, char *)); +static struct _sudo_krb5_data { + krb5_context sudo_context; + krb5_principal princ; + krb5_ccache ccache; +} sudo_krb5_data = { NULL, NULL, NULL }; +typedef struct _sudo_krb5_data *sudo_krb5_datap; -static int verify_krb_v5_tgt __P((krb5_ccache)); +extern krb5_cc_ops krb5_mcc_ops; int kerb5_init(pw, promptp, auth) @@ -74,107 +78,177 @@ kerb5_init(pw, promptp, auth) char **promptp; sudo_auth *auth; { - char *lrealm; - krb5_error_code error; - extern int arg_prompt; + krb5_context sudo_context; + krb5_ccache ccache; + krb5_principal princ; + krb5_error_code error; + char cache_name[64]; + char *pname; + + auth->data = (VOID *) &sudo_krb5_data; /* Stash all our data here */ - /* XXX - make these errors non-fatal for better fallback? */ - if (error = krb5_init_context(&sudo_context)) { - /* XXX - map error to error string? */ + if (error = krb5_init_context(&(sudo_krb5_data.sudo_context))) { log_error(NO_EXIT|NO_MAIL, - "unable to initialize Kerberos V context"); - return(AUTH_FATAL); + "%s: unable to initialize context: %s", auth->name, + error_message(error)); + return(AUTH_FAILURE); } - auth->data = (VOID *) &sudo_context; /* save a pointer to the context */ - - krb5_init_ets(sudo_context); + sudo_context = sudo_krb5_data.sudo_context; - if (error = krb5_get_default_realm(sudo_context, &lrealm)) { + if (error = krb5_parse_name(sudo_context, pw->pw_name, + &(sudo_krb5_data.princ))) { log_error(NO_EXIT|NO_MAIL, - "unable to get default Kerberos V realm"); - return(AUTH_FATAL); + "%s: unable to parse '%s': %s", auth->name, pw->pw_name, + error_message(error)); + return(AUTH_FAILURE); } + princ = sudo_krb5_data.princ; - if (realm) { - if (strcmp(realm, lrealm) != 0) - xrealm = 1; /* User supplied realm is not the system default */ - free(lrealm); - } else - realm = lrealm; + /* + * Really, we need to tell the caller not to prompt for password. + * The API does not currently provide this unless the auth is standalone. + */ +#if 1 + if (error = krb5_unparse_name(sudo_context, princ, &pname)) { + log_error(NO_EXIT|NO_MAIL, + "%s: unable to unparse princ ('%s'): %s", auth->name, + pw->pw_name, error_message(error)); + return(AUTH_FAILURE); + } /* Only rewrite prompt if user didn't specify their own. */ - if (user_prompt == NULL) - easprintf(promptp, "Password for %s@%s: ", pw->pw_name, realm); + /*if (!strcmp(prompt, PASSPROMPT)) { */ + easprintf(promptp, "Password for %s: ", pname); + /*}*/ + free(pname); +#endif + + /* For CNS compatibility */ + if (error = krb5_cc_register(sudo_context, &krb5_mcc_ops, FALSE)) { + if (error != KRB5_CC_TYPE_EXISTS) { + log_error(NO_EXIT|NO_MAIL, + "%s: unable to use Memory ccache: %s", auth->name, + error_message(error)); + return(AUTH_FAILURE); + } + } + + (void) snprintf(cache_name, sizeof(cache_name), "MEMORY:sudocc_%ld", + (long) getpid()); + if (error = krb5_cc_resolve(sudo_context, cache_name, + &(sudo_krb5_data.ccache))) { + log_error(NO_EXIT|NO_MAIL, + "%s: unable to resolve ccache: %s", auth->name, + error_message(error)); + return(AUTH_FAILURE); + } + ccache = sudo_krb5_data.ccache; + + if (error = krb5_cc_initialize(sudo_context, ccache, princ)) { + log_error(NO_EXIT|NO_MAIL, + "%s: unable to initialize ccache: %s", auth->name, + error_message(error)); + return(AUTH_FAILURE); + } + return(AUTH_SUCCESS); } -/* XXX - some of this should move into the init or setup function. */ int kerb5_verify(pw, pass, auth) struct passwd *pw; char *pass; sudo_auth *auth; { - krb5_error_code error; + krb5_context sudo_context; krb5_principal princ; - krb5_creds creds; krb5_ccache ccache; - char cache_name[64]; - char *princ_name; + krb5_creds creds; + krb5_error_code error; krb5_get_init_creds_opt opts; + char cache_name[64]; - /* Initialize */ - krb5_get_init_creds_opt_init(&opts); - - princ_name = emalloc(strlen(pw->pw_name) + strlen(realm) + 2); - (void) sprintf(princ_name, "%s@%s", pw->pw_name, realm); - if (krb5_parse_name(sudo_context, princ_name, &princ)) - return(AUTH_FAILURE); + sudo_context = ((sudo_krb5_datap) auth->data)->sudo_context; + princ = ((sudo_krb5_datap) auth->data)->princ; + ccache = ((sudo_krb5_datap) auth->data)->ccache; - /* Set the ticket file to be in /tmp so we don't need to change perms. */ - /* XXX - potential /tmp race? */ - (void) snprintf(cache_name, sizeof(cache_name), "FILE:/tmp/sudocc_%ld", - (long) getpid()); - if (krb5_cc_resolve(sudo_context, cache_name, &ccache) - return(AUTH_FAILURE); + /* Initialize options to defaults */ + krb5_get_init_creds_opt_init(&opts); - if (krb5_get_init_creds_password(sudo_context, &creds, princ, pass, - krb5_prompter_posix, NULL, 0, NULL, &opts)) + /* Note that we always obtain a new TGT to verify the user */ + if (error = krb5_get_init_creds_password(sudo_context, &creds, princ, + pass, krb5_prompter_posix, + NULL, 0, NULL, &opts)) { + if (error == KRB5KRB_AP_ERR_BAD_INTEGRITY) /* Bad password */ + return(AUTH_FAILURE); + /* Some other error */ + log_error(NO_EXIT|NO_MAIL, + "%s: unable to get credentials: %s", auth->name, + error_message(error)); return(AUTH_FAILURE); + } /* Stash the TGT so we can verify it. */ - if (krb5_cc_initialize(sudo_context, ccache, princ)) - return(AUTH_FAILURE); - if (krb5_cc_store_cred(sudo_context, ccache, &creds)) { - (void) krb5_cc_destroy(sudo_context, ccache); - return(AUTH_FAILURE); + if (error = krb5_cc_store_cred(sudo_context, ccache, &creds)) { + log_error(NO_EXIT|NO_MAIL, + "%s: unable to store credentials: %s", auth->name, + error_message(error)); + } else { + error = verify_krb_v5_tgt(sudo_context, ccache, auth->name); } - error = verify_krb_v5_tgt(ccache); - (void) krb5_cc_destroy(sudo_context, ccache); + krb5_free_cred_contents(sudo_context, &creds); return (error ? AUTH_FAILURE : AUTH_SUCCESS); } +int +kerb5_cleanup(pw, auth) + struct passwd *pw; + sudo_auth *auth; +{ + krb5_context sudo_context; + krb5_principal princ; + krb5_ccache ccache; + + sudo_context = ((sudo_krb5_datap) auth->data)->sudo_context; + princ = ((sudo_krb5_datap) auth->data)->princ; + ccache = ((sudo_krb5_datap) auth->data)->ccache; + + if (sudo_context) { + if (ccache) + krb5_cc_destroy(sudo_context, ccache); + if (princ) + krb5_free_principal(sudo_context, princ); + krb5_free_context(sudo_context); + } + + return(AUTH_SUCCESS); +} + /* * 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. + * supposedly our KDC). If the host/ service is unknown (i.e., + * the local keytab doesn't have it), return success but log the error. + * + * This needs to run as root (to read the host service ticket). * * Returns 0 for successful authentication, non-zero for failure. */ static int -verify_krb_v5_tgt(ccache) +verify_krb_v5_tgt(sudo_context, ccache, auth_name) + krb5_context sudo_context; krb5_ccache ccache; + char *auth_name; /* For error reporting */ { char phost[BUFSIZ]; krb5_error_code error; krb5_principal princ; - krb5_keyblock * keyblock = 0; krb5_data packet; + krb5_keyblock *keyblock = 0; krb5_auth_context auth_context = NULL; packet.data = 0; @@ -183,12 +257,17 @@ verify_krb_v5_tgt(ccache) * 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)) + if (error = krb5_sname_to_principal(sudo_context, NULL, NULL, + KRB5_NT_SRV_HST, &princ)) { + log_error(NO_EXIT|NO_MAIL, + "%s: unable to get host principal: %s", auth_name, + error_message(error)); return(-1); + } - /* Extract the name directly. */ - strncpy(phost, krb5_princ_component(c, princ, 1)->data, sizeof(phost) - 1); + /* Extract the name directly. Yow. */ + strncpy(phost, krb5_princ_component(sudo_context, princ, 1)->data, + sizeof(phost) - 1); phost[sizeof(phost) - 1] = '\0'; /* @@ -199,6 +278,10 @@ verify_krb_v5_tgt(ccache) if (error = krb5_kt_read_service_key(sudo_context, NULL, princ, 0, ENCTYPE_DES_CBC_MD5, &keyblock)) { /* Keytab or service key does not exist. */ + log_error(NO_EXIT, + "%s: host service key not found: %s", auth_name, + error_message(error)); + error = 0; goto cleanup; } if (keyblock) @@ -221,5 +304,9 @@ cleanup: krb5_free_data_contents(sudo_context, &packet); krb5_free_principal(sudo_context, princ); + if (error) + log_error(NO_EXIT|NO_MAIL, + "%s: Cannot verify TGT! Possible attack!: %s", auth_name, + error_message(error)); return(error); } diff --git a/auth/sudo_auth.c b/auth/sudo_auth.c index 980cce470..ba37bcf64 100644 --- a/auth/sudo_auth.c +++ b/auth/sudo_auth.c @@ -80,7 +80,7 @@ sudo_auth auth_switch[] = { AUTH_ENTRY(0, "kerb4", kerb4_init, NULL, kerb4_verify, NULL) # endif # ifdef HAVE_KERB5 - AUTH_ENTRY(0, "kerb5", kerb5_init, NULL, kerb5_verify, NULL) + AUTH_ENTRY(0, "kerb5", kerb5_init, NULL, kerb5_verify, kerb5_cleanup) # endif # ifdef HAVE_SKEY AUTH_ENTRY(0, "S/Key", NULL, rfc1938_setup, rfc1938_verify, NULL) diff --git a/auth/sudo_auth.h b/auth/sudo_auth.h index e1bca180b..6395c7116 100644 --- a/auth/sudo_auth.h +++ b/auth/sudo_auth.h @@ -88,6 +88,7 @@ int kerb4_init __P((struct passwd *pw, char **prompt, sudo_auth *auth)); int kerb4_verify __P((struct passwd *pw, char *pass, sudo_auth *auth)); int kerb5_init __P((struct passwd *pw, char **prompt, sudo_auth *auth)); int kerb5_verify __P((struct passwd *pw, char *pass, sudo_auth *auth)); +int kerb5_cleanup __P((struct passwd *pw, sudo_auth *auth)); int securid_init __P((struct passwd *pw, char **prompt, sudo_auth *auth)); int securid_setup __P((struct passwd *pw, char **prompt, sudo_auth *auth)); int securid_verify __P((struct passwd *pw, char *pass, sudo_auth *auth)); diff --git a/configure b/configure index c8d186d6f..95f045a28 100755 --- a/configure +++ b/configure @@ -7662,58 +7662,32 @@ fi fi if test "$with_kerb5" = "yes"; then - echo $ac_n "checking for krb5_get_init_creds_opt in -lkrb5""... $ac_c" 1>&6 -echo "configure:7667: checking for krb5_get_init_creds_opt in -lkrb5" >&5 -if test -n ""; then - ac_lib_var=`echo krb5'_'krb5_get_init_creds_opt | sed 'y% ./+-%___p_%'` -else - ac_lib_var=`echo krb5'_'krb5_get_init_creds_opt | sed 'y%./+-%__p_%'` -fi -if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then - echo $ac_n "(cached) $ac_c" 1>&6 -else - ac_save_LIBS="$LIBS" -LIBS="-lkrb5 $LIBS" -cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then - rm -rf conftest* - eval "ac_cv_lib_$ac_lib_var=yes" -else - echo "configure: failed program was:" >&5 - cat conftest.$ac_ext >&5 - rm -rf conftest* - eval "ac_cv_lib_$ac_lib_var=no" -fi -rm -f conftest* -LIBS="$ac_save_LIBS" - -fi -if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then - echo "$ac_t""yes" 1>&6 - - cat >> confdefs.h <<\EOF + cat >> confdefs.h <<\EOF #define HAVE_KERB5 1 EOF - SUDO_LIBS="${SUDO_LIBS} -lkrb5 -lk5crypto -lcom_err" - AUTH_OBJS="${AUTH_OBJS} kerb5.o" - -else - echo "$ac_t""no" 1>&6 -with_kerb4=yes -fi + if test -f "/usr/local/include/krb5.h"; then + CPPFLAGS="$CPPFLAGS -I/usr/local/include" + elif test -f "/usr/local/kerberos/include/krb5.h"; then + CPPFLAGS="$CPPFLAGS -I/usr/local/kerberos/include" + elif test -f "/usr/local/krb5/include/krb5.h"; then + CPPFLAGS="$CPPFLAGS -I/usr/local/krb5/include" + else + echo 'Unable to locate kerberos 5 include files, you will have to edit the Makefile and add -I/path/to/krb/includes to CPPFLAGS' + fi + if test -f "/usr/local/lib/libkrb5.a"; then + SUDO_LDFLAGS="${SUDO_LDFLAGS} -L/usr/local/lib" + elif test -f "/usr/local/kerberos/lib/libkrb5.a"; then + SUDO_LDFLAGS="${SUDO_LDFLAGS} -L/usr/local/kerberos/lib" + elif test -f "/usr/local/krb5/lib/libkrb5.a"; then + SUDO_LDFLAGS="${SUDO_LDFLAGS} -L/usr/local/krb5/lib" + else + echo 'Unable to locate kerberos 5 libraries, you will have to edit the Makefile and add -L/path/to/krb/libs to SUDO_LDFLAGS' + fi + + SUDO_LIBS="${SUDO_LIBS} -lkrb5 -lk5crypto -lcom_err" + AUTH_OBJS="${AUTH_OBJS} kerb5.o" fi if test "$with_kerb4" = "yes"; then @@ -7743,93 +7717,22 @@ EOF 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:7749: 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:7784: checking for -ldes" >&5 + echo $ac_n "checking for -ldes""... $ac_c" 1>&6 +echo "configure:7722: 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 - echo $ac_n "checking for -ldes""... $ac_c" 1>&6 -echo "configure:7819: 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:7736: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then rm -rf conftest* ac_cv_lib_des=yes else @@ -7850,7 +7753,6 @@ else fi AUTH_OBJS="${AUTH_OBJS} kerb4.o" - fi fi if test "$with_pam" = "yes"; then @@ -7953,7 +7855,7 @@ if test "$with_authenticate" = "yes"; then fi echo $ac_n "checking for log file location""... $ac_c" 1>&6 -echo "configure:7957: checking for log file location" >&5 +echo "configure:7859: 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:7987: checking for timestamp file location" >&5 +echo "configure:7889: checking for timestamp file location" >&5 if test -n "$with_timedir"; then echo "$ac_t""$with_timedir" 1>&6 cat >> confdefs.h <\n"); exit(exit_val); }