17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * Copyright (c) 2000 Damien Miller. All rights reserved.
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * Redistribution and use in source and binary forms, with or without
57c478bd9Sstevel@tonic-gate * modification, are permitted provided that the following conditions
67c478bd9Sstevel@tonic-gate * are met:
77c478bd9Sstevel@tonic-gate * 1. Redistributions of source code must retain the above copyright
87c478bd9Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer.
97c478bd9Sstevel@tonic-gate * 2. Redistributions in binary form must reproduce the above copyright
107c478bd9Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer in the
117c478bd9Sstevel@tonic-gate * documentation and/or other materials provided with the distribution.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
147c478bd9Sstevel@tonic-gate * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
157c478bd9Sstevel@tonic-gate * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
167c478bd9Sstevel@tonic-gate * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
177c478bd9Sstevel@tonic-gate * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
187c478bd9Sstevel@tonic-gate * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
197c478bd9Sstevel@tonic-gate * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
207c478bd9Sstevel@tonic-gate * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
217c478bd9Sstevel@tonic-gate * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
227c478bd9Sstevel@tonic-gate * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
237c478bd9Sstevel@tonic-gate */
247c478bd9Sstevel@tonic-gate /*
25*bdb005b5SDarren J Moffat * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
267c478bd9Sstevel@tonic-gate */
277c478bd9Sstevel@tonic-gate
287c478bd9Sstevel@tonic-gate #include "includes.h"
297c478bd9Sstevel@tonic-gate
307c478bd9Sstevel@tonic-gate #ifdef USE_PAM
317c478bd9Sstevel@tonic-gate #include "xmalloc.h"
327c478bd9Sstevel@tonic-gate #include "log.h"
337c478bd9Sstevel@tonic-gate #include "auth.h"
347c478bd9Sstevel@tonic-gate #include "auth-options.h"
357c478bd9Sstevel@tonic-gate #include "auth-pam.h"
36b07b2f5cSHuie-Ying Lee #include "buffer.h"
377c478bd9Sstevel@tonic-gate #include "servconf.h"
387c478bd9Sstevel@tonic-gate #include "canohost.h"
397c478bd9Sstevel@tonic-gate #include "compat.h"
407c478bd9Sstevel@tonic-gate #include "misc.h"
417c478bd9Sstevel@tonic-gate #include "sshlogin.h"
429a8058b5Sjp161948 #include "ssh-gss.h"
437c478bd9Sstevel@tonic-gate
447c478bd9Sstevel@tonic-gate #include <security/pam_appl.h>
457c478bd9Sstevel@tonic-gate
467c478bd9Sstevel@tonic-gate extern char *__progname;
477c478bd9Sstevel@tonic-gate
487c478bd9Sstevel@tonic-gate extern u_int utmp_len;
497c478bd9Sstevel@tonic-gate extern ServerOptions options;
507c478bd9Sstevel@tonic-gate
517c478bd9Sstevel@tonic-gate extern Authmethod method_kbdint;
527c478bd9Sstevel@tonic-gate
537c478bd9Sstevel@tonic-gate RCSID("$Id: auth-pam.c,v 1.54 2002/07/28 20:24:08 stevesk Exp $");
547c478bd9Sstevel@tonic-gate
557c478bd9Sstevel@tonic-gate #define NEW_AUTHTOK_MSG \
567c478bd9Sstevel@tonic-gate "Warning: Your password has expired, please change it now."
577c478bd9Sstevel@tonic-gate
587c478bd9Sstevel@tonic-gate /* PAM conversation for non-interactive userauth methods */
597c478bd9Sstevel@tonic-gate static int do_pam_conversation(int num_msg, const struct pam_message **msg,
607c478bd9Sstevel@tonic-gate struct pam_response **resp, void *appdata_ptr);
617c478bd9Sstevel@tonic-gate
627c478bd9Sstevel@tonic-gate static void do_pam_cleanup_proc(void *context);
637c478bd9Sstevel@tonic-gate
647c478bd9Sstevel@tonic-gate static char *get_method_name(Authctxt *authctxt);
657c478bd9Sstevel@tonic-gate
667c478bd9Sstevel@tonic-gate /* PAM conversation for non-interactive userauth methods */
677c478bd9Sstevel@tonic-gate static struct pam_conv conv = {
687c478bd9Sstevel@tonic-gate (int (*)())do_pam_conversation,
697c478bd9Sstevel@tonic-gate NULL
707c478bd9Sstevel@tonic-gate };
717c478bd9Sstevel@tonic-gate static char *__pam_msg = NULL;
727c478bd9Sstevel@tonic-gate
737c478bd9Sstevel@tonic-gate static
747c478bd9Sstevel@tonic-gate char *
get_method_name(Authctxt * authctxt)757c478bd9Sstevel@tonic-gate get_method_name(Authctxt *authctxt)
767c478bd9Sstevel@tonic-gate {
777c478bd9Sstevel@tonic-gate if (!authctxt)
787c478bd9Sstevel@tonic-gate return "(unknown)";
797c478bd9Sstevel@tonic-gate
807c478bd9Sstevel@tonic-gate if (!compat20)
817c478bd9Sstevel@tonic-gate return (authctxt->v1_auth_name) ? authctxt->v1_auth_name :
827c478bd9Sstevel@tonic-gate "(sshv1-unknown)";
837c478bd9Sstevel@tonic-gate
847c478bd9Sstevel@tonic-gate if (!authctxt->method || !authctxt->method->name)
857c478bd9Sstevel@tonic-gate return "(sshv2-unknown)";
867c478bd9Sstevel@tonic-gate
877c478bd9Sstevel@tonic-gate return authctxt->method->name;
887c478bd9Sstevel@tonic-gate }
897c478bd9Sstevel@tonic-gate
907c478bd9Sstevel@tonic-gate char *
derive_pam_service_name(Authmethod * method)91*bdb005b5SDarren J Moffat derive_pam_service_name(Authmethod *method)
927c478bd9Sstevel@tonic-gate {
93*bdb005b5SDarren J Moffat char *svcname = xmalloc(BUFSIZ);
94*bdb005b5SDarren J Moffat
95*bdb005b5SDarren J Moffat /*
96*bdb005b5SDarren J Moffat * If PamServiceName is set we use that for everything, including
97*bdb005b5SDarren J Moffat * SSHv1
98*bdb005b5SDarren J Moffat */
99*bdb005b5SDarren J Moffat if (options.pam_service_name != NULL) {
100*bdb005b5SDarren J Moffat (void) strlcpy(svcname, options.pam_service_name, BUFSIZ);
101*bdb005b5SDarren J Moffat return (svcname);
102*bdb005b5SDarren J Moffat }
103*bdb005b5SDarren J Moffat
1047c478bd9Sstevel@tonic-gate if (compat20 && method) {
1057c478bd9Sstevel@tonic-gate char *method_name = method->name;
1067c478bd9Sstevel@tonic-gate
1077c478bd9Sstevel@tonic-gate if (!method_name)
1087c478bd9Sstevel@tonic-gate fatal("Userauth method unknown while starting PAM");
1097c478bd9Sstevel@tonic-gate
110*bdb005b5SDarren J Moffat /*
111*bdb005b5SDarren J Moffat * For SSHv2 we use "sshd-<userauth name>
112*bdb005b5SDarren J Moffat * The "sshd" prefix can be changed via the PAMServicePrefix
113*bdb005b5SDarren J Moffat * sshd_config option.
114*bdb005b5SDarren J Moffat */
1157c478bd9Sstevel@tonic-gate if (strcmp(method_name, "none") == 0) {
116*bdb005b5SDarren J Moffat snprintf(svcname, BUFSIZ, "%s-none",
117*bdb005b5SDarren J Moffat options.pam_service_prefix);
1187c478bd9Sstevel@tonic-gate }
1197c478bd9Sstevel@tonic-gate if (strcmp(method_name, "password") == 0) {
120*bdb005b5SDarren J Moffat snprintf(svcname, BUFSIZ, "%s-password",
121*bdb005b5SDarren J Moffat options.pam_service_prefix);
1227c478bd9Sstevel@tonic-gate }
1237c478bd9Sstevel@tonic-gate if (strcmp(method_name, "keyboard-interactive") == 0) {
1247c478bd9Sstevel@tonic-gate /* "keyboard-interactive" is too long, shorten it */
125*bdb005b5SDarren J Moffat snprintf(svcname, BUFSIZ, "%s-kbdint",
126*bdb005b5SDarren J Moffat options.pam_service_prefix);
1277c478bd9Sstevel@tonic-gate }
1287c478bd9Sstevel@tonic-gate if (strcmp(method_name, "publickey") == 0) {
1297c478bd9Sstevel@tonic-gate /* "publickey" is too long, shorten it */
130*bdb005b5SDarren J Moffat snprintf(svcname, BUFSIZ, "%s-pubkey",
131*bdb005b5SDarren J Moffat options.pam_service_prefix);
1327c478bd9Sstevel@tonic-gate }
1337c478bd9Sstevel@tonic-gate if (strcmp(method_name, "hostbased") == 0) {
1347c478bd9Sstevel@tonic-gate /* "hostbased" can't really be shortened... */
135*bdb005b5SDarren J Moffat snprintf(svcname, BUFSIZ, "%s-hostbased",
136*bdb005b5SDarren J Moffat options.pam_service_prefix);
1377c478bd9Sstevel@tonic-gate }
1387c478bd9Sstevel@tonic-gate if (strncmp(method_name, "gss", 3) == 0) {
139b350d31dSme23304 /* "gss" is too short, elongate it */
140*bdb005b5SDarren J Moffat snprintf(svcname, BUFSIZ, "%s-gssapi",
141*bdb005b5SDarren J Moffat options.pam_service_prefix);
1427c478bd9Sstevel@tonic-gate }
143*bdb005b5SDarren J Moffat return svcname;
144*bdb005b5SDarren J Moffat } else {
145*bdb005b5SDarren J Moffat /* SSHv1 doesn't get to be so cool */
146*bdb005b5SDarren J Moffat snprintf(svcname, BUFSIZ, "%s-v1",
147*bdb005b5SDarren J Moffat options.pam_service_prefix);
1487c478bd9Sstevel@tonic-gate }
149*bdb005b5SDarren J Moffat return svcname;
1507c478bd9Sstevel@tonic-gate }
1517c478bd9Sstevel@tonic-gate
1527c478bd9Sstevel@tonic-gate void
new_start_pam(Authctxt * authctxt,struct pam_conv * conv)1537c478bd9Sstevel@tonic-gate new_start_pam(Authctxt *authctxt, struct pam_conv *conv)
1547c478bd9Sstevel@tonic-gate {
1557c478bd9Sstevel@tonic-gate int retval;
1567c478bd9Sstevel@tonic-gate pam_handle_t *pamh;
157*bdb005b5SDarren J Moffat const char *rhost;
158*bdb005b5SDarren J Moffat char *svc;
1597c478bd9Sstevel@tonic-gate char *user = NULL;
1607c478bd9Sstevel@tonic-gate pam_stuff *pam;
1617c478bd9Sstevel@tonic-gate
1627c478bd9Sstevel@tonic-gate if (authctxt == NULL)
1637c478bd9Sstevel@tonic-gate fatal("Internal error during userauth");
1647c478bd9Sstevel@tonic-gate
1657c478bd9Sstevel@tonic-gate if (compat20 && authctxt->method == NULL)
1667c478bd9Sstevel@tonic-gate fatal("Userauth method unknown while starting PAM");
1677c478bd9Sstevel@tonic-gate
1687c478bd9Sstevel@tonic-gate /* PAM service selected here */
169*bdb005b5SDarren J Moffat svc = derive_pam_service_name(authctxt->method);
1707c478bd9Sstevel@tonic-gate debug2("Starting PAM service %s for method %s", svc,
1717c478bd9Sstevel@tonic-gate get_method_name(authctxt));
1727c478bd9Sstevel@tonic-gate
1737c478bd9Sstevel@tonic-gate if (authctxt->user != NULL)
1747c478bd9Sstevel@tonic-gate user = authctxt->user;
1757c478bd9Sstevel@tonic-gate
1767c478bd9Sstevel@tonic-gate /* Cleanup previous PAM state */
1777c478bd9Sstevel@tonic-gate if (authctxt->pam != NULL) {
1787c478bd9Sstevel@tonic-gate fatal_remove_cleanup(&do_pam_cleanup_proc, authctxt->pam);
1797c478bd9Sstevel@tonic-gate do_pam_cleanup_proc(authctxt->pam);
1807c478bd9Sstevel@tonic-gate }
1817c478bd9Sstevel@tonic-gate
1827c478bd9Sstevel@tonic-gate pam = xmalloc(sizeof(pam_stuff));
1837c478bd9Sstevel@tonic-gate (void) memset(pam, 0, sizeof(pam_stuff));
1847c478bd9Sstevel@tonic-gate
1857c478bd9Sstevel@tonic-gate /*
1867c478bd9Sstevel@tonic-gate * pam->last_pam_retval has to be and is considered
1877c478bd9Sstevel@tonic-gate * along with pam->state.
1887c478bd9Sstevel@tonic-gate *
1897c478bd9Sstevel@tonic-gate * pam->state = 0; -> no PAM auth, account, etc, work
1907c478bd9Sstevel@tonic-gate * done yet. (Set by memset() above.)
1917c478bd9Sstevel@tonic-gate *
1927c478bd9Sstevel@tonic-gate * pam->last_pam_retval = PAM_SUCCESS; -> meaningless at
1937c478bd9Sstevel@tonic-gate * this point.
1947c478bd9Sstevel@tonic-gate *
1957c478bd9Sstevel@tonic-gate * See finish_userauth_do_pam() below.
1967c478bd9Sstevel@tonic-gate */
1977c478bd9Sstevel@tonic-gate pam->authctxt = authctxt;
1987c478bd9Sstevel@tonic-gate pam->last_pam_retval = PAM_SUCCESS;
1997c478bd9Sstevel@tonic-gate
2007c478bd9Sstevel@tonic-gate authctxt->pam = pam;
2017c478bd9Sstevel@tonic-gate
2027c478bd9Sstevel@tonic-gate /* Free any previously stored text/error PAM prompts */
2037c478bd9Sstevel@tonic-gate if (__pam_msg) {
2047c478bd9Sstevel@tonic-gate xfree(__pam_msg);
2057c478bd9Sstevel@tonic-gate __pam_msg = NULL;
2067c478bd9Sstevel@tonic-gate }
2077c478bd9Sstevel@tonic-gate
2087c478bd9Sstevel@tonic-gate if ((retval = pam_start(svc, user, conv, &pamh)) != PAM_SUCCESS) {
2097c478bd9Sstevel@tonic-gate fatal("PAM initialization failed during %s userauth",
2107c478bd9Sstevel@tonic-gate get_method_name(authctxt));
2117c478bd9Sstevel@tonic-gate }
2127c478bd9Sstevel@tonic-gate
213*bdb005b5SDarren J Moffat free(svc);
214*bdb005b5SDarren J Moffat
2157c478bd9Sstevel@tonic-gate fatal_add_cleanup((void (*)(void *)) &do_pam_cleanup_proc,
2167c478bd9Sstevel@tonic-gate (void *) authctxt->pam);
2177c478bd9Sstevel@tonic-gate
2187c478bd9Sstevel@tonic-gate rhost = get_remote_name_or_ip(utmp_len, options.verify_reverse_mapping);
2197c478bd9Sstevel@tonic-gate if ((retval = pam_set_item(pamh, PAM_RHOST, rhost)) != PAM_SUCCESS) {
2207c478bd9Sstevel@tonic-gate (void) pam_end(pamh, retval);
2217c478bd9Sstevel@tonic-gate fatal("Could not set PAM_RHOST item during %s userauth",
2227c478bd9Sstevel@tonic-gate get_method_name(authctxt));
2237c478bd9Sstevel@tonic-gate }
2247c478bd9Sstevel@tonic-gate
2257c478bd9Sstevel@tonic-gate if ((retval = pam_set_item(pamh, PAM_TTY, "sshd")) != PAM_SUCCESS) {
2267c478bd9Sstevel@tonic-gate (void) pam_end(pamh, retval);
2277c478bd9Sstevel@tonic-gate fatal("Could not set PAM_TTY item during %s userauth",
2287c478bd9Sstevel@tonic-gate get_method_name(authctxt));
2297c478bd9Sstevel@tonic-gate }
2307c478bd9Sstevel@tonic-gate
231f44ef466Sjp161948 if (authctxt->cuser != NULL)
232f44ef466Sjp161948 if ((retval = pam_set_item(pamh, PAM_AUSER, authctxt->cuser)) != PAM_SUCCESS) {
233f44ef466Sjp161948 (void) pam_end(pamh, retval);
234f44ef466Sjp161948 fatal("Could not set PAM_AUSER item during %s userauth",
235f44ef466Sjp161948 get_method_name(authctxt));
236f44ef466Sjp161948 }
237f44ef466Sjp161948
2387c478bd9Sstevel@tonic-gate authctxt->pam->h = pamh;
2397c478bd9Sstevel@tonic-gate }
2407c478bd9Sstevel@tonic-gate
2417c478bd9Sstevel@tonic-gate /*
2427c478bd9Sstevel@tonic-gate * To be called from userauth methods, directly (as in keyboard-interactive) or
2437c478bd9Sstevel@tonic-gate * indirectly (from auth_pam_password() or from do_pam_non_initial_userauth().
2447c478bd9Sstevel@tonic-gate *
2457c478bd9Sstevel@tonic-gate * The caller is responsible for calling new_start_pam() first.
2467c478bd9Sstevel@tonic-gate *
2477c478bd9Sstevel@tonic-gate * PAM state is not cleaned up here on error. This is left to subsequent calls
2487c478bd9Sstevel@tonic-gate * to new_start_pam() or to the cleanup function upon authentication error.
2497c478bd9Sstevel@tonic-gate */
2507c478bd9Sstevel@tonic-gate int
finish_userauth_do_pam(Authctxt * authctxt)2517c478bd9Sstevel@tonic-gate finish_userauth_do_pam(Authctxt *authctxt)
2527c478bd9Sstevel@tonic-gate {
2537c478bd9Sstevel@tonic-gate int retval;
2547c478bd9Sstevel@tonic-gate char *user, *method;
2557c478bd9Sstevel@tonic-gate
2567c478bd9Sstevel@tonic-gate /* Various checks; fail gracefully */
2577c478bd9Sstevel@tonic-gate if (authctxt == NULL || authctxt->pam == NULL)
2587c478bd9Sstevel@tonic-gate return PAM_SYSTEM_ERR; /* shouldn't happen */
2597c478bd9Sstevel@tonic-gate
2607c478bd9Sstevel@tonic-gate if (compat20) {
2617c478bd9Sstevel@tonic-gate if (authctxt->method == NULL || authctxt->method->name == NULL)
2627c478bd9Sstevel@tonic-gate return PAM_SYSTEM_ERR; /* shouldn't happen */
2637c478bd9Sstevel@tonic-gate method = authctxt->method->name;
2647c478bd9Sstevel@tonic-gate } else if ((method = authctxt->v1_auth_name) == NULL)
2657c478bd9Sstevel@tonic-gate return PAM_SYSTEM_ERR; /* shouldn't happen */
2667c478bd9Sstevel@tonic-gate
2677c478bd9Sstevel@tonic-gate if (AUTHPAM_DONE(authctxt))
2687c478bd9Sstevel@tonic-gate return PAM_SYSTEM_ERR; /* shouldn't happen */
2697c478bd9Sstevel@tonic-gate
2707c478bd9Sstevel@tonic-gate if (!(authctxt->pam->state & PAM_S_DONE_ACCT_MGMT)) {
2717c478bd9Sstevel@tonic-gate retval = pam_acct_mgmt(authctxt->pam->h, 0);
2727c478bd9Sstevel@tonic-gate authctxt->pam->last_pam_retval = retval;
2737c478bd9Sstevel@tonic-gate if (retval == PAM_NEW_AUTHTOK_REQD) {
2747c478bd9Sstevel@tonic-gate userauth_force_kbdint();
2757c478bd9Sstevel@tonic-gate return retval;
2767c478bd9Sstevel@tonic-gate }
2777c478bd9Sstevel@tonic-gate if (retval != PAM_SUCCESS)
2787c478bd9Sstevel@tonic-gate return retval;
2797c478bd9Sstevel@tonic-gate authctxt->pam->state |= PAM_S_DONE_ACCT_MGMT;
2807c478bd9Sstevel@tonic-gate }
2817c478bd9Sstevel@tonic-gate
2827c478bd9Sstevel@tonic-gate /*
2837c478bd9Sstevel@tonic-gate * Handle PAM_USER change, if any.
2847c478bd9Sstevel@tonic-gate *
2857c478bd9Sstevel@tonic-gate * We do this before pam_open_session() because we need the PAM_USER's
2867c478bd9Sstevel@tonic-gate * UID for:
2877c478bd9Sstevel@tonic-gate *
2887c478bd9Sstevel@tonic-gate * a) PermitRootLogin checking
2897c478bd9Sstevel@tonic-gate * b) to get at the lastlog entry before pam_open_session() updates it.
2907c478bd9Sstevel@tonic-gate */
2917c478bd9Sstevel@tonic-gate retval = pam_get_item(authctxt->pam->h, PAM_USER, (void **) &user);
2927c478bd9Sstevel@tonic-gate if (retval != PAM_SUCCESS) {
2937c478bd9Sstevel@tonic-gate fatal("PAM failure: pam_get_item(PAM_USER) "
2947c478bd9Sstevel@tonic-gate "returned %d: %.200s", retval,
2957c478bd9Sstevel@tonic-gate PAM_STRERROR(authctxt->pam->h, retval));
2967c478bd9Sstevel@tonic-gate }
2977c478bd9Sstevel@tonic-gate
2987c478bd9Sstevel@tonic-gate if (user == NULL || *user == '\0') {
2997c478bd9Sstevel@tonic-gate debug("PAM set NULL PAM_USER");
3007c478bd9Sstevel@tonic-gate return PAM_PERM_DENIED;
3017c478bd9Sstevel@tonic-gate }
3027c478bd9Sstevel@tonic-gate
3037c478bd9Sstevel@tonic-gate if (strcmp(user, authctxt->user) != 0) {
3047c478bd9Sstevel@tonic-gate log("PAM changed the SSH username");
3057c478bd9Sstevel@tonic-gate pwfree(&authctxt->pw);
3069a8058b5Sjp161948 authctxt->pw = getpwnamallow(user);
3077c478bd9Sstevel@tonic-gate authctxt->valid = (authctxt->pw != NULL);
3087c478bd9Sstevel@tonic-gate xfree(authctxt->user);
3097c478bd9Sstevel@tonic-gate authctxt->user = xstrdup(user);
3107c478bd9Sstevel@tonic-gate }
3117c478bd9Sstevel@tonic-gate
3127c478bd9Sstevel@tonic-gate if (!authctxt->valid) {
3137c478bd9Sstevel@tonic-gate debug2("PAM set PAM_USER to unknown user");
3147c478bd9Sstevel@tonic-gate /*
3157c478bd9Sstevel@tonic-gate * Return success, userauth_finish() will catch
3167c478bd9Sstevel@tonic-gate * this and send back a failure message.
3177c478bd9Sstevel@tonic-gate */
3187c478bd9Sstevel@tonic-gate return PAM_SUCCESS;
3197c478bd9Sstevel@tonic-gate }
3207c478bd9Sstevel@tonic-gate
3217c478bd9Sstevel@tonic-gate /* Check PermitRootLogin semantics */
3227c478bd9Sstevel@tonic-gate if (authctxt->pw->pw_uid == 0 && !auth_root_allowed(method))
3237c478bd9Sstevel@tonic-gate return PAM_PERM_DENIED;
3247c478bd9Sstevel@tonic-gate
3257c478bd9Sstevel@tonic-gate if (!(authctxt->pam->state & PAM_S_DONE_SETCRED)) {
3267c478bd9Sstevel@tonic-gate retval = pam_setcred(authctxt->pam->h,
3277c478bd9Sstevel@tonic-gate PAM_ESTABLISH_CRED);
3287c478bd9Sstevel@tonic-gate authctxt->pam->last_pam_retval = retval;
3297c478bd9Sstevel@tonic-gate if (retval != PAM_SUCCESS)
3307c478bd9Sstevel@tonic-gate return retval;
3317c478bd9Sstevel@tonic-gate authctxt->pam->state |= PAM_S_DONE_SETCRED;
3327c478bd9Sstevel@tonic-gate
3337c478bd9Sstevel@tonic-gate #ifdef GSSAPI
3347c478bd9Sstevel@tonic-gate /*
3357c478bd9Sstevel@tonic-gate * Store GSS-API delegated creds after pam_setcred(), which may
3367c478bd9Sstevel@tonic-gate * have set the current credential store.
3377c478bd9Sstevel@tonic-gate */
3387c478bd9Sstevel@tonic-gate ssh_gssapi_storecreds(NULL, authctxt);
3397c478bd9Sstevel@tonic-gate #endif /* GSSAPI */
3407c478bd9Sstevel@tonic-gate }
3417c478bd9Sstevel@tonic-gate
3427c478bd9Sstevel@tonic-gate /*
3437c478bd9Sstevel@tonic-gate * On Solaris pam_unix_session.so updates the lastlog, but does
3447c478bd9Sstevel@tonic-gate * not converse a PAM_TEXT_INFO message about it. So we need to
3457c478bd9Sstevel@tonic-gate * fetch the lastlog entry here and save it for use later.
3467c478bd9Sstevel@tonic-gate */
3477c478bd9Sstevel@tonic-gate authctxt->last_login_time =
3487c478bd9Sstevel@tonic-gate get_last_login_time(authctxt->pw->pw_uid,
3497c478bd9Sstevel@tonic-gate authctxt->pw->pw_name,
3507c478bd9Sstevel@tonic-gate authctxt->last_login_host,
3517c478bd9Sstevel@tonic-gate sizeof(authctxt->last_login_host));
3527c478bd9Sstevel@tonic-gate
3537c478bd9Sstevel@tonic-gate if (!(authctxt->pam->state & PAM_S_DONE_OPEN_SESSION)) {
3547c478bd9Sstevel@tonic-gate retval = pam_open_session(authctxt->pam->h, 0);
3557c478bd9Sstevel@tonic-gate authctxt->pam->last_pam_retval = retval;
3567c478bd9Sstevel@tonic-gate if (retval != PAM_SUCCESS)
3577c478bd9Sstevel@tonic-gate return retval;
3587c478bd9Sstevel@tonic-gate authctxt->pam->state |= PAM_S_DONE_OPEN_SESSION;
3597c478bd9Sstevel@tonic-gate }
3607c478bd9Sstevel@tonic-gate
3617c478bd9Sstevel@tonic-gate /*
3627c478bd9Sstevel@tonic-gate * All PAM work done successfully.
3637c478bd9Sstevel@tonic-gate *
3647c478bd9Sstevel@tonic-gate * PAM handle stays around so we can call pam_close_session() on
3657c478bd9Sstevel@tonic-gate * it later.
3667c478bd9Sstevel@tonic-gate */
3677c478bd9Sstevel@tonic-gate return PAM_SUCCESS;
3687c478bd9Sstevel@tonic-gate }
3697c478bd9Sstevel@tonic-gate
3707c478bd9Sstevel@tonic-gate /*
3717c478bd9Sstevel@tonic-gate * PAM conversation function for non-interactive userauth methods that
3727c478bd9Sstevel@tonic-gate * really cannot do any prompting. Password userauth and CHANGEREQ can
3737c478bd9Sstevel@tonic-gate * always set the PAM_AUTHTOK and PAM_OLDAUTHTOK items to avoid
3747c478bd9Sstevel@tonic-gate * conversation (and if they do and nonetheless some module tries to
3757c478bd9Sstevel@tonic-gate * converse, then password userauth / CHANGEREQ MUST fail).
3767c478bd9Sstevel@tonic-gate *
3777c478bd9Sstevel@tonic-gate * Except, PAM_TEXT_INFO and PAM_ERROR_MSG prompts can be squirelled
3787c478bd9Sstevel@tonic-gate * away and shown to the user later.
3797c478bd9Sstevel@tonic-gate *
3807c478bd9Sstevel@tonic-gate * Keyboard-interactive userauth has its own much more interesting
3817c478bd9Sstevel@tonic-gate * conversation function.
3827c478bd9Sstevel@tonic-gate *
3837c478bd9Sstevel@tonic-gate */
3847c478bd9Sstevel@tonic-gate static int
do_pam_conversation(int num_msg,const struct pam_message ** msg,struct pam_response ** resp,void * appdata_ptr)3857c478bd9Sstevel@tonic-gate do_pam_conversation(int num_msg, const struct pam_message **msg,
3867c478bd9Sstevel@tonic-gate struct pam_response **resp, void *appdata_ptr)
3877c478bd9Sstevel@tonic-gate {
3887c478bd9Sstevel@tonic-gate struct pam_response *reply;
3897c478bd9Sstevel@tonic-gate int count;
3907c478bd9Sstevel@tonic-gate
3917c478bd9Sstevel@tonic-gate /* PAM will free this later */
3927c478bd9Sstevel@tonic-gate reply = xmalloc(num_msg * sizeof(*reply));
3937c478bd9Sstevel@tonic-gate
3947c478bd9Sstevel@tonic-gate (void) memset(reply, 0, num_msg * sizeof(*reply));
3957c478bd9Sstevel@tonic-gate
3967c478bd9Sstevel@tonic-gate for (count = 0; count < num_msg; count++) {
3977c478bd9Sstevel@tonic-gate /*
3987c478bd9Sstevel@tonic-gate * We can't use stdio yet, queue messages for
3997c478bd9Sstevel@tonic-gate * printing later
4007c478bd9Sstevel@tonic-gate */
4017c478bd9Sstevel@tonic-gate switch(PAM_MSG_MEMBER(msg, count, msg_style)) {
4027c478bd9Sstevel@tonic-gate case PAM_PROMPT_ECHO_ON:
4037c478bd9Sstevel@tonic-gate xfree(reply);
4047c478bd9Sstevel@tonic-gate return PAM_CONV_ERR;
4057c478bd9Sstevel@tonic-gate case PAM_PROMPT_ECHO_OFF:
4067c478bd9Sstevel@tonic-gate xfree(reply);
4077c478bd9Sstevel@tonic-gate return PAM_CONV_ERR;
4087c478bd9Sstevel@tonic-gate break;
4097c478bd9Sstevel@tonic-gate case PAM_ERROR_MSG:
4107c478bd9Sstevel@tonic-gate case PAM_TEXT_INFO:
4117c478bd9Sstevel@tonic-gate if (PAM_MSG_MEMBER(msg, count, msg) != NULL) {
4127c478bd9Sstevel@tonic-gate message_cat(&__pam_msg,
4137c478bd9Sstevel@tonic-gate PAM_MSG_MEMBER(msg, count, msg));
4147c478bd9Sstevel@tonic-gate }
4157c478bd9Sstevel@tonic-gate reply[count].resp = xstrdup("");
4167c478bd9Sstevel@tonic-gate reply[count].resp_retcode = PAM_SUCCESS;
4177c478bd9Sstevel@tonic-gate break;
4187c478bd9Sstevel@tonic-gate default:
4197c478bd9Sstevel@tonic-gate xfree(reply);
4207c478bd9Sstevel@tonic-gate return PAM_CONV_ERR;
4217c478bd9Sstevel@tonic-gate }
4227c478bd9Sstevel@tonic-gate }
4237c478bd9Sstevel@tonic-gate
4247c478bd9Sstevel@tonic-gate *resp = reply;
4257c478bd9Sstevel@tonic-gate
4267c478bd9Sstevel@tonic-gate return PAM_SUCCESS;
4277c478bd9Sstevel@tonic-gate }
4287c478bd9Sstevel@tonic-gate
4297c478bd9Sstevel@tonic-gate /* Called at exit to cleanly shutdown PAM */
4307c478bd9Sstevel@tonic-gate static void
do_pam_cleanup_proc(void * context)4317c478bd9Sstevel@tonic-gate do_pam_cleanup_proc(void *context)
4327c478bd9Sstevel@tonic-gate {
4337c478bd9Sstevel@tonic-gate int pam_retval;
4347c478bd9Sstevel@tonic-gate pam_stuff *pam = (pam_stuff *) context;
4357c478bd9Sstevel@tonic-gate
4367c478bd9Sstevel@tonic-gate if (pam == NULL)
4377c478bd9Sstevel@tonic-gate return;
4387c478bd9Sstevel@tonic-gate
4397c478bd9Sstevel@tonic-gate if (pam->authctxt != NULL && pam->authctxt->pam == pam) {
4407c478bd9Sstevel@tonic-gate pam->authctxt->pam_retval = pam->last_pam_retval;
4417c478bd9Sstevel@tonic-gate pam->authctxt->pam = NULL;
4427c478bd9Sstevel@tonic-gate pam->authctxt = NULL;
4437c478bd9Sstevel@tonic-gate }
4447c478bd9Sstevel@tonic-gate
4457c478bd9Sstevel@tonic-gate if (pam->h == NULL)
4467c478bd9Sstevel@tonic-gate return;
4477c478bd9Sstevel@tonic-gate
4487c478bd9Sstevel@tonic-gate /*
4497c478bd9Sstevel@tonic-gate * We're in fatal_cleanup() or not in userauth or without a
4507c478bd9Sstevel@tonic-gate * channel -- can't converse now, too bad.
4517c478bd9Sstevel@tonic-gate */
4527c478bd9Sstevel@tonic-gate pam_retval = pam_set_item(pam->h, PAM_CONV, NULL);
4537c478bd9Sstevel@tonic-gate if (pam_retval != PAM_SUCCESS) {
4547c478bd9Sstevel@tonic-gate log("Cannot remove PAM conv, close session or delete creds[%d]: %.200s",
4557c478bd9Sstevel@tonic-gate pam_retval, PAM_STRERROR(pam->h, pam_retval));
4567c478bd9Sstevel@tonic-gate goto cleanup;
4577c478bd9Sstevel@tonic-gate }
4587c478bd9Sstevel@tonic-gate
4597c478bd9Sstevel@tonic-gate if (pam->state & PAM_S_DONE_OPEN_SESSION) {
4607c478bd9Sstevel@tonic-gate pam_retval = pam_close_session(pam->h, 0);
4617c478bd9Sstevel@tonic-gate if (pam_retval != PAM_SUCCESS)
4627c478bd9Sstevel@tonic-gate log("Cannot close PAM session[%d]: %.200s",
4637c478bd9Sstevel@tonic-gate pam_retval, PAM_STRERROR(pam->h, pam_retval));
4647c478bd9Sstevel@tonic-gate }
4657c478bd9Sstevel@tonic-gate
4667c478bd9Sstevel@tonic-gate if (pam->state & PAM_S_DONE_SETCRED) {
4677c478bd9Sstevel@tonic-gate pam_retval = pam_setcred(pam->h, PAM_DELETE_CRED);
4687c478bd9Sstevel@tonic-gate if (pam_retval != PAM_SUCCESS)
4697c478bd9Sstevel@tonic-gate debug("Cannot delete credentials[%d]: %.200s",
4707c478bd9Sstevel@tonic-gate pam_retval, PAM_STRERROR(pam->h, pam_retval));
4717c478bd9Sstevel@tonic-gate }
4727c478bd9Sstevel@tonic-gate
4737c478bd9Sstevel@tonic-gate cleanup:
4747c478bd9Sstevel@tonic-gate
4757c478bd9Sstevel@tonic-gate /* Use the previous PAM result, if not PAM_SUCCESS for pam_end() */
4767c478bd9Sstevel@tonic-gate if (pam->last_pam_retval != PAM_SUCCESS)
4777c478bd9Sstevel@tonic-gate pam_retval = pam_end(pam->h, pam->last_pam_retval);
4787c478bd9Sstevel@tonic-gate else if (pam_retval != PAM_SUCCESS)
4797c478bd9Sstevel@tonic-gate pam_retval = pam_end(pam->h, pam_retval);
4807c478bd9Sstevel@tonic-gate else
4817c478bd9Sstevel@tonic-gate pam_retval = pam_end(pam->h, PAM_ABORT);
4827c478bd9Sstevel@tonic-gate
4837c478bd9Sstevel@tonic-gate if (pam_retval != PAM_SUCCESS)
4847c478bd9Sstevel@tonic-gate log("Cannot release PAM authentication[%d]: %.200s",
4857c478bd9Sstevel@tonic-gate pam_retval, PAM_STRERROR(pam->h, pam_retval));
4867c478bd9Sstevel@tonic-gate
4877c478bd9Sstevel@tonic-gate xfree(pam);
4887c478bd9Sstevel@tonic-gate }
4897c478bd9Sstevel@tonic-gate
4907c478bd9Sstevel@tonic-gate /* Attempt password authentation using PAM */
4917c478bd9Sstevel@tonic-gate int
auth_pam_password(Authctxt * authctxt,const char * password)4927c478bd9Sstevel@tonic-gate auth_pam_password(Authctxt *authctxt, const char *password)
4937c478bd9Sstevel@tonic-gate {
4947c478bd9Sstevel@tonic-gate int retval;
4957c478bd9Sstevel@tonic-gate
4967c478bd9Sstevel@tonic-gate /* Ensure we have a fresh PAM handle / state */
4977c478bd9Sstevel@tonic-gate new_start_pam(authctxt, &conv);
4987c478bd9Sstevel@tonic-gate
4997c478bd9Sstevel@tonic-gate retval = pam_set_item(authctxt->pam->h, PAM_AUTHTOK, password);
500bca3984eSBrent Paulson if (retval != PAM_SUCCESS) {
501bca3984eSBrent Paulson authctxt->pam->last_pam_retval = retval;
5027c478bd9Sstevel@tonic-gate return 1;
503bca3984eSBrent Paulson }
5047c478bd9Sstevel@tonic-gate
5057c478bd9Sstevel@tonic-gate retval = pam_authenticate(authctxt->pam->h,
5067c478bd9Sstevel@tonic-gate options.permit_empty_passwd ? 0 :
5077c478bd9Sstevel@tonic-gate PAM_DISALLOW_NULL_AUTHTOK);
5087c478bd9Sstevel@tonic-gate
509bca3984eSBrent Paulson if (retval != PAM_SUCCESS) {
510bca3984eSBrent Paulson authctxt->pam->last_pam_retval = retval;
5117c478bd9Sstevel@tonic-gate return 0;
512bca3984eSBrent Paulson }
5137c478bd9Sstevel@tonic-gate
5147c478bd9Sstevel@tonic-gate if ((retval = finish_userauth_do_pam(authctxt)) != PAM_SUCCESS)
5157c478bd9Sstevel@tonic-gate return 0;
5167c478bd9Sstevel@tonic-gate
5177c478bd9Sstevel@tonic-gate if (authctxt->method)
5187c478bd9Sstevel@tonic-gate authctxt->method->authenticated = 1; /* SSHv2 */
5197c478bd9Sstevel@tonic-gate
5207c478bd9Sstevel@tonic-gate return 1;
5217c478bd9Sstevel@tonic-gate }
5227c478bd9Sstevel@tonic-gate
5237c478bd9Sstevel@tonic-gate int
do_pam_non_initial_userauth(Authctxt * authctxt)5247c478bd9Sstevel@tonic-gate do_pam_non_initial_userauth(Authctxt *authctxt)
5257c478bd9Sstevel@tonic-gate {
5267c478bd9Sstevel@tonic-gate new_start_pam(authctxt, NULL);
5277c478bd9Sstevel@tonic-gate return (finish_userauth_do_pam(authctxt) == PAM_SUCCESS);
5287c478bd9Sstevel@tonic-gate }
5297c478bd9Sstevel@tonic-gate
5307c478bd9Sstevel@tonic-gate /* Cleanly shutdown PAM */
finish_pam(Authctxt * authctxt)5317c478bd9Sstevel@tonic-gate void finish_pam(Authctxt *authctxt)
5327c478bd9Sstevel@tonic-gate {
5337c478bd9Sstevel@tonic-gate fatal_remove_cleanup(&do_pam_cleanup_proc, authctxt->pam);
5347c478bd9Sstevel@tonic-gate do_pam_cleanup_proc(authctxt->pam);
5357c478bd9Sstevel@tonic-gate }
5367c478bd9Sstevel@tonic-gate
5377c478bd9Sstevel@tonic-gate static
5387c478bd9Sstevel@tonic-gate char **
find_env(char ** env,char * var)5397c478bd9Sstevel@tonic-gate find_env(char **env, char *var)
5407c478bd9Sstevel@tonic-gate {
5417c478bd9Sstevel@tonic-gate char **p;
5427c478bd9Sstevel@tonic-gate int len;
5437c478bd9Sstevel@tonic-gate
5447c478bd9Sstevel@tonic-gate if (strchr(var, '=') == NULL)
5457c478bd9Sstevel@tonic-gate len = strlen(var);
5467c478bd9Sstevel@tonic-gate else
5477c478bd9Sstevel@tonic-gate len = (strchr(var, '=') - var) + 1;
5487c478bd9Sstevel@tonic-gate
5497c478bd9Sstevel@tonic-gate for ( p = env ; p != NULL && *p != NULL ; p++ ) {
5507c478bd9Sstevel@tonic-gate if (strncmp(*p, var, len) == 0)
5517c478bd9Sstevel@tonic-gate return (p);
5527c478bd9Sstevel@tonic-gate }
5537c478bd9Sstevel@tonic-gate
5547c478bd9Sstevel@tonic-gate return (NULL);
5557c478bd9Sstevel@tonic-gate }
5567c478bd9Sstevel@tonic-gate
5577c478bd9Sstevel@tonic-gate /* Return list of PAM environment strings */
5587c478bd9Sstevel@tonic-gate char **
fetch_pam_environment(Authctxt * authctxt)5597c478bd9Sstevel@tonic-gate fetch_pam_environment(Authctxt *authctxt)
5607c478bd9Sstevel@tonic-gate {
5617c478bd9Sstevel@tonic-gate #ifdef HAVE_PAM_GETENVLIST
5627c478bd9Sstevel@tonic-gate char **penv;
5637c478bd9Sstevel@tonic-gate
5647c478bd9Sstevel@tonic-gate if (authctxt == NULL || authctxt->pam == NULL ||
5657c478bd9Sstevel@tonic-gate authctxt->pam->h == NULL)
5667c478bd9Sstevel@tonic-gate return (NULL);
5677c478bd9Sstevel@tonic-gate
5687c478bd9Sstevel@tonic-gate penv = pam_getenvlist(authctxt->pam->h);
5697c478bd9Sstevel@tonic-gate
5707c478bd9Sstevel@tonic-gate return (penv);
5717c478bd9Sstevel@tonic-gate #else /* HAVE_PAM_GETENVLIST */
5727c478bd9Sstevel@tonic-gate return(NULL);
5737c478bd9Sstevel@tonic-gate #endif /* HAVE_PAM_GETENVLIST */
5747c478bd9Sstevel@tonic-gate }
5757c478bd9Sstevel@tonic-gate
free_pam_environment(char ** env)5767c478bd9Sstevel@tonic-gate void free_pam_environment(char **env)
5777c478bd9Sstevel@tonic-gate {
5787c478bd9Sstevel@tonic-gate int i;
5797c478bd9Sstevel@tonic-gate
5807c478bd9Sstevel@tonic-gate if (env != NULL) {
5817c478bd9Sstevel@tonic-gate for (i = 0; env[i] != NULL; i++)
5827c478bd9Sstevel@tonic-gate xfree(env[i]);
5837c478bd9Sstevel@tonic-gate }
5847c478bd9Sstevel@tonic-gate
5857c478bd9Sstevel@tonic-gate xfree(env);
5867c478bd9Sstevel@tonic-gate }
5877c478bd9Sstevel@tonic-gate
5887c478bd9Sstevel@tonic-gate /* Print any messages that have been generated during authentication */
5897c478bd9Sstevel@tonic-gate /* or account checking to stderr */
print_pam_messages(void)5907c478bd9Sstevel@tonic-gate void print_pam_messages(void)
5917c478bd9Sstevel@tonic-gate {
5927c478bd9Sstevel@tonic-gate if (__pam_msg != NULL)
5937c478bd9Sstevel@tonic-gate (void) fputs(__pam_msg, stderr);
5947c478bd9Sstevel@tonic-gate }
5957c478bd9Sstevel@tonic-gate
5967c478bd9Sstevel@tonic-gate /* Append a message to buffer */
message_cat(char ** p,const char * a)5977c478bd9Sstevel@tonic-gate void message_cat(char **p, const char *a)
5987c478bd9Sstevel@tonic-gate {
5997c478bd9Sstevel@tonic-gate char *cp;
6007c478bd9Sstevel@tonic-gate size_t new_len;
6017c478bd9Sstevel@tonic-gate
6027c478bd9Sstevel@tonic-gate new_len = strlen(a);
6037c478bd9Sstevel@tonic-gate
6047c478bd9Sstevel@tonic-gate if (*p) {
6057c478bd9Sstevel@tonic-gate size_t len = strlen(*p);
6067c478bd9Sstevel@tonic-gate
6077c478bd9Sstevel@tonic-gate *p = xrealloc(*p, new_len + len + 2);
6087c478bd9Sstevel@tonic-gate cp = *p + len;
6097c478bd9Sstevel@tonic-gate } else
6107c478bd9Sstevel@tonic-gate *p = cp = xmalloc(new_len + 2);
6117c478bd9Sstevel@tonic-gate
6127c478bd9Sstevel@tonic-gate (void) memcpy(cp, a, new_len);
6137c478bd9Sstevel@tonic-gate cp[new_len] = '\n';
6147c478bd9Sstevel@tonic-gate cp[new_len + 1] = '\0';
6157c478bd9Sstevel@tonic-gate }
6167c478bd9Sstevel@tonic-gate
6177c478bd9Sstevel@tonic-gate #endif /* USE_PAM */
618