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