summaryrefslogtreecommitdiff
path: root/usr/src/cmd/ssh/sshd/auth2-pam.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/ssh/sshd/auth2-pam.c')
-rw-r--r--usr/src/cmd/ssh/sshd/auth2-pam.c458
1 files changed, 0 insertions, 458 deletions
diff --git a/usr/src/cmd/ssh/sshd/auth2-pam.c b/usr/src/cmd/ssh/sshd/auth2-pam.c
deleted file mode 100644
index 1b0fa40f2b..0000000000
--- a/usr/src/cmd/ssh/sshd/auth2-pam.c
+++ /dev/null
@@ -1,458 +0,0 @@
-/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-#include "includes.h"
-
-RCSID("$Id: auth2-pam.c,v 1.14 2002/06/28 16:48:12 mouring Exp $");
-
-#ifdef USE_PAM
-#include <security/pam_appl.h>
-
-#include "ssh.h"
-#include "ssh2.h"
-#include "auth.h"
-#include "auth-pam.h"
-#include "auth-options.h"
-#include "packet.h"
-#include "xmalloc.h"
-#include "dispatch.h"
-#include "canohost.h"
-#include "log.h"
-#include "servconf.h"
-#include "misc.h"
-
-#ifdef HAVE_BSM
-#include "bsmaudit.h"
-#endif /* HAVE_BSM */
-
-extern u_int utmp_len;
-extern ServerOptions options;
-
-extern Authmethod method_kbdint;
-extern Authmethod method_passwd;
-
-#define SSHD_PAM_KBDINT_SVC "sshd-kbdint"
-/* Maximum attempts for changing expired password */
-#define DEF_ATTEMPTS 3
-
-static int do_pam_conv_kbd_int(int num_msg,
- struct pam_message **msg, struct pam_response **resp,
- void *appdata_ptr);
-static void input_userauth_info_response_pam(int type,
- u_int32_t seqnr,
- void *ctxt);
-
-static struct pam_conv conv2 = {
- do_pam_conv_kbd_int,
- NULL,
-};
-
-static void do_pam_kbdint_cleanup(pam_handle_t *pamh);
-static void do_pam_kbdint(Authctxt *authctxt);
-
-void
-auth2_pam(Authctxt *authctxt)
-{
- if (authctxt->user == NULL)
- fatal("auth2_pam: internal error: no user");
- if (authctxt->method == NULL)
- fatal("auth2_pam: internal error: no method");
-
- conv2.appdata_ptr = authctxt;
- new_start_pam(authctxt, &conv2);
-
- authctxt->method->method_data = NULL; /* freed in the conv func */
- dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE,
- &input_userauth_info_response_pam);
-
- /*
- * Since password userauth and keyboard-interactive userauth
- * both use PAM, and since keyboard-interactive is so much
- * better than password userauth, we should not allow the user
- * to try password userauth after trying keyboard-interactive.
- */
- if (method_passwd.enabled)
- *method_passwd.enabled = 0;
-
- do_pam_kbdint(authctxt);
-
- dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL);
-}
-
-static void
-do_pam_kbdint(Authctxt *authctxt)
-{
- int retval, retval2;
- pam_handle_t *pamh = authctxt->pam->h;
- const char *where = "authenticating";
- char *text = NULL;
-
- debug2("Calling pam_authenticate()");
- retval = pam_authenticate(pamh,
- options.permit_empty_passwd ? 0 :
- PAM_DISALLOW_NULL_AUTHTOK);
-
- if (retval != PAM_SUCCESS)
- goto cleanup;
-
- debug2("kbd-int: pam_authenticate() succeeded");
- where = "authorizing";
- retval = pam_acct_mgmt(pamh, 0);
-
- if (retval == PAM_NEW_AUTHTOK_REQD) {
- if (authctxt->valid && authctxt->pw != NULL) {
- /* send password expiration warning */
- message_cat(&text,
- gettext("Warning: Your password has expired,"
- " please change it now."));
- packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST);
- packet_put_cstring(""); /* name */
- packet_put_utf8_cstring(text); /* instructions */
- packet_put_cstring(""); /* language, unused */
- packet_put_int(0);
- packet_send();
- packet_write_wait();
- debug("expiration message sent");
- if (text)
- xfree(text);
- /*
- * wait for the response so it does not mix
- * with the upcoming PAM conversation
- */
- packet_read_expect(SSH2_MSG_USERAUTH_INFO_RESPONSE);
- /*
- * Can't use temporarily_use_uid() and restore_uid()
- * here because we need (euid == 0 && ruid == pw_uid)
- * whereas temporarily_use_uid() arranges for
- * (suid = 0 && euid == pw_uid && ruid == pw_uid).
- */
- (void) setreuid(authctxt->pw->pw_uid, -1);
- debug2("kbd-int: changing expired password");
- where = "changing authentication tokens (password)";
- /*
- * Depending on error returned from pam_chauthtok, we
- * need to try to change password a few times before
- * we error out and return.
- */
- int tries = 0;
- while ((retval = pam_chauthtok(pamh,
- PAM_CHANGE_EXPIRED_AUTHTOK)) != PAM_SUCCESS) {
- if (tries++ < DEF_ATTEMPTS) {
- if ((retval == PAM_AUTHTOK_ERR) ||
- (retval == PAM_TRY_AGAIN)) {
- continue;
- }
- }
- break;
- }
- audit_sshd_chauthtok(retval, authctxt->pw->pw_uid,
- authctxt->pw->pw_gid);
- (void) setreuid(0, -1);
- } else {
- retval = PAM_PERM_DENIED;
- }
- }
-
- if (retval != PAM_SUCCESS)
- goto cleanup;
-
- authctxt->pam->state |= PAM_S_DONE_ACCT_MGMT;
-
- retval = finish_userauth_do_pam(authctxt);
-
- if (retval != PAM_SUCCESS)
- goto cleanup;
-
- /*
- * PAM handle stays around so we can call pam_close_session()
- * on it later.
- */
- authctxt->method->authenticated = 1;
- debug2("kbd-int: success (pam->state == %x)", authctxt->pam->state);
- return;
-
-cleanup:
- /*
- * Check for abandonment and cleanup. When kbdint is abandoned
- * authctxt->pam->h is NULLed and by this point a new handle may
- * be allocated.
- */
- if (authctxt->pam->h != pamh) {
- log("Keyboard-interactive (PAM) userauth abandoned "
- "while %s", where);
- if ((retval2 = pam_end(pamh, retval)) != PAM_SUCCESS) {
- log("Cannot close PAM handle after "
- "kbd-int userauth abandonment[%d]: %.200s",
- retval2, PAM_STRERROR(pamh, retval2));
- }
- authctxt->method->abandoned = 1;
-
- /*
- * Avoid double counting; these are incremented in
- * kbdint_pam_abandon() so that they reflect the correct
- * count when userauth_finish() is called before
- * unwinding the dispatch_run() loop, but they are
- * incremented again in input_userauth_request() when
- * the loop is unwound, right here.
- */
- if (authctxt->method->abandons)
- authctxt->method->abandons--;
- if (authctxt->method->attempts)
- authctxt->method->attempts--;
- }
- else {
- /* Save error value for pam_end() */
- authctxt->pam->last_pam_retval = retval;
- log("Keyboard-interactive (PAM) userauth failed[%d] "
- "while %s: %.200s", retval, where,
- PAM_STRERROR(pamh, retval));
- /* pam handle can be reused elsewhere, so no pam_end() here */
- }
-
- return;
-}
-
-static int
-do_pam_conv_kbd_int(int num_msg, struct pam_message **msg,
- struct pam_response **resp, void *appdata_ptr)
-{
- int i, j;
- char *text;
- Convctxt *conv_ctxt;
- Authctxt *authctxt = (Authctxt *)appdata_ptr;
-
- if (!authctxt || !authctxt->method) {
- debug("Missing state during PAM conversation");
- return PAM_CONV_ERR;
- }
-
- conv_ctxt = xmalloc(sizeof(Convctxt));
- (void) memset(conv_ctxt, 0, sizeof(Convctxt));
- conv_ctxt->finished = 0;
- conv_ctxt->num_received = 0;
- conv_ctxt->num_expected = 0;
- conv_ctxt->prompts = xmalloc(sizeof(int) * num_msg);
- conv_ctxt->responses = xmalloc(sizeof(struct pam_response) * num_msg);
- (void) memset(conv_ctxt->responses, 0, sizeof(struct pam_response) * num_msg);
-
- text = NULL;
- for (i = 0, conv_ctxt->num_expected = 0; i < num_msg; i++) {
- int style = PAM_MSG_MEMBER(msg, i, msg_style);
- switch (style) {
- case PAM_PROMPT_ECHO_ON:
- debug2("PAM echo on prompt: %s",
- PAM_MSG_MEMBER(msg, i, msg));
- conv_ctxt->num_expected++;
- break;
- case PAM_PROMPT_ECHO_OFF:
- debug2("PAM echo off prompt: %s",
- PAM_MSG_MEMBER(msg, i, msg));
- conv_ctxt->num_expected++;
- break;
- case PAM_TEXT_INFO:
- debug2("PAM text info prompt: %s",
- PAM_MSG_MEMBER(msg, i, msg));
- message_cat(&text, PAM_MSG_MEMBER(msg, i, msg));
- break;
- case PAM_ERROR_MSG:
- debug2("PAM error prompt: %s",
- PAM_MSG_MEMBER(msg, i, msg));
- message_cat(&text, PAM_MSG_MEMBER(msg, i, msg));
- break;
- default:
- /* Capture all these messages to be sent at once */
- message_cat(&text, PAM_MSG_MEMBER(msg, i, msg));
- break;
- }
- }
-
- if (conv_ctxt->num_expected == 0 && text == NULL) {
- xfree(conv_ctxt->prompts);
- xfree(conv_ctxt->responses);
- xfree(conv_ctxt);
- return PAM_SUCCESS;
- }
-
- authctxt->method->method_data = (void *) conv_ctxt;
-
- packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST);
- packet_put_cstring(""); /* Name */
- packet_put_utf8_cstring(text ? text : ""); /* Instructions */
- packet_put_cstring(""); /* Language */
- packet_put_int(conv_ctxt->num_expected);
-
- if (text)
- xfree(text);
-
- for (i = 0, j = 0; i < num_msg; i++) {
- int style = PAM_MSG_MEMBER(msg, i, msg_style);
-
- /* Skip messages which don't need a reply */
- if (style != PAM_PROMPT_ECHO_ON && style != PAM_PROMPT_ECHO_OFF)
- continue;
-
- conv_ctxt->prompts[j++] = i;
- packet_put_utf8_cstring(PAM_MSG_MEMBER(msg, i, msg));
- packet_put_char(style == PAM_PROMPT_ECHO_ON);
- }
- packet_send();
- packet_write_wait();
-
- /*
- * Here the dispatch_run() loop is nested. It should be unwound
- * if keyboard-interactive userauth is abandoned (or restarted;
- * same thing).
- *
- * The condition for breaking out of the nested dispatch_run() loop is
- * ((got kbd-int info reponse) || (kbd-int abandoned))
- *
- * conv_ctxt->finished is set in either of those cases.
- *
- * When abandonment is detected the conv_ctxt->finished is set as
- * is conv_ctxt->abandoned, causing this function to signal
- * userauth nested dispatch_run() loop unwinding and to return
- * PAM_CONV_ERR;
- */
- debug2("Nesting dispatch_run loop");
- dispatch_run(DISPATCH_BLOCK, &conv_ctxt->finished, appdata_ptr);
- debug2("Nested dispatch_run loop exited");
-
- if (conv_ctxt->abandoned) {
- authctxt->unwind_dispatch_loop = 1;
- xfree(conv_ctxt->prompts);
- xfree(conv_ctxt->responses);
- xfree(conv_ctxt);
- debug("PAM conv function returns PAM_CONV_ERR");
- return PAM_CONV_ERR;
- }
-
- if (conv_ctxt->num_received == conv_ctxt->num_expected) {
- *resp = conv_ctxt->responses;
- xfree(conv_ctxt->prompts);
- xfree(conv_ctxt);
- debug("PAM conv function returns PAM_SUCCESS");
- return PAM_SUCCESS;
- }
-
- debug("PAM conv function returns PAM_CONV_ERR");
- xfree(conv_ctxt->prompts);
- xfree(conv_ctxt->responses);
- xfree(conv_ctxt);
- return PAM_CONV_ERR;
-}
-
-static void
-input_userauth_info_response_pam(int type, u_int32_t seqnr, void *ctxt)
-{
- Authctxt *authctxt = ctxt;
- Convctxt *conv_ctxt;
- unsigned int nresp = 0, rlen = 0, i = 0;
- char *resp;
-
- if (authctxt == NULL)
- fatal("input_userauth_info_response_pam: no authentication context");
-
- /* Check for spurious/unexpected info response */
- if (method_kbdint.method_data == NULL) {
- debug("input_userauth_info_response_pam: no method context");
- return;
- }
-
- conv_ctxt = (Convctxt *) method_kbdint.method_data;
-
- nresp = packet_get_int(); /* Number of responses. */
- debug("got %d responses", nresp);
-
-
-#if 0
- if (nresp != conv_ctxt->num_expected)
- fatal("%s: Received incorrect number of responses "
- "(expected %d, received %u)", __func__,
- conv_ctxt->num_expected, nresp);
-#endif
-
- if (nresp > 100)
- fatal("%s: too many replies", __func__);
-
- for (i = 0; i < nresp && i < conv_ctxt->num_expected ; i++) {
- int j = conv_ctxt->prompts[i];
-
- /*
- * We assume that ASCII charset is used for password
- * although the protocol requires UTF-8 encoding for the
- * password string. Therefore, we don't perform code
- * conversion for the string.
- */
- resp = packet_get_string(&rlen);
- if (i < conv_ctxt->num_expected) {
- conv_ctxt->responses[j].resp_retcode = PAM_SUCCESS;
- conv_ctxt->responses[j].resp = xstrdup(resp);
- conv_ctxt->num_received++;
- }
- xfree(resp);
- }
-
- if (nresp < conv_ctxt->num_expected)
- fatal("%s: too few replies (%d < %d)", __func__,
- nresp, conv_ctxt->num_expected);
-
- /* XXX - This could make a covert channel... */
- if (nresp > conv_ctxt->num_expected)
- debug("Ignoring additional PAM replies");
-
- conv_ctxt->finished = 1;
-
- packet_check_eom();
-}
-
-#if 0
-int
-kbdint_pam_abandon_chk(Authctxt *authctxt, Authmethod *method)
-{
- if (!method)
- return 0; /* fatal(), really; it'll happen somewhere else */
-
- if (!method->method_data)
- return 0;
-
- return 1;
-}
-#endif
-
-void
-kbdint_pam_abandon(Authctxt *authctxt, Authmethod *method)
-{
- Convctxt *conv_ctxt;
-
- /*
- * But, if it ever becomes desirable and possible to support
- * kbd-int userauth abandonment, here's what must be done.
- */
- if (!method)
- return;
-
- if (!method->method_data)
- return;
-
- conv_ctxt = (Convctxt *) method->method_data;
-
- /* dispatch_run() loop will exit */
- conv_ctxt->abandoned = 1;
- conv_ctxt->finished = 1;
-
- /*
- * The method_data will be free in the corresponding, active
- * conversation function
- */
- method->method_data = NULL;
-
- /* update counts that can't be updated elsewhere */
- method->abandons++;
- method->attempts++;
-
- /* Finally, we cannot re-use the current current PAM handle */
- authctxt->pam->h = NULL; /* Let the conv function cleanup */
-}
-#endif