Annotation of embedaddon/sudo/plugins/sudoers/auth/pam.c, revision 1.1.1.1
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:";
74: static int gotintr;
75:
76: #ifndef PAM_DATA_SILENT
77: #define PAM_DATA_SILENT 0
78: #endif
79:
80: static pam_handle_t *pamh;
81:
82: int
83: pam_init(struct passwd *pw, sudo_auth *auth)
84: {
85: static struct pam_conv pam_conv;
86: static int pam_status;
87:
88: /* Initial PAM setup */
89: if (auth != NULL)
90: auth->data = (void *) &pam_status;
91: pam_conv.conv = converse;
92: #ifdef HAVE_PAM_LOGIN
93: if (ISSET(sudo_mode, MODE_LOGIN_SHELL))
94: pam_status = pam_start("sudo-i", pw->pw_name, &pam_conv, &pamh);
95: else
96: #endif
97: pam_status = pam_start("sudo", pw->pw_name, &pam_conv, &pamh);
98: if (pam_status != PAM_SUCCESS) {
99: log_error(USE_ERRNO|NO_EXIT|NO_MAIL, _("unable to initialize PAM"));
100: return AUTH_FATAL;
101: }
102:
103: /*
104: * Set PAM_RUSER to the invoking user (the "from" user).
105: * We set PAM_RHOST to avoid a bug in Solaris 7 and below.
106: */
107: (void) pam_set_item(pamh, PAM_RUSER, user_name);
108: #ifdef __sun__
109: (void) pam_set_item(pamh, PAM_RHOST, user_host);
110: #endif
111:
112: /*
113: * Some versions of pam_lastlog have a bug that
114: * will cause a crash if PAM_TTY is not set so if
115: * there is no tty, set PAM_TTY to the empty string.
116: */
117: if (user_ttypath == NULL)
118: (void) pam_set_item(pamh, PAM_TTY, "");
119: else
120: (void) pam_set_item(pamh, PAM_TTY, user_ttypath);
121:
122: return AUTH_SUCCESS;
123: }
124:
125: int
126: pam_verify(struct passwd *pw, char *prompt, sudo_auth *auth)
127: {
128: const char *s;
129: int *pam_status = (int *) auth->data;
130:
131: def_prompt = prompt; /* for converse */
132:
133: /* PAM_SILENT prevents the authentication service from generating output. */
134: *pam_status = pam_authenticate(pamh, PAM_SILENT);
135: switch (*pam_status) {
136: case PAM_SUCCESS:
137: *pam_status = pam_acct_mgmt(pamh, PAM_SILENT);
138: switch (*pam_status) {
139: case PAM_SUCCESS:
140: return AUTH_SUCCESS;
141: case PAM_AUTH_ERR:
142: log_error(NO_EXIT|NO_MAIL, _("account validation failure, "
143: "is your account locked?"));
144: return AUTH_FATAL;
145: case PAM_NEW_AUTHTOK_REQD:
146: log_error(NO_EXIT|NO_MAIL, _("Account or password is "
147: "expired, reset your password and try again"));
148: *pam_status = pam_chauthtok(pamh,
149: PAM_CHANGE_EXPIRED_AUTHTOK);
150: if (*pam_status == PAM_SUCCESS)
151: return AUTH_SUCCESS;
152: if ((s = pam_strerror(pamh, *pam_status)))
153: log_error(NO_EXIT|NO_MAIL, _("pam_chauthtok: %s"), s);
154: return AUTH_FAILURE;
155: case PAM_AUTHTOK_EXPIRED:
156: log_error(NO_EXIT|NO_MAIL,
157: _("Password expired, contact your system administrator"));
158: return AUTH_FATAL;
159: case PAM_ACCT_EXPIRED:
160: log_error(NO_EXIT|NO_MAIL,
161: _("Account expired or PAM config lacks an \"account\" "
162: "section for sudo, contact your system administrator"));
163: return AUTH_FATAL;
164: }
165: /* FALLTHROUGH */
166: case PAM_AUTH_ERR:
167: if (gotintr) {
168: /* error or ^C from tgetpass() */
169: return AUTH_INTR;
170: }
171: case PAM_MAXTRIES:
172: case PAM_PERM_DENIED:
173: return AUTH_FAILURE;
174: default:
175: if ((s = pam_strerror(pamh, *pam_status)))
176: log_error(NO_EXIT|NO_MAIL, _("pam_authenticate: %s"), s);
177: return AUTH_FATAL;
178: }
179: }
180:
181: int
182: pam_cleanup(struct passwd *pw, sudo_auth *auth)
183: {
184: int *pam_status = (int *) auth->data;
185:
186: /* If successful, we can't close the session until pam_end_session() */
187: if (*pam_status == AUTH_SUCCESS)
188: return AUTH_SUCCESS;
189:
190: *pam_status = pam_end(pamh, *pam_status | PAM_DATA_SILENT);
191: pamh = NULL;
192: return *pam_status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE;
193: }
194:
195: int
196: pam_begin_session(struct passwd *pw, sudo_auth *auth)
197: {
198: int status = PAM_SUCCESS;
199:
200: /*
201: * If there is no valid user we cannot open a PAM session.
202: * This is not an error as sudo can run commands with arbitrary
203: * uids, it just means we are done from a session management standpoint.
204: */
205: if (pw == NULL) {
206: if (pamh != NULL) {
207: (void) pam_end(pamh, PAM_SUCCESS | PAM_DATA_SILENT);
208: pamh = NULL;
209: }
210: goto done;
211: }
212:
213: /*
214: * Update PAM_USER to reference the user we are running the command
215: * as, as opposed to the user we authenticated as.
216: */
217: (void) pam_set_item(pamh, PAM_USER, pw->pw_name);
218:
219: /*
220: * Set credentials (may include resource limits, device ownership, etc).
221: * We don't check the return value here because in Linux-PAM 0.75
222: * it returns the last saved return code, not the return code
223: * for the setcred module. Because we haven't called pam_authenticate(),
224: * this is not set and so pam_setcred() returns PAM_PERM_DENIED.
225: * We can't call pam_acct_mgmt() with Linux-PAM for a similar reason.
226: */
227: (void) pam_setcred(pamh, PAM_ESTABLISH_CRED);
228:
229: #ifndef NO_PAM_SESSION
230: status = pam_open_session(pamh, 0);
231: if (status != PAM_SUCCESS) {
232: (void) pam_end(pamh, status | PAM_DATA_SILENT);
233: pamh = NULL;
234: }
235: #endif
236:
237: done:
238: return status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE;
239: }
240:
241: int
242: pam_end_session(struct passwd *pw, sudo_auth *auth)
243: {
244: int status = PAM_SUCCESS;
245:
246: if (pamh != NULL) {
247: #ifndef NO_PAM_SESSION
248: /*
249: * Update PAM_USER to reference the user we are running the command
250: * as to match the call to pam_open_session().
251: */
252: (void) pam_set_item(pamh, PAM_USER, pw->pw_name);
253: (void) pam_close_session(pamh, PAM_SILENT);
254: #endif
255: status = pam_end(pamh, PAM_SUCCESS | PAM_DATA_SILENT);
256: pamh = NULL;
257: }
258:
259: return status == PAM_SUCCESS ? AUTH_SUCCESS : AUTH_FAILURE;
260: }
261:
262: /*
263: * ``Conversation function'' for PAM.
264: * XXX - does not handle PAM_BINARY_PROMPT
265: */
266: static int
267: converse(int num_msg, PAM_CONST struct pam_message **msg,
268: struct pam_response **response, void *appdata_ptr)
269: {
270: struct pam_response *pr;
271: PAM_CONST struct pam_message *pm;
272: const char *prompt;
273: char *pass;
274: int n, type, std_prompt;
275:
276: if ((*response = malloc(num_msg * sizeof(struct pam_response))) == NULL)
277: return PAM_SYSTEM_ERR;
278: zero_bytes(*response, num_msg * sizeof(struct pam_response));
279:
280: for (pr = *response, pm = *msg, n = num_msg; n--; pr++, pm++) {
281: type = SUDO_CONV_PROMPT_ECHO_OFF;
282: switch (pm->msg_style) {
283: case PAM_PROMPT_ECHO_ON:
284: type = SUDO_CONV_PROMPT_ECHO_ON;
285: case PAM_PROMPT_ECHO_OFF:
286: prompt = def_prompt;
287:
288: /* Error out if the last password read was interrupted. */
289: if (gotintr)
290: goto err;
291:
292: /* Is the sudo prompt standard? (If so, we'l just use PAM's) */
293: std_prompt = strncmp(def_prompt, "Password:", 9) == 0 &&
294: (def_prompt[9] == '\0' ||
295: (def_prompt[9] == ' ' && def_prompt[10] == '\0'));
296:
297: /* Only override PAM prompt if it matches /^Password: ?/ */
298: #if defined(PAM_TEXT_DOMAIN) && defined(HAVE_LIBINTL_H)
299: if (!def_passprompt_override && (std_prompt ||
300: (strcmp(pm->msg, dgettext(PAM_TEXT_DOMAIN, "Password: ")) &&
301: strcmp(pm->msg, dgettext(PAM_TEXT_DOMAIN, "Password:")))))
302: prompt = pm->msg;
303: #else
304: if (!def_passprompt_override && (std_prompt ||
305: strncmp(pm->msg, "Password:", 9) || (pm->msg[9] != '\0'
306: && (pm->msg[9] != ' ' || pm->msg[10] != '\0'))))
307: prompt = pm->msg;
308: #endif
309: /* Read the password unless interrupted. */
310: pass = auth_getpass(prompt, def_passwd_timeout * 60, type);
311: if (pass == NULL) {
312: /* We got ^C instead of a password; abort quickly. */
313: if (errno == EINTR)
314: gotintr = 1;
315: #if defined(__darwin__) || defined(__APPLE__)
316: pass = "";
317: #else
318: goto err;
319: #endif
320: }
321: pr->resp = estrdup(pass);
322: zero_bytes(pass, strlen(pass));
323: break;
324: case PAM_TEXT_INFO:
325: if (pm->msg)
326: (void) puts(pm->msg);
327: break;
328: case PAM_ERROR_MSG:
329: if (pm->msg) {
330: (void) fputs(pm->msg, stderr);
331: (void) fputc('\n', stderr);
332: }
333: break;
334: default:
335: goto err;
336: }
337: }
338:
339: return PAM_SUCCESS;
340:
341: err:
342: /* Zero and free allocated memory and return an error. */
343: for (pr = *response, n = num_msg; n--; pr++) {
344: if (pr->resp != NULL) {
345: zero_bytes(pr->resp, strlen(pr->resp));
346: free(pr->resp);
347: pr->resp = NULL;
348: }
349: }
350: zero_bytes(*response, num_msg * sizeof(struct pam_response));
351: free(*response);
352: *response = NULL;
353: return gotintr ? PAM_AUTH_ERR : PAM_CONV_ERR;
354: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>