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