From: Todd C. Miller Date: Tue, 27 Sep 2011 20:29:27 +0000 (-0400) Subject: Modify the authentication API such that the init and cleanup functions X-Git-Tag: SUDO_1_7_8~3 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=431f2094daad3f7c2cf09f2e30301bd8c063a862;p=sudo Modify the authentication API such that the init and cleanup functions are always called, regardless of whether or not we are going to verify a password. This is needed for proper PAM session support. --HG-- branch : 1.7 --- diff --git a/auth/API b/auth/API index fd183fe84..d1535cb95 100644 --- a/auth/API +++ b/auth/API @@ -12,7 +12,7 @@ typedef struct sudo_auth { char *name; /* name of the method in string form */ void *data; /* method-specific data pointer */ - int (*init) __P((struct passwd *pw, char **prompt, sudo_auth *auth)); + int (*init) __P((struct passwd *pw, sudo_auth *auth)); int (*setup) __P((struct passwd *pw, char **prompt, sudo_auth *auth)); int (*verify) __P((struct passwd *pw, char *p, sudo_auth *auth)); int (*cleanup) __P((struct passwd *pw, sudo_auth *auth)); @@ -61,11 +61,10 @@ The member functions can return the following values: The functions in the struct are as follows: - int init(struct passwd *pw, char **prompt, sudo_auth *auth) + int init(struct passwd *pw, sudo_auth *auth) Function to do any one-time initialization for the auth method. All of the "init" functions are run before anything - else. A pointer to the prompt string may be used to add - method-specific info to the prompt. + else. int setup(struct passwd *pw, char **prompt, sudo_auth *auth) Function to do method-specific setup. All the "setup" diff --git a/auth/bsdauth.c b/auth/bsdauth.c index 727df13b0..fd244a7d8 100644 --- a/auth/bsdauth.c +++ b/auth/bsdauth.c @@ -54,9 +54,8 @@ extern char *login_style; /* from sudo.c */ int -bsdauth_init(pw, promptp, auth) +bsdauth_init(pw, auth) struct passwd *pw; - char **promptp; sudo_auth *auth; { static auth_session_t *as; diff --git a/auth/fwtk.c b/auth/fwtk.c index f1c164e97..2764fd5ed 100644 --- a/auth/fwtk.c +++ b/auth/fwtk.c @@ -50,9 +50,8 @@ #include "sudo_auth.h" int -fwtk_init(pw, promptp, auth) +fwtk_init(pw, auth) struct passwd *pw; - char **promptp; sudo_auth *auth; { static Cfg *confp; /* Configuration entry struct */ diff --git a/auth/kerb4.c b/auth/kerb4.c index 883035d95..a1bcfb8e2 100644 --- a/auth/kerb4.c +++ b/auth/kerb4.c @@ -48,9 +48,8 @@ #include "sudo_auth.h" int -kerb4_init(pw, promptp, auth) +kerb4_init(pw, auth) struct passwd *pw; - char **promptp; sudo_auth *auth; { static char realm[REALM_SZ]; diff --git a/auth/kerb5.c b/auth/kerb5.c index 4ce04aa12..9582c6f5c 100644 --- a/auth/kerb5.c +++ b/auth/kerb5.c @@ -90,17 +90,54 @@ krb5_get_init_creds_opt_free(opts) #endif int -kerb5_init(pw, promptp, auth) +kerb5_setup(pw, promptp, auth) struct passwd *pw; char **promptp; sudo_auth *auth; +{ + static char *krb5_prompt; + + if (krb5_prompt == NULL) { + krb5_context sudo_context; + krb5_principal princ; + char *pname; + krb5_error_code error; + + sudo_context = ((sudo_krb5_datap) auth->data)->sudo_context; + princ = ((sudo_krb5_datap) auth->data)->princ; + + /* + * 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 ((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 (!strcmp(prompt, PASSPROMPT)) { */ + easprintf(&krb5_prompt, "Password for %s: ", pname); + /*}*/ + free(pname); + } + *promptp = krb5_prompt; + + return AUTH_SUCCESS; +} + +int +kerb5_init(pw, auth) + struct passwd *pw; + sudo_auth *auth; { 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 */ @@ -122,25 +159,6 @@ kerb5_init(pw, promptp, auth) } princ = sudo_krb5_data.princ; - /* - * 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 (!strcmp(prompt, PASSPROMPT)) { */ - easprintf(promptp, "Password for %s: ", pname); - /*}*/ - free(pname); -#endif - (void) snprintf(cache_name, sizeof(cache_name), "MEMORY:sudocc_%ld", (long) getpid()); if ((error = krb5_cc_resolve(sudo_context, cache_name, diff --git a/auth/pam.c b/auth/pam.c index 265de36e3..39d658e5d 100644 --- a/auth/pam.c +++ b/auth/pam.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2005, 2007-2010 Todd C. Miller + * Copyright (c) 1999-2005, 2007-2011 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -78,12 +78,11 @@ static int gotintr; #define PAM_DATA_SILENT 0 #endif -static pam_handle_t *pamh; /* global due to pam_prep_user() */ +static pam_handle_t *pamh; int -pam_init(pw, promptp, auth) +pam_init(pw, auth) struct passwd *pw; - char **promptp; sudo_auth *auth; { static struct pam_conv pam_conv; @@ -194,11 +193,12 @@ pam_cleanup(pw, auth) { int *pam_status = (int *) auth->data; - /* If successful, we can't close the session until pam_prep_user() */ - if (auth->status == AUTH_SUCCESS) + /* If successful, we can't close the session until pam_end_session() */ + if (*pam_status == AUTH_SUCCESS) return AUTH_SUCCESS; *pam_status = pam_end(pamh, *pam_status | PAM_DATA_SILENT); + pamh = NULL; return *pam_status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE; } @@ -206,11 +206,20 @@ int pam_begin_session(pw) struct passwd *pw; { - int status = PAM_SUCCESS; + int status = PAM_SUCCESS; - /* If the user did not have to authenticate there is no pam handle yet. */ - if (pamh == NULL) - pam_init(pw, NULL, NULL); + /* + * If there is no valid user we cannot open a PAM session. + * This is not an error as sudo can run commands with arbitrary + * uids, it just means we are done from a session management standpoint. + */ + if (pw == NULL) { + if (pamh != NULL) { + (void) pam_end(pamh, PAM_SUCCESS | PAM_DATA_SILENT); + pamh = NULL; + } + goto done; + } /* * Update PAM_USER to reference the user we are running the command @@ -230,25 +239,35 @@ pam_begin_session(pw) #ifndef NO_PAM_SESSION status = pam_open_session(pamh, 0); - if (status != PAM_SUCCESS) { + if (status != PAM_SUCCESS) { (void) pam_end(pamh, status | PAM_DATA_SILENT); pamh = NULL; } #endif + +done: return status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE; } int -pam_end_session() +pam_end_session(pw) + struct passwd *pw; { int status = PAM_SUCCESS; if (pamh != NULL) { #ifndef NO_PAM_SESSION - (void) pam_close_session(pamh, 0); + /* + * Update PAM_USER to reference the user we are running the command + * as to match the call to pam_open_session(). + */ + (void) pam_set_item(pamh, PAM_USER, pw->pw_name); + (void) pam_close_session(pamh, PAM_SILENT); #endif status = pam_end(pamh, PAM_SUCCESS | PAM_DATA_SILENT); + pamh = NULL; } + return status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE; } diff --git a/auth/passwd.c b/auth/passwd.c index 8835c0363..0cee8a303 100644 --- a/auth/passwd.c +++ b/auth/passwd.c @@ -50,9 +50,8 @@ #define HAS_AGEINFO(p, l) (l == 18 && p[DESLEN] == ',') int -passwd_init(pw, promptp, auth) +passwd_init(pw, auth) struct passwd *pw; - char **promptp; sudo_auth *auth; { #ifdef HAVE_SKEYACCESS diff --git a/auth/secureware.c b/auth/secureware.c index c1c43d04c..57a032f71 100644 --- a/auth/secureware.c +++ b/auth/secureware.c @@ -53,9 +53,8 @@ #include "sudo_auth.h" int -secureware_init(pw, promptp, auth) +secureware_init(pw, auth) struct passwd *pw; - char **promptp; sudo_auth *auth; { #ifdef __alpha diff --git a/auth/securid.c b/auth/securid.c index 83ff94c13..ceaddc672 100644 --- a/auth/securid.c +++ b/auth/securid.c @@ -56,9 +56,8 @@ union config_record configure; int -securid_init(pw, promptp, auth) +securid_init(pw, auth) struct passwd *pw; - char **promptp; sudo_auth *auth; { static struct SD_CLIENT sd_dat; /* SecurID data block */ diff --git a/auth/securid5.c b/auth/securid5.c index 6880c1a1d..7ec7dcdf8 100644 --- a/auth/securid5.c +++ b/auth/securid5.c @@ -59,7 +59,6 @@ * securid_init - Initialises communications with ACE server * Arguments in: * pw - UNUSED - * promptp - UNUSED * auth - sudo authentication structure * * Results out: @@ -68,9 +67,8 @@ * success. */ int -securid_init(pw, promptp, auth) +securid_init(pw, auth) struct passwd *pw; - char **promptp; sudo_auth *auth; { static SDI_HANDLE sd_dat; /* SecurID handle */ diff --git a/auth/sudo_auth.c b/auth/sudo_auth.c index 42455ee31..6885f0fa0 100644 --- a/auth/sudo_auth.c +++ b/auth/sudo_auth.c @@ -68,7 +68,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, kerb5_cleanup) + AUTH_ENTRY(0, "kerb5", kerb5_init, kerb5_setup, kerb5_verify, kerb5_cleanup) # endif # ifdef HAVE_SKEY AUTH_ENTRY(0, "S/Key", NULL, rfc1938_setup, rfc1938_verify, NULL) @@ -80,6 +80,71 @@ sudo_auth auth_switch[] = { AUTH_ENTRY(0, NULL, NULL, NULL, NULL, NULL) }; +void +sudo_auth_init(pw) + struct passwd *pw; +{ + sudo_auth *auth; + int status; + + if (auth_switch[0].name == NULL) + return; + + /* Set FLAG_ONEANDONLY if there is only one auth method. */ + if (auth_switch[1].name == NULL) + SET(auth_switch[0].flags, FLAG_ONEANDONLY); + + /* Initialize auth methods and unconfigure the method if necessary. */ + for (auth = auth_switch; auth->name; auth++) { + if (auth->init && IS_CONFIGURED(auth)) { + if (NEEDS_USER(auth)) + set_perms(PERM_USER); + + status = (auth->init)(pw, auth); + + if (NEEDS_USER(auth)) + set_perms(PERM_ROOT); + + if (status == AUTH_FAILURE) + CLR(auth->flags, FLAG_CONFIGURED); + else if (status == AUTH_FATAL) { /* XXX log */ +#ifdef HAVE_BSM_AUDIT + audit_failure(NewArgv, "authentication failure"); +#endif + exit(1); /* assume error msg already printed */ + } + } + } +} + +void +sudo_auth_cleanup(pw) + struct passwd *pw; +{ + sudo_auth *auth; + int status; + + /* Call cleanup routines. */ + for (auth = auth_switch; auth->name; auth++) { + if (auth->cleanup && IS_CONFIGURED(auth)) { + if (NEEDS_USER(auth)) + set_perms(PERM_USER); + + status = (auth->cleanup)(pw, auth); + + if (NEEDS_USER(auth)) + set_perms(PERM_ROOT); + + if (status == AUTH_FATAL) { /* XXX log */ +#ifdef HAVE_BSM_AUDIT + audit_failure(NewArgv, "authentication failure"); +#endif + exit(1); /* assume error msg already printed */ + } + } + } +} + void verify_user(pw, prompt) struct passwd *pw; @@ -113,31 +178,6 @@ verify_user(pw, prompt) "--disable-authentication configure option."); } - /* Set FLAG_ONEANDONLY if there is only one auth method. */ - if (auth_switch[1].name == NULL) - SET(auth_switch[0].flags, FLAG_ONEANDONLY); - - /* Initialize auth methods and unconfigure the method if necessary. */ - for (auth = auth_switch; auth->name; auth++) { - if (auth->init && IS_CONFIGURED(auth)) { - if (NEEDS_USER(auth)) - set_perms(PERM_USER); - - status = (auth->init)(pw, &prompt, auth); - if (status == AUTH_FAILURE) - CLR(auth->flags, FLAG_CONFIGURED); - else if (status == AUTH_FATAL) { /* XXX log */ -#ifdef HAVE_BSM_AUDIT - audit_failure(NewArgv, "authentication failure"); -#endif - exit(1); /* assume error msg already printed */ - } - - if (NEEDS_USER(auth)) - set_perms(PERM_ROOT); - } - } - while (--counter) { /* Do any per-method setup and unconfigure the method if needed */ for (auth = auth_switch; auth->name; auth++) { @@ -146,6 +186,10 @@ verify_user(pw, prompt) set_perms(PERM_USER); status = (auth->setup)(pw, &prompt, auth); + + if (NEEDS_USER(auth)) + set_perms(PERM_ROOT); + if (status == AUTH_FAILURE) CLR(auth->flags, FLAG_CONFIGURED); else if (status == AUTH_FATAL) {/* XXX log */ @@ -154,9 +198,6 @@ verify_user(pw, prompt) #endif exit(1); /* assume error msg already printed */ } - - if (NEEDS_USER(auth)) - set_perms(PERM_ROOT); } } @@ -182,7 +223,7 @@ verify_user(pw, prompt) set_perms(PERM_ROOT); if (auth->status != AUTH_FAILURE) - goto cleanup; + goto done; } #ifndef AUTH_STANDALONE if (p == NULL) @@ -193,26 +234,7 @@ verify_user(pw, prompt) pass_warn(stderr); } -cleanup: - /* Call cleanup routines. */ - for (auth = auth_switch; auth->name; auth++) { - if (auth->cleanup && IS_CONFIGURED(auth)) { - if (NEEDS_USER(auth)) - set_perms(PERM_USER); - - status = (auth->cleanup)(pw, auth); - if (status == AUTH_FATAL) { /* XXX log */ -#ifdef HAVE_BSM_AUDIT - audit_failure(NewArgv, "authentication failure"); -#endif - exit(1); /* assume error msg already printed */ - } - - if (NEEDS_USER(auth)) - set_perms(PERM_ROOT); - } - } - +done: switch (success) { case AUTH_SUCCESS: (void) sigaction(SIGTSTP, &osa, NULL); diff --git a/auth/sudo_auth.h b/auth/sudo_auth.h index 50249552b..f75212503 100644 --- a/auth/sudo_auth.h +++ b/auth/sudo_auth.h @@ -28,7 +28,7 @@ typedef struct sudo_auth { short status; /* status from verify routine */ char *name; /* name of the method as a string */ void *data; /* method-specific data pointer */ - int (*init) __P((struct passwd *pw, char **prompt, struct sudo_auth *auth)); + int (*init) __P((struct passwd *pw, struct sudo_auth *auth)); int (*setup) __P((struct passwd *pw, char **prompt, struct sudo_auth *auth)); int (*verify) __P((struct passwd *pw, char *p, struct sudo_auth *auth)); int (*cleanup) __P((struct passwd *pw, struct sudo_auth *auth)); @@ -46,10 +46,10 @@ typedef struct sudo_auth { #define IS_ONEANDONLY(x) ((x)->flags & FLAG_ONEANDONLY) /* Prototypes for standalone methods */ -int fwtk_init __P((struct passwd *pw, char **prompt, sudo_auth *auth)); +int fwtk_init __P((struct passwd *pw, sudo_auth *auth)); int fwtk_verify __P((struct passwd *pw, char *prompt, sudo_auth *auth)); int fwtk_cleanup __P((struct passwd *pw, sudo_auth *auth)); -int pam_init __P((struct passwd *pw, char **prompt, sudo_auth *auth)); +int pam_init __P((struct passwd *pw, sudo_auth *auth)); int pam_verify __P((struct passwd *pw, char *prompt, sudo_auth *auth)); int pam_cleanup __P((struct passwd *pw, sudo_auth *auth)); int sia_setup __P((struct passwd *pw, char **prompt, sudo_auth *auth)); @@ -57,27 +57,28 @@ int sia_verify __P((struct passwd *pw, char *prompt, sudo_auth *auth)); int sia_cleanup __P((struct passwd *pw, sudo_auth *auth)); int aixauth_verify __P((struct passwd *pw, char *pass, sudo_auth *auth)); int aixauth_cleanup __P((struct passwd *pw, sudo_auth *auth)); -int bsdauth_init __P((struct passwd *pw, char **prompt, sudo_auth *auth)); +int bsdauth_init __P((struct passwd *pw, sudo_auth *auth)); int bsdauth_verify __P((struct passwd *pw, char *prompt, sudo_auth *auth)); int bsdauth_cleanup __P((struct passwd *pw, sudo_auth *auth)); /* Prototypes for normal methods */ -int passwd_init __P((struct passwd *pw, char **prompt, sudo_auth *auth)); +int passwd_init __P((struct passwd *pw, sudo_auth *auth)); int passwd_verify __P((struct passwd *pw, char *pass, sudo_auth *auth)); int passwd_cleanup __P((struct passwd *pw, sudo_auth *auth)); -int secureware_init __P((struct passwd *pw, char **prompt, sudo_auth *auth)); +int secureware_init __P((struct passwd *pw, sudo_auth *auth)); int secureware_verify __P((struct passwd *pw, char *pass, sudo_auth *auth)); int secureware_cleanup __P((struct passwd *pw, sudo_auth *auth)); int rfc1938_setup __P((struct passwd *pw, char **prompt, sudo_auth *auth)); int rfc1938_verify __P((struct passwd *pw, char *pass, sudo_auth *auth)); int afs_verify __P((struct passwd *pw, char *pass, sudo_auth *auth)); int dce_verify __P((struct passwd *pw, char *pass, sudo_auth *auth)); -int kerb4_init __P((struct passwd *pw, char **prompt, sudo_auth *auth)); +int kerb4_init __P((struct passwd *pw, 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_init __P((struct passwd *pw, sudo_auth *auth)); +int kerb5_setup __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_init __P((struct passwd *pw, 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/check.c b/check.c index 035d4b6bc..c2cfa3252 100644 --- a/check.c +++ b/check.c @@ -101,6 +101,7 @@ check_user(validated, mode) int validated; int mode; { + struct passwd *auth_pw; char *timestampdir = NULL; char *timestampfile = NULL; char *prompt; @@ -116,6 +117,10 @@ check_user(validated, mode) ctim_get(&sb, &tty_info.ctime); } + /* Init authentication system regardless of whether we need a password. */ + auth_pw = get_authpw(); + sudo_auth_init(auth_pw); + /* Always prompt for a password when -k was specified with the command. */ if (ISSET(mode, MODE_INVALIDATE)) { SET(validated, FLAG_CHECK_USER); @@ -127,7 +132,7 @@ check_user(validated, mode) if (user_uid == 0 || (user_uid == runas_pw->pw_uid && (!runas_gr || user_in_group(sudo_user.pw, runas_gr->gr_name))) || user_is_exempt()) - return; + goto done; } build_timestamp(×tampdir, ×tampfile); @@ -135,8 +140,6 @@ check_user(validated, mode) TS_MAKE_DIRS); if (status != TS_CURRENT || ISSET(validated, FLAG_CHECK_USER)) { - struct passwd *auth_pw; - /* Bail out if we are non-interactive and a password is required */ if (ISSET(mode, MODE_NONINTERACTIVE)) errorx(1, "sorry, a password is required to run %s", getprogname()); @@ -165,15 +168,17 @@ check_user(validated, mode) prompt = expand_prompt(user_prompt ? user_prompt : def_passprompt, user_name, user_shost); - auth_pw = get_authpw(); verify_user(auth_pw, prompt); - pw_delref(auth_pw); } /* Only update timestamp if user was validated. */ if (ISSET(validated, VALIDATE_OK) && !ISSET(mode, MODE_INVALIDATE) && status != TS_ERROR) update_timestamp(timestampdir, timestampfile); efree(timestampdir); efree(timestampfile); + +done: + sudo_auth_cleanup(auth_pw); + pw_delref(auth_pw); } /* diff --git a/sudo.c b/sudo.c index 6f7e05481..f494a61e2 100644 --- a/sudo.c +++ b/sudo.c @@ -968,7 +968,7 @@ run_command(path, argv, envp, uid, dowait) break; } #ifdef HAVE_PAM - pam_end_session(); + pam_end_session(runas_pw); #endif /* HAVE_PAM */ #ifdef _PATH_SUDO_IO_LOGDIR io_log_close(); diff --git a/sudo.h b/sudo.h index acd23633b..bbb80ac23 100644 --- a/sudo.h +++ b/sudo.h @@ -270,7 +270,7 @@ void io_nextid __P((void)); /* pam.c */ int pam_begin_session __P((struct passwd *)); -int pam_end_session __P((void)); +int pam_end_session __P((struct passwd *)); /* parse.c */ int sudo_file_open __P((struct sudo_nss *)); @@ -328,6 +328,8 @@ void set_fqdn __P((void)); void verify_user __P((struct passwd *, char *)); void pass_warn __P((FILE *)); void dump_auth_methods __P((void)); +void sudo_auth_init __P((struct passwd *)); +void sudo_auth_cleanup __P((struct passwd *)); /* sudo_nss.c */ void display_privs __P((struct sudo_nss_list *, struct passwd *));