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, 341 insertions, 631 deletions
diff --git a/auth-pam.c b/auth-pam.c
index 04cabbca2..cb57ba110 100644
--- a/auth-pam.c
+++ b/auth-pam.c
@@ -1,11 +1,5 @@
1/*- 1/*
2 * Copyright (c) 2002 Networks Associates Technology, Inc. 2 * Copyright (c) 2000 Damien Miller. All rights reserved.
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.
9 * 3 *
10 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
@@ -16,730 +10,446 @@
16 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution. 11 * documentation and/or other materials provided with the distribution.
18 * 12 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 * SUCH DAMAGE.
30 */ 23 */
31 24
32/* Based on $FreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des Exp $ */
33#include "includes.h" 25#include "includes.h"
34RCSID("$Id: auth-pam.c,v 1.72.2.2 2003/09/23 09:24:21 djm Exp $");
35 26
36#ifdef USE_PAM 27#ifdef USE_PAM
37#include <security/pam_appl.h> 28#include "xmalloc.h"
38 29#include "log.h"
39#include "auth.h" 30#include "auth.h"
31#include "auth-options.h"
40#include "auth-pam.h" 32#include "auth-pam.h"
41#include "buffer.h" 33#include "servconf.h"
42#include "bufaux.h"
43#include "canohost.h" 34#include "canohost.h"
44#include "log.h"
45#include "monitor_wrap.h"
46#include "msg.h"
47#include "packet.h"
48#include "readpass.h" 35#include "readpass.h"
49#include "servconf.h"
50#include "ssh2.h"
51#include "xmalloc.h"
52#include "auth-options.h"
53 36
54extern ServerOptions options; 37extern char *__progname;
55 38
56#define __unused 39extern int use_privsep;
57 40
58#ifdef USE_POSIX_THREADS 41RCSID("$Id: auth-pam.c,v 1.55.4.1 2003/04/29 09:12:08 djm Exp $");
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;
71 42
72static void 43#define NEW_AUTHTOK_MSG \
73pthread_exit(void *value __unused) 44 "Warning: Your password has expired, please change it now."
74{ 45#define NEW_AUTHTOK_MSG_PRIVSEP \
75 _exit(0); 46 "Your password has expired, the session cannot proceed."
76}
77 47
78static int 48static int do_pam_conversation(int num_msg, const struct pam_message **msg,
79pthread_create(sp_pthread_t *thread, const void *attr __unused, 49 struct pam_response **resp, void *appdata_ptr);
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}
96 50
97static int 51/* module-local variables */
98pthread_cancel(sp_pthread_t thread) 52static struct pam_conv conv = {
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)
99{ 74{
100 return (kill(thread, SIGTERM)); 75 pam_set_item(__pamh, PAM_CONV, conv);
101} 76}
102 77
103static int 78/* start an authentication run */
104pthread_join(sp_pthread_t thread, void **value __unused) 79int do_pam_authenticate(int flags)
105{ 80{
106 int status; 81 int retval = pam_authenticate(__pamh, flags);
107 82 was_authenticated = (retval == PAM_SUCCESS);
108 waitpid(thread, &status, 0); 83 return retval;
109 return (status);
110} 84}
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 *);
129 85
130/* 86/*
131 * Conversation function for authentication thread. 87 * PAM conversation function.
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.
132 */ 98 */
133static int 99static int do_pam_conversation(int num_msg, const struct pam_message **msg,
134sshpam_thread_conv(int n, const struct pam_message **msg, 100 struct pam_response **resp, void *appdata_ptr)
135 struct pam_response **resp, void *data)
136{ 101{
137 Buffer buffer;
138 struct pam_ctxt *ctxt;
139 struct pam_response *reply; 102 struct pam_response *reply;
140 int i; 103 int count;
141 104 char buf[1024];
142 *resp = NULL; 105
143 106 /* PAM will free this later */
144 ctxt = data; 107 reply = xmalloc(num_msg * sizeof(*reply));
145 if (n <= 0 || n > PAM_MAX_NUM_MSG) 108
146 return (PAM_CONV_ERR); 109 for (count = 0; count < num_msg; count++) {
147 110 if (pamstate == INITIAL_LOGIN) {
148 if ((reply = malloc(n * sizeof(*reply))) == NULL) 111 /*
149 return (PAM_CONV_ERR); 112 * We can't use stdio yet, queue messages for
150 memset(reply, 0, n * sizeof(*reply)); 113 * printing later
151 114 */
152 buffer_init(&buffer); 115 switch(PAM_MSG_MEMBER(msg, count, msg_style)) {
153 for (i = 0; i < n; ++i) { 116 case PAM_PROMPT_ECHO_ON:
154 switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 117 xfree(reply);
155 case PAM_PROMPT_ECHO_OFF: 118 return PAM_CONV_ERR;
156 buffer_put_cstring(&buffer, 119 case PAM_PROMPT_ECHO_OFF:
157 PAM_MSG_MEMBER(msg, i, msg)); 120 if (__pampasswd == NULL) {
158 ssh_msg_send(ctxt->pam_csock, 121 xfree(reply);
159 PAM_MSG_MEMBER(msg, i, msg_style), &buffer); 122 return PAM_CONV_ERR;
160 ssh_msg_recv(ctxt->pam_csock, &buffer); 123 }
161 if (buffer_get_char(&buffer) != PAM_AUTHTOK) 124 reply[count].resp = xstrdup(__pampasswd);
162 goto fail; 125 reply[count].resp_retcode = PAM_SUCCESS;
163 reply[i].resp = buffer_get_string(&buffer, NULL); 126 break;
164 break; 127 case PAM_ERROR_MSG:
165 case PAM_PROMPT_ECHO_ON: 128 case PAM_TEXT_INFO:
166 buffer_put_cstring(&buffer, 129 if (PAM_MSG_MEMBER(msg, count, msg) != NULL) {
167 PAM_MSG_MEMBER(msg, i, msg)); 130 message_cat(&__pam_msg,
168 ssh_msg_send(ctxt->pam_csock, 131 PAM_MSG_MEMBER(msg, count, msg));
169 PAM_MSG_MEMBER(msg, i, msg_style), &buffer); 132 }
170 ssh_msg_recv(ctxt->pam_csock, &buffer); 133 reply[count].resp = xstrdup("");
171 if (buffer_get_char(&buffer) != PAM_AUTHTOK) 134 reply[count].resp_retcode = PAM_SUCCESS;
172 goto fail; 135 break;
173 reply[i].resp = buffer_get_string(&buffer, NULL); 136 default:
174 break; 137 xfree(reply);
175 case PAM_ERROR_MSG: 138 return PAM_CONV_ERR;
176 buffer_put_cstring(&buffer, 139 }
177 PAM_MSG_MEMBER(msg, i, msg)); 140 } else {
178 ssh_msg_send(ctxt->pam_csock, 141 /*
179 PAM_MSG_MEMBER(msg, i, msg_style), &buffer); 142 * stdio is connected, so interact directly
180 break; 143 */
181 case PAM_TEXT_INFO: 144 switch(PAM_MSG_MEMBER(msg, count, msg_style)) {
182 buffer_put_cstring(&buffer, 145 case PAM_PROMPT_ECHO_ON:
183 PAM_MSG_MEMBER(msg, i, msg)); 146 fputs(PAM_MSG_MEMBER(msg, count, msg), stderr);
184 ssh_msg_send(ctxt->pam_csock, 147 fgets(buf, sizeof(buf), stdin);
185 PAM_MSG_MEMBER(msg, i, msg_style), &buffer); 148 reply[count].resp = xstrdup(buf);
186 break; 149 reply[count].resp_retcode = PAM_SUCCESS;
187 default: 150 break;
188 goto fail; 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 }
189 } 169 }
190 buffer_clear(&buffer);
191 } 170 }
192 buffer_free(&buffer); 171
193 *resp = reply; 172 *resp = reply;
194 return (PAM_SUCCESS);
195 173
196 fail: 174 return PAM_SUCCESS;
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);
204} 175}
205 176
206/* 177/* Called at exit to cleanly shutdown PAM */
207 * Authentication thread. 178void do_pam_cleanup_proc(void *context)
208 */
209static void *
210sshpam_thread(void *ctxtp)
211{ 179{
212 struct pam_ctxt *ctxt = ctxtp; 180 int pam_retval = PAM_SUCCESS;
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
221 181
222 sshpam_conv.conv = sshpam_thread_conv; 182 if (__pamh && session_opened) {
223 sshpam_conv.appdata_ptr = ctxt; 183 pam_retval = pam_close_session(__pamh, 0);
224 184 if (pam_retval != PAM_SUCCESS)
225 buffer_init(&buffer); 185 log("Cannot close PAM session[%d]: %.200s",
226 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 186 pam_retval, PAM_STRERROR(__pamh, pam_retval));
227 (const void *)&sshpam_conv); 187 }
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}
247 188
248static void 189 if (__pamh && creds_set) {
249sshpam_thread_cleanup(void *ctxtp) 190 pam_retval = pam_setcred(__pamh, PAM_DELETE_CRED);
250{ 191 if (pam_retval != PAM_SUCCESS)
251 struct pam_ctxt *ctxt = ctxtp; 192 debug("Cannot delete credentials[%d]: %.200s",
193 pam_retval, PAM_STRERROR(__pamh, pam_retval));
194 }
252 195
253 pthread_cancel(ctxt->pam_thread); 196 if (__pamh) {
254 pthread_join(ctxt->pam_thread, NULL); 197 pam_retval = pam_end(__pamh, pam_retval);
255 close(ctxt->pam_psock); 198 if (pam_retval != PAM_SUCCESS)
256 close(ctxt->pam_csock); 199 log("Cannot release PAM authentication[%d]: %.200s",
200 pam_retval, PAM_STRERROR(__pamh, pam_retval));
201 }
257} 202}
258 203
259static int 204/* Attempt password authentication using PAM */
260sshpam_null_conv(int n, const struct pam_message **msg, 205int auth_pam_password(Authctxt *authctxt, const char *password)
261 struct pam_response **resp, void *data)
262{ 206{
263 return (PAM_CONV_ERR); 207 extern ServerOptions options;
264} 208 int pam_retval;
209 struct passwd *pw = authctxt->pw;
265 210
266static struct pam_conv null_conv = { sshpam_null_conv, NULL }; 211 do_pam_set_conv(&conv);
267 212
268static void 213 __pampasswd = password;
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}
288 214
289static int 215 pamstate = INITIAL_LOGIN;
290sshpam_init(const char *user) 216 pam_retval = do_pam_authenticate(
291{ 217 options.permit_empty_passwd == 0 ? PAM_DISALLOW_NULL_AUTHTOK : 0);
292 extern u_int utmp_len; 218 if (pam_retval == PAM_SUCCESS && pw) {
293 extern char *__progname; 219 debug("PAM password authentication accepted for "
294 const char *pam_rhost, *pam_user; 220 "%.100s", pw->pw_name);
295 221 return 1;
296 if (sshpam_handle != NULL) { 222 } else {
297 /* We already have a PAM context; check if the user matches */ 223 debug("PAM password authentication failed for "
298 sshpam_err = pam_get_item(sshpam_handle, 224 "%.100s: %s", pw ? pw->pw_name : "an illegal user",
299 PAM_USER, (const void **)&pam_user); 225 PAM_STRERROR(__pamh, pam_retval));
300 if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0) 226 return 0;
301 return (0);
302 fatal_remove_cleanup(sshpam_cleanup, NULL);
303 pam_end(sshpam_handle, sshpam_err);
304 sshpam_handle = NULL;
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 } 227 }
335#endif
336 fatal_add_cleanup(sshpam_cleanup, NULL);
337 return (0);
338} 228}
339 229
340static void * 230/* Do account management using PAM */
341sshpam_init_ctx(Authctxt *authctxt) 231int do_pam_account(char *username, char *remote_user)
342{ 232{
343 struct pam_ctxt *ctxt; 233 int pam_retval;
344 int socks[2];
345
346 /* Refuse to start if we don't have PAM enabled */
347 if (!options.use_pam)
348 return NULL;
349
350 /* Initialize PAM */
351 if (sshpam_init(authctxt->user) == -1) {
352 error("PAM: initialization failed");
353 return (NULL);
354 }
355 234
356 ctxt = xmalloc(sizeof *ctxt); 235 do_pam_set_conv(&conv);
357 ctxt->pam_done = 0;
358 236
359 /* Start the authentication thread */ 237 if (remote_user) {
360 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) { 238 debug("PAM setting ruser to \"%.200s\"", remote_user);
361 error("PAM: failed create sockets: %s", strerror(errno)); 239 pam_retval = pam_set_item(__pamh, PAM_RUSER, remote_user);
362 xfree(ctxt); 240 if (pam_retval != PAM_SUCCESS)
363 return (NULL); 241 fatal("PAM set ruser failed[%d]: %.200s", pam_retval,
242 PAM_STRERROR(__pamh, pam_retval));
364 } 243 }
365 ctxt->pam_psock = socks[0];
366 ctxt->pam_csock = socks[1];
367 if (pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt) == -1) {
368 error("PAM: failed to start authentication thread: %s",
369 strerror(errno));
370 close(socks[0]);
371 close(socks[1]);
372 xfree(ctxt);
373 return (NULL);
374 }
375 fatal_add_cleanup(sshpam_thread_cleanup, ctxt);
376 return (ctxt);
377}
378 244
379static int 245 pam_retval = pam_acct_mgmt(__pamh, 0);
380sshpam_query(void *ctx, char **name, char **info, 246 debug2("pam_acct_mgmt() = %d", pam_retval);
381 u_int *num, char ***prompts, u_int **echo_on) 247 switch (pam_retval) {
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);
417 break;
418 case PAM_SUCCESS: 248 case PAM_SUCCESS:
419 case PAM_AUTH_ERR: 249 /* This is what we want */
420 if (**prompts != NULL) { 250 break;
421 /* drain any accumulated messages */ 251#if 0
422#if 0 /* XXX - not compatible with privsep */ 252 case PAM_NEW_AUTHTOK_REQD:
423 packet_start(SSH2_MSG_USERAUTH_BANNER); 253 message_cat(&__pam_msg, use_privsep ?
424 packet_put_cstring(**prompts); 254 NEW_AUTHTOK_MSG_PRIVSEP : NEW_AUTHTOK_MSG);
425 packet_put_cstring(""); 255 /* flag that password change is necessary */
426 packet_send(); 256 password_change_required = 1;
427 packet_write_wait(); 257 /* disallow other functionality for now */
258 no_port_forwarding_flag |= 2;
259 no_agent_forwarding_flag |= 2;
260 no_x11_forwarding_flag |= 2;
261 break;
428#endif 262#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);
440 default: 263 default:
441 *num = 0; 264 log("PAM rejected by account configuration[%d]: "
442 **echo_on = 0; 265 "%.200s", pam_retval, PAM_STRERROR(__pamh,
443 xfree(msg); 266 pam_retval));
444 ctxt->pam_done = -1; 267 return(0);
445 return (-1);
446 }
447 } 268 }
448 return (-1);
449}
450 269
451/* XXX - see also comment in auth-chall.c:verify_response */ 270 return(1);
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);
477} 271}
478 272
479static void 273/* Do PAM-specific session initialisation */
480sshpam_free_ctx(void *ctxtp) 274void do_pam_session(char *username, const char *ttyname)
481{ 275{
482 struct pam_ctxt *ctxt = ctxtp; 276 int pam_retval;
483 277
484 fatal_remove_cleanup(sshpam_thread_cleanup, ctxt); 278 do_pam_set_conv(&conv);
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}
494 279
495KbdintDevice sshpam_device = { 280 if (ttyname != NULL) {
496 "pam", 281 debug("PAM setting tty to \"%.200s\"", ttyname);
497 sshpam_init_ctx, 282 pam_retval = pam_set_item(__pamh, PAM_TTY, ttyname);
498 sshpam_query, 283 if (pam_retval != PAM_SUCCESS)
499 sshpam_respond, 284 fatal("PAM set tty failed[%d]: %.200s",
500 sshpam_free_ctx 285 pam_retval, PAM_STRERROR(__pamh, pam_retval));
501}; 286 }
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};
510 287
511/* 288 pam_retval = pam_open_session(__pamh, 0);
512 * This replaces auth-pam.c 289 if (pam_retval != PAM_SUCCESS)
513 */ 290 fatal("PAM session setup failed[%d]: %.200s",
514void 291 pam_retval, PAM_STRERROR(__pamh, pam_retval));
515start_pam(const char *user)
516{
517 if (!options.use_pam)
518 fatal("PAM: initialisation requested when UsePAM=no");
519 292
520 if (sshpam_init(user) == -1) 293 session_opened = 1;
521 fatal("PAM: initialisation failed");
522} 294}
523 295
524void 296/* Set PAM credentials */
525finish_pam(void) 297void do_pam_setcred(int init)
526{ 298{
527 fatal_remove_cleanup(sshpam_cleanup, NULL); 299 int pam_retval;
528 sshpam_cleanup(NULL);
529}
530 300
531u_int 301 if (__pamh == NULL)
532do_pam_account(void) 302 return;
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 }
548 303
549 return (1); 304 do_pam_set_conv(&conv);
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;
550} 318}
551 319
552void 320/* accessor function for file scope static variable */
553do_pam_session(void) 321int is_pam_password_change_required(void)
554{ 322{
555 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 323 return password_change_required;
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;
565} 324}
566 325
567void 326/*
568do_pam_set_tty(const char *tty) 327 * Have user change authentication token if pam_acct_mgmt() indicated
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)
569{ 333{
570 if (tty != NULL) { 334 int pam_retval;
571 debug("PAM: setting PAM_TTY to \"%s\"", tty); 335
572 sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, tty); 336 do_pam_set_conv(&conv);
573 if (sshpam_err != PAM_SUCCESS) 337
574 fatal("PAM: failed to set PAM_TTY: %s", 338 if (password_change_required) {
575 pam_strerror(sshpam_handle, sshpam_err)); 339 if (use_privsep)
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
576 } 356 }
577} 357}
578 358
579void 359/* Cleanly shutdown PAM */
580do_pam_setcred(int init) 360void finish_pam(void)
581{ 361{
582 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 362 do_pam_cleanup_proc(NULL);
583 (const void *)&null_conv); 363 fatal_remove_cleanup(&do_pam_cleanup_proc, NULL);
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));
604} 364}
605 365
606int 366/* Start PAM authentication for specified account */
607is_pam_password_change_required(void) 367void start_pam(const char *user)
608{ 368{
609 return (sshpam_new_authtok_reqd); 369 int pam_retval;
610} 370 extern ServerOptions options;
371 extern u_int utmp_len;
372 const char *rhost;
611 373
612static int 374 debug("Starting up PAM with username \"%.200s\"", user);
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;
619 375
620 *resp = NULL; 376 pam_retval = pam_start(SSHD_PAM_SERVICE, user, &conv, &__pamh);
621 377
622 if (n <= 0 || n > PAM_MAX_NUM_MSG) 378 if (pam_retval != PAM_SUCCESS)
623 return (PAM_CONV_ERR); 379 fatal("PAM initialisation failed[%d]: %.200s",
380 pam_retval, PAM_STRERROR(__pamh, pam_retval));
624 381
625 if ((reply = malloc(n * sizeof(*reply))) == NULL) 382 rhost = get_remote_name_or_ip(utmp_len, options.verify_reverse_mapping);
626 return (PAM_CONV_ERR); 383 debug("PAM setting rhost to \"%.200s\"", rhost);
627 memset(reply, 0, n * sizeof(*reply));
628 384
629 for (i = 0; i < n; ++i) { 385 pam_retval = pam_set_item(__pamh, PAM_RHOST, rhost);
630 switch (PAM_MSG_MEMBER(msg, i, msg_style)) { 386 if (pam_retval != PAM_SUCCESS)
631 case PAM_PROMPT_ECHO_OFF: 387 fatal("PAM set rhost failed[%d]: %.200s", pam_retval,
632 reply[i].resp = 388 PAM_STRERROR(__pamh, pam_retval));
633 read_passphrase(PAM_MSG_MEMBER(msg, i, msg), 389#ifdef PAM_TTY_KLUDGE
634 RP_ALLOW_STDIN); 390 /*
635 reply[i].resp_retcode = PAM_SUCCESS; 391 * Some PAM modules (e.g. pam_time) require a TTY to operate,
636 break; 392 * and will fail in various stupid ways if they don't get one.
637 case PAM_PROMPT_ECHO_ON: 393 * sshd doesn't set the tty until too late in the auth process and may
638 fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg)); 394 * not even need one (for tty-less connections)
639 fgets(input, sizeof input, stdin); 395 * Kludge: Set a fake PAM_TTY
640 reply[i].resp = xstrdup(input); 396 */
641 reply[i].resp_retcode = PAM_SUCCESS; 397 pam_retval = pam_set_item(__pamh, PAM_TTY, "NODEVssh");
642 break; 398 if (pam_retval != PAM_SUCCESS)
643 case PAM_ERROR_MSG: 399 fatal("PAM set tty failed[%d]: %.200s",
644 case PAM_TEXT_INFO: 400 pam_retval, PAM_STRERROR(__pamh, pam_retval));
645 fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg)); 401#endif /* PAM_TTY_KLUDGE */
646 reply[i].resp_retcode = PAM_SUCCESS;
647 break;
648 default:
649 goto fail;
650 }
651 }
652 *resp = reply;
653 return (PAM_SUCCESS);
654 402
655 fail: 403 fatal_add_cleanup(&do_pam_cleanup_proc, NULL);
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);
662} 404}
663 405
664/* 406/* Return list of PAM environment strings */
665 * XXX this should be done in the authentication phase, but ssh1 doesn't 407char **fetch_pam_environment(void)
666 * support that
667 */
668void
669do_pam_chauthtok(void)
670{ 408{
671 struct pam_conv pam_conv; 409#ifdef HAVE_PAM_GETENVLIST
672 410 return(pam_getenvlist(__pamh));
673 pam_conv.conv = pam_chauthtok_conv; 411#else /* HAVE_PAM_GETENVLIST */
674 pam_conv.appdata_ptr = NULL; 412 return(NULL);
675 413#endif /* HAVE_PAM_GETENVLIST */
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));
688} 414}
689 415
690/* 416void free_pam_environment(char **env)
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)
698{ 417{
699 int ret = 1; 418 int i;
700#ifdef HAVE_PAM_PUTENV
701 char *compound;
702 size_t len;
703
704 len = strlen(name) + strlen(value) + 2;
705 compound = xmalloc(len);
706
707 snprintf(compound, len, "%s=%s", name, value);
708 ret = pam_putenv(sshpam_handle, compound);
709 xfree(compound);
710#endif
711 419
712 return (ret); 420 if (env != NULL) {
421 for (i = 0; env[i] != NULL; i++)
422 xfree(env[i]);
423 }
713} 424}
714 425
715void 426/* Print any messages that have been generated during authentication */
716print_pam_messages(void) 427/* or account checking to stderr */
428void print_pam_messages(void)
717{ 429{
718 /* XXX */ 430 if (__pam_msg != NULL)
431 fputs(__pam_msg, stderr);
719} 432}
720 433
721char ** 434/* Append a message to buffer */
722fetch_pam_environment(void) 435void message_cat(char **p, const char *a)
723{ 436{
724#ifdef HAVE_PAM_GETENVLIST 437 char *cp;
725 debug("PAM: retrieving environment"); 438 size_t new_len;
726 return (pam_getenvlist(sshpam_handle));
727#else
728 return (NULL);
729#endif
730}
731 439
732void 440 new_len = strlen(a);
733free_pam_environment(char **env)
734{
735 char **envp;
736 441
737 if (env == NULL) 442 if (*p) {
738 return; 443 size_t len = strlen(*p);
444
445 *p = xrealloc(*p, new_len + len + 2);
446 cp = *p + len;
447 } else
448 *p = cp = xmalloc(new_len + 2);
739 449
740 for (envp = env; *envp; envp++) 450 memcpy(cp, a, new_len);
741 xfree(*envp); 451 cp[new_len] = '\n';
742 xfree(env); 452 cp[new_len + 1] = '\0';
743} 453}
744 454
745#endif /* USE_PAM */ 455#endif /* USE_PAM */