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