Initial import from FreeBSD RELENG_4:
[dragonfly.git] / crypto / openssh / auth2-pam.c
1 #include "includes.h"
2 RCSID("$Id: auth2-pam.c,v 1.14 2002/06/28 16:48:12 mouring Exp $");
3 RCSID("$FreeBSD: src/crypto/openssh/auth2-pam.c,v 1.4.2.2 2003/02/03 17:31:06 des Exp $");
4
5 #ifdef USE_PAM
6 #include <security/pam_appl.h>
7
8 #include "ssh.h"
9 #include "ssh2.h"
10 #include "auth.h"
11 #include "auth-pam.h"
12 #include "packet.h"
13 #include "xmalloc.h"
14 #include "dispatch.h"
15 #include "log.h"
16
17 static int do_pam_conversation_kbd_int(int num_msg, 
18     const struct pam_message **msg, struct pam_response **resp, 
19     void *appdata_ptr);
20 void input_userauth_info_response_pam(int type, u_int32_t seqnr, void *ctxt);
21
22 struct {
23         int finished, num_received, num_expected;
24         int *prompts;
25         struct pam_response *responses;
26 } context_pam2 = {0, 0, 0, NULL};
27
28 static struct pam_conv conv2 = {
29         do_pam_conversation_kbd_int,
30         NULL,
31 };
32
33 int
34 auth2_pam(Authctxt *authctxt)
35 {
36         int retval = -1;
37
38         if (authctxt->user == NULL)
39                 fatal("auth2_pam: internal error: no user");
40
41         conv2.appdata_ptr = authctxt;
42         do_pam_set_conv(&conv2);
43
44         dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE,
45             &input_userauth_info_response_pam);
46         retval = (do_pam_authenticate(0) == PAM_SUCCESS);
47         dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL);
48
49         return retval;
50 }
51
52 static int
53 do_pam_conversation_kbd_int(int num_msg, const struct pam_message **msg,
54     struct pam_response **resp, void *appdata_ptr)
55 {
56         int i, j, done;
57         char *text;
58
59         context_pam2.finished = 0;
60         context_pam2.num_received = 0;
61         context_pam2.num_expected = 0;
62         context_pam2.prompts = xmalloc(sizeof(int) * num_msg);
63         context_pam2.responses = xmalloc(sizeof(struct pam_response) * num_msg);
64         memset(context_pam2.responses, 0, sizeof(struct pam_response) * num_msg);
65
66         text = NULL;
67         for (i = 0, context_pam2.num_expected = 0; i < num_msg; i++) {
68                 int style = PAM_MSG_MEMBER(msg, i, msg_style);
69                 switch (style) {
70                 case PAM_PROMPT_ECHO_ON:
71                 case PAM_PROMPT_ECHO_OFF:
72                         context_pam2.num_expected++;
73                         break;
74                 case PAM_TEXT_INFO:
75                 case PAM_ERROR_MSG:
76                 default:
77                         /* Capture all these messages to be sent at once */
78                         message_cat(&text, PAM_MSG_MEMBER(msg, i, msg));
79                         break;
80                 }
81         }
82
83         if (context_pam2.num_expected == 0)
84                 return PAM_SUCCESS;
85
86         packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST);
87         packet_put_cstring(""); /* Name */
88         packet_put_cstring(""); /* Instructions */
89         packet_put_cstring(""); /* Language */
90         packet_put_int(context_pam2.num_expected);
91         
92         for (i = 0, j = 0; i < num_msg; i++) {
93                 int style = PAM_MSG_MEMBER(msg, i, msg_style);
94                 
95                 /* Skip messages which don't need a reply */
96                 if (style != PAM_PROMPT_ECHO_ON && style != PAM_PROMPT_ECHO_OFF)
97                         continue;
98                 
99                 context_pam2.prompts[j++] = i;
100                 if (text) {
101                         message_cat(&text, PAM_MSG_MEMBER(msg, i, msg));
102                         packet_put_cstring(text);
103                         text = NULL;
104                 } else
105                         packet_put_cstring(PAM_MSG_MEMBER(msg, i, msg));
106                 packet_put_char(style == PAM_PROMPT_ECHO_ON);
107         }
108         packet_send();
109         packet_write_wait();
110
111         /*
112          * Grabbing control of execution and spinning until we get what
113          * we want is probably rude, but it seems to work properly, and
114          * the client *should* be in lock-step with us, so the loop should
115          * only be traversed once.
116          */
117         while(context_pam2.finished == 0) {
118                 done = 1;
119                 dispatch_run(DISPATCH_BLOCK, &done, appdata_ptr);
120                 if (context_pam2.finished == 0)
121                         debug("extra packet during conversation");
122         }
123
124         if (context_pam2.num_received == context_pam2.num_expected) {
125                 *resp = context_pam2.responses;
126                 return PAM_SUCCESS;
127         } else
128                 return PAM_CONV_ERR;
129 }
130
131 void
132 input_userauth_info_response_pam(int type, u_int32_t seqnr, void *ctxt)
133 {
134         Authctxt *authctxt = ctxt;
135         unsigned int nresp = 0, rlen = 0, i = 0;
136         char *resp;
137
138         if (authctxt == NULL)
139                 fatal("input_userauth_info_response_pam: no authentication context");
140
141         nresp = packet_get_int();       /* Number of responses. */
142         debug("got %d responses", nresp);
143
144
145         if (nresp != context_pam2.num_expected)
146                 fatal("%s: Received incorrect number of responses "
147                     "(expected %d, received %u)", __func__, 
148                     context_pam2.num_expected, nresp);
149
150         if (nresp > 100)
151                 fatal("%s: too many replies", __func__);
152
153         for (i = 0; i < nresp; i++) {
154                 int j = context_pam2.prompts[i];
155
156                 resp = packet_get_string(&rlen);
157                 context_pam2.responses[j].resp_retcode = PAM_SUCCESS;
158                 context_pam2.responses[j].resp = xstrdup(resp);
159                 xfree(resp);
160                 context_pam2.num_received++;
161         }
162
163         context_pam2.finished = 1;
164
165         packet_check_eom();
166 }
167 #endif