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