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