Annotation of src/usr.bin/ssh/auth2-chall.c, Revision 1.13
1.1 markus 1: /*
1.3 deraadt 2: * Copyright (c) 2001 Markus Friedl. All rights reserved.
1.5 markus 3: * Copyright (c) 2001 Per Allansson. All rights reserved.
1.1 markus 4: *
5: * Redistribution and use in source and binary forms, with or without
6: * modification, are permitted provided that the following conditions
7: * are met:
8: * 1. Redistributions of source code must retain the above copyright
9: * notice, this list of conditions and the following disclaimer.
10: * 2. Redistributions in binary form must reproduce the above copyright
11: * notice, this list of conditions and the following disclaimer in the
12: * documentation and/or other materials provided with the distribution.
13: *
14: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24: */
25: #include "includes.h"
1.13 ! markus 26: RCSID("$OpenBSD: auth2-chall.c,v 1.12 2001/12/28 12:14:27 markus Exp $");
1.1 markus 27:
28: #include "ssh2.h"
29: #include "auth.h"
30: #include "packet.h"
31: #include "xmalloc.h"
32: #include "dispatch.h"
1.5 markus 33: #include "auth.h"
1.2 markus 34: #include "log.h"
1.1 markus 35:
1.7 itojun 36: static int auth2_challenge_start(Authctxt *);
37: static int send_userauth_info_request(Authctxt *);
1.13 ! markus 38: static void input_userauth_info_response(int, u_int32_t, void *);
1.5 markus 39:
40: #ifdef BSD_AUTH
41: extern KbdintDevice bsdauth_device;
42: #else
43: #ifdef SKEY
44: extern KbdintDevice skey_device;
45: #endif
46: #endif
47:
48: KbdintDevice *devices[] = {
49: #ifdef BSD_AUTH
50: &bsdauth_device,
51: #else
52: #ifdef SKEY
53: &skey_device,
54: #endif
55: #endif
56: NULL
57: };
58:
59: typedef struct KbdintAuthctxt KbdintAuthctxt;
60: struct KbdintAuthctxt
61: {
62: char *devices;
63: void *ctxt;
64: KbdintDevice *device;
65: };
66:
1.7 itojun 67: static KbdintAuthctxt *
1.5 markus 68: kbdint_alloc(const char *devs)
69: {
70: KbdintAuthctxt *kbdintctxt;
71: int i;
72: char buf[1024];
73:
74: kbdintctxt = xmalloc(sizeof(KbdintAuthctxt));
75: if (strcmp(devs, "") == 0) {
76: buf[0] = '\0';
77: for (i = 0; devices[i]; i++) {
78: if (i != 0)
79: strlcat(buf, ",", sizeof(buf));
80: strlcat(buf, devices[i]->name, sizeof(buf));
81: }
82: debug("kbdint_alloc: devices '%s'", buf);
83: kbdintctxt->devices = xstrdup(buf);
84: } else {
85: kbdintctxt->devices = xstrdup(devs);
86: }
87: kbdintctxt->ctxt = NULL;
88: kbdintctxt->device = NULL;
89:
90: return kbdintctxt;
91: }
1.7 itojun 92: static void
1.5 markus 93: kbdint_reset_device(KbdintAuthctxt *kbdintctxt)
94: {
95: if (kbdintctxt->ctxt) {
96: kbdintctxt->device->free_ctx(kbdintctxt->ctxt);
97: kbdintctxt->ctxt = NULL;
98: }
99: kbdintctxt->device = NULL;
100: }
1.7 itojun 101: static void
1.5 markus 102: kbdint_free(KbdintAuthctxt *kbdintctxt)
103: {
104: if (kbdintctxt->device)
105: kbdint_reset_device(kbdintctxt);
106: if (kbdintctxt->devices) {
107: xfree(kbdintctxt->devices);
108: kbdintctxt->devices = NULL;
109: }
110: xfree(kbdintctxt);
111: }
112: /* get next device */
1.7 itojun 113: static int
1.5 markus 114: kbdint_next_device(KbdintAuthctxt *kbdintctxt)
115: {
116: size_t len;
117: char *t;
118: int i;
119:
120: if (kbdintctxt->device)
121: kbdint_reset_device(kbdintctxt);
122: do {
123: len = kbdintctxt->devices ?
124: strcspn(kbdintctxt->devices, ",") : 0;
125:
126: if (len == 0)
127: break;
128: for (i = 0; devices[i]; i++)
129: if (strncmp(kbdintctxt->devices, devices[i]->name, len) == 0)
130: kbdintctxt->device = devices[i];
131: t = kbdintctxt->devices;
132: kbdintctxt->devices = t[len] ? xstrdup(t+len+1) : NULL;
133: xfree(t);
134: debug2("kbdint_next_device: devices %s", kbdintctxt->devices ?
135: kbdintctxt->devices : "<empty>");
136: } while (kbdintctxt->devices && !kbdintctxt->device);
137:
138: return kbdintctxt->device ? 1 : 0;
139: }
1.1 markus 140:
141: /*
1.8 markus 142: * try challenge-response, set authctxt->postponed if we have to
1.1 markus 143: * wait for the response.
144: */
145: int
146: auth2_challenge(Authctxt *authctxt, char *devs)
147: {
1.5 markus 148: debug("auth2_challenge: user=%s devs=%s",
149: authctxt->user ? authctxt->user : "<nouser>",
150: devs ? devs : "<no devs>");
151:
1.6 markus 152: if (authctxt->user == NULL || !devs)
1.5 markus 153: return 0;
1.10 deraadt 154: if (authctxt->kbdintctxt == NULL)
1.5 markus 155: authctxt->kbdintctxt = kbdint_alloc(devs);
156: return auth2_challenge_start(authctxt);
157: }
158:
1.9 markus 159: /* unregister kbd-int callbacks and context */
160: void
161: auth2_challenge_stop(Authctxt *authctxt)
162: {
163: /* unregister callback */
164: dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL);
165: if (authctxt->kbdintctxt != NULL) {
166: kbdint_free(authctxt->kbdintctxt);
167: authctxt->kbdintctxt = NULL;
168: }
169: }
170:
1.5 markus 171: /* side effect: sets authctxt->postponed if a reply was sent*/
172: static int
173: auth2_challenge_start(Authctxt *authctxt)
174: {
175: KbdintAuthctxt *kbdintctxt = authctxt->kbdintctxt;
176:
177: debug2("auth2_challenge_start: devices %s",
178: kbdintctxt->devices ? kbdintctxt->devices : "<empty>");
179:
180: if (kbdint_next_device(kbdintctxt) == 0) {
1.9 markus 181: auth2_challenge_stop(authctxt);
1.5 markus 182: return 0;
183: }
184: debug("auth2_challenge_start: trying authentication method '%s'",
185: kbdintctxt->device->name);
1.1 markus 186:
1.5 markus 187: if ((kbdintctxt->ctxt = kbdintctxt->device->init_ctx(authctxt)) == NULL) {
1.9 markus 188: auth2_challenge_stop(authctxt);
1.1 markus 189: return 0;
1.5 markus 190: }
191: if (send_userauth_info_request(authctxt) == 0) {
1.9 markus 192: auth2_challenge_stop(authctxt);
1.1 markus 193: return 0;
1.5 markus 194: }
1.1 markus 195: dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE,
196: &input_userauth_info_response);
1.5 markus 197:
1.1 markus 198: authctxt->postponed = 1;
199: return 0;
200: }
201:
1.5 markus 202: static int
203: send_userauth_info_request(Authctxt *authctxt)
1.1 markus 204: {
1.5 markus 205: KbdintAuthctxt *kbdintctxt;
206: char *name, *instr, **prompts;
207: int i;
208: u_int numprompts, *echo_on;
209:
210: kbdintctxt = authctxt->kbdintctxt;
211: if (kbdintctxt->device->query(kbdintctxt->ctxt,
212: &name, &instr, &numprompts, &prompts, &echo_on))
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);
218: packet_put_cstring(""); /* language not used */
219: packet_put_int(numprompts);
220: for (i = 0; i < numprompts; i++) {
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:
227: for (i = 0; i < numprompts; i++)
228: xfree(prompts[i]);
229: xfree(prompts);
230: xfree(echo_on);
231: xfree(name);
232: xfree(instr);
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;
241: int i, authenticated = 0, res, len;
242: u_int nresp;
243: char **response = NULL, *method;
1.1 markus 244:
245: if (authctxt == NULL)
246: fatal("input_userauth_info_response: no authctxt");
1.5 markus 247: kbdintctxt = authctxt->kbdintctxt;
248: if (kbdintctxt == NULL || kbdintctxt->ctxt == NULL)
249: fatal("input_userauth_info_response: no kbdintctxt");
250: if (kbdintctxt->device == NULL)
251: fatal("input_userauth_info_response: no device");
1.1 markus 252:
253: authctxt->postponed = 0; /* reset */
254: nresp = packet_get_int();
1.5 markus 255: if (nresp > 0) {
256: response = xmalloc(nresp * sizeof(char*));
257: for (i = 0; i < nresp; i++)
258: response[i] = packet_get_string(NULL);
259: }
1.12 markus 260: packet_check_eom();
1.5 markus 261:
262: if (authctxt->valid) {
263: res = kbdintctxt->device->respond(kbdintctxt->ctxt,
264: nresp, response);
265: } else {
266: res = -1;
267: }
268:
269: for (i = 0; i < nresp; i++) {
270: memset(response[i], 'r', strlen(response[i]));
271: xfree(response[i]);
272: }
273: if (response)
274: xfree(response);
275:
276: switch (res) {
277: case 0:
278: /* Success! */
279: authenticated = 1;
280: break;
281: case 1:
282: /* Authentication needs further interaction */
1.9 markus 283: if (send_userauth_info_request(authctxt) == 1)
284: authctxt->postponed = 1;
1.5 markus 285: break;
286: default:
287: /* Failure! */
288: break;
1.1 markus 289: }
1.5 markus 290:
291: len = strlen("keyboard-interactive") + 2 +
292: strlen(kbdintctxt->device->name);
293: method = xmalloc(len);
294: method[0] = '\0';
295: strlcat(method, "keyboard-interactive", len);
296: strlcat(method, "/", len);
297: strlcat(method, kbdintctxt->device->name, len);
298:
299: if (!authctxt->postponed) {
300: if (authenticated) {
1.9 markus 301: auth2_challenge_stop(authctxt);
1.5 markus 302: } else {
303: /* start next device */
304: /* may set authctxt->postponed */
305: auth2_challenge_start(authctxt);
306: }
307: }
1.4 markus 308: userauth_finish(authctxt, authenticated, method);
1.5 markus 309: xfree(method);
1.1 markus 310: }