auth2-chall.c revision 99052
167754Smsmith/*
267754Smsmith * Copyright (c) 2001 Markus Friedl.  All rights reserved.
367754Smsmith * Copyright (c) 2001 Per Allansson.  All rights reserved.
4128212Snjl *
567754Smsmith * Redistribution and use in source and binary forms, with or without
667754Smsmith * modification, are permitted provided that the following conditions
767754Smsmith * are met:
867754Smsmith * 1. Redistributions of source code must retain the above copyright
967754Smsmith *    notice, this list of conditions and the following disclaimer.
1067754Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1167754Smsmith *    notice, this list of conditions and the following disclaimer in the
12126372Snjl *    documentation and/or other materials provided with the distribution.
1370243Smsmith *
1467754Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1567754Smsmith * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1667754Smsmith * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1767754Smsmith * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1867754Smsmith * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1967754Smsmith * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2067754Smsmith * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2167754Smsmith * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2267754Smsmith * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2367754Smsmith * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2467754Smsmith */
2567754Smsmith#include "includes.h"
2667754SmsmithRCSID("$OpenBSD: auth2-chall.c,v 1.18 2002/06/19 00:27:55 deraadt Exp $");
2767754SmsmithRCSID("$FreeBSD: head/crypto/openssh/auth2-chall.c 99052 2002-06-29 10:56:23Z des $");
2867754Smsmith
2967754Smsmith#include "ssh2.h"
3067754Smsmith#include "auth.h"
3167754Smsmith#include "buffer.h"
3267754Smsmith#include "packet.h"
3367754Smsmith#include "xmalloc.h"
3467754Smsmith#include "dispatch.h"
3567754Smsmith#include "auth.h"
3667754Smsmith#include "log.h"
3767754Smsmith
3867754Smsmithstatic int auth2_challenge_start(Authctxt *);
3967754Smsmithstatic int send_userauth_info_request(Authctxt *);
4067754Smsmithstatic void input_userauth_info_response(int, u_int32_t, void *);
4167754Smsmith
4267754Smsmith#ifdef BSD_AUTH
4367754Smsmithextern KbdintDevice bsdauth_device;
4467754Smsmith#else
4567754Smsmith#ifdef USE_PAM
4667754Smsmithextern KbdintDevice pam_device;
4767754Smsmith#endif
4867754Smsmith#ifdef SKEY
4967754Smsmithextern KbdintDevice skey_device;
5067754Smsmith#endif
5167754Smsmith#endif
5267754Smsmith
5367754SmsmithKbdintDevice *devices[] = {
5467754Smsmith#ifdef BSD_AUTH
5567754Smsmith	&bsdauth_device,
5667754Smsmith#else
5767754Smsmith#ifdef USE_PAM
5867754Smsmith	&pam_device,
5967754Smsmith#endif
6067754Smsmith#ifdef SKEY
6167754Smsmith	&skey_device,
6267754Smsmith#endif
6367754Smsmith#endif
6467754Smsmith	NULL
6567754Smsmith};
6667754Smsmith
6767754Smsmithtypedef struct KbdintAuthctxt KbdintAuthctxt;
6867754Smsmithstruct KbdintAuthctxt
6967754Smsmith{
7067754Smsmith	char *devices;
7167754Smsmith	void *ctxt;
7267754Smsmith	KbdintDevice *device;
7367754Smsmith};
7467754Smsmith
7567754Smsmithstatic KbdintAuthctxt *
7667754Smsmithkbdint_alloc(const char *devs)
7767754Smsmith{
7867754Smsmith	KbdintAuthctxt *kbdintctxt;
7967754Smsmith	Buffer b;
8067754Smsmith	int i;
8167754Smsmith
8267754Smsmith	kbdintctxt = xmalloc(sizeof(KbdintAuthctxt));
8367754Smsmith	if (strcmp(devs, "") == 0) {
8467754Smsmith		buffer_init(&b);
8567754Smsmith		for (i = 0; devices[i]; i++) {
8667754Smsmith			if (buffer_len(&b) > 0)
8767754Smsmith				buffer_append(&b, ",", 1);
8867754Smsmith			buffer_append(&b, devices[i]->name,
8967754Smsmith			    strlen(devices[i]->name));
9067754Smsmith		}
9167754Smsmith		buffer_append(&b, "\0", 1);
9267754Smsmith		kbdintctxt->devices = xstrdup(buffer_ptr(&b));
9367754Smsmith		buffer_free(&b);
9467754Smsmith	} else {
9567754Smsmith		kbdintctxt->devices = xstrdup(devs);
9667754Smsmith	}
9767754Smsmith	debug("kbdint_alloc: devices '%s'", kbdintctxt->devices);
9867754Smsmith	kbdintctxt->ctxt = NULL;
9967754Smsmith	kbdintctxt->device = NULL;
10067754Smsmith
10167754Smsmith	return kbdintctxt;
10267754Smsmith}
10367754Smsmithstatic void
10467754Smsmithkbdint_reset_device(KbdintAuthctxt *kbdintctxt)
10567754Smsmith{
10667754Smsmith	if (kbdintctxt->ctxt) {
10767754Smsmith		kbdintctxt->device->free_ctx(kbdintctxt->ctxt);
10867754Smsmith		kbdintctxt->ctxt = NULL;
10967754Smsmith	}
11067754Smsmith	kbdintctxt->device = NULL;
11167754Smsmith}
11267754Smsmithstatic void
11367754Smsmithkbdint_free(KbdintAuthctxt *kbdintctxt)
11467754Smsmith{
11567754Smsmith	if (kbdintctxt->device)
11667754Smsmith		kbdint_reset_device(kbdintctxt);
11767754Smsmith	if (kbdintctxt->devices) {
11867754Smsmith		xfree(kbdintctxt->devices);
11967754Smsmith		kbdintctxt->devices = NULL;
12067754Smsmith	}
12167754Smsmith	xfree(kbdintctxt);
12267754Smsmith}
12377424Smsmith/* get next device */
12491116Smsmithstatic int
12567754Smsmithkbdint_next_device(KbdintAuthctxt *kbdintctxt)
12667754Smsmith{
12767754Smsmith	size_t len;
12867754Smsmith	char *t;
12967754Smsmith	int i;
13067754Smsmith
13167754Smsmith	if (kbdintctxt->device)
132107325Siwasaki		kbdint_reset_device(kbdintctxt);
13367754Smsmith	do {
13477424Smsmith		len = kbdintctxt->devices ?
13567754Smsmith		    strcspn(kbdintctxt->devices, ",") : 0;
13667754Smsmith
13767754Smsmith		if (len == 0)
138114237Snjl			break;
139114237Snjl		for (i = 0; devices[i]; i++)
140107325Siwasaki			if (strncmp(kbdintctxt->devices, devices[i]->name, len) == 0)
14167754Smsmith				kbdintctxt->device = devices[i];
14267754Smsmith		t = kbdintctxt->devices;
14367754Smsmith		kbdintctxt->devices = t[len] ? xstrdup(t+len+1) : NULL;
14467754Smsmith		xfree(t);
14567754Smsmith		debug2("kbdint_next_device: devices %s", kbdintctxt->devices ?
14667754Smsmith		   kbdintctxt->devices : "<empty>");
14767754Smsmith	} while (kbdintctxt->devices && !kbdintctxt->device);
14867754Smsmith
14967754Smsmith	return kbdintctxt->device ? 1 : 0;
15067754Smsmith}
15167754Smsmith
15267754Smsmith/*
15367754Smsmith * try challenge-response, set authctxt->postponed if we have to
15467754Smsmith * wait for the response.
15567754Smsmith */
15667754Smsmithint
15767754Smsmithauth2_challenge(Authctxt *authctxt, char *devs)
15891116Smsmith{
15967754Smsmith	debug("auth2_challenge: user=%s devs=%s",
16067754Smsmith	    authctxt->user ? authctxt->user : "<nouser>",
16167754Smsmith	    devs ? devs : "<no devs>");
16267754Smsmith
16367754Smsmith	if (authctxt->user == NULL || !devs)
16491116Smsmith		return 0;
16567754Smsmith	if (authctxt->kbdintctxt == NULL)
16671867Smsmith		authctxt->kbdintctxt = kbdint_alloc(devs);
167102550Siwasaki	return auth2_challenge_start(authctxt);
16882367Smsmith}
16967754Smsmith
170114237Snjl/* unregister kbd-int callbacks and context */
17177424Smsmithvoid
17291116Smsmithauth2_challenge_stop(Authctxt *authctxt)
17371867Smsmith{
17471867Smsmith	/* unregister callback */
175123315Snjl	dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL);
17691116Smsmith	if (authctxt->kbdintctxt != NULL)  {
17771867Smsmith		kbdint_free(authctxt->kbdintctxt);
17880062Smsmith		authctxt->kbdintctxt = NULL;
17971867Smsmith	}
18067754Smsmith}
18171867Smsmith
18267754Smsmith/* side effect: sets authctxt->postponed if a reply was sent*/
18367754Smsmithstatic int
184114237Snjlauth2_challenge_start(Authctxt *authctxt)
185107325Siwasaki{
18667754Smsmith	KbdintAuthctxt *kbdintctxt = authctxt->kbdintctxt;
18767754Smsmith
18867754Smsmith	debug2("auth2_challenge_start: devices %s",
18967754Smsmith	    kbdintctxt->devices ?  kbdintctxt->devices : "<empty>");
19067754Smsmith
19167754Smsmith	if (kbdint_next_device(kbdintctxt) == 0) {
19299146Siwasaki		auth2_challenge_stop(authctxt);
19367754Smsmith		return 0;
194128212Snjl	}
195128212Snjl	debug("auth2_challenge_start: trying authentication method '%s'",
196128212Snjl	    kbdintctxt->device->name);
197128212Snjl
198128212Snjl	if ((kbdintctxt->ctxt = kbdintctxt->device->init_ctx(authctxt)) == NULL) {
199128212Snjl		auth2_challenge_stop(authctxt);
200128212Snjl		return 0;
20167754Smsmith	}
202107325Siwasaki	if (send_userauth_info_request(authctxt) == 0) {
20367754Smsmith		auth2_challenge_stop(authctxt);
20483174Smsmith		return 0;
205123315Snjl	}
206117521Snjl	dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE,
207123315Snjl	    &input_userauth_info_response);
20867754Smsmith
20967754Smsmith	authctxt->postponed = 1;
21067754Smsmith	return 0;
21167754Smsmith}
21267754Smsmith
21367754Smsmithstatic int
21467754Smsmithsend_userauth_info_request(Authctxt *authctxt)
21567754Smsmith{
21667754Smsmith	KbdintAuthctxt *kbdintctxt;
21767754Smsmith	char *name, *instr, **prompts;
21867754Smsmith	int i;
21967754Smsmith	u_int numprompts, *echo_on;
22067754Smsmith
22167754Smsmith	kbdintctxt = authctxt->kbdintctxt;
22267754Smsmith	if (kbdintctxt->device->query(kbdintctxt->ctxt,
22367754Smsmith	    &name, &instr, &numprompts, &prompts, &echo_on))
22467754Smsmith		return 0;
22567754Smsmith
22667754Smsmith	packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST);
22767754Smsmith	packet_put_cstring(name);
22867754Smsmith	packet_put_cstring(instr);
229107325Siwasaki	packet_put_cstring("");		/* language not used */
23067754Smsmith	packet_put_int(numprompts);
231117521Snjl	for (i = 0; i < numprompts; i++) {
232123315Snjl		packet_put_cstring(prompts[i]);
233117521Snjl		packet_put_char(echo_on[i]);
234123315Snjl	}
23567754Smsmith	packet_send();
23667754Smsmith	packet_write_wait();
23767754Smsmith
23867754Smsmith	for (i = 0; i < numprompts; i++)
23967754Smsmith		xfree(prompts[i]);
24067754Smsmith	xfree(prompts);
24167754Smsmith	xfree(echo_on);
24267754Smsmith	xfree(name);
24367754Smsmith	xfree(instr);
24467754Smsmith	return 1;
245107325Siwasaki}
24667754Smsmith
24777424Smsmithstatic void
24867754Smsmithinput_userauth_info_response(int type, u_int32_t seq, void *ctxt)
24967754Smsmith{
25067754Smsmith	Authctxt *authctxt = ctxt;
25167754Smsmith	KbdintAuthctxt *kbdintctxt;
252107325Siwasaki	int i, authenticated = 0, res, len;
25367754Smsmith	u_int nresp;
25467754Smsmith	char **response = NULL, *method;
25567754Smsmith
25667754Smsmith	if (authctxt == NULL)
25767754Smsmith		fatal("input_userauth_info_response: no authctxt");
25867754Smsmith	kbdintctxt = authctxt->kbdintctxt;
25967754Smsmith	if (kbdintctxt == NULL || kbdintctxt->ctxt == NULL)
26067754Smsmith		fatal("input_userauth_info_response: no kbdintctxt");
26167754Smsmith	if (kbdintctxt->device == NULL)
26267754Smsmith		fatal("input_userauth_info_response: no device");
26367754Smsmith
26467754Smsmith	authctxt->postponed = 0;	/* reset */
26569450Smsmith	nresp = packet_get_int();
26667754Smsmith	if (nresp > 0) {
26767754Smsmith		response = xmalloc(nresp * sizeof(char*));
26867754Smsmith		for (i = 0; i < nresp; i++)
26991116Smsmith			response[i] = packet_get_string(NULL);
27067754Smsmith	}
27167754Smsmith	packet_check_eom();
27267754Smsmith
27367754Smsmith	if (authctxt->valid) {
27467754Smsmith		res = kbdintctxt->device->respond(kbdintctxt->ctxt,
27567754Smsmith		    nresp, response);
27691116Smsmith	} else {
27767754Smsmith		res = -1;
27867754Smsmith	}
27991116Smsmith
28067754Smsmith	for (i = 0; i < nresp; i++) {
28167754Smsmith		memset(response[i], 'r', strlen(response[i]));
282114237Snjl		xfree(response[i]);
283107325Siwasaki	}
28467754Smsmith	if (response)
285107325Siwasaki		xfree(response);
28667754Smsmith
287107325Siwasaki	switch (res) {
288107325Siwasaki	case 0:
289107325Siwasaki		/* Success! */
290107325Siwasaki		authenticated = 1;
29167754Smsmith		break;
292107325Siwasaki	case 1:
293107325Siwasaki		/* Authentication needs further interaction */
294107325Siwasaki		if (send_userauth_info_request(authctxt) == 1)
295107325Siwasaki			authctxt->postponed = 1;
296107325Siwasaki		break;
29767754Smsmith	default:
29867754Smsmith		/* Failure! */
29967754Smsmith		break;
30067754Smsmith	}
30167754Smsmith
30287031Smsmith	len = strlen("keyboard-interactive") + 2 +
30367754Smsmith		strlen(kbdintctxt->device->name);
30467754Smsmith	method = xmalloc(len);
30567754Smsmith	snprintf(method, len, "keyboard-interactive/%s",
30667754Smsmith	    kbdintctxt->device->name);
30767754Smsmith
30867754Smsmith	if (!authctxt->postponed) {
30967754Smsmith		if (authenticated) {
31091116Smsmith			auth2_challenge_stop(authctxt);
31187031Smsmith		} else {
31291116Smsmith			/* start next device */
31387031Smsmith			/* may set authctxt->postponed */
31487031Smsmith			auth2_challenge_start(authctxt);
31567754Smsmith		}
31667754Smsmith	}
31767754Smsmith	userauth_finish(authctxt, authenticated, method);
31867754Smsmith	xfree(method);
31967754Smsmith}
32067754Smsmith
32167754Smsmithvoid
32267754Smsmithprivsep_challenge_enable(void)
32367754Smsmith{
32467754Smsmith#ifdef BSD_AUTH
32567754Smsmith	extern KbdintDevice mm_bsdauth_device;
32691116Smsmith#endif
32767754Smsmith#ifdef USE_PAM
32867754Smsmith	extern KbdintDevice mm_pam_device;
32967754Smsmith#endif
33067754Smsmith#ifdef SKEY
33167754Smsmith	extern KbdintDevice mm_skey_device;
33267754Smsmith#endif
33367754Smsmith	int n = 0;
33467754Smsmith
33567754Smsmith#ifdef BSD_AUTH
33667754Smsmith	devices[n++] = &mm_bsdauth_device;
33767754Smsmith#else
33867754Smsmith#ifdef USE_PAM
33967754Smsmith	devices[n++] = &mm_pam_device;
34067754Smsmith#endif
341107325Siwasaki#ifdef SKEY
342107325Siwasaki	devices[n++] = &mm_skey_device;
34367754Smsmith#endif
34467754Smsmith#endif
34567754Smsmith}
34677424Smsmith