summaryrefslogtreecommitdiff
path: root/auth-pam.c
diff options
context:
space:
mode:
Diffstat (limited to 'auth-pam.c')
-rw-r--r--auth-pam.c962
1 files changed, 614 insertions, 348 deletions
diff --git a/auth-pam.c b/auth-pam.c
index cb57ba110..754cbf6df 100644
--- a/auth-pam.c
+++ b/auth-pam.c
@@ -1,5 +1,11 @@
1/* 1/*-
2 * Copyright (c) 2000 Damien Miller. All rights reserved. 2 * Copyright (c) 2002 Networks Associates Technology, Inc.
3 * All rights reserved.
4 *
5 * This software was developed for the FreeBSD Project by ThinkSec AS and
6 * NAI Labs, the Security Research Division of Network Associates, Inc.
7 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
8 * DARPA CHATS research program.
3 * 9 *
4 * Redistribution and use in source and binary forms, with or without 10 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 11 * modification, are permitted provided that the following conditions
@@ -10,446 +16,706 @@
10 * notice, this list of conditions and the following disclaimer in the 16 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution. 17 * documentation and/or other materials provided with the distribution.
12 * 18 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
23 */ 30 */
24 31
32/* Based on $FreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des Exp $ */
25#include "includes.h" 33#include "includes.h"
34RCSID("$Id: auth-pam.c,v 1.72.2.1 2003/09/16 06:00:52 djm Exp $");
26 35
27#ifdef USE_PAM 36#ifdef USE_PAM
28#include "xmalloc.h" 37#include <security/pam_appl.h>
29#include "log.h" 38
30#include "auth.h" 39#include "auth.h"
31#include "auth-options.h"
32#include "auth-pam.h" 40#include "auth-pam.h"
33#include "servconf.h" 41#include "buffer.h"
42#include "bufaux.h"
34#include "canohost.h" 43#include "canohost.h"
44#include "log.h"
45#include "monitor_wrap.h"
46#include "msg.h"
47#include "packet.h"
35#include "readpass.h" 48#include "readpass.h"
49#include "servconf.h"
50#include "ssh2.h"
51#include "xmalloc.h"
52#include "auth-options.h"
36 53
37extern char *__progname; 54extern ServerOptions options;
38 55
39extern int use_privsep; 56#define __unused
40 57
41RCSID("$Id: auth-pam.c,v 1.55.4.1 2003/04/29 09:12:08 djm Exp $"); 58#ifdef USE_POSIX_THREADS
59#include <pthread.h>
60/*
61 * Avoid namespace clash when *not* using pthreads for systems *with*
62 * pthreads, which unconditionally define pthread_t via sys/types.h
63 * (e.g. Linux)
64 */
65typedef pthread_t sp_pthread_t;
66#else
67/*
68 * Simulate threads with processes.
69 */
70typedef pid_t sp_pthread_t;
42 71
43#define NEW_AUTHTOK_MSG \ 72static void
44 "Warning: Your password has expired, please change it now." 73pthread_exit(void *value __unused)
45#define NEW_AUTHTOK_MSG_PRIVSEP \ 74{
46 "Your password has expired, the session cannot proceed." 75 _exit(0);
76}
47 77
48static int do_pam_conversation(int num_msg, const struct pam_message **msg, 78static int
49 struct pam_response **resp, void *appdata_ptr); 79pthread_create(sp_pthread_t *thread, const void *attr __unused,
80 void *(*thread_start)(void *), void *arg)
81{
82 pid_t pid;
83
84 switch ((pid = fork())) {
85 case -1:
86 error("fork(): %s", strerror(errno));
87 return (-1);
88 case 0:
89 thread_start(arg);
90 _exit(1);
91 default:
92 *thread = pid;
93 return (0);
94 }
95}
50 96
51/* module-local variables */ 97static int
52static struct pam_conv conv = { 98pthread_cancel(sp_pthread_t thread)
53 (int (*)())do_pam_conversation,
54 NULL
55};
56static char *__pam_msg = NULL;
57static pam_handle_t *__pamh = NULL;
58static const char *__pampasswd = NULL;
59
60/* states for do_pam_conversation() */
61enum { INITIAL_LOGIN, OTHER } pamstate = INITIAL_LOGIN;
62/* remember whether pam_acct_mgmt() returned PAM_NEW_AUTHTOK_REQD */
63static int password_change_required = 0;
64/* remember whether the last pam_authenticate() succeeded or not */
65static int was_authenticated = 0;
66
67/* Remember what has been initialised */
68static int session_opened = 0;
69static int creds_set = 0;
70
71/* accessor which allows us to switch conversation structs according to
72 * the authentication method being used */
73void do_pam_set_conv(struct pam_conv *conv)
74{ 99{
75 pam_set_item(__pamh, PAM_CONV, conv); 100 return (kill(thread, SIGTERM));
76} 101}
77 102
78/* start an authentication run */ 103static int
79int do_pam_authenticate(int flags) 104pthread_join(sp_pthread_t thread, void **value __unused)
80{ 105{
81 int retval = pam_authenticate(__pamh, flags); 106 int status;
82 was_authenticated = (retval == PAM_SUCCESS); 107
83 return retval; 108 waitpid(thread, &status, 0);
109 return (status);
84} 110}
111#endif
112
113
114static pam_handle_t *sshpam_handle;
115static int sshpam_err;
116static int sshpam_authenticated;
117static int sshpam_new_authtok_reqd;
118static int sshpam_session_open;
119static int sshpam_cred_established;
120
121struct pam_ctxt {
122 sp_pthread_t pam_thread;
123 int pam_psock;
124 int pam_csock;
125 int pam_done;
126};
127
128static void sshpam_free_ctx(void *);
85 129
86/* 130/*
87 * PAM conversation function. 131 * Conversation function for authentication thread.
88 * There are two states this can run in.
89 *
90 * INITIAL_LOGIN mode simply feeds the password from the client into
91 * PAM in response to PAM_PROMPT_ECHO_OFF, and collects output
92 * messages with into __pam_msg. This is used during initial
93 * authentication to bypass the normal PAM password prompt.
94 *
95 * OTHER mode handles PAM_PROMPT_ECHO_OFF with read_passphrase()
96 * and outputs messages to stderr. This mode is used if pam_chauthtok()
97 * is called to update expired passwords.
98 */ 132 */
99static int do_pam_conversation(int num_msg, const struct pam_message **msg, 133static int
100 struct pam_response **resp, void *appdata_ptr) 134sshpam_thread_conv(int n, const struct pam_message **msg,
135 struct pam_response **resp, void *data)
101{ 136{
102 struct pam_response *reply; 137 Buffer buffer;
103 int count; 138 struct pam_ctxt *ctxt;
104 char buf[1024]; 139 int i;
105 140
106 /* PAM will free this later */ 141 ctxt = data;
107 reply = xmalloc(num_msg * sizeof(*reply)); 142 if (n <= 0 || n > PAM_MAX_NUM_MSG)
108 143 return (PAM_CONV_ERR);
109 for (count = 0; count < num_msg; count++) { 144 *resp = xmalloc(n * sizeof **resp);
110 if (pamstate == INITIAL_LOGIN) { 145 buffer_init(&buffer);
111 /* 146 for (i = 0; i < n; ++i) {
112 * We can't use stdio yet, queue messages for 147 resp[i]->resp_retcode = 0;
113 * printing later 148 resp[i]->resp = NULL;
114 */ 149 switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
115 switch(PAM_MSG_MEMBER(msg, count, msg_style)) { 150 case PAM_PROMPT_ECHO_OFF:
116 case PAM_PROMPT_ECHO_ON: 151 buffer_put_cstring(&buffer, PAM_MSG_MEMBER(msg, i, msg));
117 xfree(reply); 152 ssh_msg_send(ctxt->pam_csock,
118 return PAM_CONV_ERR; 153 PAM_MSG_MEMBER(msg, i, msg_style), &buffer);
119 case PAM_PROMPT_ECHO_OFF: 154 ssh_msg_recv(ctxt->pam_csock, &buffer);
120 if (__pampasswd == NULL) { 155 if (buffer_get_char(&buffer) != PAM_AUTHTOK)
121 xfree(reply); 156 goto fail;
122 return PAM_CONV_ERR; 157 resp[i]->resp = buffer_get_string(&buffer, NULL);
123 } 158 break;
124 reply[count].resp = xstrdup(__pampasswd); 159 case PAM_PROMPT_ECHO_ON:
125 reply[count].resp_retcode = PAM_SUCCESS; 160 buffer_put_cstring(&buffer, PAM_MSG_MEMBER(msg, i, msg));
126 break; 161 ssh_msg_send(ctxt->pam_csock,
127 case PAM_ERROR_MSG: 162 PAM_MSG_MEMBER(msg, i, msg_style), &buffer);
128 case PAM_TEXT_INFO: 163 ssh_msg_recv(ctxt->pam_csock, &buffer);
129 if (PAM_MSG_MEMBER(msg, count, msg) != NULL) { 164 if (buffer_get_char(&buffer) != PAM_AUTHTOK)
130 message_cat(&__pam_msg, 165 goto fail;
131 PAM_MSG_MEMBER(msg, count, msg)); 166 resp[i]->resp = buffer_get_string(&buffer, NULL);
132 } 167 break;
133 reply[count].resp = xstrdup(""); 168 case PAM_ERROR_MSG:
134 reply[count].resp_retcode = PAM_SUCCESS; 169 buffer_put_cstring(&buffer, PAM_MSG_MEMBER(msg, i, msg));
135 break; 170 ssh_msg_send(ctxt->pam_csock,
136 default: 171 PAM_MSG_MEMBER(msg, i, msg_style), &buffer);
137 xfree(reply); 172 break;
138 return PAM_CONV_ERR; 173 case PAM_TEXT_INFO:
139 } 174 buffer_put_cstring(&buffer, PAM_MSG_MEMBER(msg, i, msg));
140 } else { 175 ssh_msg_send(ctxt->pam_csock,
141 /* 176 PAM_MSG_MEMBER(msg, i, msg_style), &buffer);
142 * stdio is connected, so interact directly 177 break;
143 */ 178 default:
144 switch(PAM_MSG_MEMBER(msg, count, msg_style)) { 179 goto fail;
145 case PAM_PROMPT_ECHO_ON:
146 fputs(PAM_MSG_MEMBER(msg, count, msg), stderr);
147 fgets(buf, sizeof(buf), stdin);
148 reply[count].resp = xstrdup(buf);
149 reply[count].resp_retcode = PAM_SUCCESS;
150 break;
151 case PAM_PROMPT_ECHO_OFF:
152 reply[count].resp =
153 read_passphrase(PAM_MSG_MEMBER(msg, count,
154 msg), RP_ALLOW_STDIN);
155 reply[count].resp_retcode = PAM_SUCCESS;
156 break;
157 case PAM_ERROR_MSG:
158 case PAM_TEXT_INFO:
159 if (PAM_MSG_MEMBER(msg, count, msg) != NULL)
160 fprintf(stderr, "%s\n",
161 PAM_MSG_MEMBER(msg, count, msg));
162 reply[count].resp = xstrdup("");
163 reply[count].resp_retcode = PAM_SUCCESS;
164 break;
165 default:
166 xfree(reply);
167 return PAM_CONV_ERR;
168 }
169 } 180 }
181 buffer_clear(&buffer);
170 } 182 }
171 183 buffer_free(&buffer);
172 *resp = reply; 184 return (PAM_SUCCESS);
173 185 fail:
174 return PAM_SUCCESS; 186 while (i)
187 xfree(resp[--i]);
188 xfree(*resp);
189 *resp = NULL;
190 buffer_free(&buffer);
191 return (PAM_CONV_ERR);
175} 192}
176 193
177/* Called at exit to cleanly shutdown PAM */ 194/*
178void do_pam_cleanup_proc(void *context) 195 * Authentication thread.
196 */
197static void *
198sshpam_thread(void *ctxtp)
179{ 199{
180 int pam_retval = PAM_SUCCESS; 200 struct pam_ctxt *ctxt = ctxtp;
201 Buffer buffer;
202 struct pam_conv sshpam_conv;
203#ifndef USE_POSIX_THREADS
204 const char *pam_user;
205
206 pam_get_item(sshpam_handle, PAM_USER, (const void **)&pam_user);
207 setproctitle("%s [pam]", pam_user);
208#endif
181 209
182 if (__pamh && session_opened) { 210 sshpam_conv.conv = sshpam_thread_conv;
183 pam_retval = pam_close_session(__pamh, 0); 211 sshpam_conv.appdata_ptr = ctxt;
184 if (pam_retval != PAM_SUCCESS) 212
185 log("Cannot close PAM session[%d]: %.200s", 213 buffer_init(&buffer);
186 pam_retval, PAM_STRERROR(__pamh, pam_retval)); 214 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
187 } 215 (const void *)&sshpam_conv);
216 if (sshpam_err != PAM_SUCCESS)
217 goto auth_fail;
218 sshpam_err = pam_authenticate(sshpam_handle, 0);
219 if (sshpam_err != PAM_SUCCESS)
220 goto auth_fail;
221 buffer_put_cstring(&buffer, "OK");
222 ssh_msg_send(ctxt->pam_csock, sshpam_err, &buffer);
223 buffer_free(&buffer);
224 pthread_exit(NULL);
225
226 auth_fail:
227 buffer_put_cstring(&buffer,
228 pam_strerror(sshpam_handle, sshpam_err));
229 ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer);
230 buffer_free(&buffer);
231 pthread_exit(NULL);
232
233 return (NULL); /* Avoid warning for non-pthread case */
234}
188 235
189 if (__pamh && creds_set) { 236static void
190 pam_retval = pam_setcred(__pamh, PAM_DELETE_CRED); 237sshpam_thread_cleanup(void *ctxtp)
191 if (pam_retval != PAM_SUCCESS) 238{
192 debug("Cannot delete credentials[%d]: %.200s", 239 struct pam_ctxt *ctxt = ctxtp;
193 pam_retval, PAM_STRERROR(__pamh, pam_retval));
194 }
195 240
196 if (__pamh) { 241 pthread_cancel(ctxt->pam_thread);
197 pam_retval = pam_end(__pamh, pam_retval); 242 pthread_join(ctxt->pam_thread, NULL);
198 if (pam_retval != PAM_SUCCESS) 243 close(ctxt->pam_psock);
199 log("Cannot release PAM authentication[%d]: %.200s", 244 close(ctxt->pam_csock);
200 pam_retval, PAM_STRERROR(__pamh, pam_retval));
201 }
202} 245}
203 246
204/* Attempt password authentication using PAM */ 247static int
205int auth_pam_password(Authctxt *authctxt, const char *password) 248sshpam_null_conv(int n, const struct pam_message **msg,
249 struct pam_response **resp, void *data)
206{ 250{
207 extern ServerOptions options; 251 return (PAM_CONV_ERR);
208 int pam_retval; 252}
209 struct passwd *pw = authctxt->pw;
210 253
211 do_pam_set_conv(&conv); 254static struct pam_conv null_conv = { sshpam_null_conv, NULL };
212 255
213 __pampasswd = password; 256static void
257sshpam_cleanup(void *arg)
258{
259 (void)arg;
260 debug("PAM: cleanup");
261 pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv);
262 if (sshpam_cred_established) {
263 pam_setcred(sshpam_handle, PAM_DELETE_CRED);
264 sshpam_cred_established = 0;
265 }
266 if (sshpam_session_open) {
267 pam_close_session(sshpam_handle, PAM_SILENT);
268 sshpam_session_open = 0;
269 }
270 sshpam_authenticated = sshpam_new_authtok_reqd = 0;
271 pam_end(sshpam_handle, sshpam_err);
272 sshpam_handle = NULL;
273}
214 274
215 pamstate = INITIAL_LOGIN; 275static int
216 pam_retval = do_pam_authenticate( 276sshpam_init(const char *user)
217 options.permit_empty_passwd == 0 ? PAM_DISALLOW_NULL_AUTHTOK : 0); 277{
218 if (pam_retval == PAM_SUCCESS && pw) { 278 extern u_int utmp_len;
219 debug("PAM password authentication accepted for " 279 extern char *__progname;
220 "%.100s", pw->pw_name); 280 const char *pam_rhost, *pam_user;
221 return 1; 281
222 } else { 282 if (sshpam_handle != NULL) {
223 debug("PAM password authentication failed for " 283 /* We already have a PAM context; check if the user matches */
224 "%.100s: %s", pw ? pw->pw_name : "an illegal user", 284 sshpam_err = pam_get_item(sshpam_handle,
225 PAM_STRERROR(__pamh, pam_retval)); 285 PAM_USER, (const void **)&pam_user);
226 return 0; 286 if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0)
287 return (0);
288 fatal_remove_cleanup(sshpam_cleanup, NULL);
289 pam_end(sshpam_handle, sshpam_err);
290 sshpam_handle = NULL;
291 }
292 debug("PAM: initializing for \"%s\"", user);
293 sshpam_err =
294 pam_start(SSHD_PAM_SERVICE, user, &null_conv, &sshpam_handle);
295 if (sshpam_err != PAM_SUCCESS) {
296 pam_end(sshpam_handle, sshpam_err);
297 sshpam_handle = NULL;
298 return (-1);
227 } 299 }
300 pam_rhost = get_remote_name_or_ip(utmp_len, options.use_dns);
301 debug("PAM: setting PAM_RHOST to \"%s\"", pam_rhost);
302 sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, pam_rhost);
303 if (sshpam_err != PAM_SUCCESS) {
304 pam_end(sshpam_handle, sshpam_err);
305 sshpam_handle = NULL;
306 return (-1);
307 }
308#ifdef PAM_TTY_KLUDGE
309 /*
310 * Some silly PAM modules (e.g. pam_time) require a TTY to operate.
311 * sshd doesn't set the tty until too late in the auth process and
312 * may not even set one (for tty-less connections)
313 */
314 debug("PAM: setting PAM_TTY to \"ssh\"");
315 sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, "ssh");
316 if (sshpam_err != PAM_SUCCESS) {
317 pam_end(sshpam_handle, sshpam_err);
318 sshpam_handle = NULL;
319 return (-1);
320 }
321#endif
322 fatal_add_cleanup(sshpam_cleanup, NULL);
323 return (0);
228} 324}
229 325
230/* Do account management using PAM */ 326static void *
231int do_pam_account(char *username, char *remote_user) 327sshpam_init_ctx(Authctxt *authctxt)
232{ 328{
233 int pam_retval; 329 struct pam_ctxt *ctxt;
330 int socks[2];
234 331
235 do_pam_set_conv(&conv); 332 /* Refuse to start if we don't have PAM enabled */
333 if (!options.use_pam)
334 return NULL;
236 335
237 if (remote_user) { 336 /* Initialize PAM */
238 debug("PAM setting ruser to \"%.200s\"", remote_user); 337 if (sshpam_init(authctxt->user) == -1) {
239 pam_retval = pam_set_item(__pamh, PAM_RUSER, remote_user); 338 error("PAM: initialization failed");
240 if (pam_retval != PAM_SUCCESS) 339 return (NULL);
241 fatal("PAM set ruser failed[%d]: %.200s", pam_retval,
242 PAM_STRERROR(__pamh, pam_retval));
243 } 340 }
244 341
245 pam_retval = pam_acct_mgmt(__pamh, 0); 342 ctxt = xmalloc(sizeof *ctxt);
246 debug2("pam_acct_mgmt() = %d", pam_retval); 343 ctxt->pam_done = 0;
247 switch (pam_retval) { 344
248 case PAM_SUCCESS: 345 /* Start the authentication thread */
249 /* This is what we want */ 346 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) {
250 break; 347 error("PAM: failed create sockets: %s", strerror(errno));
251#if 0 348 xfree(ctxt);
252 case PAM_NEW_AUTHTOK_REQD: 349 return (NULL);
253 message_cat(&__pam_msg, use_privsep ? 350 }
254 NEW_AUTHTOK_MSG_PRIVSEP : NEW_AUTHTOK_MSG); 351 ctxt->pam_psock = socks[0];
255 /* flag that password change is necessary */ 352 ctxt->pam_csock = socks[1];
256 password_change_required = 1; 353 if (pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt) == -1) {
257 /* disallow other functionality for now */ 354 error("PAM: failed to start authentication thread: %s",
258 no_port_forwarding_flag |= 2; 355 strerror(errno));
259 no_agent_forwarding_flag |= 2; 356 close(socks[0]);
260 no_x11_forwarding_flag |= 2; 357 close(socks[1]);
358 xfree(ctxt);
359 return (NULL);
360 }
361 fatal_add_cleanup(sshpam_thread_cleanup, ctxt);
362 return (ctxt);
363}
364
365static int
366sshpam_query(void *ctx, char **name, char **info,
367 u_int *num, char ***prompts, u_int **echo_on)
368{
369 Buffer buffer;
370 struct pam_ctxt *ctxt = ctx;
371 size_t plen;
372 u_char type;
373 char *msg;
374 size_t len;
375
376 buffer_init(&buffer);
377 *name = xstrdup("");
378 *info = xstrdup("");
379 *prompts = xmalloc(sizeof(char *));
380 **prompts = NULL;
381 plen = 0;
382 *echo_on = xmalloc(sizeof(u_int));
383 while (ssh_msg_recv(ctxt->pam_psock, &buffer) == 0) {
384 type = buffer_get_char(&buffer);
385 msg = buffer_get_string(&buffer, NULL);
386 switch (type) {
387 case PAM_PROMPT_ECHO_ON:
388 case PAM_PROMPT_ECHO_OFF:
389 *num = 1;
390 len = plen + strlen(msg) + 1;
391 **prompts = xrealloc(**prompts, len);
392 plen += snprintf(**prompts + plen, len, "%s", msg);
393 **echo_on = (type == PAM_PROMPT_ECHO_ON);
394 xfree(msg);
395 return (0);
396 case PAM_ERROR_MSG:
397 case PAM_TEXT_INFO:
398 /* accumulate messages */
399 len = plen + strlen(msg) + 1;
400 **prompts = xrealloc(**prompts, len);
401 plen += snprintf(**prompts + plen, len, "%s", msg);
402 xfree(msg);
261 break; 403 break;
404 case PAM_SUCCESS:
405 case PAM_AUTH_ERR:
406 if (**prompts != NULL) {
407 /* drain any accumulated messages */
408#if 0 /* XXX - not compatible with privsep */
409 packet_start(SSH2_MSG_USERAUTH_BANNER);
410 packet_put_cstring(**prompts);
411 packet_put_cstring("");
412 packet_send();
413 packet_write_wait();
262#endif 414#endif
415 xfree(**prompts);
416 **prompts = NULL;
417 }
418 if (type == PAM_SUCCESS) {
419 *num = 0;
420 **echo_on = 0;
421 ctxt->pam_done = 1;
422 xfree(msg);
423 return (0);
424 }
425 error("PAM: %s", msg);
263 default: 426 default:
264 log("PAM rejected by account configuration[%d]: " 427 *num = 0;
265 "%.200s", pam_retval, PAM_STRERROR(__pamh, 428 **echo_on = 0;
266 pam_retval)); 429 xfree(msg);
267 return(0); 430 ctxt->pam_done = -1;
431 return (-1);
432 }
268 } 433 }
434 return (-1);
435}
269 436
270 return(1); 437/* XXX - see also comment in auth-chall.c:verify_response */
438static int
439sshpam_respond(void *ctx, u_int num, char **resp)
440{
441 Buffer buffer;
442 struct pam_ctxt *ctxt = ctx;
443
444 debug2("PAM: %s", __func__);
445 switch (ctxt->pam_done) {
446 case 1:
447 sshpam_authenticated = 1;
448 return (0);
449 case 0:
450 break;
451 default:
452 return (-1);
453 }
454 if (num != 1) {
455 error("PAM: expected one response, got %u", num);
456 return (-1);
457 }
458 buffer_init(&buffer);
459 buffer_put_cstring(&buffer, *resp);
460 ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer);
461 buffer_free(&buffer);
462 return (1);
271} 463}
272 464
273/* Do PAM-specific session initialisation */ 465static void
274void do_pam_session(char *username, const char *ttyname) 466sshpam_free_ctx(void *ctxtp)
275{ 467{
276 int pam_retval; 468 struct pam_ctxt *ctxt = ctxtp;
277 469
278 do_pam_set_conv(&conv); 470 fatal_remove_cleanup(sshpam_thread_cleanup, ctxt);
471 sshpam_thread_cleanup(ctxtp);
472 xfree(ctxt);
473 /*
474 * We don't call sshpam_cleanup() here because we may need the PAM
475 * handle at a later stage, e.g. when setting up a session. It's
476 * still on the cleanup list, so pam_end() *will* be called before
477 * the server process terminates.
478 */
479}
279 480
280 if (ttyname != NULL) { 481KbdintDevice sshpam_device = {
281 debug("PAM setting tty to \"%.200s\"", ttyname); 482 "pam",
282 pam_retval = pam_set_item(__pamh, PAM_TTY, ttyname); 483 sshpam_init_ctx,
283 if (pam_retval != PAM_SUCCESS) 484 sshpam_query,
284 fatal("PAM set tty failed[%d]: %.200s", 485 sshpam_respond,
285 pam_retval, PAM_STRERROR(__pamh, pam_retval)); 486 sshpam_free_ctx
286 } 487};
287 488
288 pam_retval = pam_open_session(__pamh, 0); 489KbdintDevice mm_sshpam_device = {
289 if (pam_retval != PAM_SUCCESS) 490 "pam",
290 fatal("PAM session setup failed[%d]: %.200s", 491 mm_sshpam_init_ctx,
291 pam_retval, PAM_STRERROR(__pamh, pam_retval)); 492 mm_sshpam_query,
493 mm_sshpam_respond,
494 mm_sshpam_free_ctx
495};
496
497/*
498 * This replaces auth-pam.c
499 */
500void
501start_pam(const char *user)
502{
503 if (!options.use_pam)
504 fatal("PAM: initialisation requested when UsePAM=no");
292 505
293 session_opened = 1; 506 if (sshpam_init(user) == -1)
507 fatal("PAM: initialisation failed");
294} 508}
295 509
296/* Set PAM credentials */ 510void
297void do_pam_setcred(int init) 511finish_pam(void)
298{ 512{
299 int pam_retval; 513 fatal_remove_cleanup(sshpam_cleanup, NULL);
514 sshpam_cleanup(NULL);
515}
300 516
301 if (__pamh == NULL) 517u_int
302 return; 518do_pam_account(void)
519{
520 sshpam_err = pam_acct_mgmt(sshpam_handle, 0);
521 debug3("%s: pam_acct_mgmt = %d", __func__, sshpam_err);
522
523 if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD)
524 return (0);
525
526 if (sshpam_err == PAM_NEW_AUTHTOK_REQD) {
527 sshpam_new_authtok_reqd = 1;
528
529 /* Prevent forwardings until password changed */
530 no_port_forwarding_flag |= 2;
531 no_agent_forwarding_flag |= 2;
532 no_x11_forwarding_flag |= 2;
533 }
303 534
304 do_pam_set_conv(&conv); 535 return (1);
305
306 debug("PAM establishing creds");
307 pam_retval = pam_setcred(__pamh,
308 init ? PAM_ESTABLISH_CRED : PAM_REINITIALIZE_CRED);
309 if (pam_retval != PAM_SUCCESS) {
310 if (was_authenticated)
311 fatal("PAM setcred failed[%d]: %.200s",
312 pam_retval, PAM_STRERROR(__pamh, pam_retval));
313 else
314 debug("PAM setcred failed[%d]: %.200s",
315 pam_retval, PAM_STRERROR(__pamh, pam_retval));
316 } else
317 creds_set = 1;
318} 536}
319 537
320/* accessor function for file scope static variable */ 538void
321int is_pam_password_change_required(void) 539do_pam_session(void)
322{ 540{
323 return password_change_required; 541 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
542 (const void *)&null_conv);
543 if (sshpam_err != PAM_SUCCESS)
544 fatal("PAM: failed to set PAM_CONV: %s",
545 pam_strerror(sshpam_handle, sshpam_err));
546 sshpam_err = pam_open_session(sshpam_handle, 0);
547 if (sshpam_err != PAM_SUCCESS)
548 fatal("PAM: pam_open_session(): %s",
549 pam_strerror(sshpam_handle, sshpam_err));
550 sshpam_session_open = 1;
324} 551}
325 552
326/* 553void
327 * Have user change authentication token if pam_acct_mgmt() indicated 554do_pam_set_tty(const char *tty)
328 * it was expired. This needs to be called after an interactive
329 * session is established and the user's pty is connected to
330 * stdin/stdout/stderr.
331 */
332void do_pam_chauthtok(void)
333{ 555{
334 int pam_retval; 556 if (tty != NULL) {
335 557 debug("PAM: setting PAM_TTY to \"%s\"", tty);
336 do_pam_set_conv(&conv); 558 sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, tty);
337 559 if (sshpam_err != PAM_SUCCESS)
338 if (password_change_required) { 560 fatal("PAM: failed to set PAM_TTY: %s",
339 if (use_privsep) 561 pam_strerror(sshpam_handle, sshpam_err));
340 fatal("Password changing is currently unsupported" 562 }
341 " with privilege separation"); 563}
342 pamstate = OTHER; 564
343 pam_retval = pam_chauthtok(__pamh, PAM_CHANGE_EXPIRED_AUTHTOK); 565void
344 if (pam_retval != PAM_SUCCESS) 566do_pam_setcred(int init)
345 fatal("PAM pam_chauthtok failed[%d]: %.200s", 567{
346 pam_retval, PAM_STRERROR(__pamh, pam_retval)); 568 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
347#if 0 569 (const void *)&null_conv);
348 /* XXX: This would need to be done in the parent process, 570 if (sshpam_err != PAM_SUCCESS)
349 * but there's currently no way to pass such request. */ 571 fatal("PAM: failed to set PAM_CONV: %s",
350 no_port_forwarding_flag &= ~2; 572 pam_strerror(sshpam_handle, sshpam_err));
351 no_agent_forwarding_flag &= ~2; 573 if (init) {
352 no_x11_forwarding_flag &= ~2; 574 debug("PAM: establishing credentials");
353 if (!no_port_forwarding_flag && options.allow_tcp_forwarding) 575 sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED);
354 channel_permit_all_opens(); 576 } else {
355#endif 577 debug("PAM: reinitializing credentials");
578 sshpam_err = pam_setcred(sshpam_handle, PAM_REINITIALIZE_CRED);
579 }
580 if (sshpam_err == PAM_SUCCESS) {
581 sshpam_cred_established = 1;
582 return;
356 } 583 }
584 if (sshpam_authenticated)
585 fatal("PAM: pam_setcred(): %s",
586 pam_strerror(sshpam_handle, sshpam_err));
587 else
588 debug("PAM: pam_setcred(): %s",
589 pam_strerror(sshpam_handle, sshpam_err));
357} 590}
358 591
359/* Cleanly shutdown PAM */ 592int
360void finish_pam(void) 593is_pam_password_change_required(void)
361{ 594{
362 do_pam_cleanup_proc(NULL); 595 return (sshpam_new_authtok_reqd);
363 fatal_remove_cleanup(&do_pam_cleanup_proc, NULL);
364} 596}
365 597
366/* Start PAM authentication for specified account */ 598static int
367void start_pam(const char *user) 599pam_chauthtok_conv(int n, const struct pam_message **msg,
600 struct pam_response **resp, void *data)
368{ 601{
369 int pam_retval; 602 char input[PAM_MAX_MSG_SIZE];
370 extern ServerOptions options; 603 int i;
371 extern u_int utmp_len;
372 const char *rhost;
373 604
374 debug("Starting up PAM with username \"%.200s\"", user); 605 if (n <= 0 || n > PAM_MAX_NUM_MSG)
606 return (PAM_CONV_ERR);
607 *resp = xmalloc(n * sizeof **resp);
608 for (i = 0; i < n; ++i) {
609 switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
610 case PAM_PROMPT_ECHO_OFF:
611 resp[i]->resp =
612 read_passphrase(PAM_MSG_MEMBER(msg, i, msg),
613 RP_ALLOW_STDIN);
614 resp[i]->resp_retcode = PAM_SUCCESS;
615 break;
616 case PAM_PROMPT_ECHO_ON:
617 fputs(PAM_MSG_MEMBER(msg, i, msg), stderr);
618 fgets(input, sizeof input, stdin);
619 resp[i]->resp = xstrdup(input);
620 resp[i]->resp_retcode = PAM_SUCCESS;
621 break;
622 case PAM_ERROR_MSG:
623 case PAM_TEXT_INFO:
624 fputs(PAM_MSG_MEMBER(msg, i, msg), stderr);
625 resp[i]->resp_retcode = PAM_SUCCESS;
626 break;
627 default:
628 goto fail;
629 }
630 }
631 return (PAM_SUCCESS);
632 fail:
633 while (i)
634 xfree(resp[--i]);
635 xfree(*resp);
636 *resp = NULL;
637 return (PAM_CONV_ERR);
638}
375 639
376 pam_retval = pam_start(SSHD_PAM_SERVICE, user, &conv, &__pamh); 640/*
641 * XXX this should be done in the authentication phase, but ssh1 doesn't
642 * support that
643 */
644void
645do_pam_chauthtok(void)
646{
647 struct pam_conv pam_conv;
648
649 pam_conv.conv = pam_chauthtok_conv;
650 pam_conv.appdata_ptr = NULL;
651
652 if (use_privsep)
653 fatal("Password expired (unable to change with privsep)");
654 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
655 (const void *)&pam_conv);
656 if (sshpam_err != PAM_SUCCESS)
657 fatal("PAM: failed to set PAM_CONV: %s",
658 pam_strerror(sshpam_handle, sshpam_err));
659 debug("PAM: changing password");
660 sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
661 if (sshpam_err != PAM_SUCCESS)
662 fatal("PAM: pam_chauthtok(): %s",
663 pam_strerror(sshpam_handle, sshpam_err));
664}
377 665
378 if (pam_retval != PAM_SUCCESS) 666/*
379 fatal("PAM initialisation failed[%d]: %.200s", 667 * Set a PAM environment string. We need to do this so that the session
380 pam_retval, PAM_STRERROR(__pamh, pam_retval)); 668 * modules can handle things like Kerberos/GSI credentials that appear
669 * during the ssh authentication process.
670 */
381 671
382 rhost = get_remote_name_or_ip(utmp_len, options.verify_reverse_mapping); 672int
383 debug("PAM setting rhost to \"%.200s\"", rhost); 673do_pam_putenv(char *name, char *value)
674{
675 int ret = 1;
676#ifdef HAVE_PAM_PUTENV
677 char *compound;
678 size_t len;
384 679
385 pam_retval = pam_set_item(__pamh, PAM_RHOST, rhost); 680 len = strlen(name) + strlen(value) + 2;
386 if (pam_retval != PAM_SUCCESS) 681 compound = xmalloc(len);
387 fatal("PAM set rhost failed[%d]: %.200s", pam_retval,
388 PAM_STRERROR(__pamh, pam_retval));
389#ifdef PAM_TTY_KLUDGE
390 /*
391 * Some PAM modules (e.g. pam_time) require a TTY to operate,
392 * and will fail in various stupid ways if they don't get one.
393 * sshd doesn't set the tty until too late in the auth process and may
394 * not even need one (for tty-less connections)
395 * Kludge: Set a fake PAM_TTY
396 */
397 pam_retval = pam_set_item(__pamh, PAM_TTY, "NODEVssh");
398 if (pam_retval != PAM_SUCCESS)
399 fatal("PAM set tty failed[%d]: %.200s",
400 pam_retval, PAM_STRERROR(__pamh, pam_retval));
401#endif /* PAM_TTY_KLUDGE */
402 682
403 fatal_add_cleanup(&do_pam_cleanup_proc, NULL); 683 snprintf(compound, len, "%s=%s", name, value);
404} 684 ret = pam_putenv(sshpam_handle, compound);
685 xfree(compound);
686#endif
405 687
406/* Return list of PAM environment strings */ 688 return (ret);
407char **fetch_pam_environment(void)
408{
409#ifdef HAVE_PAM_GETENVLIST
410 return(pam_getenvlist(__pamh));
411#else /* HAVE_PAM_GETENVLIST */
412 return(NULL);
413#endif /* HAVE_PAM_GETENVLIST */
414} 689}
415 690
416void free_pam_environment(char **env) 691void
692print_pam_messages(void)
417{ 693{
418 int i; 694 /* XXX */
419
420 if (env != NULL) {
421 for (i = 0; env[i] != NULL; i++)
422 xfree(env[i]);
423 }
424} 695}
425 696
426/* Print any messages that have been generated during authentication */ 697char **
427/* or account checking to stderr */ 698fetch_pam_environment(void)
428void print_pam_messages(void)
429{ 699{
430 if (__pam_msg != NULL) 700#ifdef HAVE_PAM_GETENVLIST
431 fputs(__pam_msg, stderr); 701 debug("PAM: retrieving environment");
702 return (pam_getenvlist(sshpam_handle));
703#else
704 return (NULL);
705#endif
432} 706}
433 707
434/* Append a message to buffer */ 708void
435void message_cat(char **p, const char *a) 709free_pam_environment(char **env)
436{ 710{
437 char *cp; 711 char **envp;
438 size_t new_len;
439
440 new_len = strlen(a);
441 712
442 if (*p) { 713 if (env == NULL)
443 size_t len = strlen(*p); 714 return;
444
445 *p = xrealloc(*p, new_len + len + 2);
446 cp = *p + len;
447 } else
448 *p = cp = xmalloc(new_len + 2);
449 715
450 memcpy(cp, a, new_len); 716 for (envp = env; *envp; envp++)
451 cp[new_len] = '\n'; 717 xfree(*envp);
452 cp[new_len + 1] = '\0'; 718 xfree(env);
453} 719}
454 720
455#endif /* USE_PAM */ 721#endif /* USE_PAM */