]> andersk Git - openssh.git/blob - auth-pam.c
- (djm) Merge FreeBSD PAM code: replaces PAM password auth kludge with
[openssh.git] / auth-pam.c
1 /*-
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.
9  *
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.
18  *
19  * 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.
30  */
31
32 #include "includes.h"
33 RCSID("$FreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des Exp $");
34
35 #ifdef USE_PAM
36 #include <security/pam_appl.h>
37
38 #include "auth.h"
39 #include "auth-pam.h"
40 #include "buffer.h"
41 #include "bufaux.h"
42 #include "canohost.h"
43 #include "log.h"
44 #include "monitor_wrap.h"
45 #include "msg.h"
46 #include "packet.h"
47 #include "readpass.h"
48 #include "servconf.h"
49 #include "ssh2.h"
50 #include "xmalloc.h"
51
52 #define __unused
53
54 #ifdef USE_POSIX_THREADS
55 #include <pthread.h>
56 /*
57  * Avoid namespace clash when *not* using pthreads for systems *with* 
58  * pthreads, which unconditionally define pthread_t via sys/types.h 
59  * (e.g. Linux)
60  */
61 typedef pthread_t sp_pthread_t; 
62 #else
63 /*
64  * Simulate threads with processes.
65  */
66 typedef pid_t sp_pthread_t;
67
68 static void
69 pthread_exit(void *value __unused)
70 {
71         _exit(0);
72 }
73
74 static int
75 pthread_create(sp_pthread_t *thread, const void *attr __unused,
76     void *(*thread_start)(void *), void *arg)
77 {
78         pid_t pid;
79
80         switch ((pid = fork())) {
81         case -1:
82                 error("fork(): %s", strerror(errno));
83                 return (-1);
84         case 0:
85                 thread_start(arg);
86                 _exit(1);
87         default:
88                 *thread = pid;
89                 return (0);
90         }
91 }
92
93 static int
94 pthread_cancel(sp_pthread_t thread)
95 {
96         return (kill(thread, SIGTERM));
97 }
98
99 static int
100 pthread_join(sp_pthread_t thread, void **value __unused)
101 {
102         int status;
103
104         waitpid(thread, &status, 0);
105         return (status);
106 }
107 #endif
108
109
110 static pam_handle_t *sshpam_handle;
111 static int sshpam_err;
112 static int sshpam_authenticated;
113 static int sshpam_new_authtok_reqd;
114 static int sshpam_session_open;
115 static int sshpam_cred_established;
116
117 struct pam_ctxt {
118         sp_pthread_t     pam_thread;
119         int              pam_psock;
120         int              pam_csock;
121         int              pam_done;
122 };
123
124 static void sshpam_free_ctx(void *);
125
126 /*
127  * Conversation function for authentication thread.
128  */
129 static int
130 sshpam_thread_conv(int n,
131          const struct pam_message **msg,
132          struct pam_response **resp,
133          void *data)
134 {
135         Buffer buffer;
136         struct pam_ctxt *ctxt;
137         int i;
138
139         ctxt = data;
140         if (n <= 0 || n > PAM_MAX_NUM_MSG)
141                 return (PAM_CONV_ERR);
142         *resp = xmalloc(n * sizeof **resp);
143         buffer_init(&buffer);
144         for (i = 0; i < n; ++i) {
145                 resp[i]->resp_retcode = 0;
146                 resp[i]->resp = NULL;
147                 switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
148                 case PAM_PROMPT_ECHO_OFF:
149                         buffer_put_cstring(&buffer, PAM_MSG_MEMBER(msg, i, msg));
150                         ssh_msg_send(ctxt->pam_csock, 
151                             PAM_MSG_MEMBER(msg, i, msg_style), &buffer);
152                         ssh_msg_recv(ctxt->pam_csock, &buffer);
153                         if (buffer_get_char(&buffer) != PAM_AUTHTOK)
154                                 goto fail;
155                         resp[i]->resp = buffer_get_string(&buffer, NULL);
156                         break;
157                 case PAM_PROMPT_ECHO_ON:
158                         buffer_put_cstring(&buffer, PAM_MSG_MEMBER(msg, i, msg));
159                         ssh_msg_send(ctxt->pam_csock, 
160                             PAM_MSG_MEMBER(msg, i, msg_style), &buffer);
161                         ssh_msg_recv(ctxt->pam_csock, &buffer);
162                         if (buffer_get_char(&buffer) != PAM_AUTHTOK)
163                                 goto fail;
164                         resp[i]->resp = buffer_get_string(&buffer, NULL);
165                         break;
166                 case PAM_ERROR_MSG:
167                         buffer_put_cstring(&buffer, PAM_MSG_MEMBER(msg, i, msg));
168                         ssh_msg_send(ctxt->pam_csock, 
169                             PAM_MSG_MEMBER(msg, i, msg_style), &buffer);
170                         break;
171                 case PAM_TEXT_INFO:
172                         buffer_put_cstring(&buffer, PAM_MSG_MEMBER(msg, i, msg));
173                         ssh_msg_send(ctxt->pam_csock, 
174                             PAM_MSG_MEMBER(msg, i, msg_style), &buffer);
175                         break;
176                 default:
177                         goto fail;
178                 }
179                 buffer_clear(&buffer);
180         }
181         buffer_free(&buffer);
182         return (PAM_SUCCESS);
183  fail:
184         while (i)
185                 xfree(resp[--i]);
186         xfree(*resp);
187         *resp = NULL;
188         buffer_free(&buffer);
189         return (PAM_CONV_ERR);
190 }
191
192 /*
193  * Authentication thread.
194  */
195 static void *
196 sshpam_thread(void *ctxtp)
197 {
198         struct pam_ctxt *ctxt = ctxtp;
199         Buffer buffer;
200         struct pam_conv sshpam_conv = { sshpam_thread_conv, ctxt };
201 #ifndef USE_POSIX_THREADS
202         const char *pam_user;
203
204         pam_get_item(sshpam_handle, PAM_USER, (const void **)&pam_user);
205         setproctitle("%s [pam]", pam_user);
206 #endif
207
208         buffer_init(&buffer);
209         sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
210             (const void *)&sshpam_conv);
211         if (sshpam_err != PAM_SUCCESS)
212                 goto auth_fail;
213         sshpam_err = pam_authenticate(sshpam_handle, 0);
214         if (sshpam_err != PAM_SUCCESS)
215                 goto auth_fail;
216         sshpam_err = pam_acct_mgmt(sshpam_handle, 0);
217         if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD)
218                 goto auth_fail;
219         buffer_put_cstring(&buffer, "OK");
220         ssh_msg_send(ctxt->pam_csock, sshpam_err, &buffer);
221         buffer_free(&buffer);
222         pthread_exit(NULL);
223
224  auth_fail:
225         buffer_put_cstring(&buffer,
226             pam_strerror(sshpam_handle, sshpam_err));
227         ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer);
228         buffer_free(&buffer);
229         pthread_exit(NULL);
230         
231         return (NULL); /* Avoid warning for non-pthread case */
232 }
233
234 static void
235 sshpam_thread_cleanup(void *ctxtp)
236 {
237         struct pam_ctxt *ctxt = ctxtp;
238
239         pthread_cancel(ctxt->pam_thread);
240         pthread_join(ctxt->pam_thread, NULL);
241         close(ctxt->pam_psock);
242         close(ctxt->pam_csock);
243 }
244
245 static int
246 sshpam_null_conv(int n,
247          const struct pam_message **msg,
248          struct pam_response **resp,
249          void *data)
250 {
251
252         return (PAM_CONV_ERR);
253 }
254
255 static struct pam_conv null_conv = { sshpam_null_conv, NULL };
256
257 static void
258 sshpam_cleanup(void *arg)
259 {
260         (void)arg;
261         debug("PAM: cleanup");
262         pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv);
263         if (sshpam_cred_established) {
264                 pam_setcred(sshpam_handle, PAM_DELETE_CRED);
265                 sshpam_cred_established = 0;
266         }
267         if (sshpam_session_open) {
268                 pam_close_session(sshpam_handle, PAM_SILENT);
269                 sshpam_session_open = 0;
270         }
271         sshpam_authenticated = sshpam_new_authtok_reqd = 0;
272         pam_end(sshpam_handle, sshpam_err);
273         sshpam_handle = NULL;
274 }
275
276 static int
277 sshpam_init(const char *user)
278 {
279         extern ServerOptions options;
280         extern u_int utmp_len;
281         const char *pam_rhost, *pam_user;
282
283         if (sshpam_handle != NULL) {
284                 /* We already have a PAM context; check if the user matches */
285                 sshpam_err = pam_get_item(sshpam_handle,
286                     PAM_USER, (const void **)&pam_user);
287                 if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0)
288                         return (0);
289                 fatal_remove_cleanup(sshpam_cleanup, NULL);
290                 pam_end(sshpam_handle, sshpam_err);
291                 sshpam_handle = NULL;
292         }
293         debug("PAM: initializing for \"%s\"", user);
294         sshpam_err = pam_start("sshd", user, &null_conv, &sshpam_handle);
295         if (sshpam_err != PAM_SUCCESS)
296                 return (-1);
297         pam_rhost = get_remote_name_or_ip(utmp_len,
298             options.verify_reverse_mapping);
299         debug("PAM: setting PAM_RHOST to \"%s\"", pam_rhost);
300         sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, pam_rhost);
301         if (sshpam_err != PAM_SUCCESS) {
302                 pam_end(sshpam_handle, sshpam_err);
303                 sshpam_handle = NULL;
304                 return (-1);
305         }
306         fatal_add_cleanup(sshpam_cleanup, NULL);
307         return (0);
308 }
309
310 static void *
311 sshpam_init_ctx(Authctxt *authctxt)
312 {
313         struct pam_ctxt *ctxt;
314         int socks[2];
315
316         /* Initialize PAM */
317         if (sshpam_init(authctxt->user) == -1) {
318                 error("PAM: initialization failed");
319                 return (NULL);
320         }
321
322         ctxt = xmalloc(sizeof *ctxt);
323         ctxt->pam_done = 0;
324
325         /* Start the authentication thread */
326         if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) {
327                 error("PAM: failed create sockets: %s", strerror(errno));
328                 xfree(ctxt);
329                 return (NULL);
330         }
331         ctxt->pam_psock = socks[0];
332         ctxt->pam_csock = socks[1];
333         if (pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt) == -1) {
334                 error("PAM: failed to start authentication thread: %s",
335                     strerror(errno));
336                 close(socks[0]);
337                 close(socks[1]);
338                 xfree(ctxt);
339                 return (NULL);
340         }
341         fatal_add_cleanup(sshpam_thread_cleanup, ctxt);
342         return (ctxt);
343 }
344
345 static int
346 sshpam_query(void *ctx, char **name, char **info,
347     u_int *num, char ***prompts, u_int **echo_on)
348 {
349         Buffer buffer;
350         struct pam_ctxt *ctxt = ctx;
351         size_t plen;
352         u_char type;
353         char *msg;
354
355         buffer_init(&buffer);
356         *name = xstrdup("");
357         *info = xstrdup("");
358         *prompts = xmalloc(sizeof(char *));
359         **prompts = NULL;
360         plen = 0;
361         *echo_on = xmalloc(sizeof(u_int));
362         while (ssh_msg_recv(ctxt->pam_psock, &buffer) == 0) {
363                 type = buffer_get_char(&buffer);
364                 msg = buffer_get_string(&buffer, NULL);
365                 switch (type) {
366                 case PAM_PROMPT_ECHO_ON:
367                 case PAM_PROMPT_ECHO_OFF:
368                         *num = 1;
369                         **prompts = xrealloc(**prompts, plen + strlen(msg) + 1);
370                         plen += sprintf(**prompts + plen, "%s", msg);
371                         **echo_on = (type == PAM_PROMPT_ECHO_ON);
372                         xfree(msg);
373                         return (0);
374                 case PAM_ERROR_MSG:
375                 case PAM_TEXT_INFO:
376                         /* accumulate messages */
377                         **prompts = xrealloc(**prompts, plen + strlen(msg) + 1);
378                         plen += sprintf(**prompts + plen, "%s", msg);
379                         xfree(msg);
380                         break;
381                 case PAM_NEW_AUTHTOK_REQD:
382                         sshpam_new_authtok_reqd = 1;
383                         /* FALLTHROUGH */
384                 case PAM_SUCCESS:
385                 case PAM_AUTH_ERR:
386                         if (**prompts != NULL) {
387                                 /* drain any accumulated messages */
388 #if 0 /* XXX - not compatible with privsep */
389                                 packet_start(SSH2_MSG_USERAUTH_BANNER);
390                                 packet_put_cstring(**prompts);
391                                 packet_put_cstring("");
392                                 packet_send();
393                                 packet_write_wait();
394 #endif
395                                 xfree(**prompts);
396                                 **prompts = NULL;
397                         }
398                         if (type == PAM_SUCCESS) {
399                                 *num = 0;
400                                 **echo_on = 0;
401                                 ctxt->pam_done = 1;
402                                 xfree(msg);
403                                 return (0);
404                         }
405                         error("PAM: %s", msg);
406                 default:
407                         *num = 0;
408                         **echo_on = 0;
409                         xfree(msg);
410                         ctxt->pam_done = -1;
411                         return (-1);
412                 }
413         }
414         return (-1);
415 }
416
417 /* XXX - see also comment in auth-chall.c:verify_response */
418 static int
419 sshpam_respond(void *ctx, u_int num, char **resp)
420 {
421         Buffer buffer;
422         struct pam_ctxt *ctxt = ctx;
423
424         debug2("PAM: %s", __func__);
425         switch (ctxt->pam_done) {
426         case 1:
427                 sshpam_authenticated = 1;
428                 return (0);
429         case 0:
430                 break;
431         default:
432                 return (-1);
433         }
434         if (num != 1) {
435                 error("PAM: expected one response, got %u", num);
436                 return (-1);
437         }
438         buffer_init(&buffer);
439         buffer_put_cstring(&buffer, *resp);
440         ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer);
441         buffer_free(&buffer);
442         return (1);
443 }
444
445 static void
446 sshpam_free_ctx(void *ctxtp)
447 {
448         struct pam_ctxt *ctxt = ctxtp;
449
450         fatal_remove_cleanup(sshpam_thread_cleanup, ctxt);
451         sshpam_thread_cleanup(ctxtp);
452         xfree(ctxt);
453         /*
454          * We don't call sshpam_cleanup() here because we may need the PAM
455          * handle at a later stage, e.g. when setting up a session.  It's
456          * still on the cleanup list, so pam_end() *will* be called before
457          * the server process terminates.
458          */
459 }
460
461 KbdintDevice sshpam_device = {
462         "pam",
463         sshpam_init_ctx,
464         sshpam_query,
465         sshpam_respond,
466         sshpam_free_ctx
467 };
468
469 KbdintDevice mm_sshpam_device = {
470         "pam",
471         mm_sshpam_init_ctx,
472         mm_sshpam_query,
473         mm_sshpam_respond,
474         mm_sshpam_free_ctx
475 };
476
477 /*
478  * This replaces auth-pam.c
479  */
480 void
481 start_pam(const char *user)
482 {
483         if (sshpam_init(user) == -1)
484                 fatal("PAM: initialisation failed");
485 }
486
487 void
488 finish_pam(void)
489 {
490         fatal_remove_cleanup(sshpam_cleanup, NULL);
491         sshpam_cleanup(NULL);
492 }
493
494 int
495 do_pam_account(const char *user, const char *ruser)
496 {
497         /* XXX */
498         return (1);
499 }
500
501 void
502 do_pam_session(const char *user, const char *tty)
503 {
504         sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, 
505             (const void *)&null_conv);
506         if (sshpam_err != PAM_SUCCESS)
507                 fatal("PAM: failed to set PAM_CONV: %s",
508                     pam_strerror(sshpam_handle, sshpam_err));
509         debug("PAM: setting PAM_TTY to \"%s\"", tty);
510         sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, tty);
511         if (sshpam_err != PAM_SUCCESS)
512                 fatal("PAM: failed to set PAM_TTY: %s",
513                     pam_strerror(sshpam_handle, sshpam_err));
514         sshpam_err = pam_open_session(sshpam_handle, 0);
515         if (sshpam_err != PAM_SUCCESS)
516                 fatal("PAM: pam_open_session(): %s",
517                     pam_strerror(sshpam_handle, sshpam_err));
518         sshpam_session_open = 1;
519 }
520
521 void
522 do_pam_setcred(int init)
523 {
524         sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
525             (const void *)&null_conv);
526         if (sshpam_err != PAM_SUCCESS)
527                 fatal("PAM: failed to set PAM_CONV: %s",
528                     pam_strerror(sshpam_handle, sshpam_err));
529         if (init) {
530                 debug("PAM: establishing credentials");
531                 sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED);
532         } else {
533                 debug("PAM: reinitializing credentials");
534                 sshpam_err = pam_setcred(sshpam_handle, PAM_REINITIALIZE_CRED);
535         }
536         if (sshpam_err == PAM_SUCCESS) {
537                 sshpam_cred_established = 1;
538                 return;
539         }
540         if (sshpam_authenticated)
541                 fatal("PAM: pam_setcred(): %s",
542                     pam_strerror(sshpam_handle, sshpam_err));
543         else
544                 debug("PAM: pam_setcred(): %s",
545                     pam_strerror(sshpam_handle, sshpam_err));
546 }
547
548 int
549 is_pam_password_change_required(void)
550 {
551         return (sshpam_new_authtok_reqd);
552 }
553
554 static int
555 pam_chauthtok_conv(int n,
556          const struct pam_message **msg,
557          struct pam_response **resp,
558          void *data)
559 {
560         char input[PAM_MAX_MSG_SIZE];
561         int i;
562
563         if (n <= 0 || n > PAM_MAX_NUM_MSG)
564                 return (PAM_CONV_ERR);
565         *resp = xmalloc(n * sizeof **resp);
566         for (i = 0; i < n; ++i) {
567                 switch (PAM_MSG_MEMBER(msg, i, msg_style)) {
568                 case PAM_PROMPT_ECHO_OFF:
569                         resp[i]->resp =
570                             read_passphrase(PAM_MSG_MEMBER(msg, i, msg), 
571                             RP_ALLOW_STDIN);
572                         resp[i]->resp_retcode = PAM_SUCCESS;
573                         break;
574                 case PAM_PROMPT_ECHO_ON:
575                         fputs(PAM_MSG_MEMBER(msg, i, msg), stderr);
576                         fgets(input, sizeof input, stdin);
577                         resp[i]->resp = xstrdup(input);
578                         resp[i]->resp_retcode = PAM_SUCCESS;
579                         break;
580                 case PAM_ERROR_MSG:
581                 case PAM_TEXT_INFO:
582                         fputs(PAM_MSG_MEMBER(msg, i, msg), stderr);
583                         resp[i]->resp_retcode = PAM_SUCCESS;
584                         break;
585                 default:
586                         goto fail;
587                 }
588         }
589         return (PAM_SUCCESS);
590  fail:
591         while (i)
592                 xfree(resp[--i]);
593         xfree(*resp);
594         *resp = NULL;
595         return (PAM_CONV_ERR);
596 }
597
598 /*
599  * XXX this should be done in the authentication phase, but ssh1 doesn't
600  * support that
601  */
602 void
603 do_pam_chauthtok(void)
604 {
605         struct pam_conv pam_conv = { pam_chauthtok_conv, NULL };
606
607         if (use_privsep)
608                 fatal("PAM: chauthtok not supprted with privsep");
609         sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
610             (const void *)&pam_conv);
611         if (sshpam_err != PAM_SUCCESS)
612                 fatal("PAM: failed to set PAM_CONV: %s",
613                     pam_strerror(sshpam_handle, sshpam_err));
614         debug("PAM: changing password");
615         sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
616         if (sshpam_err != PAM_SUCCESS)
617                 fatal("PAM: pam_chauthtok(): %s",
618                     pam_strerror(sshpam_handle, sshpam_err));
619 }
620
621 void
622 print_pam_messages(void)
623 {
624         /* XXX */
625 }
626
627 char **
628 fetch_pam_environment(void)
629 {
630 #ifdef HAVE_PAM_GETENVLIST
631         debug("PAM: retrieving environment");
632         return (pam_getenvlist(sshpam_handle));
633 #else
634         return (NULL);
635 #endif
636 }
637
638 void
639 free_pam_environment(char **env)
640 {
641         char **envp;
642
643         for (envp = env; *envp; envp++)
644                 xfree(*envp);
645         xfree(env);
646 }
647
648 #endif /* USE_PAM */
This page took 0.091154 seconds and 5 git commands to generate.