From: Todd C. Miller Date: Mon, 5 Jul 1999 20:14:21 +0000 (+0000) Subject: New (correct) PAM code X-Git-Tag: SUDO_1_6_0~264 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=69a00325ececbae9b7cc19792651e85874f90057;p=sudo New (correct) PAM code Tgetpass now takes an echo flag for use with PAM_PROMPT_ECHO_ON Block SIGINT and SIGTSTP during auth remove a useless umask setting Change error from BAD_ALLOCATION -> BAD_AUTH_INIT (for use with sia/PAM) Some cosmetic changes to auth.c for consistency --- diff --git a/auth.c b/auth.c index a169402e1..9f52f0abd 100644 --- a/auth.c +++ b/auth.c @@ -92,8 +92,7 @@ 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 int pam_auth __P((char *, char *)); -static int PAM_conv __P((int, +static int sudo_conv __P((int, PAM_CONST struct pam_message **, struct pam_response **, void *)); #endif /* HAVE_PAM */ @@ -150,9 +149,7 @@ check_passwd() exit(1); } - /* - * you get TRIES_FOR_PASSWORD times to guess your password - */ + /* You get TRIES_FOR_PASSWORD times to guess your password */ while (counter > 0) { if (sd_auth(sd) == ACM_OK) { set_perms(PERM_USER, 0); @@ -164,14 +161,8 @@ check_passwd() } set_perms(PERM_USER, 0); - if (counter > 0) { - log_error(PASSWORD_NOT_CORRECT); - inform_user(PASSWORD_NOT_CORRECT); - } else { - log_error(PASSWORDS_NOT_CORRECT); - inform_user(PASSWORDS_NOT_CORRECT); - } - + log_error(counter ? PASSWORD_NOT_CORRECT : PASSWORDS_NOT_CORRECT); + inform_user(counter ? PASSWORD_NOT_CORRECT : PASSWORDS_NOT_CORRECT); exit(1); } #else /* !HAVE_SECURID */ @@ -207,9 +198,7 @@ check_passwd() exit(1); } - /* - * you get TRIES_FOR_PASSWORD times to guess your password - */ + /* You get TRIES_FOR_PASSWORD times to guess your password */ while (counter > 0) { sprintf(cbuf,"authorize %s sudo",user_name); @@ -219,9 +208,9 @@ check_passwd() if (!strncmp(cbuf, "challenge ", 10)) { sprintf(buf, "Challenge \"%s\": ", &cbuf[10]); - pass = GETPASS(buf, PASSWORD_TIMEOUT * 60); + pass = GETPASS(buf, PASSWORD_TIMEOUT * 60, 1); } else if (!strncmp(cbuf, "password", 8)) { - pass = GETPASS(buf, PASSWORD_TIMEOUT * 60); + pass = GETPASS(buf, PASSWORD_TIMEOUT * 60, 1); } else { fprintf(stderr, "Server sent %s\n", cbuf); auth_close(); @@ -249,13 +238,8 @@ check_passwd() auth_close(); - if (counter > 0) { - log_error(PASSWORD_NOT_CORRECT); - inform_user(PASSWORD_NOT_CORRECT); - } else { - log_error(PASSWORDS_NOT_CORRECT); - inform_user(PASSWORDS_NOT_CORRECT); - } + log_error(counter ? PASSWORD_NOT_CORRECT : PASSWORDS_NOT_CORRECT); + inform_user(counter ? PASSWORD_NOT_CORRECT : PASSWORDS_NOT_CORRECT); exit(1); } @@ -281,14 +265,12 @@ check_passwd() (void) memset((VOID *)&opie, 0, sizeof(opie)); #endif /* HAVE_OPIE */ - /* - * you get TRIES_FOR_PASSWORD times to guess your password - */ + /* You get TRIES_FOR_PASSWORD times to guess your password */ while (counter > 0) { #ifdef HAVE_AUTHENTICATE /* use AIX authenticate() function */ - pass = GETPASS(prompt, PASSWORD_TIMEOUT * 60); + pass = GETPASS(prompt, PASSWORD_TIMEOUT * 60, 1); reenter = 1; if (authenticate(user_name, pass, &reenter, &message) == 0) return; /* valid password */ @@ -311,7 +293,7 @@ check_passwd() (void) des_read_pw_string(kpass, sizeof(kpass) - 1, prompt, 0); pass = kpass; # else - pass = (char *) GETPASS(prompt, PASSWORD_TIMEOUT * 60); + pass = (char *) GETPASS(prompt, PASSWORD_TIMEOUT * 60, 1); # endif /* HAVE_KERB4 */ # ifdef HAVE_SKEY @@ -399,14 +381,8 @@ check_passwd() pass_warn(stderr); } - if (counter > 0) { - log_error(PASSWORD_NOT_CORRECT); - inform_user(PASSWORD_NOT_CORRECT); - } else { - log_error(PASSWORDS_NOT_CORRECT); - inform_user(PASSWORDS_NOT_CORRECT); - } - + log_error(counter ? PASSWORD_NOT_CORRECT : PASSWORDS_NOT_CORRECT); + inform_user(counter ? PASSWORD_NOT_CORRECT : PASSWORDS_NOT_CORRECT); exit(1); } #endif /* HAVE_AUTHSRV */ @@ -496,7 +472,7 @@ sudo_krb5_validate_user(pw, pass) princ_name = emalloc(strlen(pw->pw_name) + strlen(realm) + 2); - sprintf(princ_name, "%s@%s", pw->pw_name, realm); + (void) sprintf(princ_name, "%s@%s", pw->pw_name, realm); if (retval = krb5_parse_name(sudo_context, princ_name, &princ)) return retval; @@ -613,118 +589,109 @@ cleanup: * pam_attempt_auth() * * Try to authenticate the user using Pluggable Authentication - * Modules (PAM). Added 9/11/98 by Gary J. Calvin - * Reworked for stock PAM by Amos Elberg and Todd Miller + * Modules (PAM). */ -static char *PAM_username; -static char *PAM_password; - -static int -PAM_conv(num_msg, msg, resp, appdata_ptr) - int num_msg; - PAM_CONST struct pam_message **msg; - struct pam_response **resp; - void *appdata_ptr; +void +pam_attempt_auth() { - int replies = 0; - struct pam_response *reply = NULL; + int counter = TRIES_FOR_PASSWORD; + int null_pw = 0; + static struct pam_conv pam_conv; + static pam_handle_t *pamh; - if ((reply = malloc(sizeof(struct pam_response) * num_msg)) == NULL) - return(PAM_CONV_ERR); + set_perms(PERM_ROOT, 0); - for (replies = 0; replies < num_msg; replies++) { - switch (msg[replies]->msg_style) { - case PAM_PROMPT_ECHO_ON: - reply[replies].resp_retcode = PAM_SUCCESS; - reply[replies].resp = estrdup(PAM_username); - /* PAM frees resp */ - break; - case PAM_PROMPT_ECHO_OFF: - reply[replies].resp_retcode = PAM_SUCCESS; - reply[replies].resp = estrdup(PAM_password); - /* PAM frees resp */ - break; - case PAM_TEXT_INFO: - /* fall through */ - case PAM_ERROR_MSG: - /* ignore it... */ - reply[replies].resp_retcode = PAM_SUCCESS; - reply[replies].resp = NULL; - break; - default: - /* Must be an error of some sort... */ - free(reply); - return(PAM_CONV_ERR); - } + /* Initial PAM setup + use our default prompt */ + pam_conv.conv = sudo_conv; + pam_conv.appdata_ptr = &null_pw; + if (pam_start("sudo", user_name, &pam_conv, &pamh) != PAM_SUCCESS || + pam_set_item(pamh, PAM_USER_PROMPT, (void *) prompt) != PAM_SUCCESS) { + set_perms(PERM_USER, 0); + log_error(BAD_AUTH_INIT); + inform_user(BAD_AUTH_INIT); + exit(1); } - if (reply) - *resp = reply; - return(PAM_SUCCESS); -} - -static int -pam_auth(user, password) - char *user; - char *password; -{ - struct pam_conv PAM_conversation; - pam_handle_t *pamh; + /* You get TRIES_FOR_PASSWORD times to guess your password */ + while (counter > 0) { - /* Initialize our variables for PAM */ - PAM_conversation.conv = PAM_conv; - PAM_conversation.appdata_ptr = NULL; - PAM_password = password; - PAM_username = user; + /* PAM_SILENT prevents error messages from going to syslog(3) */ + if (pam_authenticate(pamh, PAM_SILENT) == PAM_SUCCESS) { + pam_end(pamh, PAM_SUCCESS); + set_perms(PERM_USER, 0); + return; + } + if (null_pw) + break; - /* - * Setting PAM_SILENT stops generation of error messages to syslog - * to enable debugging on Red Hat Linux set: - * /etc/pam.d/sudo: - * auth required /lib/security/pam_pwdb.so shadow nullok audit - * _OR_ change PAM_SILENT to 0 to force detailed reporting (logging) - */ - if (pam_start("sudo", user, &PAM_conversation, &pamh) != PAM_SUCCESS || - pam_authenticate(pamh, PAM_SILENT) != PAM_SUCCESS) { - pam_end(pamh, 0); - return(0); + --counter; /* otherwise, try again */ + pass_warn(stderr); } + pam_end(pamh, 0); + set_perms(PERM_USER, 0); - /* User authenticated successfully */ - pam_end(pamh, PAM_SUCCESS); - - return(1); + log_error(counter ? PASSWORD_NOT_CORRECT : PASSWORDS_NOT_CORRECT); + inform_user(counter ? PASSWORD_NOT_CORRECT : PASSWORDS_NOT_CORRECT); + exit(1); } -void -pam_attempt_auth() -{ - int i = TRIES_FOR_PASSWORD; - set_perms(PERM_ROOT, 0); - while (i > 0) { - char *pamPass = (char *) GETPASS(prompt, PASSWORD_TIMEOUT * 60); +/******************************************************************** + * sudo_conv() + * + * ``Conversation function'' for PAM. + */ +static int +sudo_conv(num_msg, msg, response, appdata_ptr) + int num_msg; + PAM_CONST struct pam_message **msg; + struct pam_response **response; + void *appdata_ptr; +{ + struct pam_response *pr; + struct pam_message *pm; + int echo = 0; - if (pam_auth(user_name, pamPass)) { - set_perms(PERM_USER, 0); - return; - } - --i; - pass_warn(stderr); + if ((*response = malloc(num_msg * sizeof(struct pam_response))) == NULL) + return(PAM_CONV_ERR); + (void) memset((VOID *)*response, 0, num_msg * sizeof(struct pam_response)); + + for (pr = *response, pm = *msg; num_msg--; pr++, pm++) { + switch (pm->msg_style) { + case PAM_PROMPT_ECHO_ON: + echo = 1; + case PAM_PROMPT_ECHO_OFF: + pr->resp = estrdup((char *) GETPASS(pm->msg, + PASSWORD_TIMEOUT * 60, !echo)); + /* Solaris PAM does not pass through appdata_ptr! */ + if (pr->resp[0] == '\0' && appdata_ptr != NULL) + *((int *) appdata_ptr) = 1; /* indicate an empty password */ + break; + case PAM_TEXT_INFO: + if (pm->msg) + (void) puts(pm->msg); + break; + case PAM_ERROR_MSG: + if (pm->msg) { + (void) fputs(pm->msg, stderr); + (void) fputc('\n', stderr); + } + break; + default: + /* Something odd happened */ + /* XXX - should free non-NULL response members */ + free(*response); + *response = NULL; + return(PAM_CONV_ERR); + break; + } } - set_perms(PERM_USER, 0); - if (i == 0) { - log_error(PASSWORD_NOT_CORRECT); - inform_user(PASSWORD_NOT_CORRECT); - } else { - log_error(PASSWORDS_NOT_CORRECT); - inform_user(PASSWORDS_NOT_CORRECT); - } - exit(1); + return(PAM_SUCCESS); } #endif /* HAVE_PAM */ + #ifdef HAVE_SKEY /******************************************************************** * diff --git a/check.c b/check.c index 644407cd8..47dfbc807 100644 --- a/check.c +++ b/check.c @@ -45,6 +45,7 @@ #include #endif /* HAVE_STRINGS_H */ #include +#include #include #include #include @@ -96,13 +97,28 @@ static char timestampfile[MAXPATHLEN]; void check_user() { - register int rtn; - mode_t oldmask; + int rtn; +#ifdef POSIX_SIGNALS + sigset_t set, oset; +#else + int omask; +#endif /* POSIX_SIGNALS */ if (user_is_exempt()) /* some users don't need to enter a passwd */ return; - oldmask = umask(077); /* make sure the timestamp files are private */ + /* + * Block SIGINT and SIGTSTP during authentication so the user + * can't abort the logging. + */ +#ifdef POSIX_SIGNALS + (void) sigemptyset(&set); + (void) sigaddset(&set, SIGINT); + (void) sigaddset(&set, SIGTSTP); + (void) sigprocmask(SIG_BLOCK, &set, &oset); +#else + omask = sigblock(sigmask(SIGINT)|sigmask(SIGTSTP)); +#endif /* POSIX_SIGNALS */ rtn = check_timestamp(); if (rtn && user_uid) { /* if timestamp is not current... */ @@ -123,9 +139,14 @@ check_user() #endif /* HAVE_SIA */ } - update_timestamp(); - (void) umask(oldmask); /* want a real umask to exec() the command */ + /* Unblock signals */ +#ifdef POSIX_SIGNALS + (void) sigprocmask(SIG_SETMASK, &oset, NULL); +#else + (void) sigsetmask(omask); +#endif /* POSIX_SIGNALS */ + update_timestamp(); } diff --git a/check_sia.c b/check_sia.c index 681eb6fc2..8a389fdb1 100644 --- a/check_sia.c +++ b/check_sia.c @@ -113,8 +113,8 @@ sia_attempt_auth() 1, NULL); if (retval != SIASUCCESS) { set_perms(PERM_USER, 0); - log_error(BAD_ALLOCATION); - inform_user(BAD_ALLOCATION); + log_error(BAD_AUTH_INIT); + inform_user(BAD_AUTH_INIT); exit(1); } /* XXX - need a way to detect user hitting return or EOF at prompt */ diff --git a/logging.c b/logging.c index 830936547..bcaa4bac1 100644 --- a/logging.c +++ b/logging.c @@ -264,9 +264,9 @@ log_error(code) tty, cwd, runas_user); break; - case BAD_ALLOCATION: + case BAD_AUTH_INIT: (void) sprintf(p, - "allocation failure; TTY=%s ; PWD=%s ; USER=%s ; COMMAND=", + "unable to initialize authentication scheme; TTY=%s ; PWD=%s ; USER=%s ; COMMAND=", tty, cwd, runas_user); break; @@ -668,9 +668,9 @@ inform_user(code) "Your timestamp file has a preposterous date, ignoring.\n"); break; - case BAD_ALLOCATION: + case BAD_AUTH_INIT: (void) fprintf(stderr, - "Resource allocation failure.\n"); + "Unable to initialize authentication scheme.\n"); break; case NO_CMND_SAFE: @@ -736,7 +736,7 @@ appropriate(code) case NO_SUDOERS_FILE: case BAD_STAMPDIR: case BAD_STAMPFILE: - case BAD_ALLOCATION: + case BAD_AUTH_INIT: case NO_CMND_SAFE: default: return (1); diff --git a/sudo.h b/sudo.h index 044429eea..d9aa1f653 100644 --- a/sudo.h +++ b/sudo.h @@ -152,7 +152,7 @@ struct generic_alias { #define SUDOERS_NOT_FILE ( 0x0B | GLOBAL_PROBLEM ) #define BAD_STAMPDIR 0x0C #define BAD_STAMPFILE 0x0D -#define BAD_ALLOCATION 0x0E +#define BAD_AUTH_INIT 0x0E #define NO_CMND_SAFE 0x0F #ifdef HAVE_KERB5 #define GLOBAL_KRB5_INIT_ERR ( 0x10 | GLOBAL_PROBLEM ) @@ -209,9 +209,9 @@ struct generic_alias { * Use either tgetpass() or system getpass() */ #ifdef USE_GETPASS -#define GETPASS(p, t) getpass(p) +#define GETPASS(p, t, e) getpass(p) #else -#define GETPASS(p, t) tgetpass(p, t) +#define GETPASS(p, t, e) tgetpass(p, t, e) #endif /* @@ -235,7 +235,7 @@ int putenv __P((const char *)); #endif char *sudo_goodpath __P((const char *)); int sudo_setenv __P((char *, char *)); -char *tgetpass __P((const char *, int)); +char *tgetpass __P((const char *, int, int)); int find_path __P((char *, char **)); void log_error __P((int)); void inform_user __P((int)); diff --git a/tgetpass.c b/tgetpass.c index 68d713891..d4754e811 100644 --- a/tgetpass.c +++ b/tgetpass.c @@ -88,9 +88,10 @@ static const char rcsid[] = "$Sudo$"; */ char * -tgetpass(prompt, timeout) +tgetpass(prompt, timeout, echo_off) const char *prompt; int timeout; + int echo_off; { #ifdef HAVE_TERMIOS_H struct termios term; @@ -102,12 +103,11 @@ tgetpass(prompt, timeout) #endif /* HAVE_TERMIO_H */ #endif /* HAVE_TERMIOS_H */ #ifdef POSIX_SIGNALS - sigset_t oldmask; - sigset_t mask; + sigset_t set, oset; #else - int oldmask; + int omask; #endif /* POSIX_SIGNALS */ - int n, echo, input, output; + int n, input, output; static char buf[SUDO_PASS_MAX + 1]; fd_set *readfds; struct timeval tv; @@ -116,13 +116,13 @@ tgetpass(prompt, timeout) * mask out SIGINT and SIGTSTP, should probably just catch and deal. */ #ifdef POSIX_SIGNALS - (void) sigemptyset(&mask); - (void) sigaddset(&mask, SIGINT); - (void) sigaddset(&mask, SIGTSTP); - (void) sigprocmask(SIG_BLOCK, &mask, &oldmask); + (void) sigemptyset(&set); + (void) sigaddset(&set, SIGINT); + (void) sigaddset(&set, SIGTSTP); + (void) sigprocmask(SIG_BLOCK, &set, &oset); #else - oldmask = sigblock(sigmask(SIGINT)|sigmask(SIGTSTP)); -#endif + omask = sigblock(sigmask(SIGINT)|sigmask(SIGTSTP)); +#endif /* POSIX_SIGNALS */ /* * open /dev/tty for reading/writing if possible or use @@ -138,29 +138,31 @@ tgetpass(prompt, timeout) (void) write(output, prompt, strlen(prompt) + 1); /* - * turn off echo + * turn off echo unless asked to keep it on */ + if (echo_off) { #ifdef HAVE_TERMIOS_H - (void) tcgetattr(input, &term); - if ((echo = (term.c_lflag & ECHO))) { - term.c_lflag &= ~ECHO; - (void) tcsetattr(input, TCSAFLUSH|TCSASOFT, &term); - } + (void) tcgetattr(input, &term); + if ((echo_off = (term.c_lflag & ECHO))) { + term.c_lflag &= ~ECHO; + (void) tcsetattr(input, TCSAFLUSH|TCSASOFT, &term); + } #else #ifdef HAVE_TERMIO_H - (void) ioctl(input, TCGETA, &term); - if ((echo = (term.c_lflag & ECHO))) { - term.c_lflag &= ~ECHO; - (void) ioctl(input, TCSETA, &term); - } + (void) ioctl(input, TCGETA, &term); + if ((echo_off = (term.c_lflag & ECHO))) { + term.c_lflag &= ~ECHO; + (void) ioctl(input, TCSETA, &term); + } #else - (void) ioctl(input, TIOCGETP, &ttyb); - if ((echo = (ttyb.sg_flags & ECHO))) { - ttyb.sg_flags &= ~ECHO; - (void) ioctl(input, TIOCSETP, &ttyb); - } + (void) ioctl(input, TIOCGETP, &ttyb); + if ((echo_off = (ttyb.sg_flags & ECHO))) { + ttyb.sg_flags &= ~ECHO; + (void) ioctl(input, TIOCSETP, &ttyb); + } #endif /* HAVE_TERMIO_H */ #endif /* HAVE_TERMIOS_H */ + } /* * Timeout of <= 0 means no timeout @@ -198,20 +200,20 @@ tgetpass(prompt, timeout) } } - /* turn on echo */ + /* turn on echo if we turned it off above */ #ifdef HAVE_TERMIOS_H - if (echo) { + if (echo_off) { term.c_lflag |= ECHO; (void) tcsetattr(input, TCSAFLUSH|TCSASOFT, &term); } #else #ifdef HAVE_TERMIO_H - if (echo) { + if (echo_off) { term.c_lflag |= ECHO; (void) ioctl(input, TCSETA, &term); } #else - if (echo) { + if (echo_off) { ttyb.sg_flags |= ECHO; (void) ioctl(input, TIOCSETP, &ttyb); } @@ -219,14 +221,15 @@ tgetpass(prompt, timeout) #endif /* HAVE_TERMIOS_H */ /* print a newline since echo is turned off */ - (void) write(output, "\n", 1); + if (echo_off) + (void) write(output, "\n", 1); /* restore old signal mask */ #ifdef POSIX_SIGNALS - (void) sigprocmask(SIG_SETMASK, &oldmask, NULL); + (void) sigprocmask(SIG_SETMASK, &oset, NULL); #else - (void) sigsetmask(oldmask); -#endif + (void) sigsetmask(omask); +#endif /* POSIX_SIGNALS */ /* close /dev/tty if that's what we opened */ if (input != STDIN_FILENO)