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