Branch data Line data Source code
1 : : /* $OpenBSD: auth2-chall.c,v 1.41 2014/02/02 03:44:31 djm Exp $ */
2 : : /*
3 : : * Copyright (c) 2001 Markus Friedl. All rights reserved.
4 : : * Copyright (c) 2001 Per Allansson. All rights reserved.
5 : : *
6 : : * Redistribution and use in source and binary forms, with or without
7 : : * modification, are permitted provided that the following conditions
8 : : * are met:
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer.
11 : : * 2. Redistributions in binary form must reproduce the above copyright
12 : : * notice, this list of conditions and the following disclaimer in the
13 : : * documentation and/or other materials provided with the distribution.
14 : : *
15 : : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 : : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 : : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 : : * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 : : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 : : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 : : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 : : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 : : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 : : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 : : */
26 : :
27 : : #include "includes.h"
28 : :
29 : : #include <sys/types.h>
30 : :
31 : : #include <stdarg.h>
32 : : #include <stdio.h>
33 : : #include <string.h>
34 : :
35 : : #include "xmalloc.h"
36 : : #include "ssh2.h"
37 : : #include "key.h"
38 : : #include "hostfile.h"
39 : : #include "auth.h"
40 : : #include "buffer.h"
41 : : #include "packet.h"
42 : : #include "dispatch.h"
43 : : #include "log.h"
44 : : #include "servconf.h"
45 : :
46 : : /* import */
47 : : extern ServerOptions options;
48 : :
49 : : static int auth2_challenge_start(Authctxt *);
50 : : static int send_userauth_info_request(Authctxt *);
51 : : static void input_userauth_info_response(int, u_int32_t, void *);
52 : :
53 : : #ifdef BSD_AUTH
54 : : extern KbdintDevice bsdauth_device;
55 : : #else
56 : : #ifdef USE_PAM
57 : : extern KbdintDevice sshpam_device;
58 : : #endif
59 : : #ifdef SKEY
60 : : extern KbdintDevice skey_device;
61 : : #endif
62 : : #endif
63 : :
64 : : KbdintDevice *devices[] = {
65 : : #ifdef BSD_AUTH
66 : : &bsdauth_device,
67 : : #else
68 : : #ifdef USE_PAM
69 : : &sshpam_device,
70 : : #endif
71 : : #ifdef SKEY
72 : : &skey_device,
73 : : #endif
74 : : #endif
75 : : NULL
76 : : };
77 : :
78 : : typedef struct KbdintAuthctxt KbdintAuthctxt;
79 : : struct KbdintAuthctxt
80 : : {
81 : : char *devices;
82 : : void *ctxt;
83 : : KbdintDevice *device;
84 : : u_int nreq;
85 : : };
86 : :
87 : : #ifdef USE_PAM
88 : : void
89 : : remove_kbdint_device(const char *devname)
90 : : {
91 : : int i, j;
92 : :
93 : : for (i = 0; devices[i] != NULL; i++)
94 : : if (strcmp(devices[i]->name, devname) == 0) {
95 : : for (j = i; devices[j] != NULL; j++)
96 : : devices[j] = devices[j+1];
97 : : i--;
98 : : }
99 : : }
100 : : #endif
101 : :
102 : : static KbdintAuthctxt *
103 : 0 : kbdint_alloc(const char *devs)
104 : : {
105 : : KbdintAuthctxt *kbdintctxt;
106 : : Buffer b;
107 : : int i;
108 : :
109 : : #ifdef USE_PAM
110 : : if (!options.use_pam)
111 : : remove_kbdint_device("pam");
112 : : #endif
113 : :
114 : 0 : kbdintctxt = xcalloc(1, sizeof(KbdintAuthctxt));
115 [ # # ]: 0 : if (strcmp(devs, "") == 0) {
116 : 0 : buffer_init(&b);
117 [ # # ]: 0 : for (i = 0; devices[i]; i++) {
118 [ # # ]: 0 : if (buffer_len(&b) > 0)
119 : 0 : buffer_append(&b, ",", 1);
120 : 0 : buffer_append(&b, devices[i]->name,
121 : 0 : strlen(devices[i]->name));
122 : : }
123 : 0 : buffer_append(&b, "\0", 1);
124 : 0 : kbdintctxt->devices = xstrdup(buffer_ptr(&b));
125 : 0 : buffer_free(&b);
126 : : } else {
127 : 0 : kbdintctxt->devices = xstrdup(devs);
128 : : }
129 : 0 : debug("kbdint_alloc: devices '%s'", kbdintctxt->devices);
130 : 0 : kbdintctxt->ctxt = NULL;
131 : 0 : kbdintctxt->device = NULL;
132 : 0 : kbdintctxt->nreq = 0;
133 : :
134 : 0 : return kbdintctxt;
135 : : }
136 : : static void
137 : : kbdint_reset_device(KbdintAuthctxt *kbdintctxt)
138 : : {
139 [ # # ][ # # ]: 0 : if (kbdintctxt->ctxt) {
140 : 0 : kbdintctxt->device->free_ctx(kbdintctxt->ctxt);
141 : 0 : kbdintctxt->ctxt = NULL;
142 : : }
143 : 0 : kbdintctxt->device = NULL;
144 : : }
145 : : static void
146 : 0 : kbdint_free(KbdintAuthctxt *kbdintctxt)
147 : : {
148 [ # # ]: 0 : if (kbdintctxt->device)
149 : 0 : kbdint_reset_device(kbdintctxt);
150 : 0 : free(kbdintctxt->devices);
151 : 0 : explicit_bzero(kbdintctxt, sizeof(*kbdintctxt));
152 : 0 : free(kbdintctxt);
153 : 0 : }
154 : : /* get next device */
155 : : static int
156 : 0 : kbdint_next_device(Authctxt *authctxt, KbdintAuthctxt *kbdintctxt)
157 : : {
158 : : size_t len;
159 : : char *t;
160 : : int i;
161 : :
162 [ # # ]: 0 : if (kbdintctxt->device)
163 : 0 : kbdint_reset_device(kbdintctxt);
164 : : do {
165 : 0 : len = kbdintctxt->devices ?
166 [ # # ]: 0 : strcspn(kbdintctxt->devices, ",") : 0;
167 : :
168 [ # # ]: 0 : if (len == 0)
169 : : break;
170 [ # # ]: 0 : for (i = 0; devices[i]; i++) {
171 [ # # ]: 0 : if (!auth2_method_allowed(authctxt,
172 : : "keyboard-interactive", devices[i]->name))
173 : 0 : continue;
174 [ # # ]: 0 : if (strncmp(kbdintctxt->devices, devices[i]->name, len) == 0)
175 : 0 : kbdintctxt->device = devices[i];
176 : : }
177 : 0 : t = kbdintctxt->devices;
178 [ # # ]: 0 : kbdintctxt->devices = t[len] ? xstrdup(t+len+1) : NULL;
179 : 0 : free(t);
180 [ # # ]: 0 : debug2("kbdint_next_device: devices %s", kbdintctxt->devices ?
181 : : kbdintctxt->devices : "<empty>");
182 [ # # ][ # # ]: 0 : } while (kbdintctxt->devices && !kbdintctxt->device);
183 : :
184 : 0 : return kbdintctxt->device ? 1 : 0;
185 : : }
186 : :
187 : : /*
188 : : * try challenge-response, set authctxt->postponed if we have to
189 : : * wait for the response.
190 : : */
191 : : int
192 : 0 : auth2_challenge(Authctxt *authctxt, char *devs)
193 : : {
194 [ # # ][ # # ]: 0 : debug("auth2_challenge: user=%s devs=%s",
195 : 0 : authctxt->user ? authctxt->user : "<nouser>",
196 : : devs ? devs : "<no devs>");
197 : :
198 [ # # ][ # # ]: 0 : if (authctxt->user == NULL || !devs)
199 : : return 0;
200 [ # # ]: 0 : if (authctxt->kbdintctxt == NULL)
201 : 0 : authctxt->kbdintctxt = kbdint_alloc(devs);
202 : 0 : return auth2_challenge_start(authctxt);
203 : : }
204 : :
205 : : /* unregister kbd-int callbacks and context */
206 : : void
207 : 2342 : auth2_challenge_stop(Authctxt *authctxt)
208 : : {
209 : : /* unregister callback */
210 : 2342 : dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL);
211 [ - + ]: 2342 : if (authctxt->kbdintctxt != NULL) {
212 : 0 : kbdint_free(authctxt->kbdintctxt);
213 : 0 : authctxt->kbdintctxt = NULL;
214 : : }
215 : 2342 : }
216 : :
217 : : /* side effect: sets authctxt->postponed if a reply was sent*/
218 : : static int
219 : 0 : auth2_challenge_start(Authctxt *authctxt)
220 : : {
221 : 0 : KbdintAuthctxt *kbdintctxt = authctxt->kbdintctxt;
222 : :
223 [ # # ]: 0 : debug2("auth2_challenge_start: devices %s",
224 : 0 : kbdintctxt->devices ? kbdintctxt->devices : "<empty>");
225 : :
226 [ # # ]: 0 : if (kbdint_next_device(authctxt, kbdintctxt) == 0) {
227 : 0 : auth2_challenge_stop(authctxt);
228 : 0 : return 0;
229 : : }
230 : 0 : debug("auth2_challenge_start: trying authentication method '%s'",
231 : 0 : kbdintctxt->device->name);
232 : :
233 [ # # ]: 0 : if ((kbdintctxt->ctxt = kbdintctxt->device->init_ctx(authctxt)) == NULL) {
234 : 0 : auth2_challenge_stop(authctxt);
235 : 0 : return 0;
236 : : }
237 [ # # ]: 0 : if (send_userauth_info_request(authctxt) == 0) {
238 : 0 : auth2_challenge_stop(authctxt);
239 : 0 : return 0;
240 : : }
241 : 0 : dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE,
242 : : &input_userauth_info_response);
243 : :
244 : 0 : authctxt->postponed = 1;
245 : 0 : return 0;
246 : : }
247 : :
248 : : static int
249 : 0 : send_userauth_info_request(Authctxt *authctxt)
250 : : {
251 : : KbdintAuthctxt *kbdintctxt;
252 : : char *name, *instr, **prompts;
253 : : u_int i, *echo_on;
254 : :
255 : 0 : kbdintctxt = authctxt->kbdintctxt;
256 [ # # ]: 0 : if (kbdintctxt->device->query(kbdintctxt->ctxt,
257 : : &name, &instr, &kbdintctxt->nreq, &prompts, &echo_on))
258 : : return 0;
259 : :
260 : 0 : packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST);
261 : 0 : packet_put_cstring(name);
262 : 0 : packet_put_cstring(instr);
263 : 0 : packet_put_cstring(""); /* language not used */
264 : 0 : packet_put_int(kbdintctxt->nreq);
265 [ # # ]: 0 : for (i = 0; i < kbdintctxt->nreq; i++) {
266 : 0 : packet_put_cstring(prompts[i]);
267 : 0 : packet_put_char(echo_on[i]);
268 : : }
269 : 0 : packet_send();
270 : 0 : packet_write_wait();
271 : :
272 [ # # ]: 0 : for (i = 0; i < kbdintctxt->nreq; i++)
273 : 0 : free(prompts[i]);
274 : 0 : free(prompts);
275 : 0 : free(echo_on);
276 : 0 : free(name);
277 : 0 : free(instr);
278 : : return 1;
279 : : }
280 : :
281 : : static void
282 : 0 : input_userauth_info_response(int type, u_int32_t seq, void *ctxt)
283 : : {
284 : 0 : Authctxt *authctxt = ctxt;
285 : : KbdintAuthctxt *kbdintctxt;
286 : 0 : int authenticated = 0, res;
287 : : u_int i, nresp;
288 : 0 : const char *devicename = NULL;
289 : 0 : char **response = NULL;
290 : :
291 [ # # ]: 0 : if (authctxt == NULL)
292 : 0 : fatal("input_userauth_info_response: no authctxt");
293 : 0 : kbdintctxt = authctxt->kbdintctxt;
294 [ # # ][ # # ]: 0 : if (kbdintctxt == NULL || kbdintctxt->ctxt == NULL)
295 : 0 : fatal("input_userauth_info_response: no kbdintctxt");
296 [ # # ]: 0 : if (kbdintctxt->device == NULL)
297 : 0 : fatal("input_userauth_info_response: no device");
298 : :
299 : 0 : authctxt->postponed = 0; /* reset */
300 : 0 : nresp = packet_get_int();
301 [ # # ]: 0 : if (nresp != kbdintctxt->nreq)
302 : 0 : fatal("input_userauth_info_response: wrong number of replies");
303 [ # # ]: 0 : if (nresp > 100)
304 : 0 : fatal("input_userauth_info_response: too many replies");
305 [ # # ]: 0 : if (nresp > 0) {
306 : 0 : response = xcalloc(nresp, sizeof(char *));
307 [ # # ]: 0 : for (i = 0; i < nresp; i++)
308 : 0 : response[i] = packet_get_string(NULL);
309 : : }
310 [ # # ]: 0 : packet_check_eom();
311 : :
312 : 0 : res = kbdintctxt->device->respond(kbdintctxt->ctxt, nresp, response);
313 : :
314 [ # # ]: 0 : for (i = 0; i < nresp; i++) {
315 : 0 : explicit_bzero(response[i], strlen(response[i]));
316 : 0 : free(response[i]);
317 : : }
318 : 0 : free(response);
319 : :
320 [ # # # ]: 0 : switch (res) {
321 : : case 0:
322 : : /* Success! */
323 : 0 : authenticated = authctxt->valid ? 1 : 0;
324 : 0 : break;
325 : : case 1:
326 : : /* Authentication needs further interaction */
327 [ # # ]: 0 : if (send_userauth_info_request(authctxt) == 1)
328 : 0 : authctxt->postponed = 1;
329 : : break;
330 : : default:
331 : : /* Failure! */
332 : : break;
333 : : }
334 : 0 : devicename = kbdintctxt->device->name;
335 [ # # ]: 0 : if (!authctxt->postponed) {
336 [ # # ]: 0 : if (authenticated) {
337 : 0 : auth2_challenge_stop(authctxt);
338 : : } else {
339 : : /* start next device */
340 : : /* may set authctxt->postponed */
341 : 0 : auth2_challenge_start(authctxt);
342 : : }
343 : : }
344 : 0 : userauth_finish(authctxt, authenticated, "keyboard-interactive",
345 : : devicename);
346 : 0 : }
347 : :
348 : : void
349 : 0 : privsep_challenge_enable(void)
350 : : {
351 : : #if defined(BSD_AUTH) || defined(USE_PAM) || defined(SKEY)
352 : : int n = 0;
353 : : #endif
354 : : #ifdef BSD_AUTH
355 : : extern KbdintDevice mm_bsdauth_device;
356 : : #endif
357 : : #ifdef USE_PAM
358 : : extern KbdintDevice mm_sshpam_device;
359 : : #endif
360 : : #ifdef SKEY
361 : : extern KbdintDevice mm_skey_device;
362 : : #endif
363 : :
364 : : #ifdef BSD_AUTH
365 : : devices[n++] = &mm_bsdauth_device;
366 : : #else
367 : : #ifdef USE_PAM
368 : : devices[n++] = &mm_sshpam_device;
369 : : #endif
370 : : #ifdef SKEY
371 : : devices[n++] = &mm_skey_device;
372 : : #endif
373 : : #endif
374 : 0 : }
|