Annotation of embedaddon/sudo/plugins/sudoers/auth/pam.c, revision 1.1.1.3
1.1 misho 1: /*
1.1.1.3 ! misho 2: * Copyright (c) 1999-2005, 2007-2013 Todd C. Miller <Todd.Miller@courtesan.com>
1.1 misho 3: *
4: * Permission to use, copy, modify, and distribute this software for any
5: * purpose with or without fee is hereby granted, provided that the above
6: * copyright notice and this permission notice appear in all copies.
7: *
8: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15: *
16: * Sponsored in part by the Defense Advanced Research Projects
17: * Agency (DARPA) and Air Force Research Laboratory, Air Force
18: * Materiel Command, USAF, under agreement number F39502-99-1-0512.
19: */
20:
21: #include <config.h>
22:
23: #include <sys/types.h>
24: #include <stdio.h>
25: #ifdef STDC_HEADERS
26: # include <stdlib.h>
27: # include <stddef.h>
28: #else
29: # ifdef HAVE_STDLIB_H
30: # include <stdlib.h>
31: # endif
32: #endif /* STDC_HEADERS */
33: #ifdef HAVE_STRING_H
34: # include <string.h>
35: #endif /* HAVE_STRING_H */
36: #ifdef HAVE_STRINGS_H
37: # include <strings.h>
38: #endif /* HAVE_STRINGS_H */
39: #ifdef HAVE_UNISTD_H
40: # include <unistd.h>
41: #endif /* HAVE_UNISTD_H */
42: #include <pwd.h>
43: #include <errno.h>
44:
45: #ifdef HAVE_PAM_PAM_APPL_H
46: # include <pam/pam_appl.h>
47: #else
48: # include <security/pam_appl.h>
49: #endif
50:
51: #ifdef HAVE_LIBINTL_H
52: # if defined(__LINUX_PAM__)
53: # define PAM_TEXT_DOMAIN "Linux-PAM"
54: # elif defined(__sun__)
55: # define PAM_TEXT_DOMAIN "SUNW_OST_SYSOSPAM"
56: # endif
57: #endif
58:
1.1.1.3 ! misho 59: /* We don't want to translate the strings in the calls to dgt(). */
! 60: #ifdef PAM_TEXT_DOMAIN
! 61: # define dgt(d, t) dgettext(d, t)
! 62: #endif
! 63:
1.1 misho 64: #include "sudoers.h"
65: #include "sudo_auth.h"
66:
67: /* Only OpenPAM and Linux PAM use const qualifiers. */
68: #if defined(_OPENPAM) || defined(OPENPAM_VERSION) || \
69: defined(__LIBPAM_VERSION) || defined(__LINUX_PAM__)
70: # define PAM_CONST const
71: #else
72: # define PAM_CONST
73: #endif
74:
75: #ifndef PAM_DATA_SILENT
76: #define PAM_DATA_SILENT 0
77: #endif
78:
1.1.1.3 ! misho 79: static int converse(int, PAM_CONST struct pam_message **,
! 80: struct pam_response **, void *);
! 81: static char *def_prompt = "Password:";
! 82: static bool sudo_pam_cred_established;
! 83: static bool sudo_pam_authenticated;
! 84: static int getpass_error;
1.1 misho 85: static pam_handle_t *pamh;
86:
87: int
1.1.1.2 misho 88: sudo_pam_init(struct passwd *pw, sudo_auth *auth)
1.1 misho 89: {
90: static struct pam_conv pam_conv;
91: static int pam_status;
1.1.1.2 misho 92: debug_decl(sudo_pam_init, SUDO_DEBUG_AUTH)
1.1 misho 93:
94: /* Initial PAM setup */
95: if (auth != NULL)
96: auth->data = (void *) &pam_status;
97: pam_conv.conv = converse;
98: #ifdef HAVE_PAM_LOGIN
99: if (ISSET(sudo_mode, MODE_LOGIN_SHELL))
100: pam_status = pam_start("sudo-i", pw->pw_name, &pam_conv, &pamh);
101: else
102: #endif
103: pam_status = pam_start("sudo", pw->pw_name, &pam_conv, &pamh);
104: if (pam_status != PAM_SUCCESS) {
1.1.1.3 ! misho 105: log_warning(USE_ERRNO|NO_MAIL, N_("unable to initialize PAM"));
1.1.1.2 misho 106: debug_return_int(AUTH_FATAL);
1.1 misho 107: }
108:
109: /*
110: * Set PAM_RUSER to the invoking user (the "from" user).
111: * We set PAM_RHOST to avoid a bug in Solaris 7 and below.
112: */
113: (void) pam_set_item(pamh, PAM_RUSER, user_name);
114: #ifdef __sun__
115: (void) pam_set_item(pamh, PAM_RHOST, user_host);
116: #endif
117:
118: /*
119: * Some versions of pam_lastlog have a bug that
120: * will cause a crash if PAM_TTY is not set so if
121: * there is no tty, set PAM_TTY to the empty string.
122: */
123: if (user_ttypath == NULL)
124: (void) pam_set_item(pamh, PAM_TTY, "");
125: else
126: (void) pam_set_item(pamh, PAM_TTY, user_ttypath);
127:
1.1.1.2 misho 128: debug_return_int(AUTH_SUCCESS);
1.1 misho 129: }
130:
131: int
1.1.1.2 misho 132: sudo_pam_verify(struct passwd *pw, char *prompt, sudo_auth *auth)
1.1 misho 133: {
134: const char *s;
135: int *pam_status = (int *) auth->data;
1.1.1.2 misho 136: debug_decl(sudo_pam_verify, SUDO_DEBUG_AUTH)
1.1 misho 137:
138: def_prompt = prompt; /* for converse */
139:
140: /* PAM_SILENT prevents the authentication service from generating output. */
141: *pam_status = pam_authenticate(pamh, PAM_SILENT);
142: switch (*pam_status) {
143: case PAM_SUCCESS:
144: *pam_status = pam_acct_mgmt(pamh, PAM_SILENT);
145: switch (*pam_status) {
146: case PAM_SUCCESS:
1.1.1.3 ! misho 147: sudo_pam_authenticated = true;
1.1.1.2 misho 148: debug_return_int(AUTH_SUCCESS);
1.1 misho 149: case PAM_AUTH_ERR:
1.1.1.3 ! misho 150: log_warning(NO_MAIL, N_("account validation failure, "
1.1 misho 151: "is your account locked?"));
1.1.1.2 misho 152: debug_return_int(AUTH_FATAL);
1.1 misho 153: case PAM_NEW_AUTHTOK_REQD:
1.1.1.3 ! misho 154: log_warning(NO_MAIL, N_("Account or password is "
1.1 misho 155: "expired, reset your password and try again"));
156: *pam_status = pam_chauthtok(pamh,
157: PAM_CHANGE_EXPIRED_AUTHTOK);
158: if (*pam_status == PAM_SUCCESS)
1.1.1.2 misho 159: debug_return_int(AUTH_SUCCESS);
1.1.1.3 ! misho 160: if ((s = pam_strerror(pamh, *pam_status)) != NULL) {
! 161: log_warning(NO_MAIL,
! 162: N_("unable to change expired password: %s"), s);
! 163: }
1.1.1.2 misho 164: debug_return_int(AUTH_FAILURE);
1.1 misho 165: case PAM_AUTHTOK_EXPIRED:
1.1.1.3 ! misho 166: log_warning(NO_MAIL,
! 167: N_("Password expired, contact your system administrator"));
1.1.1.2 misho 168: debug_return_int(AUTH_FATAL);
1.1 misho 169: case PAM_ACCT_EXPIRED:
1.1.1.3 ! misho 170: log_warning(NO_MAIL,
! 171: N_("Account expired or PAM config lacks an \"account\" "
1.1 misho 172: "section for sudo, contact your system administrator"));
1.1.1.2 misho 173: debug_return_int(AUTH_FATAL);
1.1 misho 174: }
175: /* FALLTHROUGH */
176: case PAM_AUTH_ERR:
1.1.1.2 misho 177: case PAM_AUTHINFO_UNAVAIL:
178: if (getpass_error) {
1.1 misho 179: /* error or ^C from tgetpass() */
1.1.1.2 misho 180: debug_return_int(AUTH_INTR);
1.1 misho 181: }
1.1.1.2 misho 182: /* FALLTHROUGH */
1.1 misho 183: case PAM_MAXTRIES:
184: case PAM_PERM_DENIED:
1.1.1.2 misho 185: debug_return_int(AUTH_FAILURE);
1.1 misho 186: default:
1.1.1.3 ! misho 187: if ((s = pam_strerror(pamh, *pam_status)) != NULL)
! 188: log_warning(NO_MAIL, N_("PAM authentication error: %s"), s);
1.1.1.2 misho 189: debug_return_int(AUTH_FATAL);
1.1 misho 190: }
191: }
192:
193: int
1.1.1.2 misho 194: sudo_pam_cleanup(struct passwd *pw, sudo_auth *auth)
1.1 misho 195: {
196: int *pam_status = (int *) auth->data;
1.1.1.2 misho 197: debug_decl(sudo_pam_cleanup, SUDO_DEBUG_AUTH)
1.1 misho 198:
199: /* If successful, we can't close the session until pam_end_session() */
200: if (*pam_status == AUTH_SUCCESS)
1.1.1.2 misho 201: debug_return_int(AUTH_SUCCESS);
1.1 misho 202:
203: *pam_status = pam_end(pamh, *pam_status | PAM_DATA_SILENT);
204: pamh = NULL;
1.1.1.2 misho 205: debug_return_int(*pam_status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE);
1.1 misho 206: }
207:
208: int
1.1.1.2 misho 209: sudo_pam_begin_session(struct passwd *pw, char **user_envp[], sudo_auth *auth)
1.1 misho 210: {
211: int status = PAM_SUCCESS;
1.1.1.2 misho 212: debug_decl(sudo_pam_begin_session, SUDO_DEBUG_AUTH)
1.1 misho 213:
214: /*
215: * If there is no valid user we cannot open a PAM session.
216: * This is not an error as sudo can run commands with arbitrary
217: * uids, it just means we are done from a session management standpoint.
218: */
219: if (pw == NULL) {
220: if (pamh != NULL) {
221: (void) pam_end(pamh, PAM_SUCCESS | PAM_DATA_SILENT);
222: pamh = NULL;
223: }
224: goto done;
225: }
226:
227: /*
228: * Update PAM_USER to reference the user we are running the command
229: * as, as opposed to the user we authenticated as.
230: */
231: (void) pam_set_item(pamh, PAM_USER, pw->pw_name);
232:
233: /*
234: * Set credentials (may include resource limits, device ownership, etc).
235: * We don't check the return value here because in Linux-PAM 0.75
236: * it returns the last saved return code, not the return code
237: * for the setcred module. Because we haven't called pam_authenticate(),
238: * this is not set and so pam_setcred() returns PAM_PERM_DENIED.
239: * We can't call pam_acct_mgmt() with Linux-PAM for a similar reason.
240: */
1.1.1.3 ! misho 241: status = pam_setcred(pamh, PAM_ESTABLISH_CRED);
! 242: if (status == PAM_SUCCESS) {
! 243: sudo_pam_cred_established = true;
! 244: } else if (sudo_pam_authenticated) {
! 245: const char *s = pam_strerror(pamh, status);
! 246: if (s != NULL)
! 247: log_warning(NO_MAIL, N_("unable to establish credentials: %s"), s);
! 248: goto done;
! 249: }
1.1 misho 250:
1.1.1.2 misho 251: #ifdef HAVE_PAM_GETENVLIST
252: /*
253: * Update environment based on what is stored in pamh.
254: * If no authentication is done we will only have environment
255: * variables if pam_env is called via session.
256: */
257: if (user_envp != NULL) {
258: char **pam_envp = pam_getenvlist(pamh);
259: if (pam_envp != NULL) {
260: /* Merge pam env with user env but do not overwrite. */
261: env_init(*user_envp);
262: env_merge(pam_envp, false);
263: *user_envp = env_get();
264: env_init(NULL);
265: efree(pam_envp);
266: /* XXX - we leak any duplicates that were in pam_envp */
267: }
268: }
269: #endif /* HAVE_PAM_GETENVLIST */
270:
1.1.1.3 ! misho 271: if (def_pam_session) {
! 272: status = pam_open_session(pamh, 0);
! 273: if (status != PAM_SUCCESS) {
! 274: (void) pam_end(pamh, status | PAM_DATA_SILENT);
! 275: pamh = NULL;
! 276: }
1.1 misho 277: }
278:
279: done:
1.1.1.2 misho 280: debug_return_int(status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE);
1.1 misho 281: }
282:
283: int
1.1.1.2 misho 284: sudo_pam_end_session(struct passwd *pw, sudo_auth *auth)
1.1 misho 285: {
286: int status = PAM_SUCCESS;
1.1.1.2 misho 287: debug_decl(sudo_pam_end_session, SUDO_DEBUG_AUTH)
1.1 misho 288:
289: if (pamh != NULL) {
290: /*
291: * Update PAM_USER to reference the user we are running the command
1.1.1.2 misho 292: * as, as opposed to the user we authenticated as.
293: * XXX - still needed now that session init is in parent?
1.1 misho 294: */
295: (void) pam_set_item(pamh, PAM_USER, pw->pw_name);
1.1.1.3 ! misho 296: if (def_pam_session)
! 297: (void) pam_close_session(pamh, PAM_SILENT);
! 298: if (sudo_pam_cred_established)
! 299: (void) pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT);
1.1 misho 300: status = pam_end(pamh, PAM_SUCCESS | PAM_DATA_SILENT);
301: pamh = NULL;
302: }
303:
1.1.1.2 misho 304: debug_return_int(status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE);
1.1 misho 305: }
306:
307: /*
308: * ``Conversation function'' for PAM.
309: * XXX - does not handle PAM_BINARY_PROMPT
310: */
311: static int
312: converse(int num_msg, PAM_CONST struct pam_message **msg,
313: struct pam_response **response, void *appdata_ptr)
314: {
315: struct pam_response *pr;
316: PAM_CONST struct pam_message *pm;
317: const char *prompt;
318: char *pass;
319: int n, type, std_prompt;
1.1.1.2 misho 320: int ret = PAM_AUTH_ERR;
321: debug_decl(converse, SUDO_DEBUG_AUTH)
1.1 misho 322:
323: if ((*response = malloc(num_msg * sizeof(struct pam_response))) == NULL)
1.1.1.2 misho 324: debug_return_int(PAM_SYSTEM_ERR);
1.1 misho 325: zero_bytes(*response, num_msg * sizeof(struct pam_response));
326:
327: for (pr = *response, pm = *msg, n = num_msg; n--; pr++, pm++) {
328: type = SUDO_CONV_PROMPT_ECHO_OFF;
329: switch (pm->msg_style) {
330: case PAM_PROMPT_ECHO_ON:
331: type = SUDO_CONV_PROMPT_ECHO_ON;
1.1.1.2 misho 332: /* FALLTHROUGH */
1.1 misho 333: case PAM_PROMPT_ECHO_OFF:
334: prompt = def_prompt;
335:
336: /* Error out if the last password read was interrupted. */
1.1.1.2 misho 337: if (getpass_error)
338: goto done;
1.1 misho 339:
1.1.1.3 ! misho 340: /* Is the sudo prompt standard? (If so, we'll just use PAM's) */
1.1 misho 341: std_prompt = strncmp(def_prompt, "Password:", 9) == 0 &&
342: (def_prompt[9] == '\0' ||
343: (def_prompt[9] == ' ' && def_prompt[10] == '\0'));
344:
345: /* Only override PAM prompt if it matches /^Password: ?/ */
346: #if defined(PAM_TEXT_DOMAIN) && defined(HAVE_LIBINTL_H)
347: if (!def_passprompt_override && (std_prompt ||
1.1.1.3 ! misho 348: (strcmp(pm->msg, dgt(PAM_TEXT_DOMAIN, "Password: ")) &&
! 349: strcmp(pm->msg, dgt(PAM_TEXT_DOMAIN, "Password:")))))
1.1 misho 350: prompt = pm->msg;
351: #else
352: if (!def_passprompt_override && (std_prompt ||
353: strncmp(pm->msg, "Password:", 9) || (pm->msg[9] != '\0'
354: && (pm->msg[9] != ' ' || pm->msg[10] != '\0'))))
355: prompt = pm->msg;
356: #endif
357: /* Read the password unless interrupted. */
358: pass = auth_getpass(prompt, def_passwd_timeout * 60, type);
359: if (pass == NULL) {
1.1.1.2 misho 360: /* Error (or ^C) reading password, don't try again. */
361: getpass_error = 1;
362: #if (defined(__darwin__) || defined(__APPLE__)) && !defined(OPENPAM_VERSION)
1.1 misho 363: pass = "";
364: #else
1.1.1.2 misho 365: goto done;
1.1 misho 366: #endif
367: }
368: pr->resp = estrdup(pass);
369: zero_bytes(pass, strlen(pass));
370: break;
371: case PAM_TEXT_INFO:
372: if (pm->msg)
373: (void) puts(pm->msg);
374: break;
375: case PAM_ERROR_MSG:
376: if (pm->msg) {
377: (void) fputs(pm->msg, stderr);
378: (void) fputc('\n', stderr);
379: }
380: break;
381: default:
1.1.1.2 misho 382: ret = PAM_CONV_ERR;
383: goto done;
1.1 misho 384: }
385: }
1.1.1.2 misho 386: ret = PAM_SUCCESS;
1.1 misho 387:
1.1.1.2 misho 388: done:
389: if (ret != PAM_SUCCESS) {
390: /* Zero and free allocated memory and return an error. */
391: for (pr = *response, n = num_msg; n--; pr++) {
392: if (pr->resp != NULL) {
393: zero_bytes(pr->resp, strlen(pr->resp));
394: free(pr->resp);
395: pr->resp = NULL;
396: }
1.1 misho 397: }
1.1.1.2 misho 398: zero_bytes(*response, num_msg * sizeof(struct pam_response));
399: free(*response);
400: *response = NULL;
1.1 misho 401: }
1.1.1.2 misho 402: debug_return_int(ret);
1.1 misho 403: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>