blob: 4d2f9c597675d5c2dc5c953c16ca00c9984d27ae [file] [log] [blame]
Damien Miller4f9f42a2003-05-10 19:28:02 +10001/*-
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.
Damien Millere69f18c2000-06-12 16:38:54 +10009 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
Damien Millere69f18c2000-06-12 16:38:54 +100018 *
Damien Miller4f9f42a2003-05-10 19:28:02 +100019 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
Damien Millere72b7af1999-12-30 15:08:44 +110030 */
31
Damien Miller25d93422003-05-18 20:45:47 +100032/* Based on $FreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des Exp $ */
Damien Millere72b7af1999-12-30 15:08:44 +110033#include "includes.h"
Damien Miller9bdba702003-11-17 21:27:55 +110034RCSID("$Id: auth-pam.c,v 1.79 2003/11/17 10:27:55 djm Exp $");
Damien Millere72b7af1999-12-30 15:08:44 +110035
36#ifdef USE_PAM
Damien Miller4f9f42a2003-05-10 19:28:02 +100037#include <security/pam_appl.h>
38
Kevin Stevese683e762002-04-04 19:02:28 +000039#include "auth.h"
Damien Miller63dc3e92001-02-07 12:58:33 +110040#include "auth-pam.h"
Damien Miller4f9f42a2003-05-10 19:28:02 +100041#include "buffer.h"
42#include "bufaux.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000043#include "canohost.h"
Damien Miller4f9f42a2003-05-10 19:28:02 +100044#include "log.h"
45#include "monitor_wrap.h"
46#include "msg.h"
47#include "packet.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000048#include "readpass.h"
Damien Miller4f9f42a2003-05-10 19:28:02 +100049#include "servconf.h"
50#include "ssh2.h"
51#include "xmalloc.h"
Damien Miller1f499fd2003-08-25 13:08:49 +100052#include "auth-options.h"
Damien Millere72b7af1999-12-30 15:08:44 +110053
Damien Miller4e448a32003-05-14 15:11:48 +100054extern ServerOptions options;
55
Damien Miller4f9f42a2003-05-10 19:28:02 +100056#define __unused
Kevin Steves85ecbe72001-04-20 17:43:47 +000057
Damien Miller4f9f42a2003-05-10 19:28:02 +100058#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;
Kevin Steves63007d42002-07-21 17:57:01 +000071
Damien Miller4f9f42a2003-05-10 19:28:02 +100072static void
73pthread_exit(void *value __unused)
74{
75 _exit(0);
76}
Damien Miller2f6a0ad2000-05-31 11:20:11 +100077
Damien Miller4f9f42a2003-05-10 19:28:02 +100078static int
79pthread_create(sp_pthread_t *thread, const void *attr __unused,
80 void *(*thread_start)(void *), void *arg)
81{
82 pid_t pid;
Damien Millere72b7af1999-12-30 15:08:44 +110083
Damien Miller4f9f42a2003-05-10 19:28:02 +100084 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}
Damien Millere72b7af1999-12-30 15:08:44 +110096
Damien Miller4f9f42a2003-05-10 19:28:02 +100097static int
98pthread_cancel(sp_pthread_t thread)
99{
100 return (kill(thread, SIGTERM));
101}
102
103static int
104pthread_join(sp_pthread_t thread, void **value __unused)
105{
106 int status;
107
108 waitpid(thread, &status, 0);
109 return (status);
110}
111#endif
112
113
Damien Miller5c3a5582003-09-23 22:12:38 +1000114static 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;
Damien Miller4f9f42a2003-05-10 19:28:02 +1000120
121struct pam_ctxt {
122 sp_pthread_t pam_thread;
123 int pam_psock;
124 int pam_csock;
125 int pam_done;
Damien Millere72b7af1999-12-30 15:08:44 +1100126};
Damien Millere72b7af1999-12-30 15:08:44 +1100127
Damien Miller4f9f42a2003-05-10 19:28:02 +1000128static void sshpam_free_ctx(void *);
Darren Tucker8846a072003-10-07 11:30:15 +1000129static struct pam_ctxt *cleanup_ctxt;
Damien Miller9d5705a2000-09-16 16:09:27 +1100130
131/*
Damien Miller4f9f42a2003-05-10 19:28:02 +1000132 * Conversation function for authentication thread.
Damien Miller9d5705a2000-09-16 16:09:27 +1100133 */
Damien Miller4f9f42a2003-05-10 19:28:02 +1000134static int
Damien Miller1f499fd2003-08-25 13:08:49 +1000135sshpam_thread_conv(int n, const struct pam_message **msg,
136 struct pam_response **resp, void *data)
Damien Millere72b7af1999-12-30 15:08:44 +1100137{
Damien Miller4f9f42a2003-05-10 19:28:02 +1000138 Buffer buffer;
139 struct pam_ctxt *ctxt;
Damien Miller5c3a5582003-09-23 22:12:38 +1000140 struct pam_response *reply;
Kevin Steves38b050a2002-07-23 00:44:07 +0000141 int i;
142
Damien Miller5c3a5582003-09-23 22:12:38 +1000143 *resp = NULL;
144
Damien Miller4f9f42a2003-05-10 19:28:02 +1000145 ctxt = data;
146 if (n <= 0 || n > PAM_MAX_NUM_MSG)
147 return (PAM_CONV_ERR);
Damien Miller5c3a5582003-09-23 22:12:38 +1000148
149 if ((reply = malloc(n * sizeof(*reply))) == NULL)
150 return (PAM_CONV_ERR);
151 memset(reply, 0, n * sizeof(*reply));
152
Damien Miller4f9f42a2003-05-10 19:28:02 +1000153 buffer_init(&buffer);
154 for (i = 0; i < n; ++i) {
Damien Miller4f9f42a2003-05-10 19:28:02 +1000155 switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
156 case PAM_PROMPT_ECHO_OFF:
Damien Miller5c3a5582003-09-23 22:12:38 +1000157 buffer_put_cstring(&buffer,
158 PAM_MSG_MEMBER(msg, i, msg));
Damien Miller9bdba702003-11-17 21:27:55 +1100159 if (ssh_msg_send(ctxt->pam_csock,
160 PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
161 goto fail;
162 if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1)
163 goto fail;
Damien Miller4f9f42a2003-05-10 19:28:02 +1000164 if (buffer_get_char(&buffer) != PAM_AUTHTOK)
165 goto fail;
Damien Miller5c3a5582003-09-23 22:12:38 +1000166 reply[i].resp = buffer_get_string(&buffer, NULL);
Damien Miller4f9f42a2003-05-10 19:28:02 +1000167 break;
168 case PAM_PROMPT_ECHO_ON:
Damien Miller5c3a5582003-09-23 22:12:38 +1000169 buffer_put_cstring(&buffer,
170 PAM_MSG_MEMBER(msg, i, msg));
Damien Miller9bdba702003-11-17 21:27:55 +1100171 if (ssh_msg_send(ctxt->pam_csock,
172 PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
173 goto fail;
174 if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1)
175 goto fail;
Damien Miller4f9f42a2003-05-10 19:28:02 +1000176 if (buffer_get_char(&buffer) != PAM_AUTHTOK)
177 goto fail;
Damien Miller5c3a5582003-09-23 22:12:38 +1000178 reply[i].resp = buffer_get_string(&buffer, NULL);
Damien Miller4f9f42a2003-05-10 19:28:02 +1000179 break;
180 case PAM_ERROR_MSG:
Damien Miller5c3a5582003-09-23 22:12:38 +1000181 buffer_put_cstring(&buffer,
182 PAM_MSG_MEMBER(msg, i, msg));
Damien Miller9bdba702003-11-17 21:27:55 +1100183 if (ssh_msg_send(ctxt->pam_csock,
184 PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
185 goto fail;
Damien Miller4f9f42a2003-05-10 19:28:02 +1000186 break;
187 case PAM_TEXT_INFO:
Damien Miller5c3a5582003-09-23 22:12:38 +1000188 buffer_put_cstring(&buffer,
189 PAM_MSG_MEMBER(msg, i, msg));
Damien Miller9bdba702003-11-17 21:27:55 +1100190 if (ssh_msg_send(ctxt->pam_csock,
191 PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)
192 goto fail;
Damien Miller4f9f42a2003-05-10 19:28:02 +1000193 break;
194 default:
195 goto fail;
196 }
197 buffer_clear(&buffer);
Kevin Steves38b050a2002-07-23 00:44:07 +0000198 }
Damien Miller4f9f42a2003-05-10 19:28:02 +1000199 buffer_free(&buffer);
Damien Miller5c3a5582003-09-23 22:12:38 +1000200 *resp = reply;
Damien Miller4f9f42a2003-05-10 19:28:02 +1000201 return (PAM_SUCCESS);
Damien Miller5c3a5582003-09-23 22:12:38 +1000202
Damien Miller4f9f42a2003-05-10 19:28:02 +1000203 fail:
Damien Miller5c3a5582003-09-23 22:12:38 +1000204 for(i = 0; i < n; i++) {
205 if (reply[i].resp != NULL)
206 xfree(reply[i].resp);
207 }
208 xfree(reply);
Damien Miller4f9f42a2003-05-10 19:28:02 +1000209 buffer_free(&buffer);
210 return (PAM_CONV_ERR);
Kevin Steves38b050a2002-07-23 00:44:07 +0000211}
212
Damien Miller4f9f42a2003-05-10 19:28:02 +1000213/*
214 * Authentication thread.
215 */
216static void *
217sshpam_thread(void *ctxtp)
Damien Millere72b7af1999-12-30 15:08:44 +1100218{
Damien Miller4f9f42a2003-05-10 19:28:02 +1000219 struct pam_ctxt *ctxt = ctxtp;
220 Buffer buffer;
Damien Millerf4b6f102003-09-02 23:12:06 +1000221 struct pam_conv sshpam_conv;
Damien Miller4f9f42a2003-05-10 19:28:02 +1000222#ifndef USE_POSIX_THREADS
223 const char *pam_user;
224
225 pam_get_item(sshpam_handle, PAM_USER, (const void **)&pam_user);
226 setproctitle("%s [pam]", pam_user);
227#endif
228
Damien Millerf4b6f102003-09-02 23:12:06 +1000229 sshpam_conv.conv = sshpam_thread_conv;
230 sshpam_conv.appdata_ptr = ctxt;
231
Damien Miller4f9f42a2003-05-10 19:28:02 +1000232 buffer_init(&buffer);
233 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
234 (const void *)&sshpam_conv);
235 if (sshpam_err != PAM_SUCCESS)
236 goto auth_fail;
237 sshpam_err = pam_authenticate(sshpam_handle, 0);
238 if (sshpam_err != PAM_SUCCESS)
239 goto auth_fail;
Damien Miller4f9f42a2003-05-10 19:28:02 +1000240 buffer_put_cstring(&buffer, "OK");
Damien Miller9bdba702003-11-17 21:27:55 +1100241 /* XXX - can't do much about an error here */
Damien Miller4f9f42a2003-05-10 19:28:02 +1000242 ssh_msg_send(ctxt->pam_csock, sshpam_err, &buffer);
243 buffer_free(&buffer);
244 pthread_exit(NULL);
245
246 auth_fail:
247 buffer_put_cstring(&buffer,
248 pam_strerror(sshpam_handle, sshpam_err));
Damien Miller9bdba702003-11-17 21:27:55 +1100249 /* XXX - can't do much about an error here */
Damien Miller4f9f42a2003-05-10 19:28:02 +1000250 ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer);
251 buffer_free(&buffer);
252 pthread_exit(NULL);
253
254 return (NULL); /* Avoid warning for non-pthread case */
Damien Miller2f6a0ad2000-05-31 11:20:11 +1000255}
256
Darren Tucker8846a072003-10-07 11:30:15 +1000257void
258sshpam_thread_cleanup(void)
Damien Miller2f6a0ad2000-05-31 11:20:11 +1000259{
Darren Tucker8846a072003-10-07 11:30:15 +1000260 struct pam_ctxt *ctxt = cleanup_ctxt;
Kevin Stevesef4eea92001-02-05 12:42:17 +0000261
Darren Tucker8846a072003-10-07 11:30:15 +1000262 if (ctxt != NULL && ctxt->pam_thread != 0) {
263 pthread_cancel(ctxt->pam_thread);
264 pthread_join(ctxt->pam_thread, NULL);
265 close(ctxt->pam_psock);
266 close(ctxt->pam_csock);
267 memset(ctxt, 0, sizeof(*ctxt));
268 cleanup_ctxt = NULL;
269 }
Damien Miller4f9f42a2003-05-10 19:28:02 +1000270}
Kevin Stevesef4eea92001-02-05 12:42:17 +0000271
Damien Miller4f9f42a2003-05-10 19:28:02 +1000272static int
Damien Miller1f499fd2003-08-25 13:08:49 +1000273sshpam_null_conv(int n, const struct pam_message **msg,
274 struct pam_response **resp, void *data)
Damien Miller4f9f42a2003-05-10 19:28:02 +1000275{
Damien Miller4f9f42a2003-05-10 19:28:02 +1000276 return (PAM_CONV_ERR);
277}
Damien Miller63dc3e92001-02-07 12:58:33 +1100278
Damien Miller4f9f42a2003-05-10 19:28:02 +1000279static struct pam_conv null_conv = { sshpam_null_conv, NULL };
280
Darren Tucker8846a072003-10-07 11:30:15 +1000281void
282sshpam_cleanup(void)
Damien Miller4f9f42a2003-05-10 19:28:02 +1000283{
Damien Miller4f9f42a2003-05-10 19:28:02 +1000284 debug("PAM: cleanup");
Damien Miller5c3a5582003-09-23 22:12:38 +1000285 if (sshpam_handle == NULL)
286 return;
Damien Miller4f9f42a2003-05-10 19:28:02 +1000287 pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv);
288 if (sshpam_cred_established) {
289 pam_setcred(sshpam_handle, PAM_DELETE_CRED);
290 sshpam_cred_established = 0;
291 }
292 if (sshpam_session_open) {
293 pam_close_session(sshpam_handle, PAM_SILENT);
294 sshpam_session_open = 0;
295 }
296 sshpam_authenticated = sshpam_new_authtok_reqd = 0;
297 pam_end(sshpam_handle, sshpam_err);
298 sshpam_handle = NULL;
299}
300
301static int
302sshpam_init(const char *user)
303{
Damien Miller4f9f42a2003-05-10 19:28:02 +1000304 extern u_int utmp_len;
Darren Tucker455813b2003-09-13 22:12:11 +1000305 extern char *__progname;
Damien Miller4f9f42a2003-05-10 19:28:02 +1000306 const char *pam_rhost, *pam_user;
307
308 if (sshpam_handle != NULL) {
309 /* We already have a PAM context; check if the user matches */
310 sshpam_err = pam_get_item(sshpam_handle,
311 PAM_USER, (const void **)&pam_user);
312 if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0)
313 return (0);
Damien Miller4f9f42a2003-05-10 19:28:02 +1000314 pam_end(sshpam_handle, sshpam_err);
315 sshpam_handle = NULL;
316 }
317 debug("PAM: initializing for \"%s\"", user);
Darren Tuckerc58c2ee2003-09-13 22:02:05 +1000318 sshpam_err =
319 pam_start(SSHD_PAM_SERVICE, user, &null_conv, &sshpam_handle);
Damien Miller4f9f42a2003-05-10 19:28:02 +1000320 if (sshpam_err != PAM_SUCCESS) {
321 pam_end(sshpam_handle, sshpam_err);
322 sshpam_handle = NULL;
323 return (-1);
324 }
Damien Miller3a961dc2003-06-03 10:25:48 +1000325 pam_rhost = get_remote_name_or_ip(utmp_len, options.use_dns);
Damien Miller46337202003-06-02 11:04:39 +1000326 debug("PAM: setting PAM_RHOST to \"%s\"", pam_rhost);
Damien Miller25d93422003-05-18 20:45:47 +1000327 sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, pam_rhost);
328 if (sshpam_err != PAM_SUCCESS) {
Damien Miller1f499fd2003-08-25 13:08:49 +1000329 pam_end(sshpam_handle, sshpam_err);
Damien Miller25d93422003-05-18 20:45:47 +1000330 sshpam_handle = NULL;
331 return (-1);
332 }
333#ifdef PAM_TTY_KLUDGE
334 /*
335 * Some silly PAM modules (e.g. pam_time) require a TTY to operate.
336 * sshd doesn't set the tty until too late in the auth process and
337 * may not even set one (for tty-less connections)
338 */
339 debug("PAM: setting PAM_TTY to \"ssh\"");
340 sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, "ssh");
341 if (sshpam_err != PAM_SUCCESS) {
342 pam_end(sshpam_handle, sshpam_err);
343 sshpam_handle = NULL;
344 return (-1);
345 }
346#endif
Damien Miller4f9f42a2003-05-10 19:28:02 +1000347 return (0);
348}
349
350static void *
351sshpam_init_ctx(Authctxt *authctxt)
352{
353 struct pam_ctxt *ctxt;
354 int socks[2];
355
Damien Miller4e448a32003-05-14 15:11:48 +1000356 /* Refuse to start if we don't have PAM enabled */
357 if (!options.use_pam)
358 return NULL;
359
Damien Miller4f9f42a2003-05-10 19:28:02 +1000360 /* Initialize PAM */
361 if (sshpam_init(authctxt->user) == -1) {
362 error("PAM: initialization failed");
363 return (NULL);
364 }
365
366 ctxt = xmalloc(sizeof *ctxt);
Darren Tucker8846a072003-10-07 11:30:15 +1000367 memset(ctxt, 0, sizeof(*ctxt));
Damien Miller4f9f42a2003-05-10 19:28:02 +1000368
369 /* Start the authentication thread */
370 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) {
371 error("PAM: failed create sockets: %s", strerror(errno));
372 xfree(ctxt);
373 return (NULL);
374 }
375 ctxt->pam_psock = socks[0];
376 ctxt->pam_csock = socks[1];
377 if (pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt) == -1) {
378 error("PAM: failed to start authentication thread: %s",
379 strerror(errno));
380 close(socks[0]);
381 close(socks[1]);
382 xfree(ctxt);
383 return (NULL);
384 }
Darren Tucker8846a072003-10-07 11:30:15 +1000385 cleanup_ctxt = ctxt;
Damien Miller4f9f42a2003-05-10 19:28:02 +1000386 return (ctxt);
387}
388
389static int
390sshpam_query(void *ctx, char **name, char **info,
391 u_int *num, char ***prompts, u_int **echo_on)
392{
393 Buffer buffer;
394 struct pam_ctxt *ctxt = ctx;
395 size_t plen;
396 u_char type;
397 char *msg;
Damien Miller7f2d7952003-07-30 14:53:11 +1000398 size_t len;
Damien Miller4f9f42a2003-05-10 19:28:02 +1000399
400 buffer_init(&buffer);
401 *name = xstrdup("");
402 *info = xstrdup("");
403 *prompts = xmalloc(sizeof(char *));
404 **prompts = NULL;
405 plen = 0;
406 *echo_on = xmalloc(sizeof(u_int));
407 while (ssh_msg_recv(ctxt->pam_psock, &buffer) == 0) {
408 type = buffer_get_char(&buffer);
409 msg = buffer_get_string(&buffer, NULL);
410 switch (type) {
411 case PAM_PROMPT_ECHO_ON:
412 case PAM_PROMPT_ECHO_OFF:
413 *num = 1;
Damien Miller7f2d7952003-07-30 14:53:11 +1000414 len = plen + strlen(msg) + 1;
415 **prompts = xrealloc(**prompts, len);
416 plen += snprintf(**prompts + plen, len, "%s", msg);
Damien Miller4f9f42a2003-05-10 19:28:02 +1000417 **echo_on = (type == PAM_PROMPT_ECHO_ON);
418 xfree(msg);
419 return (0);
420 case PAM_ERROR_MSG:
421 case PAM_TEXT_INFO:
422 /* accumulate messages */
Darren Tuckerae52b7c2003-11-13 19:52:31 +1100423 len = plen + strlen(msg) + 2;
Damien Miller7f2d7952003-07-30 14:53:11 +1000424 **prompts = xrealloc(**prompts, len);
Darren Tuckerae52b7c2003-11-13 19:52:31 +1100425 plen += snprintf(**prompts + plen, len, "%s\n", msg);
Damien Miller4f9f42a2003-05-10 19:28:02 +1000426 xfree(msg);
427 break;
Damien Miller4f9f42a2003-05-10 19:28:02 +1000428 case PAM_SUCCESS:
429 case PAM_AUTH_ERR:
430 if (**prompts != NULL) {
431 /* drain any accumulated messages */
432#if 0 /* XXX - not compatible with privsep */
433 packet_start(SSH2_MSG_USERAUTH_BANNER);
434 packet_put_cstring(**prompts);
435 packet_put_cstring("");
436 packet_send();
437 packet_write_wait();
438#endif
439 xfree(**prompts);
440 **prompts = NULL;
441 }
442 if (type == PAM_SUCCESS) {
443 *num = 0;
444 **echo_on = 0;
445 ctxt->pam_done = 1;
446 xfree(msg);
447 return (0);
448 }
449 error("PAM: %s", msg);
Darren Tucker439ce0d2003-10-09 14:20:15 +1000450 /* FALLTHROUGH */
Damien Miller4f9f42a2003-05-10 19:28:02 +1000451 default:
452 *num = 0;
453 **echo_on = 0;
454 xfree(msg);
455 ctxt->pam_done = -1;
456 return (-1);
457 }
458 }
459 return (-1);
460}
461
462/* XXX - see also comment in auth-chall.c:verify_response */
463static int
464sshpam_respond(void *ctx, u_int num, char **resp)
465{
466 Buffer buffer;
467 struct pam_ctxt *ctxt = ctx;
468
469 debug2("PAM: %s", __func__);
470 switch (ctxt->pam_done) {
471 case 1:
472 sshpam_authenticated = 1;
473 return (0);
474 case 0:
475 break;
476 default:
477 return (-1);
478 }
479 if (num != 1) {
480 error("PAM: expected one response, got %u", num);
481 return (-1);
482 }
483 buffer_init(&buffer);
484 buffer_put_cstring(&buffer, *resp);
Damien Miller9bdba702003-11-17 21:27:55 +1100485 if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer) == -1) {
486 buffer_free(&buffer);
487 return (-1);
488 }
Damien Miller4f9f42a2003-05-10 19:28:02 +1000489 buffer_free(&buffer);
490 return (1);
491}
492
493static void
494sshpam_free_ctx(void *ctxtp)
495{
496 struct pam_ctxt *ctxt = ctxtp;
497
Darren Tucker8846a072003-10-07 11:30:15 +1000498 sshpam_thread_cleanup();
Damien Miller4f9f42a2003-05-10 19:28:02 +1000499 xfree(ctxt);
500 /*
501 * We don't call sshpam_cleanup() here because we may need the PAM
502 * handle at a later stage, e.g. when setting up a session. It's
503 * still on the cleanup list, so pam_end() *will* be called before
504 * the server process terminates.
505 */
506}
507
508KbdintDevice sshpam_device = {
509 "pam",
510 sshpam_init_ctx,
511 sshpam_query,
512 sshpam_respond,
513 sshpam_free_ctx
514};
515
516KbdintDevice mm_sshpam_device = {
517 "pam",
518 mm_sshpam_init_ctx,
519 mm_sshpam_query,
520 mm_sshpam_respond,
521 mm_sshpam_free_ctx
522};
523
524/*
525 * This replaces auth-pam.c
526 */
527void
528start_pam(const char *user)
529{
Damien Miller9d507da2003-05-14 15:31:12 +1000530 if (!options.use_pam)
531 fatal("PAM: initialisation requested when UsePAM=no");
532
Damien Miller4f9f42a2003-05-10 19:28:02 +1000533 if (sshpam_init(user) == -1)
534 fatal("PAM: initialisation failed");
535}
536
537void
538finish_pam(void)
539{
Darren Tucker8846a072003-10-07 11:30:15 +1000540 sshpam_cleanup();
Damien Miller4f9f42a2003-05-10 19:28:02 +1000541}
542
Damien Miller1f499fd2003-08-25 13:08:49 +1000543u_int
544do_pam_account(void)
Damien Miller4f9f42a2003-05-10 19:28:02 +1000545{
Damien Miller1f499fd2003-08-25 13:08:49 +1000546 sshpam_err = pam_acct_mgmt(sshpam_handle, 0);
547 debug3("%s: pam_acct_mgmt = %d", __func__, sshpam_err);
548
549 if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD)
550 return (0);
551
552 if (sshpam_err == PAM_NEW_AUTHTOK_REQD) {
553 sshpam_new_authtok_reqd = 1;
554
555 /* Prevent forwardings until password changed */
556 no_port_forwarding_flag |= 2;
557 no_agent_forwarding_flag |= 2;
558 no_x11_forwarding_flag |= 2;
559 }
560
Damien Miller4f9f42a2003-05-10 19:28:02 +1000561 return (1);
562}
563
564void
Damien Miller341c6e62003-09-02 23:18:52 +1000565do_pam_session(void)
Damien Miller4f9f42a2003-05-10 19:28:02 +1000566{
567 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
568 (const void *)&null_conv);
569 if (sshpam_err != PAM_SUCCESS)
570 fatal("PAM: failed to set PAM_CONV: %s",
571 pam_strerror(sshpam_handle, sshpam_err));
Damien Miller341c6e62003-09-02 23:18:52 +1000572 sshpam_err = pam_open_session(sshpam_handle, 0);
573 if (sshpam_err != PAM_SUCCESS)
574 fatal("PAM: pam_open_session(): %s",
575 pam_strerror(sshpam_handle, sshpam_err));
576 sshpam_session_open = 1;
577}
578
579void
580do_pam_set_tty(const char *tty)
581{
Darren Tuckerf38db7f2003-08-08 13:43:37 +1000582 if (tty != NULL) {
583 debug("PAM: setting PAM_TTY to \"%s\"", tty);
584 sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, tty);
585 if (sshpam_err != PAM_SUCCESS)
586 fatal("PAM: failed to set PAM_TTY: %s",
587 pam_strerror(sshpam_handle, sshpam_err));
588 }
Damien Miller4f9f42a2003-05-10 19:28:02 +1000589}
590
591void
592do_pam_setcred(int init)
593{
594 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
595 (const void *)&null_conv);
596 if (sshpam_err != PAM_SUCCESS)
597 fatal("PAM: failed to set PAM_CONV: %s",
598 pam_strerror(sshpam_handle, sshpam_err));
599 if (init) {
600 debug("PAM: establishing credentials");
601 sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED);
602 } else {
603 debug("PAM: reinitializing credentials");
604 sshpam_err = pam_setcred(sshpam_handle, PAM_REINITIALIZE_CRED);
605 }
606 if (sshpam_err == PAM_SUCCESS) {
607 sshpam_cred_established = 1;
608 return;
609 }
610 if (sshpam_authenticated)
611 fatal("PAM: pam_setcred(): %s",
612 pam_strerror(sshpam_handle, sshpam_err));
613 else
614 debug("PAM: pam_setcred(): %s",
615 pam_strerror(sshpam_handle, sshpam_err));
616}
617
618int
619is_pam_password_change_required(void)
620{
621 return (sshpam_new_authtok_reqd);
622}
623
624static int
Damien Miller1f499fd2003-08-25 13:08:49 +1000625pam_chauthtok_conv(int n, const struct pam_message **msg,
626 struct pam_response **resp, void *data)
Damien Miller4f9f42a2003-05-10 19:28:02 +1000627{
628 char input[PAM_MAX_MSG_SIZE];
Damien Miller5c3a5582003-09-23 22:12:38 +1000629 struct pam_response *reply;
Damien Miller4f9f42a2003-05-10 19:28:02 +1000630 int i;
631
Damien Miller5c3a5582003-09-23 22:12:38 +1000632 *resp = NULL;
633
Damien Miller4f9f42a2003-05-10 19:28:02 +1000634 if (n <= 0 || n > PAM_MAX_NUM_MSG)
635 return (PAM_CONV_ERR);
Damien Miller5c3a5582003-09-23 22:12:38 +1000636
637 if ((reply = malloc(n * sizeof(*reply))) == NULL)
638 return (PAM_CONV_ERR);
639 memset(reply, 0, n * sizeof(*reply));
640
Damien Miller4f9f42a2003-05-10 19:28:02 +1000641 for (i = 0; i < n; ++i) {
642 switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
643 case PAM_PROMPT_ECHO_OFF:
Damien Miller5c3a5582003-09-23 22:12:38 +1000644 reply[i].resp =
Damien Miller4f9f42a2003-05-10 19:28:02 +1000645 read_passphrase(PAM_MSG_MEMBER(msg, i, msg),
646 RP_ALLOW_STDIN);
Damien Miller5c3a5582003-09-23 22:12:38 +1000647 reply[i].resp_retcode = PAM_SUCCESS;
Damien Miller4f9f42a2003-05-10 19:28:02 +1000648 break;
649 case PAM_PROMPT_ECHO_ON:
Darren Tucker0947ddf2003-11-13 11:21:31 +1100650 fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
Damien Miller4f9f42a2003-05-10 19:28:02 +1000651 fgets(input, sizeof input, stdin);
Damien Miller5c3a5582003-09-23 22:12:38 +1000652 reply[i].resp = xstrdup(input);
653 reply[i].resp_retcode = PAM_SUCCESS;
Damien Miller4f9f42a2003-05-10 19:28:02 +1000654 break;
655 case PAM_ERROR_MSG:
656 case PAM_TEXT_INFO:
Darren Tucker0947ddf2003-11-13 11:21:31 +1100657 fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg));
Damien Miller5c3a5582003-09-23 22:12:38 +1000658 reply[i].resp_retcode = PAM_SUCCESS;
Damien Miller4f9f42a2003-05-10 19:28:02 +1000659 break;
660 default:
661 goto fail;
662 }
663 }
Damien Miller5c3a5582003-09-23 22:12:38 +1000664 *resp = reply;
Damien Miller4f9f42a2003-05-10 19:28:02 +1000665 return (PAM_SUCCESS);
Damien Miller5c3a5582003-09-23 22:12:38 +1000666
Damien Miller4f9f42a2003-05-10 19:28:02 +1000667 fail:
Damien Miller5c3a5582003-09-23 22:12:38 +1000668 for(i = 0; i < n; i++) {
669 if (reply[i].resp != NULL)
670 xfree(reply[i].resp);
671 }
672 xfree(reply);
Damien Miller4f9f42a2003-05-10 19:28:02 +1000673 return (PAM_CONV_ERR);
674}
675
676/*
677 * XXX this should be done in the authentication phase, but ssh1 doesn't
678 * support that
679 */
680void
681do_pam_chauthtok(void)
682{
Damien Millerf4b6f102003-09-02 23:12:06 +1000683 struct pam_conv pam_conv;
684
685 pam_conv.conv = pam_chauthtok_conv;
686 pam_conv.appdata_ptr = NULL;
Damien Miller4f9f42a2003-05-10 19:28:02 +1000687
688 if (use_privsep)
Damien Miller1f499fd2003-08-25 13:08:49 +1000689 fatal("Password expired (unable to change with privsep)");
Damien Miller4f9f42a2003-05-10 19:28:02 +1000690 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
691 (const void *)&pam_conv);
692 if (sshpam_err != PAM_SUCCESS)
693 fatal("PAM: failed to set PAM_CONV: %s",
694 pam_strerror(sshpam_handle, sshpam_err));
695 debug("PAM: changing password");
696 sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
697 if (sshpam_err != PAM_SUCCESS)
698 fatal("PAM: pam_chauthtok(): %s",
699 pam_strerror(sshpam_handle, sshpam_err));
700}
701
Darren Tucker49aaf4a2003-08-26 11:58:16 +1000702/*
703 * Set a PAM environment string. We need to do this so that the session
704 * modules can handle things like Kerberos/GSI credentials that appear
705 * during the ssh authentication process.
706 */
707
708int
709do_pam_putenv(char *name, char *value)
710{
Darren Tucker49aaf4a2003-08-26 11:58:16 +1000711 int ret = 1;
Darren Tucker49aaf4a2003-08-26 11:58:16 +1000712#ifdef HAVE_PAM_PUTENV
Damien Millerf2728092003-09-17 07:24:25 +1000713 char *compound;
714 size_t len;
715
716 len = strlen(name) + strlen(value) + 2;
717 compound = xmalloc(len);
718
719 snprintf(compound, len, "%s=%s", name, value);
720 ret = pam_putenv(sshpam_handle, compound);
721 xfree(compound);
Darren Tucker49aaf4a2003-08-26 11:58:16 +1000722#endif
Damien Millerf2728092003-09-17 07:24:25 +1000723
Darren Tucker49aaf4a2003-08-26 11:58:16 +1000724 return (ret);
725}
726
Damien Miller4f9f42a2003-05-10 19:28:02 +1000727void
728print_pam_messages(void)
729{
730 /* XXX */
731}
732
733char **
734fetch_pam_environment(void)
735{
736#ifdef HAVE_PAM_GETENVLIST
737 debug("PAM: retrieving environment");
738 return (pam_getenvlist(sshpam_handle));
739#else
740 return (NULL);
741#endif
742}
743
744void
745free_pam_environment(char **env)
746{
747 char **envp;
748
Damien Millere27c6cc2003-05-16 18:21:01 +1000749 if (env == NULL)
750 return;
751
Damien Miller4f9f42a2003-05-10 19:28:02 +1000752 for (envp = env; *envp; envp++)
753 xfree(*envp);
754 xfree(env);
Damien Millere72b7af1999-12-30 15:08:44 +1100755}
756
757#endif /* USE_PAM */