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>