source: trunk/third/openssh/auth2-chall.c @ 18763

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