version 1.1, 2012/02/21 16:23:02
|
version 1.1.1.3, 2013/07/22 10:46:12
|
Line 1
|
Line 1
|
/* |
/* |
* Copyright (c) 1999-2005, 2007-2011 Todd C. Miller <Todd.Miller@courtesan.com> | * Copyright (c) 1999-2005, 2007-2013 Todd C. Miller <Todd.Miller@courtesan.com> |
* |
* |
* Permission to use, copy, modify, and distribute this software for any |
* Permission to use, copy, modify, and distribute this software for any |
* purpose with or without fee is hereby granted, provided that the above |
* purpose with or without fee is hereby granted, provided that the above |
Line 21
|
Line 21
|
#include <config.h> |
#include <config.h> |
|
|
#include <sys/types.h> |
#include <sys/types.h> |
#include <sys/param.h> |
|
#include <stdio.h> |
#include <stdio.h> |
#ifdef STDC_HEADERS |
#ifdef STDC_HEADERS |
# include <stdlib.h> |
# include <stdlib.h> |
Line 57
|
Line 56
|
# endif |
# endif |
#endif |
#endif |
|
|
|
/* We don't want to translate the strings in the calls to dgt(). */ |
|
#ifdef PAM_TEXT_DOMAIN |
|
# define dgt(d, t) dgettext(d, t) |
|
#endif |
|
|
#include "sudoers.h" |
#include "sudoers.h" |
#include "sudo_auth.h" |
#include "sudo_auth.h" |
|
|
Line 68
|
Line 72
|
# define PAM_CONST |
# define PAM_CONST |
#endif |
#endif |
|
|
static int converse(int, PAM_CONST struct pam_message **, |
|
struct pam_response **, void *); |
|
static char *def_prompt = "Password:"; |
|
static int gotintr; |
|
|
|
#ifndef PAM_DATA_SILENT |
#ifndef PAM_DATA_SILENT |
#define PAM_DATA_SILENT 0 |
#define PAM_DATA_SILENT 0 |
#endif |
#endif |
|
|
|
static int converse(int, PAM_CONST struct pam_message **, |
|
struct pam_response **, void *); |
|
static char *def_prompt = "Password:"; |
|
static bool sudo_pam_cred_established; |
|
static bool sudo_pam_authenticated; |
|
static int getpass_error; |
static pam_handle_t *pamh; |
static pam_handle_t *pamh; |
|
|
int |
int |
pam_init(struct passwd *pw, sudo_auth *auth) | sudo_pam_init(struct passwd *pw, sudo_auth *auth) |
{ |
{ |
static struct pam_conv pam_conv; |
static struct pam_conv pam_conv; |
static int pam_status; |
static int pam_status; |
|
debug_decl(sudo_pam_init, SUDO_DEBUG_AUTH) |
|
|
/* Initial PAM setup */ |
/* Initial PAM setup */ |
if (auth != NULL) |
if (auth != NULL) |
Line 96 pam_init(struct passwd *pw, sudo_auth *auth)
|
Line 102 pam_init(struct passwd *pw, sudo_auth *auth)
|
#endif |
#endif |
pam_status = pam_start("sudo", pw->pw_name, &pam_conv, &pamh); |
pam_status = pam_start("sudo", pw->pw_name, &pam_conv, &pamh); |
if (pam_status != PAM_SUCCESS) { |
if (pam_status != PAM_SUCCESS) { |
log_error(USE_ERRNO|NO_EXIT|NO_MAIL, _("unable to initialize PAM")); | log_warning(USE_ERRNO|NO_MAIL, N_("unable to initialize PAM")); |
return AUTH_FATAL; | debug_return_int(AUTH_FATAL); |
} |
} |
|
|
/* |
/* |
Line 119 pam_init(struct passwd *pw, sudo_auth *auth)
|
Line 125 pam_init(struct passwd *pw, sudo_auth *auth)
|
else |
else |
(void) pam_set_item(pamh, PAM_TTY, user_ttypath); |
(void) pam_set_item(pamh, PAM_TTY, user_ttypath); |
|
|
return AUTH_SUCCESS; | debug_return_int(AUTH_SUCCESS); |
} |
} |
|
|
int |
int |
pam_verify(struct passwd *pw, char *prompt, sudo_auth *auth) | sudo_pam_verify(struct passwd *pw, char *prompt, sudo_auth *auth) |
{ |
{ |
const char *s; |
const char *s; |
int *pam_status = (int *) auth->data; |
int *pam_status = (int *) auth->data; |
|
debug_decl(sudo_pam_verify, SUDO_DEBUG_AUTH) |
|
|
def_prompt = prompt; /* for converse */ |
def_prompt = prompt; /* for converse */ |
|
|
Line 137 pam_verify(struct passwd *pw, char *prompt, sudo_auth
|
Line 144 pam_verify(struct passwd *pw, char *prompt, sudo_auth
|
*pam_status = pam_acct_mgmt(pamh, PAM_SILENT); |
*pam_status = pam_acct_mgmt(pamh, PAM_SILENT); |
switch (*pam_status) { |
switch (*pam_status) { |
case PAM_SUCCESS: |
case PAM_SUCCESS: |
return AUTH_SUCCESS; | sudo_pam_authenticated = true; |
| debug_return_int(AUTH_SUCCESS); |
case PAM_AUTH_ERR: |
case PAM_AUTH_ERR: |
log_error(NO_EXIT|NO_MAIL, _("account validation failure, " | log_warning(NO_MAIL, N_("account validation failure, " |
"is your account locked?")); |
"is your account locked?")); |
return AUTH_FATAL; | debug_return_int(AUTH_FATAL); |
case PAM_NEW_AUTHTOK_REQD: |
case PAM_NEW_AUTHTOK_REQD: |
log_error(NO_EXIT|NO_MAIL, _("Account or password is " | log_warning(NO_MAIL, N_("Account or password is " |
"expired, reset your password and try again")); |
"expired, reset your password and try again")); |
*pam_status = pam_chauthtok(pamh, |
*pam_status = pam_chauthtok(pamh, |
PAM_CHANGE_EXPIRED_AUTHTOK); |
PAM_CHANGE_EXPIRED_AUTHTOK); |
if (*pam_status == PAM_SUCCESS) |
if (*pam_status == PAM_SUCCESS) |
return AUTH_SUCCESS; | debug_return_int(AUTH_SUCCESS); |
if ((s = pam_strerror(pamh, *pam_status))) | if ((s = pam_strerror(pamh, *pam_status)) != NULL) { |
log_error(NO_EXIT|NO_MAIL, _("pam_chauthtok: %s"), s); | log_warning(NO_MAIL, |
return AUTH_FAILURE; | N_("unable to change expired password: %s"), s); |
| } |
| debug_return_int(AUTH_FAILURE); |
case PAM_AUTHTOK_EXPIRED: |
case PAM_AUTHTOK_EXPIRED: |
log_error(NO_EXIT|NO_MAIL, | log_warning(NO_MAIL, |
_("Password expired, contact your system administrator")); | N_("Password expired, contact your system administrator")); |
return AUTH_FATAL; | debug_return_int(AUTH_FATAL); |
case PAM_ACCT_EXPIRED: |
case PAM_ACCT_EXPIRED: |
log_error(NO_EXIT|NO_MAIL, | log_warning(NO_MAIL, |
_("Account expired or PAM config lacks an \"account\" " | N_("Account expired or PAM config lacks an \"account\" " |
"section for sudo, contact your system administrator")); |
"section for sudo, contact your system administrator")); |
return AUTH_FATAL; | debug_return_int(AUTH_FATAL); |
} |
} |
/* FALLTHROUGH */ |
/* FALLTHROUGH */ |
case PAM_AUTH_ERR: |
case PAM_AUTH_ERR: |
if (gotintr) { | case PAM_AUTHINFO_UNAVAIL: |
| if (getpass_error) { |
/* error or ^C from tgetpass() */ |
/* error or ^C from tgetpass() */ |
return AUTH_INTR; | debug_return_int(AUTH_INTR); |
} |
} |
|
/* FALLTHROUGH */ |
case PAM_MAXTRIES: |
case PAM_MAXTRIES: |
case PAM_PERM_DENIED: |
case PAM_PERM_DENIED: |
return AUTH_FAILURE; | debug_return_int(AUTH_FAILURE); |
default: |
default: |
if ((s = pam_strerror(pamh, *pam_status))) | if ((s = pam_strerror(pamh, *pam_status)) != NULL) |
log_error(NO_EXIT|NO_MAIL, _("pam_authenticate: %s"), s); | log_warning(NO_MAIL, N_("PAM authentication error: %s"), s); |
return AUTH_FATAL; | debug_return_int(AUTH_FATAL); |
} |
} |
} |
} |
|
|
int |
int |
pam_cleanup(struct passwd *pw, sudo_auth *auth) | sudo_pam_cleanup(struct passwd *pw, sudo_auth *auth) |
{ |
{ |
int *pam_status = (int *) auth->data; |
int *pam_status = (int *) auth->data; |
|
debug_decl(sudo_pam_cleanup, SUDO_DEBUG_AUTH) |
|
|
/* If successful, we can't close the session until pam_end_session() */ |
/* If successful, we can't close the session until pam_end_session() */ |
if (*pam_status == AUTH_SUCCESS) |
if (*pam_status == AUTH_SUCCESS) |
return AUTH_SUCCESS; | debug_return_int(AUTH_SUCCESS); |
|
|
*pam_status = pam_end(pamh, *pam_status | PAM_DATA_SILENT); |
*pam_status = pam_end(pamh, *pam_status | PAM_DATA_SILENT); |
pamh = NULL; |
pamh = NULL; |
return *pam_status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE; | debug_return_int(*pam_status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE); |
} |
} |
|
|
int |
int |
pam_begin_session(struct passwd *pw, sudo_auth *auth) | sudo_pam_begin_session(struct passwd *pw, char **user_envp[], sudo_auth *auth) |
{ |
{ |
int status = PAM_SUCCESS; |
int status = PAM_SUCCESS; |
|
debug_decl(sudo_pam_begin_session, SUDO_DEBUG_AUTH) |
|
|
/* |
/* |
* If there is no valid user we cannot open a PAM session. |
* If there is no valid user we cannot open a PAM session. |
Line 224 pam_begin_session(struct passwd *pw, sudo_auth *auth)
|
Line 238 pam_begin_session(struct passwd *pw, sudo_auth *auth)
|
* this is not set and so pam_setcred() returns PAM_PERM_DENIED. |
* this is not set and so pam_setcred() returns PAM_PERM_DENIED. |
* We can't call pam_acct_mgmt() with Linux-PAM for a similar reason. |
* We can't call pam_acct_mgmt() with Linux-PAM for a similar reason. |
*/ |
*/ |
(void) pam_setcred(pamh, PAM_ESTABLISH_CRED); | status = pam_setcred(pamh, PAM_ESTABLISH_CRED); |
| if (status == PAM_SUCCESS) { |
| sudo_pam_cred_established = true; |
| } else if (sudo_pam_authenticated) { |
| const char *s = pam_strerror(pamh, status); |
| if (s != NULL) |
| log_warning(NO_MAIL, N_("unable to establish credentials: %s"), s); |
| goto done; |
| } |
|
|
#ifndef NO_PAM_SESSION | #ifdef HAVE_PAM_GETENVLIST |
status = pam_open_session(pamh, 0); | /* |
if (status != PAM_SUCCESS) { | * Update environment based on what is stored in pamh. |
(void) pam_end(pamh, status | PAM_DATA_SILENT); | * If no authentication is done we will only have environment |
pamh = NULL; | * variables if pam_env is called via session. |
| */ |
| if (user_envp != NULL) { |
| char **pam_envp = pam_getenvlist(pamh); |
| if (pam_envp != NULL) { |
| /* Merge pam env with user env but do not overwrite. */ |
| env_init(*user_envp); |
| env_merge(pam_envp, false); |
| *user_envp = env_get(); |
| env_init(NULL); |
| efree(pam_envp); |
| /* XXX - we leak any duplicates that were in pam_envp */ |
| } |
} |
} |
#endif | #endif /* HAVE_PAM_GETENVLIST */ |
|
|
|
if (def_pam_session) { |
|
status = pam_open_session(pamh, 0); |
|
if (status != PAM_SUCCESS) { |
|
(void) pam_end(pamh, status | PAM_DATA_SILENT); |
|
pamh = NULL; |
|
} |
|
} |
|
|
done: |
done: |
return status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE; | debug_return_int(status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE); |
} |
} |
|
|
int |
int |
pam_end_session(struct passwd *pw, sudo_auth *auth) | sudo_pam_end_session(struct passwd *pw, sudo_auth *auth) |
{ |
{ |
int status = PAM_SUCCESS; |
int status = PAM_SUCCESS; |
|
debug_decl(sudo_pam_end_session, SUDO_DEBUG_AUTH) |
|
|
if (pamh != NULL) { |
if (pamh != NULL) { |
#ifndef NO_PAM_SESSION |
|
/* |
/* |
* Update PAM_USER to reference the user we are running the command |
* Update PAM_USER to reference the user we are running the command |
* as to match the call to pam_open_session(). | * as, as opposed to the user we authenticated as. |
| * XXX - still needed now that session init is in parent? |
*/ |
*/ |
(void) pam_set_item(pamh, PAM_USER, pw->pw_name); |
(void) pam_set_item(pamh, PAM_USER, pw->pw_name); |
(void) pam_close_session(pamh, PAM_SILENT); | if (def_pam_session) |
#endif | (void) pam_close_session(pamh, PAM_SILENT); |
| if (sudo_pam_cred_established) |
| (void) pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT); |
status = pam_end(pamh, PAM_SUCCESS | PAM_DATA_SILENT); |
status = pam_end(pamh, PAM_SUCCESS | PAM_DATA_SILENT); |
pamh = NULL; |
pamh = NULL; |
} |
} |
|
|
return status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE; | debug_return_int(status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE); |
} |
} |
|
|
/* |
/* |
Line 272 converse(int num_msg, PAM_CONST struct pam_message **m
|
Line 317 converse(int num_msg, PAM_CONST struct pam_message **m
|
const char *prompt; |
const char *prompt; |
char *pass; |
char *pass; |
int n, type, std_prompt; |
int n, type, std_prompt; |
|
int ret = PAM_AUTH_ERR; |
|
debug_decl(converse, SUDO_DEBUG_AUTH) |
|
|
if ((*response = malloc(num_msg * sizeof(struct pam_response))) == NULL) |
if ((*response = malloc(num_msg * sizeof(struct pam_response))) == NULL) |
return PAM_SYSTEM_ERR; | debug_return_int(PAM_SYSTEM_ERR); |
zero_bytes(*response, num_msg * sizeof(struct pam_response)); |
zero_bytes(*response, num_msg * sizeof(struct pam_response)); |
|
|
for (pr = *response, pm = *msg, n = num_msg; n--; pr++, pm++) { |
for (pr = *response, pm = *msg, n = num_msg; n--; pr++, pm++) { |
Line 282 converse(int num_msg, PAM_CONST struct pam_message **m
|
Line 329 converse(int num_msg, PAM_CONST struct pam_message **m
|
switch (pm->msg_style) { |
switch (pm->msg_style) { |
case PAM_PROMPT_ECHO_ON: |
case PAM_PROMPT_ECHO_ON: |
type = SUDO_CONV_PROMPT_ECHO_ON; |
type = SUDO_CONV_PROMPT_ECHO_ON; |
|
/* FALLTHROUGH */ |
case PAM_PROMPT_ECHO_OFF: |
case PAM_PROMPT_ECHO_OFF: |
prompt = def_prompt; |
prompt = def_prompt; |
|
|
/* Error out if the last password read was interrupted. */ |
/* Error out if the last password read was interrupted. */ |
if (gotintr) | if (getpass_error) |
goto err; | goto done; |
|
|
/* Is the sudo prompt standard? (If so, we'l just use PAM's) */ | /* Is the sudo prompt standard? (If so, we'll just use PAM's) */ |
std_prompt = strncmp(def_prompt, "Password:", 9) == 0 && |
std_prompt = strncmp(def_prompt, "Password:", 9) == 0 && |
(def_prompt[9] == '\0' || |
(def_prompt[9] == '\0' || |
(def_prompt[9] == ' ' && def_prompt[10] == '\0')); |
(def_prompt[9] == ' ' && def_prompt[10] == '\0')); |
Line 297 converse(int num_msg, PAM_CONST struct pam_message **m
|
Line 345 converse(int num_msg, PAM_CONST struct pam_message **m
|
/* Only override PAM prompt if it matches /^Password: ?/ */ |
/* Only override PAM prompt if it matches /^Password: ?/ */ |
#if defined(PAM_TEXT_DOMAIN) && defined(HAVE_LIBINTL_H) |
#if defined(PAM_TEXT_DOMAIN) && defined(HAVE_LIBINTL_H) |
if (!def_passprompt_override && (std_prompt || |
if (!def_passprompt_override && (std_prompt || |
(strcmp(pm->msg, dgettext(PAM_TEXT_DOMAIN, "Password: ")) && | (strcmp(pm->msg, dgt(PAM_TEXT_DOMAIN, "Password: ")) && |
strcmp(pm->msg, dgettext(PAM_TEXT_DOMAIN, "Password:"))))) | strcmp(pm->msg, dgt(PAM_TEXT_DOMAIN, "Password:"))))) |
prompt = pm->msg; |
prompt = pm->msg; |
#else |
#else |
if (!def_passprompt_override && (std_prompt || |
if (!def_passprompt_override && (std_prompt || |
Line 309 converse(int num_msg, PAM_CONST struct pam_message **m
|
Line 357 converse(int num_msg, PAM_CONST struct pam_message **m
|
/* Read the password unless interrupted. */ |
/* Read the password unless interrupted. */ |
pass = auth_getpass(prompt, def_passwd_timeout * 60, type); |
pass = auth_getpass(prompt, def_passwd_timeout * 60, type); |
if (pass == NULL) { |
if (pass == NULL) { |
/* We got ^C instead of a password; abort quickly. */ | /* Error (or ^C) reading password, don't try again. */ |
if (errno == EINTR) | getpass_error = 1; |
gotintr = 1; | #if (defined(__darwin__) || defined(__APPLE__)) && !defined(OPENPAM_VERSION) |
#if defined(__darwin__) || defined(__APPLE__) | |
pass = ""; |
pass = ""; |
#else |
#else |
goto err; | goto done; |
#endif |
#endif |
} |
} |
pr->resp = estrdup(pass); |
pr->resp = estrdup(pass); |
Line 332 converse(int num_msg, PAM_CONST struct pam_message **m
|
Line 379 converse(int num_msg, PAM_CONST struct pam_message **m
|
} |
} |
break; |
break; |
default: |
default: |
goto err; | ret = PAM_CONV_ERR; |
| goto done; |
} |
} |
} |
} |
|
ret = PAM_SUCCESS; |
|
|
return PAM_SUCCESS; | done: |
| if (ret != PAM_SUCCESS) { |
err: | /* Zero and free allocated memory and return an error. */ |
/* Zero and free allocated memory and return an error. */ | for (pr = *response, n = num_msg; n--; pr++) { |
for (pr = *response, n = num_msg; n--; pr++) { | if (pr->resp != NULL) { |
if (pr->resp != NULL) { | zero_bytes(pr->resp, strlen(pr->resp)); |
zero_bytes(pr->resp, strlen(pr->resp)); | free(pr->resp); |
free(pr->resp); | pr->resp = NULL; |
pr->resp = NULL; | } |
} |
} |
|
zero_bytes(*response, num_msg * sizeof(struct pam_response)); |
|
free(*response); |
|
*response = NULL; |
} |
} |
zero_bytes(*response, num_msg * sizeof(struct pam_response)); | debug_return_int(ret); |
free(*response); | |
*response = NULL; | |
return gotintr ? PAM_AUTH_ERR : PAM_CONV_ERR; | |
} |
} |