Annotation of embedaddon/sudo/plugins/sudoers/auth/pam.c, revision 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>