version 1.1.1.2, 2012/05/29 12:26:49
|
version 1.1.1.4, 2013/10/14 07:56:35
|
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 getpass_error; |
|
|
|
#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 int getpass_error; |
static pam_handle_t *pamh; |
static pam_handle_t *pamh; |
|
|
int |
int |
Line 87 sudo_pam_init(struct passwd *pw, sudo_auth *auth)
|
Line 90 sudo_pam_init(struct passwd *pw, sudo_auth *auth)
|
debug_decl(sudo_pam_init, SUDO_DEBUG_AUTH) |
debug_decl(sudo_pam_init, SUDO_DEBUG_AUTH) |
|
|
/* Initial PAM setup */ |
/* Initial PAM setup */ |
if (auth != NULL) | auth->data = (void *) &pam_status; |
auth->data = (void *) &pam_status; | |
pam_conv.conv = converse; |
pam_conv.conv = converse; |
#ifdef HAVE_PAM_LOGIN | pam_status = pam_start(ISSET(sudo_mode, MODE_LOGIN_SHELL) ? |
if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) | def_pam_login_service : def_pam_service, pw->pw_name, &pam_conv, &pamh); |
pam_status = pam_start("sudo-i", pw->pw_name, &pam_conv, &pamh); | |
else | |
#endif | |
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_MAIL, _("unable to initialize PAM")); | log_warning(USE_ERRNO|NO_MAIL, N_("unable to initialize PAM")); |
debug_return_int(AUTH_FATAL); |
debug_return_int(AUTH_FATAL); |
} |
} |
|
|
Line 120 sudo_pam_init(struct passwd *pw, sudo_auth *auth)
|
Line 118 sudo_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); |
|
|
|
/* |
|
* If PAM session and setcred support is disabled we don't |
|
* need to keep a sudo process around to close the session. |
|
*/ |
|
if (!def_pam_session && !def_pam_setcred) |
|
auth->end_session = NULL; |
|
|
debug_return_int(AUTH_SUCCESS); |
debug_return_int(AUTH_SUCCESS); |
} |
} |
|
|
Line 141 sudo_pam_verify(struct passwd *pw, char *prompt, sudo_
|
Line 146 sudo_pam_verify(struct passwd *pw, char *prompt, sudo_
|
case PAM_SUCCESS: |
case PAM_SUCCESS: |
debug_return_int(AUTH_SUCCESS); |
debug_return_int(AUTH_SUCCESS); |
case PAM_AUTH_ERR: |
case PAM_AUTH_ERR: |
log_error(NO_MAIL, _("account validation failure, " | log_warning(NO_MAIL, N_("account validation failure, " |
"is your account locked?")); |
"is your account locked?")); |
debug_return_int(AUTH_FATAL); |
debug_return_int(AUTH_FATAL); |
case PAM_NEW_AUTHTOK_REQD: |
case PAM_NEW_AUTHTOK_REQD: |
log_error(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) |
debug_return_int(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_MAIL, _("pam_chauthtok: %s"), s); | log_warning(NO_MAIL, |
| N_("unable to change expired password: %s"), s); |
| } |
debug_return_int(AUTH_FAILURE); |
debug_return_int(AUTH_FAILURE); |
case PAM_AUTHTOK_EXPIRED: |
case PAM_AUTHTOK_EXPIRED: |
log_error(NO_MAIL, | log_warning(NO_MAIL, |
_("Password expired, contact your system administrator")); | N_("Password expired, contact your system administrator")); |
debug_return_int(AUTH_FATAL); |
debug_return_int(AUTH_FATAL); |
case PAM_ACCT_EXPIRED: |
case PAM_ACCT_EXPIRED: |
log_error(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")); |
debug_return_int(AUTH_FATAL); |
debug_return_int(AUTH_FATAL); |
} |
} |
Line 176 sudo_pam_verify(struct passwd *pw, char *prompt, sudo_
|
Line 183 sudo_pam_verify(struct passwd *pw, char *prompt, sudo_
|
case PAM_PERM_DENIED: |
case PAM_PERM_DENIED: |
debug_return_int(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_MAIL, _("pam_authenticate: %s"), s); | log_warning(NO_MAIL, N_("PAM authentication error: %s"), s); |
debug_return_int(AUTH_FATAL); |
debug_return_int(AUTH_FATAL); |
} |
} |
} |
} |
Line 188 sudo_pam_cleanup(struct passwd *pw, sudo_auth *auth)
|
Line 195 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) |
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 sudo_pam_end_session() */ |
if (*pam_status == AUTH_SUCCESS) | if (*pam_status != PAM_SUCCESS || auth->end_session == NULL) { |
debug_return_int(AUTH_SUCCESS); | *pam_status = pam_end(pamh, *pam_status | PAM_DATA_SILENT); |
| pamh = NULL; |
*pam_status = pam_end(pamh, *pam_status | PAM_DATA_SILENT); | } |
pamh = NULL; | |
debug_return_int(*pam_status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE); |
debug_return_int(*pam_status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE); |
} |
} |
|
|
int |
int |
sudo_pam_begin_session(struct passwd *pw, char **user_envp[], sudo_auth *auth) |
sudo_pam_begin_session(struct passwd *pw, char **user_envp[], sudo_auth *auth) |
{ |
{ |
int status = PAM_SUCCESS; | int status = AUTH_SUCCESS; |
| int *pam_status = (int *) auth->data; |
debug_decl(sudo_pam_begin_session, SUDO_DEBUG_AUTH) |
debug_decl(sudo_pam_begin_session, SUDO_DEBUG_AUTH) |
|
|
/* |
/* |
Line 224 sudo_pam_begin_session(struct passwd *pw, char **user_
|
Line 231 sudo_pam_begin_session(struct passwd *pw, char **user_
|
|
|
/* |
/* |
* Set credentials (may include resource limits, device ownership, etc). |
* Set credentials (may include resource limits, device ownership, etc). |
* We don't check the return value here because in Linux-PAM 0.75 | * We don't worry about a failure from pam_setcred() since with |
* it returns the last saved return code, not the return code | * stacked PAM auth modules a failure from one module may override |
* for the setcred module. Because we haven't called pam_authenticate(), | * PAM_SUCCESS from another. For example, given a non-local user, |
* this is not set and so pam_setcred() returns PAM_PERM_DENIED. | * pam_unix will fail but pam_ldap or pam_sss may succeed, but if |
* We can't call pam_acct_mgmt() with Linux-PAM for a similar reason. | * pam_unix is first in the stack, pam_setcred() will fail. |
*/ |
*/ |
(void) pam_setcred(pamh, PAM_ESTABLISH_CRED); | if (def_pam_setcred) |
| (void) pam_setcred(pamh, PAM_ESTABLISH_CRED); |
|
|
|
if (def_pam_session) { |
|
*pam_status = pam_open_session(pamh, 0); |
|
if (*pam_status != PAM_SUCCESS) { |
|
(void) pam_end(pamh, *pam_status | PAM_DATA_SILENT); |
|
pamh = NULL; |
|
status = AUTH_FAILURE; |
|
} |
|
} |
|
|
#ifdef HAVE_PAM_GETENVLIST |
#ifdef HAVE_PAM_GETENVLIST |
/* |
/* |
* Update environment based on what is stored in pamh. |
* Update environment based on what is stored in pamh. |
Line 241 sudo_pam_begin_session(struct passwd *pw, char **user_
|
Line 258 sudo_pam_begin_session(struct passwd *pw, char **user_
|
if (user_envp != NULL) { |
if (user_envp != NULL) { |
char **pam_envp = pam_getenvlist(pamh); |
char **pam_envp = pam_getenvlist(pamh); |
if (pam_envp != NULL) { |
if (pam_envp != NULL) { |
/* Merge pam env with user env but do not overwrite. */ | /* Merge pam env with user env. */ |
env_init(*user_envp); |
env_init(*user_envp); |
env_merge(pam_envp, false); | env_merge(pam_envp); |
*user_envp = env_get(); |
*user_envp = env_get(); |
env_init(NULL); |
env_init(NULL); |
efree(pam_envp); |
efree(pam_envp); |
Line 252 sudo_pam_begin_session(struct passwd *pw, char **user_
|
Line 269 sudo_pam_begin_session(struct passwd *pw, char **user_
|
} |
} |
#endif /* HAVE_PAM_GETENVLIST */ |
#endif /* HAVE_PAM_GETENVLIST */ |
|
|
#ifndef NO_PAM_SESSION |
|
status = pam_open_session(pamh, 0); |
|
if (status != PAM_SUCCESS) { |
|
(void) pam_end(pamh, status | PAM_DATA_SILENT); |
|
pamh = NULL; |
|
} |
|
#endif |
|
|
|
done: |
done: |
debug_return_int(status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE); | debug_return_int(status); |
} |
} |
|
|
int |
int |
sudo_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 = AUTH_SUCCESS; |
debug_decl(sudo_pam_end_session, SUDO_DEBUG_AUTH) |
debug_decl(sudo_pam_end_session, SUDO_DEBUG_AUTH) |
|
|
if (pamh != NULL) { |
if (pamh != NULL) { |
Line 277 sudo_pam_end_session(struct passwd *pw, sudo_auth *aut
|
Line 286 sudo_pam_end_session(struct passwd *pw, sudo_auth *aut
|
* XXX - still needed now that session init is in parent? |
* 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); |
#ifndef NO_PAM_SESSION | if (def_pam_session) |
(void) pam_close_session(pamh, PAM_SILENT); | (void) pam_close_session(pamh, PAM_SILENT); |
#endif | if (def_pam_setcred) |
(void) pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT); | (void) pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT); |
status = pam_end(pamh, PAM_SUCCESS | PAM_DATA_SILENT); | if (pam_end(pamh, PAM_SUCCESS | PAM_DATA_SILENT) != PAM_SUCCESS) |
| status = AUTH_FAILURE; |
pamh = NULL; |
pamh = NULL; |
} |
} |
|
|
debug_return_int(status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE); | debug_return_int(status); |
} |
} |
|
|
/* |
/* |
Line 306 converse(int num_msg, PAM_CONST struct pam_message **m
|
Line 316 converse(int num_msg, PAM_CONST struct pam_message **m
|
|
|
if ((*response = malloc(num_msg * sizeof(struct pam_response))) == NULL) |
if ((*response = malloc(num_msg * sizeof(struct pam_response))) == NULL) |
debug_return_int(PAM_SYSTEM_ERR); |
debug_return_int(PAM_SYSTEM_ERR); |
zero_bytes(*response, num_msg * sizeof(struct pam_response)); | memset(*response, 0, 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++) { |
type = SUDO_CONV_PROMPT_ECHO_OFF; |
type = SUDO_CONV_PROMPT_ECHO_OFF; |
Line 321 converse(int num_msg, PAM_CONST struct pam_message **m
|
Line 331 converse(int num_msg, PAM_CONST struct pam_message **m
|
if (getpass_error) |
if (getpass_error) |
goto done; |
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 329 converse(int num_msg, PAM_CONST struct pam_message **m
|
Line 339 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 350 converse(int num_msg, PAM_CONST struct pam_message **m
|
Line 360 converse(int num_msg, PAM_CONST struct pam_message **m
|
#endif |
#endif |
} |
} |
pr->resp = estrdup(pass); |
pr->resp = estrdup(pass); |
zero_bytes(pass, strlen(pass)); | memset_s(pass, SUDO_CONV_REPL_MAX, 0, strlen(pass)); |
break; |
break; |
case PAM_TEXT_INFO: |
case PAM_TEXT_INFO: |
if (pm->msg) |
if (pm->msg) |
Line 374 done:
|
Line 384 done:
|
/* 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)); | memset_s(pr->resp, SUDO_CONV_REPL_MAX, 0, 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); |
free(*response); |
*response = NULL; |
*response = NULL; |
} |
} |