Annotation of src/usr.bin/ssh/auth2-chall.c, Revision 1.23
1.1 markus 1: /*
1.3 deraadt 2: * Copyright (c) 2001 Markus Friedl. All rights reserved.
1.5 markus 3: * Copyright (c) 2001 Per Allansson. All rights reserved.
1.1 markus 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"
1.23 ! djm 26: RCSID("$OpenBSD: auth2-chall.c,v 1.22 2005/01/19 13:11:47 dtucker Exp $");
1.1 markus 27:
28: #include "ssh2.h"
29: #include "auth.h"
1.16 markus 30: #include "buffer.h"
1.1 markus 31: #include "packet.h"
32: #include "xmalloc.h"
33: #include "dispatch.h"
1.2 markus 34: #include "log.h"
1.1 markus 35:
1.7 itojun 36: static int auth2_challenge_start(Authctxt *);
37: static int send_userauth_info_request(Authctxt *);
1.13 markus 38: static void input_userauth_info_response(int, u_int32_t, void *);
1.5 markus 39:
40: #ifdef BSD_AUTH
41: extern KbdintDevice bsdauth_device;
42: #else
43: #ifdef SKEY
44: extern KbdintDevice skey_device;
45: #endif
46: #endif
47:
48: KbdintDevice *devices[] = {
49: #ifdef BSD_AUTH
50: &bsdauth_device,
51: #else
52: #ifdef SKEY
53: &skey_device,
54: #endif
55: #endif
56: NULL
57: };
58:
59: typedef struct KbdintAuthctxt KbdintAuthctxt;
60: struct KbdintAuthctxt
61: {
62: char *devices;
63: void *ctxt;
64: KbdintDevice *device;
1.19 markus 65: u_int nreq;
1.5 markus 66: };
67:
1.7 itojun 68: static KbdintAuthctxt *
1.5 markus 69: kbdint_alloc(const char *devs)
70: {
71: KbdintAuthctxt *kbdintctxt;
1.16 markus 72: Buffer b;
1.5 markus 73: int i;
74:
75: kbdintctxt = xmalloc(sizeof(KbdintAuthctxt));
76: if (strcmp(devs, "") == 0) {
1.16 markus 77: buffer_init(&b);
1.5 markus 78: for (i = 0; devices[i]; i++) {
1.16 markus 79: if (buffer_len(&b) > 0)
80: buffer_append(&b, ",", 1);
81: buffer_append(&b, devices[i]->name,
82: strlen(devices[i]->name));
1.5 markus 83: }
1.16 markus 84: buffer_append(&b, "\0", 1);
85: kbdintctxt->devices = xstrdup(buffer_ptr(&b));
86: buffer_free(&b);
1.5 markus 87: } else {
88: kbdintctxt->devices = xstrdup(devs);
89: }
1.16 markus 90: debug("kbdint_alloc: devices '%s'", kbdintctxt->devices);
1.5 markus 91: kbdintctxt->ctxt = NULL;
92: kbdintctxt->device = NULL;
1.19 markus 93: kbdintctxt->nreq = 0;
1.5 markus 94:
95: return kbdintctxt;
96: }
1.7 itojun 97: static void
1.5 markus 98: kbdint_reset_device(KbdintAuthctxt *kbdintctxt)
99: {
100: if (kbdintctxt->ctxt) {
101: kbdintctxt->device->free_ctx(kbdintctxt->ctxt);
102: kbdintctxt->ctxt = NULL;
103: }
104: kbdintctxt->device = NULL;
105: }
1.7 itojun 106: static void
1.5 markus 107: kbdint_free(KbdintAuthctxt *kbdintctxt)
108: {
109: if (kbdintctxt->device)
110: kbdint_reset_device(kbdintctxt);
111: if (kbdintctxt->devices) {
112: xfree(kbdintctxt->devices);
113: kbdintctxt->devices = NULL;
114: }
115: xfree(kbdintctxt);
116: }
117: /* get next device */
1.7 itojun 118: static int
1.5 markus 119: kbdint_next_device(KbdintAuthctxt *kbdintctxt)
120: {
121: size_t len;
122: char *t;
123: int i;
124:
125: if (kbdintctxt->device)
126: kbdint_reset_device(kbdintctxt);
127: do {
128: len = kbdintctxt->devices ?
129: strcspn(kbdintctxt->devices, ",") : 0;
130:
131: if (len == 0)
132: break;
133: for (i = 0; devices[i]; i++)
134: if (strncmp(kbdintctxt->devices, devices[i]->name, len) == 0)
135: kbdintctxt->device = devices[i];
136: t = kbdintctxt->devices;
137: kbdintctxt->devices = t[len] ? xstrdup(t+len+1) : NULL;
138: xfree(t);
139: debug2("kbdint_next_device: devices %s", kbdintctxt->devices ?
140: kbdintctxt->devices : "<empty>");
141: } while (kbdintctxt->devices && !kbdintctxt->device);
142:
143: return kbdintctxt->device ? 1 : 0;
144: }
1.1 markus 145:
146: /*
1.8 markus 147: * try challenge-response, set authctxt->postponed if we have to
1.1 markus 148: * wait for the response.
149: */
150: int
151: auth2_challenge(Authctxt *authctxt, char *devs)
152: {
1.5 markus 153: debug("auth2_challenge: user=%s devs=%s",
154: authctxt->user ? authctxt->user : "<nouser>",
155: devs ? devs : "<no devs>");
156:
1.6 markus 157: if (authctxt->user == NULL || !devs)
1.5 markus 158: return 0;
1.10 deraadt 159: if (authctxt->kbdintctxt == NULL)
1.5 markus 160: authctxt->kbdintctxt = kbdint_alloc(devs);
161: return auth2_challenge_start(authctxt);
162: }
163:
1.9 markus 164: /* unregister kbd-int callbacks and context */
165: void
166: auth2_challenge_stop(Authctxt *authctxt)
167: {
168: /* unregister callback */
169: dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL);
170: if (authctxt->kbdintctxt != NULL) {
171: kbdint_free(authctxt->kbdintctxt);
172: authctxt->kbdintctxt = NULL;
173: }
174: }
175:
1.5 markus 176: /* side effect: sets authctxt->postponed if a reply was sent*/
177: static int
178: auth2_challenge_start(Authctxt *authctxt)
179: {
180: KbdintAuthctxt *kbdintctxt = authctxt->kbdintctxt;
181:
182: debug2("auth2_challenge_start: devices %s",
183: kbdintctxt->devices ? kbdintctxt->devices : "<empty>");
184:
185: if (kbdint_next_device(kbdintctxt) == 0) {
1.9 markus 186: auth2_challenge_stop(authctxt);
1.5 markus 187: return 0;
188: }
189: debug("auth2_challenge_start: trying authentication method '%s'",
190: kbdintctxt->device->name);
1.1 markus 191:
1.5 markus 192: if ((kbdintctxt->ctxt = kbdintctxt->device->init_ctx(authctxt)) == NULL) {
1.9 markus 193: auth2_challenge_stop(authctxt);
1.1 markus 194: return 0;
1.5 markus 195: }
196: if (send_userauth_info_request(authctxt) == 0) {
1.9 markus 197: auth2_challenge_stop(authctxt);
1.1 markus 198: return 0;
1.5 markus 199: }
1.1 markus 200: dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE,
201: &input_userauth_info_response);
1.5 markus 202:
1.1 markus 203: authctxt->postponed = 1;
204: return 0;
205: }
206:
1.5 markus 207: static int
208: send_userauth_info_request(Authctxt *authctxt)
1.1 markus 209: {
1.5 markus 210: KbdintAuthctxt *kbdintctxt;
211: char *name, *instr, **prompts;
1.23 ! djm 212: u_int i, *echo_on;
1.5 markus 213:
214: kbdintctxt = authctxt->kbdintctxt;
215: if (kbdintctxt->device->query(kbdintctxt->ctxt,
1.19 markus 216: &name, &instr, &kbdintctxt->nreq, &prompts, &echo_on))
1.5 markus 217: return 0;
1.1 markus 218:
219: packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST);
1.5 markus 220: packet_put_cstring(name);
221: packet_put_cstring(instr);
1.18 deraadt 222: packet_put_cstring(""); /* language not used */
1.19 markus 223: packet_put_int(kbdintctxt->nreq);
224: for (i = 0; i < kbdintctxt->nreq; i++) {
1.5 markus 225: packet_put_cstring(prompts[i]);
226: packet_put_char(echo_on[i]);
227: }
1.1 markus 228: packet_send();
229: packet_write_wait();
1.5 markus 230:
1.19 markus 231: for (i = 0; i < kbdintctxt->nreq; i++)
1.5 markus 232: xfree(prompts[i]);
233: xfree(prompts);
234: xfree(echo_on);
235: xfree(name);
236: xfree(instr);
237: return 1;
1.1 markus 238: }
239:
1.5 markus 240: static void
1.13 markus 241: input_userauth_info_response(int type, u_int32_t seq, void *ctxt)
1.1 markus 242: {
243: Authctxt *authctxt = ctxt;
1.5 markus 244: KbdintAuthctxt *kbdintctxt;
1.23 ! djm 245: int authenticated = 0, res, len;
! 246: u_int i, nresp;
1.5 markus 247: char **response = NULL, *method;
1.1 markus 248:
249: if (authctxt == NULL)
250: fatal("input_userauth_info_response: no authctxt");
1.5 markus 251: kbdintctxt = authctxt->kbdintctxt;
252: if (kbdintctxt == NULL || kbdintctxt->ctxt == NULL)
253: fatal("input_userauth_info_response: no kbdintctxt");
254: if (kbdintctxt->device == NULL)
255: fatal("input_userauth_info_response: no device");
1.1 markus 256:
257: authctxt->postponed = 0; /* reset */
258: nresp = packet_get_int();
1.19 markus 259: if (nresp != kbdintctxt->nreq)
260: fatal("input_userauth_info_response: wrong number of replies");
261: if (nresp > 100)
262: fatal("input_userauth_info_response: too many replies");
1.5 markus 263: if (nresp > 0) {
1.20 deraadt 264: response = xmalloc(nresp * sizeof(char *));
1.5 markus 265: for (i = 0; i < nresp; i++)
266: response[i] = packet_get_string(NULL);
267: }
1.12 markus 268: packet_check_eom();
1.5 markus 269:
1.22 dtucker 270: res = kbdintctxt->device->respond(kbdintctxt->ctxt, nresp, response);
1.5 markus 271:
272: for (i = 0; i < nresp; i++) {
273: memset(response[i], 'r', strlen(response[i]));
274: xfree(response[i]);
275: }
276: if (response)
277: xfree(response);
278:
279: switch (res) {
280: case 0:
281: /* Success! */
1.22 dtucker 282: authenticated = authctxt->valid ? 1 : 0;
1.5 markus 283: break;
284: case 1:
285: /* Authentication needs further interaction */
1.9 markus 286: if (send_userauth_info_request(authctxt) == 1)
287: authctxt->postponed = 1;
1.5 markus 288: break;
289: default:
290: /* Failure! */
291: break;
1.1 markus 292: }
1.5 markus 293:
294: len = strlen("keyboard-interactive") + 2 +
295: strlen(kbdintctxt->device->name);
296: method = xmalloc(len);
1.15 markus 297: snprintf(method, len, "keyboard-interactive/%s",
298: kbdintctxt->device->name);
1.5 markus 299:
300: if (!authctxt->postponed) {
301: if (authenticated) {
1.9 markus 302: auth2_challenge_stop(authctxt);
1.5 markus 303: } else {
304: /* start next device */
305: /* may set authctxt->postponed */
306: auth2_challenge_start(authctxt);
307: }
308: }
1.4 markus 309: userauth_finish(authctxt, authenticated, method);
1.5 markus 310: xfree(method);
1.17 provos 311: }
312:
313: void
314: privsep_challenge_enable(void)
315: {
316: #ifdef BSD_AUTH
317: extern KbdintDevice mm_bsdauth_device;
318: #endif
319: #ifdef SKEY
320: extern KbdintDevice mm_skey_device;
321: #endif
322: /* As long as SSHv1 has devices[0] hard coded this is fine */
323: #ifdef BSD_AUTH
324: devices[0] = &mm_bsdauth_device;
325: #else
326: #ifdef SKEY
327: devices[0] = &mm_skey_device;
328: #endif
329: #endif
1.1 markus 330: }