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