summaryrefslogtreecommitdiff
path: root/auth-pam.c
diff options
context:
space:
mode:
Diffstat (limited to 'auth-pam.c')
-rw-r--r--auth-pam.c972
1 files changed, 631 insertions, 341 deletions
diff --git a/auth-pam.c b/auth-pam.c
index cb57ba110..04cabbca2 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,730 @@
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.2 2003/09/23 09:24:21 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 = NULL;
115static int sshpam_err = 0;
116static int sshpam_authenticated = 0;
117static int sshpam_new_authtok_reqd = 0;
118static int sshpam_session_open = 0;
119static int sshpam_cred_established = 0;
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{
137 Buffer buffer;
138 struct pam_ctxt *ctxt;
102 struct pam_response *reply; 139 struct pam_response *reply;
103 int count; 140 int i;
104 char buf[1024]; 141
105 142 *resp = NULL;
106 /* PAM will free this later */ 143
107 reply = xmalloc(num_msg * sizeof(*reply)); 144 ctxt = data;
108 145 if (n <= 0 || n > PAM_MAX_NUM_MSG)
109 for (count = 0; count < num_msg; count++) { 146 return (PAM_CONV_ERR);
110 if (pamstate == INITIAL_LOGIN) { 147
111 /* 148 if ((reply = malloc(n * sizeof(*reply))) == NULL)
112 * We can't use stdio yet, queue messages for 149 return (PAM_CONV_ERR);
113 * printing later 150 memset(reply, 0, n * sizeof(*reply));
114 */ 151
115 switch(PAM_MSG_MEMBER(msg, count, msg_style)) { 152 buffer_init(&buffer);
116 case PAM_PROMPT_ECHO_ON: 153 for (i = 0; i < n; ++i) {
117 xfree(reply); 154 switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
118 return PAM_CONV_ERR; 155 case PAM_PROMPT_ECHO_OFF:
119 case PAM_PROMPT_ECHO_OFF: 156 buffer_put_cstring(&buffer,
120 if (__pampasswd == NULL) { 157 PAM_MSG_MEMBER(msg, i, msg));
121 xfree(reply); 158 ssh_msg_send(ctxt->pam_csock,
122 return PAM_CONV_ERR; 159 PAM_MSG_MEMBER(msg, i, msg_style), &buffer);
123 } 160 ssh_msg_recv(ctxt->pam_csock, &buffer);
124 reply[count].resp = xstrdup(__pampasswd); 161 if (buffer_get_char(&buffer) != PAM_AUTHTOK)
125 reply[count].resp_retcode = PAM_SUCCESS; 162 goto fail;
126 break; 163 reply[i].resp = buffer_get_string(&buffer, NULL);
127 case PAM_ERROR_MSG: 164 break;
128 case PAM_TEXT_INFO: 165 case PAM_PROMPT_ECHO_ON:
129 if (PAM_MSG_MEMBER(msg, count, msg) != NULL) { 166 buffer_put_cstring(&buffer,
130 message_cat(&__pam_msg, 167 PAM_MSG_MEMBER(msg, i, msg));
131 PAM_MSG_MEMBER(msg, count, msg)); 168 ssh_msg_send(ctxt->pam_csock,
132 } 169 PAM_MSG_MEMBER(msg, i, msg_style), &buffer);
133 reply[count].resp = xstrdup(""); 170 ssh_msg_recv(ctxt->pam_csock, &buffer);
134 reply[count].resp_retcode = PAM_SUCCESS; 171 if (buffer_get_char(&buffer) != PAM_AUTHTOK)
135 break; 172 goto fail;
136 default: 173 reply[i].resp = buffer_get_string(&buffer, NULL);
137 xfree(reply); 174 break;
138 return PAM_CONV_ERR; 175 case PAM_ERROR_MSG:
139 } 176 buffer_put_cstring(&buffer,
140 } else { 177 PAM_MSG_MEMBER(msg, i, msg));
141 /* 178 ssh_msg_send(ctxt->pam_csock,
142 * stdio is connected, so interact directly 179 PAM_MSG_MEMBER(msg, i, msg_style), &buffer);
143 */ 180 break;
144 switch(PAM_MSG_MEMBER(msg, count, msg_style)) { 181 case PAM_TEXT_INFO:
145 case PAM_PROMPT_ECHO_ON: 182 buffer_put_cstring(&buffer,
146 fputs(PAM_MSG_MEMBER(msg, count, msg), stderr); 183 PAM_MSG_MEMBER(msg, i, msg));
147 fgets(buf, sizeof(buf), stdin); 184 ssh_msg_send(ctxt->pam_csock,
148 reply[count].resp = xstrdup(buf); 185 PAM_MSG_MEMBER(msg, i, msg_style), &buffer);
149 reply[count].resp_retcode = PAM_SUCCESS; 186 break;
150 break; 187 default:
151 case PAM_PROMPT_ECHO_OFF: 188 goto fail;
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 } 189 }
190 buffer_clear(&buffer);
170 } 191 }
171 192 buffer_free(&buffer);
172 *resp = reply; 193 *resp = reply;
194 return (PAM_SUCCESS);
173 195
174 return PAM_SUCCESS; 196 fail:
197 for(i = 0; i < n; i++) {
198 if (reply[i].resp != NULL)
199 xfree(reply[i].resp);
200 }
201 xfree(reply);
202 buffer_free(&buffer);
203 return (PAM_CONV_ERR);
175} 204}
176 205
177/* Called at exit to cleanly shutdown PAM */ 206/*
178void do_pam_cleanup_proc(void *context) 207 * Authentication thread.
208 */
209static void *
210sshpam_thread(void *ctxtp)
179{ 211{
180 int pam_retval = PAM_SUCCESS; 212 struct pam_ctxt *ctxt = ctxtp;
213 Buffer buffer;
214 struct pam_conv sshpam_conv;
215#ifndef USE_POSIX_THREADS
216 const char *pam_user;
217
218 pam_get_item(sshpam_handle, PAM_USER, (const void **)&pam_user);
219 setproctitle("%s [pam]", pam_user);
220#endif
181 221
182 if (__pamh && session_opened) { 222 sshpam_conv.conv = sshpam_thread_conv;
183 pam_retval = pam_close_session(__pamh, 0); 223 sshpam_conv.appdata_ptr = ctxt;
184 if (pam_retval != PAM_SUCCESS) 224
185 log("Cannot close PAM session[%d]: %.200s", 225 buffer_init(&buffer);
186 pam_retval, PAM_STRERROR(__pamh, pam_retval)); 226 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
187 } 227 (const void *)&sshpam_conv);
228 if (sshpam_err != PAM_SUCCESS)
229 goto auth_fail;
230 sshpam_err = pam_authenticate(sshpam_handle, 0);
231 if (sshpam_err != PAM_SUCCESS)
232 goto auth_fail;
233 buffer_put_cstring(&buffer, "OK");
234 ssh_msg_send(ctxt->pam_csock, sshpam_err, &buffer);
235 buffer_free(&buffer);
236 pthread_exit(NULL);
237
238 auth_fail:
239 buffer_put_cstring(&buffer,
240 pam_strerror(sshpam_handle, sshpam_err));
241 ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer);
242 buffer_free(&buffer);
243 pthread_exit(NULL);
244
245 return (NULL); /* Avoid warning for non-pthread case */
246}
188 247
189 if (__pamh && creds_set) { 248static void
190 pam_retval = pam_setcred(__pamh, PAM_DELETE_CRED); 249sshpam_thread_cleanup(void *ctxtp)
191 if (pam_retval != PAM_SUCCESS) 250{
192 debug("Cannot delete credentials[%d]: %.200s", 251 struct pam_ctxt *ctxt = ctxtp;
193 pam_retval, PAM_STRERROR(__pamh, pam_retval));
194 }
195 252
196 if (__pamh) { 253 pthread_cancel(ctxt->pam_thread);
197 pam_retval = pam_end(__pamh, pam_retval); 254 pthread_join(ctxt->pam_thread, NULL);
198 if (pam_retval != PAM_SUCCESS) 255 close(ctxt->pam_psock);
199 log("Cannot release PAM authentication[%d]: %.200s", 256 close(ctxt->pam_csock);
200 pam_retval, PAM_STRERROR(__pamh, pam_retval));
201 }
202} 257}
203 258
204/* Attempt password authentication using PAM */ 259static int
205int auth_pam_password(Authctxt *authctxt, const char *password) 260sshpam_null_conv(int n, const struct pam_message **msg,
261 struct pam_response **resp, void *data)
206{ 262{
207 extern ServerOptions options; 263 return (PAM_CONV_ERR);
208 int pam_retval; 264}
209 struct passwd *pw = authctxt->pw;
210 265
211 do_pam_set_conv(&conv); 266static struct pam_conv null_conv = { sshpam_null_conv, NULL };
212 267
213 __pampasswd = password; 268static void
269sshpam_cleanup(void *arg)
270{
271 (void)arg;
272 debug("PAM: cleanup");
273 if (sshpam_handle == NULL)
274 return;
275 pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv);
276 if (sshpam_cred_established) {
277 pam_setcred(sshpam_handle, PAM_DELETE_CRED);
278 sshpam_cred_established = 0;
279 }
280 if (sshpam_session_open) {
281 pam_close_session(sshpam_handle, PAM_SILENT);
282 sshpam_session_open = 0;
283 }
284 sshpam_authenticated = sshpam_new_authtok_reqd = 0;
285 pam_end(sshpam_handle, sshpam_err);
286 sshpam_handle = NULL;
287}
214 288
215 pamstate = INITIAL_LOGIN; 289static int
216 pam_retval = do_pam_authenticate( 290sshpam_init(const char *user)
217 options.permit_empty_passwd == 0 ? PAM_DISALLOW_NULL_AUTHTOK : 0); 291{
218 if (pam_retval == PAM_SUCCESS && pw) { 292 extern u_int utmp_len;
219 debug("PAM password authentication accepted for " 293 extern char *__progname;
220 "%.100s", pw->pw_name); 294 const char *pam_rhost, *pam_user;
221 return 1; 295
222 } else { 296 if (sshpam_handle != NULL) {
223 debug("PAM password authentication failed for " 297 /* We already have a PAM context; check if the user matches */
224 "%.100s: %s", pw ? pw->pw_name : "an illegal user", 298 sshpam_err = pam_get_item(sshpam_handle,
225 PAM_STRERROR(__pamh, pam_retval)); 299 PAM_USER, (const void **)&pam_user);
226 return 0; 300 if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0)
301 return (0);
302 fatal_remove_cleanup(sshpam_cleanup, NULL);
303 pam_end(sshpam_handle, sshpam_err);
304 sshpam_handle = NULL;
227 } 305 }
306 debug("PAM: initializing for \"%s\"", user);
307 sshpam_err =
308 pam_start(SSHD_PAM_SERVICE, user, &null_conv, &sshpam_handle);
309 if (sshpam_err != PAM_SUCCESS) {
310 pam_end(sshpam_handle, sshpam_err);
311 sshpam_handle = NULL;
312 return (-1);
313 }
314 pam_rhost = get_remote_name_or_ip(utmp_len, options.use_dns);
315 debug("PAM: setting PAM_RHOST to \"%s\"", pam_rhost);
316 sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, pam_rhost);
317 if (sshpam_err != PAM_SUCCESS) {
318 pam_end(sshpam_handle, sshpam_err);
319 sshpam_handle = NULL;
320 return (-1);
321 }
322#ifdef PAM_TTY_KLUDGE
323 /*
324 * Some silly PAM modules (e.g. pam_time) require a TTY to operate.
325 * sshd doesn't set the tty until too late in the auth process and
326 * may not even set one (for tty-less connections)
327 */
328 debug("PAM: setting PAM_TTY to \"ssh\"");
329 sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, "ssh");
330 if (sshpam_err != PAM_SUCCESS) {
331 pam_end(sshpam_handle, sshpam_err);
332 sshpam_handle = NULL;
333 return (-1);
334 }
335#endif
336 fatal_add_cleanup(sshpam_cleanup, NULL);
337 return (0);
228} 338}
229 339
230/* Do account management using PAM */ 340static void *
231int do_pam_account(char *username, char *remote_user) 341sshpam_init_ctx(Authctxt *authctxt)
232{ 342{
233 int pam_retval; 343 struct pam_ctxt *ctxt;
344 int socks[2];
234 345
235 do_pam_set_conv(&conv); 346 /* Refuse to start if we don't have PAM enabled */
347 if (!options.use_pam)
348 return NULL;
236 349
237 if (remote_user) { 350 /* Initialize PAM */
238 debug("PAM setting ruser to \"%.200s\"", remote_user); 351 if (sshpam_init(authctxt->user) == -1) {
239 pam_retval = pam_set_item(__pamh, PAM_RUSER, remote_user); 352 error("PAM: initialization failed");
240 if (pam_retval != PAM_SUCCESS) 353 return (NULL);
241 fatal("PAM set ruser failed[%d]: %.200s", pam_retval,
242 PAM_STRERROR(__pamh, pam_retval));
243 } 354 }
244 355
245 pam_retval = pam_acct_mgmt(__pamh, 0); 356 ctxt = xmalloc(sizeof *ctxt);
246 debug2("pam_acct_mgmt() = %d", pam_retval); 357 ctxt->pam_done = 0;
247 switch (pam_retval) { 358
248 case PAM_SUCCESS: 359 /* Start the authentication thread */
249 /* This is what we want */ 360 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) {
250 break; 361 error("PAM: failed create sockets: %s", strerror(errno));
251#if 0 362 xfree(ctxt);
252 case PAM_NEW_AUTHTOK_REQD: 363 return (NULL);
253 message_cat(&__pam_msg, use_privsep ? 364 }
254 NEW_AUTHTOK_MSG_PRIVSEP : NEW_AUTHTOK_MSG); 365 ctxt->pam_psock = socks[0];
255 /* flag that password change is necessary */ 366 ctxt->pam_csock = socks[1];
256 password_change_required = 1; 367 if (pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt) == -1) {
257 /* disallow other functionality for now */ 368 error("PAM: failed to start authentication thread: %s",
258 no_port_forwarding_flag |= 2; 369 strerror(errno));
259 no_agent_forwarding_flag |= 2; 370 close(socks[0]);
260 no_x11_forwarding_flag |= 2; 371 close(socks[1]);
372 xfree(ctxt);
373 return (NULL);
374 }
375 fatal_add_cleanup(sshpam_thread_cleanup, ctxt);
376 return (ctxt);
377}
378
379static int
380sshpam_query(void *ctx, char **name, char **info,
381 u_int *num, char ***prompts, u_int **echo_on)
382{
383 Buffer buffer;
384 struct pam_ctxt *ctxt = ctx;
385 size_t plen;
386 u_char type;
387 char *msg;
388 size_t len;
389
390 buffer_init(&buffer);
391 *name = xstrdup("");
392 *info = xstrdup("");
393 *prompts = xmalloc(sizeof(char *));
394 **prompts = NULL;
395 plen = 0;
396 *echo_on = xmalloc(sizeof(u_int));
397 while (ssh_msg_recv(ctxt->pam_psock, &buffer) == 0) {
398 type = buffer_get_char(&buffer);
399 msg = buffer_get_string(&buffer, NULL);
400 switch (type) {
401 case PAM_PROMPT_ECHO_ON:
402 case PAM_PROMPT_ECHO_OFF:
403 *num = 1;
404 len = plen + strlen(msg) + 1;
405 **prompts = xrealloc(**prompts, len);
406 plen += snprintf(**prompts + plen, len, "%s", msg);
407 **echo_on = (type == PAM_PROMPT_ECHO_ON);
408 xfree(msg);
409 return (0);
410 case PAM_ERROR_MSG:
411 case PAM_TEXT_INFO:
412 /* accumulate messages */
413 len = plen + strlen(msg) + 1;
414 **prompts = xrealloc(**prompts, len);
415 plen += snprintf(**prompts + plen, len, "%s", msg);
416 xfree(msg);
261 break; 417 break;
418 case PAM_SUCCESS:
419 case PAM_AUTH_ERR:
420 if (**prompts != NULL) {
421 /* drain any accumulated messages */
422#if 0 /* XXX - not compatible with privsep */
423 packet_start(SSH2_MSG_USERAUTH_BANNER);
424 packet_put_cstring(**prompts);
425 packet_put_cstring("");
426 packet_send();
427 packet_write_wait();
262#endif 428#endif
429 xfree(**prompts);
430 **prompts = NULL;
431 }
432 if (type == PAM_SUCCESS) {
433 *num = 0;
434 **echo_on = 0;
435 ctxt->pam_done = 1;
436 xfree(msg);
437 return (0);
438 }
439 error("PAM: %s", msg);
263 default: 440 default:
264 log("PAM rejected by account configuration[%d]: " 441 *num = 0;
265 "%.200s", pam_retval, PAM_STRERROR(__pamh, 442 **echo_on = 0;
266 pam_retval)); 443 xfree(msg);
267 return(0); 444 ctxt->pam_done = -1;
445 return (-1);
446 }
268 } 447 }
448 return (-1);
449}
269 450
270 return(1); 451/* XXX - see also comment in auth-chall.c:verify_response */
452static int
453sshpam_respond(void *ctx, u_int num, char **resp)
454{
455 Buffer buffer;
456 struct pam_ctxt *ctxt = ctx;
457
458 debug2("PAM: %s", __func__);
459 switch (ctxt->pam_done) {
460 case 1:
461 sshpam_authenticated = 1;
462 return (0);
463 case 0:
464 break;
465 default:
466 return (-1);
467 }
468 if (num != 1) {
469 error("PAM: expected one response, got %u", num);
470 return (-1);
471 }
472 buffer_init(&buffer);
473 buffer_put_cstring(&buffer, *resp);
474 ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer);
475 buffer_free(&buffer);
476 return (1);
271} 477}
272 478
273/* Do PAM-specific session initialisation */ 479static void
274void do_pam_session(char *username, const char *ttyname) 480sshpam_free_ctx(void *ctxtp)
275{ 481{
276 int pam_retval; 482 struct pam_ctxt *ctxt = ctxtp;
277 483
278 do_pam_set_conv(&conv); 484 fatal_remove_cleanup(sshpam_thread_cleanup, ctxt);
485 sshpam_thread_cleanup(ctxtp);
486 xfree(ctxt);
487 /*
488 * We don't call sshpam_cleanup() here because we may need the PAM
489 * handle at a later stage, e.g. when setting up a session. It's
490 * still on the cleanup list, so pam_end() *will* be called before
491 * the server process terminates.
492 */
493}
279 494
280 if (ttyname != NULL) { 495KbdintDevice sshpam_device = {
281 debug("PAM setting tty to \"%.200s\"", ttyname); 496 "pam",
282 pam_retval = pam_set_item(__pamh, PAM_TTY, ttyname); 497 sshpam_init_ctx,
283 if (pam_retval != PAM_SUCCESS) 498 sshpam_query,
284 fatal("PAM set tty failed[%d]: %.200s", 499 sshpam_respond,
285 pam_retval, PAM_STRERROR(__pamh, pam_retval)); 500 sshpam_free_ctx
286 } 501};
502
503KbdintDevice mm_sshpam_device = {
504 "pam",
505 mm_sshpam_init_ctx,
506 mm_sshpam_query,
507 mm_sshpam_respond,
508 mm_sshpam_free_ctx
509};
287 510
288 pam_retval = pam_open_session(__pamh, 0); 511/*
289 if (pam_retval != PAM_SUCCESS) 512 * This replaces auth-pam.c
290 fatal("PAM session setup failed[%d]: %.200s", 513 */
291 pam_retval, PAM_STRERROR(__pamh, pam_retval)); 514void
515start_pam(const char *user)
516{
517 if (!options.use_pam)
518 fatal("PAM: initialisation requested when UsePAM=no");
292 519
293 session_opened = 1; 520 if (sshpam_init(user) == -1)
521 fatal("PAM: initialisation failed");
294} 522}
295 523
296/* Set PAM credentials */ 524void
297void do_pam_setcred(int init) 525finish_pam(void)
298{ 526{
299 int pam_retval; 527 fatal_remove_cleanup(sshpam_cleanup, NULL);
528 sshpam_cleanup(NULL);
529}
300 530
301 if (__pamh == NULL) 531u_int
302 return; 532do_pam_account(void)
533{
534 sshpam_err = pam_acct_mgmt(sshpam_handle, 0);
535 debug3("%s: pam_acct_mgmt = %d", __func__, sshpam_err);
536
537 if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD)
538 return (0);
539
540 if (sshpam_err == PAM_NEW_AUTHTOK_REQD) {
541 sshpam_new_authtok_reqd = 1;
542
543 /* Prevent forwardings until password changed */
544 no_port_forwarding_flag |= 2;
545 no_agent_forwarding_flag |= 2;
546 no_x11_forwarding_flag |= 2;
547 }
303 548
304 do_pam_set_conv(&conv); 549 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} 550}
319 551
320/* accessor function for file scope static variable */ 552void
321int is_pam_password_change_required(void) 553do_pam_session(void)
322{ 554{
323 return password_change_required; 555 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
556 (const void *)&null_conv);
557 if (sshpam_err != PAM_SUCCESS)
558 fatal("PAM: failed to set PAM_CONV: %s",
559 pam_strerror(sshpam_handle, sshpam_err));
560 sshpam_err = pam_open_session(sshpam_handle, 0);
561 if (sshpam_err != PAM_SUCCESS)
562 fatal("PAM: pam_open_session(): %s",
563 pam_strerror(sshpam_handle, sshpam_err));
564 sshpam_session_open = 1;
324} 565}
325 566
326/* 567void
327 * Have user change authentication token if pam_acct_mgmt() indicated 568do_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{ 569{
334 int pam_retval; 570 if (tty != NULL) {
335 571 debug("PAM: setting PAM_TTY to \"%s\"", tty);
336 do_pam_set_conv(&conv); 572 sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, tty);
337 573 if (sshpam_err != PAM_SUCCESS)
338 if (password_change_required) { 574 fatal("PAM: failed to set PAM_TTY: %s",
339 if (use_privsep) 575 pam_strerror(sshpam_handle, sshpam_err));
340 fatal("Password changing is currently unsupported"
341 " with privilege separation");
342 pamstate = OTHER;
343 pam_retval = pam_chauthtok(__pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
344 if (pam_retval != PAM_SUCCESS)
345 fatal("PAM pam_chauthtok failed[%d]: %.200s",
346 pam_retval, PAM_STRERROR(__pamh, pam_retval));
347#if 0
348 /* XXX: This would need to be done in the parent process,
349 * but there's currently no way to pass such request. */
350 no_port_forwarding_flag &= ~2;
351 no_agent_forwarding_flag &= ~2;
352 no_x11_forwarding_flag &= ~2;
353 if (!no_port_forwarding_flag && options.allow_tcp_forwarding)
354 channel_permit_all_opens();
355#endif
356 } 576 }
357} 577}
358 578
359/* Cleanly shutdown PAM */ 579void
360void finish_pam(void) 580do_pam_setcred(int init)
361{ 581{
362 do_pam_cleanup_proc(NULL); 582 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
363 fatal_remove_cleanup(&do_pam_cleanup_proc, NULL); 583 (const void *)&null_conv);
584 if (sshpam_err != PAM_SUCCESS)
585 fatal("PAM: failed to set PAM_CONV: %s",
586 pam_strerror(sshpam_handle, sshpam_err));
587 if (init) {
588 debug("PAM: establishing credentials");
589 sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED);
590 } else {
591 debug("PAM: reinitializing credentials");
592 sshpam_err = pam_setcred(sshpam_handle, PAM_REINITIALIZE_CRED);
593 }
594 if (sshpam_err == PAM_SUCCESS) {
595 sshpam_cred_established = 1;
596 return;
597 }
598 if (sshpam_authenticated)
599 fatal("PAM: pam_setcred(): %s",
600 pam_strerror(sshpam_handle, sshpam_err));
601 else
602 debug("PAM: pam_setcred(): %s",
603 pam_strerror(sshpam_handle, sshpam_err));
364} 604}
365 605
366/* Start PAM authentication for specified account */ 606int
367void start_pam(const char *user) 607is_pam_password_change_required(void)
368{ 608{
369 int pam_retval; 609 return (sshpam_new_authtok_reqd);
370 extern ServerOptions options; 610}
371 extern u_int utmp_len;
372 const char *rhost;
373 611
374 debug("Starting up PAM with username \"%.200s\"", user); 612static int
613pam_chauthtok_conv(int n, const struct pam_message **msg,
614 struct pam_response **resp, void *data)
615{
616 char input[PAM_MAX_MSG_SIZE];
617 struct pam_response *reply;
618 int i;
375 619
376 pam_retval = pam_start(SSHD_PAM_SERVICE, user, &conv, &__pamh); 620 *resp = NULL;
377 621
378 if (pam_retval != PAM_SUCCESS) 622 if (n <= 0 || n > PAM_MAX_NUM_MSG)
379 fatal("PAM initialisation failed[%d]: %.200s", 623 return (PAM_CONV_ERR);
380 pam_retval, PAM_STRERROR(__pamh, pam_retval));
381 624
382 rhost = get_remote_name_or_ip(utmp_len, options.verify_reverse_mapping); 625 if ((reply = malloc(n * sizeof(*reply))) == NULL)
383 debug("PAM setting rhost to \"%.200s\"", rhost); 626 return (PAM_CONV_ERR);
627 memset(reply, 0, n * sizeof(*reply));
384 628
385 pam_retval = pam_set_item(__pamh, PAM_RHOST, rhost); 629 for (i = 0; i < n; ++i) {
386 if (pam_retval != PAM_SUCCESS) 630 switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
387 fatal("PAM set rhost failed[%d]: %.200s", pam_retval, 631 case PAM_PROMPT_ECHO_OFF:
388 PAM_STRERROR(__pamh, pam_retval)); 632 reply[i].resp =
389#ifdef PAM_TTY_KLUDGE 633 read_passphrase(PAM_MSG_MEMBER(msg, i, msg),
390 /* 634 RP_ALLOW_STDIN);
391 * Some PAM modules (e.g. pam_time) require a TTY to operate, 635 reply[i].resp_retcode = PAM_SUCCESS;
392 * and will fail in various stupid ways if they don't get one. 636 break;
393 * sshd doesn't set the tty until too late in the auth process and may 637 case PAM_PROMPT_ECHO_ON:
394 * not even need one (for tty-less connections) 638 fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
395 * Kludge: Set a fake PAM_TTY 639 fgets(input, sizeof input, stdin);
396 */ 640 reply[i].resp = xstrdup(input);
397 pam_retval = pam_set_item(__pamh, PAM_TTY, "NODEVssh"); 641 reply[i].resp_retcode = PAM_SUCCESS;
398 if (pam_retval != PAM_SUCCESS) 642 break;
399 fatal("PAM set tty failed[%d]: %.200s", 643 case PAM_ERROR_MSG:
400 pam_retval, PAM_STRERROR(__pamh, pam_retval)); 644 case PAM_TEXT_INFO:
401#endif /* PAM_TTY_KLUDGE */ 645 fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
646 reply[i].resp_retcode = PAM_SUCCESS;
647 break;
648 default:
649 goto fail;
650 }
651 }
652 *resp = reply;
653 return (PAM_SUCCESS);
402 654
403 fatal_add_cleanup(&do_pam_cleanup_proc, NULL); 655 fail:
656 for(i = 0; i < n; i++) {
657 if (reply[i].resp != NULL)
658 xfree(reply[i].resp);
659 }
660 xfree(reply);
661 return (PAM_CONV_ERR);
404} 662}
405 663
406/* Return list of PAM environment strings */ 664/*
407char **fetch_pam_environment(void) 665 * XXX this should be done in the authentication phase, but ssh1 doesn't
666 * support that
667 */
668void
669do_pam_chauthtok(void)
408{ 670{
409#ifdef HAVE_PAM_GETENVLIST 671 struct pam_conv pam_conv;
410 return(pam_getenvlist(__pamh)); 672
411#else /* HAVE_PAM_GETENVLIST */ 673 pam_conv.conv = pam_chauthtok_conv;
412 return(NULL); 674 pam_conv.appdata_ptr = NULL;
413#endif /* HAVE_PAM_GETENVLIST */ 675
676 if (use_privsep)
677 fatal("Password expired (unable to change with privsep)");
678 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
679 (const void *)&pam_conv);
680 if (sshpam_err != PAM_SUCCESS)
681 fatal("PAM: failed to set PAM_CONV: %s",
682 pam_strerror(sshpam_handle, sshpam_err));
683 debug("PAM: changing password");
684 sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
685 if (sshpam_err != PAM_SUCCESS)
686 fatal("PAM: pam_chauthtok(): %s",
687 pam_strerror(sshpam_handle, sshpam_err));
414} 688}
415 689
416void free_pam_environment(char **env) 690/*
691 * Set a PAM environment string. We need to do this so that the session
692 * modules can handle things like Kerberos/GSI credentials that appear
693 * during the ssh authentication process.
694 */
695
696int
697do_pam_putenv(char *name, char *value)
417{ 698{
418 int i; 699 int ret = 1;
700#ifdef HAVE_PAM_PUTENV
701 char *compound;
702 size_t len;
419 703
420 if (env != NULL) { 704 len = strlen(name) + strlen(value) + 2;
421 for (i = 0; env[i] != NULL; i++) 705 compound = xmalloc(len);
422 xfree(env[i]); 706
423 } 707 snprintf(compound, len, "%s=%s", name, value);
708 ret = pam_putenv(sshpam_handle, compound);
709 xfree(compound);
710#endif
711
712 return (ret);
424} 713}
425 714
426/* Print any messages that have been generated during authentication */ 715void
427/* or account checking to stderr */ 716print_pam_messages(void)
428void print_pam_messages(void)
429{ 717{
430 if (__pam_msg != NULL) 718 /* XXX */
431 fputs(__pam_msg, stderr);
432} 719}
433 720
434/* Append a message to buffer */ 721char **
435void message_cat(char **p, const char *a) 722fetch_pam_environment(void)
436{ 723{
437 char *cp; 724#ifdef HAVE_PAM_GETENVLIST
438 size_t new_len; 725 debug("PAM: retrieving environment");
439 726 return (pam_getenvlist(sshpam_handle));
440 new_len = strlen(a); 727#else
728 return (NULL);
729#endif
730}
441 731
442 if (*p) { 732void
443 size_t len = strlen(*p); 733free_pam_environment(char **env)
734{
735 char **envp;
444 736
445 *p = xrealloc(*p, new_len + len + 2); 737 if (env == NULL)
446 cp = *p + len; 738 return;
447 } else
448 *p = cp = xmalloc(new_len + 2);
449 739
450 memcpy(cp, a, new_len); 740 for (envp = env; *envp; envp++)
451 cp[new_len] = '\n'; 741 xfree(*envp);
452 cp[new_len + 1] = '\0'; 742 xfree(env);
453} 743}
454 744
455#endif /* USE_PAM */ 745#endif /* USE_PAM */