Annotation of src/usr.bin/ssh/auth2-chall.c, Revision 1.36.2.1
1.36.2.1! djm 1: /* $OpenBSD: auth2-chall.c,v 1.38.2.1 2013/11/08 01:33:56 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:
1.36.2.1! djm 69: kbdintctxt = xcalloc(1, sizeof(KbdintAuthctxt));
1.5 markus 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.5 markus 113: kbdint_next_device(KbdintAuthctxt *kbdintctxt)
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;
127: for (i = 0; devices[i]; i++)
128: if (strncmp(kbdintctxt->devices, devices[i]->name, len) == 0)
129: kbdintctxt->device = devices[i];
130: t = kbdintctxt->devices;
131: kbdintctxt->devices = t[len] ? xstrdup(t+len+1) : NULL;
132: xfree(t);
133: debug2("kbdint_next_device: devices %s", kbdintctxt->devices ?
1.24 djm 134: kbdintctxt->devices : "<empty>");
1.5 markus 135: } while (kbdintctxt->devices && !kbdintctxt->device);
136:
137: return kbdintctxt->device ? 1 : 0;
138: }
1.1 markus 139:
140: /*
1.8 markus 141: * try challenge-response, set authctxt->postponed if we have to
1.1 markus 142: * wait for the response.
143: */
144: int
145: auth2_challenge(Authctxt *authctxt, char *devs)
146: {
1.5 markus 147: debug("auth2_challenge: user=%s devs=%s",
148: authctxt->user ? authctxt->user : "<nouser>",
149: devs ? devs : "<no devs>");
150:
1.6 markus 151: if (authctxt->user == NULL || !devs)
1.5 markus 152: return 0;
1.10 deraadt 153: if (authctxt->kbdintctxt == NULL)
1.5 markus 154: authctxt->kbdintctxt = kbdint_alloc(devs);
155: return auth2_challenge_start(authctxt);
156: }
157:
1.9 markus 158: /* unregister kbd-int callbacks and context */
159: void
160: auth2_challenge_stop(Authctxt *authctxt)
161: {
162: /* unregister callback */
163: dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL);
1.32 stevesk 164: if (authctxt->kbdintctxt != NULL) {
1.9 markus 165: kbdint_free(authctxt->kbdintctxt);
166: authctxt->kbdintctxt = NULL;
167: }
168: }
169:
1.5 markus 170: /* side effect: sets authctxt->postponed if a reply was sent*/
171: static int
172: auth2_challenge_start(Authctxt *authctxt)
173: {
174: KbdintAuthctxt *kbdintctxt = authctxt->kbdintctxt;
175:
176: debug2("auth2_challenge_start: devices %s",
177: kbdintctxt->devices ? kbdintctxt->devices : "<empty>");
178:
179: if (kbdint_next_device(kbdintctxt) == 0) {
1.9 markus 180: auth2_challenge_stop(authctxt);
1.5 markus 181: return 0;
182: }
183: debug("auth2_challenge_start: trying authentication method '%s'",
184: kbdintctxt->device->name);
1.1 markus 185:
1.5 markus 186: if ((kbdintctxt->ctxt = kbdintctxt->device->init_ctx(authctxt)) == NULL) {
1.9 markus 187: auth2_challenge_stop(authctxt);
1.1 markus 188: return 0;
1.5 markus 189: }
190: if (send_userauth_info_request(authctxt) == 0) {
1.9 markus 191: auth2_challenge_stop(authctxt);
1.1 markus 192: return 0;
1.5 markus 193: }
1.1 markus 194: dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE,
195: &input_userauth_info_response);
1.5 markus 196:
1.1 markus 197: authctxt->postponed = 1;
198: return 0;
199: }
200:
1.5 markus 201: static int
202: send_userauth_info_request(Authctxt *authctxt)
1.1 markus 203: {
1.5 markus 204: KbdintAuthctxt *kbdintctxt;
205: char *name, *instr, **prompts;
1.23 djm 206: u_int i, *echo_on;
1.5 markus 207:
208: kbdintctxt = authctxt->kbdintctxt;
209: if (kbdintctxt->device->query(kbdintctxt->ctxt,
1.19 markus 210: &name, &instr, &kbdintctxt->nreq, &prompts, &echo_on))
1.5 markus 211: return 0;
1.1 markus 212:
213: packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST);
1.5 markus 214: packet_put_cstring(name);
215: packet_put_cstring(instr);
1.18 deraadt 216: packet_put_cstring(""); /* language not used */
1.19 markus 217: packet_put_int(kbdintctxt->nreq);
218: for (i = 0; i < kbdintctxt->nreq; i++) {
1.5 markus 219: packet_put_cstring(prompts[i]);
220: packet_put_char(echo_on[i]);
221: }
1.1 markus 222: packet_send();
223: packet_write_wait();
1.5 markus 224:
1.19 markus 225: for (i = 0; i < kbdintctxt->nreq; i++)
1.5 markus 226: xfree(prompts[i]);
227: xfree(prompts);
228: xfree(echo_on);
229: xfree(name);
230: xfree(instr);
231: return 1;
1.1 markus 232: }
233:
1.5 markus 234: static void
1.13 markus 235: input_userauth_info_response(int type, u_int32_t seq, void *ctxt)
1.1 markus 236: {
237: Authctxt *authctxt = ctxt;
1.5 markus 238: KbdintAuthctxt *kbdintctxt;
1.34 djm 239: int authenticated = 0, res;
1.23 djm 240: u_int i, nresp;
1.36 djm 241: const char *devicename = NULL;
242: char **response = NULL;
1.1 markus 243:
244: if (authctxt == NULL)
245: fatal("input_userauth_info_response: no authctxt");
1.5 markus 246: kbdintctxt = authctxt->kbdintctxt;
247: if (kbdintctxt == NULL || kbdintctxt->ctxt == NULL)
248: fatal("input_userauth_info_response: no kbdintctxt");
249: if (kbdintctxt->device == NULL)
250: fatal("input_userauth_info_response: no device");
1.1 markus 251:
252: authctxt->postponed = 0; /* reset */
253: nresp = packet_get_int();
1.19 markus 254: if (nresp != kbdintctxt->nreq)
255: fatal("input_userauth_info_response: wrong number of replies");
256: if (nresp > 100)
257: fatal("input_userauth_info_response: too many replies");
1.5 markus 258: if (nresp > 0) {
1.26 djm 259: response = xcalloc(nresp, sizeof(char *));
1.5 markus 260: for (i = 0; i < nresp; i++)
261: response[i] = packet_get_string(NULL);
262: }
1.12 markus 263: packet_check_eom();
1.5 markus 264:
1.22 dtucker 265: res = kbdintctxt->device->respond(kbdintctxt->ctxt, nresp, response);
1.5 markus 266:
267: for (i = 0; i < nresp; i++) {
268: memset(response[i], 'r', strlen(response[i]));
269: xfree(response[i]);
270: }
271: if (response)
272: xfree(response);
273:
274: switch (res) {
275: case 0:
276: /* Success! */
1.22 dtucker 277: authenticated = authctxt->valid ? 1 : 0;
1.5 markus 278: break;
279: case 1:
280: /* Authentication needs further interaction */
1.9 markus 281: if (send_userauth_info_request(authctxt) == 1)
282: authctxt->postponed = 1;
1.5 markus 283: break;
284: default:
285: /* Failure! */
286: break;
1.1 markus 287: }
1.35 djm 288: devicename = kbdintctxt->device->name;
1.5 markus 289: if (!authctxt->postponed) {
290: if (authenticated) {
1.9 markus 291: auth2_challenge_stop(authctxt);
1.5 markus 292: } else {
293: /* start next device */
294: /* may set authctxt->postponed */
295: auth2_challenge_start(authctxt);
296: }
297: }
1.35 djm 298: userauth_finish(authctxt, authenticated, "keyboard-interactive",
299: devicename);
1.17 provos 300: }
301:
302: void
303: privsep_challenge_enable(void)
304: {
305: extern KbdintDevice mm_bsdauth_device;
306: /* As long as SSHv1 has devices[0] hard coded this is fine */
307: devices[0] = &mm_bsdauth_device;
1.1 markus 308: }