auth2-chall.c revision 149753
176259Sgreen/*
276259Sgreen * Copyright (c) 2001 Markus Friedl.  All rights reserved.
392555Sdes * Copyright (c) 2001 Per Allansson.  All rights reserved.
476259Sgreen *
576259Sgreen * Redistribution and use in source and binary forms, with or without
676259Sgreen * modification, are permitted provided that the following conditions
776259Sgreen * are met:
876259Sgreen * 1. Redistributions of source code must retain the above copyright
976259Sgreen *    notice, this list of conditions and the following disclaimer.
1076259Sgreen * 2. Redistributions in binary form must reproduce the above copyright
1176259Sgreen *    notice, this list of conditions and the following disclaimer in the
1276259Sgreen *    documentation and/or other materials provided with the distribution.
1376259Sgreen *
1476259Sgreen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1576259Sgreen * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1676259Sgreen * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1776259Sgreen * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1876259Sgreen * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1976259Sgreen * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2076259Sgreen * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2176259Sgreen * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2276259Sgreen * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2376259Sgreen * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2476259Sgreen */
2576259Sgreen#include "includes.h"
26149753SdesRCSID("$OpenBSD: auth2-chall.c,v 1.24 2005/07/17 07:17:54 djm Exp $");
2799052SdesRCSID("$FreeBSD: head/crypto/openssh/auth2-chall.c 149753 2005-09-03 07:04:25Z des $");
2876259Sgreen
2976259Sgreen#include "ssh2.h"
3076259Sgreen#include "auth.h"
3192555Sdes#include "buffer.h"
3276259Sgreen#include "packet.h"
3376259Sgreen#include "xmalloc.h"
3476259Sgreen#include "dispatch.h"
3576259Sgreen#include "log.h"
36147005Sdes#include "servconf.h"
3776259Sgreen
38147005Sdes/* import */
39147005Sdesextern ServerOptions options;
40147005Sdes
4192555Sdesstatic int auth2_challenge_start(Authctxt *);
4292555Sdesstatic int send_userauth_info_request(Authctxt *);
4392555Sdesstatic void input_userauth_info_response(int, u_int32_t, void *);
4476259Sgreen
4592555Sdes#ifdef BSD_AUTH
4692555Sdesextern KbdintDevice bsdauth_device;
4798941Sdes#else
4899052Sdes#ifdef USE_PAM
49124211Sdesextern KbdintDevice sshpam_device;
5099052Sdes#endif
5198941Sdes#ifdef SKEY
5292555Sdesextern KbdintDevice skey_device;
5392555Sdes#endif
5498941Sdes#endif
5592555Sdes
5692555SdesKbdintDevice *devices[] = {
5792555Sdes#ifdef BSD_AUTH
5892555Sdes	&bsdauth_device,
5998941Sdes#else
6099052Sdes#ifdef USE_PAM
61124211Sdes	&sshpam_device,
6299052Sdes#endif
6398941Sdes#ifdef SKEY
6492555Sdes	&skey_device,
6592555Sdes#endif
6698941Sdes#endif
6792555Sdes	NULL
6892555Sdes};
6992555Sdes
7092555Sdestypedef struct KbdintAuthctxt KbdintAuthctxt;
7192555Sdesstruct KbdintAuthctxt
7292555Sdes{
7392555Sdes	char *devices;
7492555Sdes	void *ctxt;
7592555Sdes	KbdintDevice *device;
7699063Sdes	u_int nreq;
7792555Sdes};
7892555Sdes
79147005Sdes#ifdef USE_PAM
80147005Sdesvoid
81147005Sdesremove_kbdint_device(const char *devname)
82147005Sdes{
83147005Sdes	int i, j;
84147005Sdes
85147005Sdes	for (i = 0; devices[i] != NULL; i++)
86147005Sdes		if (strcmp(devices[i]->name, devname) == 0) {
87147005Sdes			for (j = i; devices[j] != NULL; j++)
88147005Sdes				devices[j] = devices[j+1];
89147005Sdes			i--;
90147005Sdes		}
91147005Sdes}
92147005Sdes#endif
93147005Sdes
9492555Sdesstatic KbdintAuthctxt *
9592555Sdeskbdint_alloc(const char *devs)
9692555Sdes{
9792555Sdes	KbdintAuthctxt *kbdintctxt;
9892555Sdes	Buffer b;
9992555Sdes	int i;
10092555Sdes
101147005Sdes#ifdef USE_PAM
102147005Sdes	if (!options.use_pam)
103147005Sdes		remove_kbdint_device("pam");
104147005Sdes#endif
105147005Sdes
10692555Sdes	kbdintctxt = xmalloc(sizeof(KbdintAuthctxt));
10792555Sdes	if (strcmp(devs, "") == 0) {
10892555Sdes		buffer_init(&b);
10992555Sdes		for (i = 0; devices[i]; i++) {
11092555Sdes			if (buffer_len(&b) > 0)
11192555Sdes				buffer_append(&b, ",", 1);
11292555Sdes			buffer_append(&b, devices[i]->name,
11392555Sdes			    strlen(devices[i]->name));
11492555Sdes		}
11592555Sdes		buffer_append(&b, "\0", 1);
11692555Sdes		kbdintctxt->devices = xstrdup(buffer_ptr(&b));
11792555Sdes		buffer_free(&b);
11892555Sdes	} else {
11992555Sdes		kbdintctxt->devices = xstrdup(devs);
12092555Sdes	}
12192555Sdes	debug("kbdint_alloc: devices '%s'", kbdintctxt->devices);
12292555Sdes	kbdintctxt->ctxt = NULL;
12392555Sdes	kbdintctxt->device = NULL;
12499063Sdes	kbdintctxt->nreq = 0;
12592555Sdes
12692555Sdes	return kbdintctxt;
12792555Sdes}
12892555Sdesstatic void
12992555Sdeskbdint_reset_device(KbdintAuthctxt *kbdintctxt)
13092555Sdes{
13192555Sdes	if (kbdintctxt->ctxt) {
13292555Sdes		kbdintctxt->device->free_ctx(kbdintctxt->ctxt);
13392555Sdes		kbdintctxt->ctxt = NULL;
13492555Sdes	}
13592555Sdes	kbdintctxt->device = NULL;
13692555Sdes}
13792555Sdesstatic void
13892555Sdeskbdint_free(KbdintAuthctxt *kbdintctxt)
13992555Sdes{
14092555Sdes	if (kbdintctxt->device)
14192555Sdes		kbdint_reset_device(kbdintctxt);
14292555Sdes	if (kbdintctxt->devices) {
14392555Sdes		xfree(kbdintctxt->devices);
14492555Sdes		kbdintctxt->devices = NULL;
14592555Sdes	}
14692555Sdes	xfree(kbdintctxt);
14792555Sdes}
14892555Sdes/* get next device */
14992555Sdesstatic int
15092555Sdeskbdint_next_device(KbdintAuthctxt *kbdintctxt)
15192555Sdes{
15292555Sdes	size_t len;
15392555Sdes	char *t;
15492555Sdes	int i;
15592555Sdes
15692555Sdes	if (kbdintctxt->device)
15792555Sdes		kbdint_reset_device(kbdintctxt);
15892555Sdes	do {
15992555Sdes		len = kbdintctxt->devices ?
16092555Sdes		    strcspn(kbdintctxt->devices, ",") : 0;
16192555Sdes
16292555Sdes		if (len == 0)
16392555Sdes			break;
16492555Sdes		for (i = 0; devices[i]; i++)
16592555Sdes			if (strncmp(kbdintctxt->devices, devices[i]->name, len) == 0)
16692555Sdes				kbdintctxt->device = devices[i];
16792555Sdes		t = kbdintctxt->devices;
16892555Sdes		kbdintctxt->devices = t[len] ? xstrdup(t+len+1) : NULL;
16992555Sdes		xfree(t);
17092555Sdes		debug2("kbdint_next_device: devices %s", kbdintctxt->devices ?
171149753Sdes		    kbdintctxt->devices : "<empty>");
17292555Sdes	} while (kbdintctxt->devices && !kbdintctxt->device);
17392555Sdes
17492555Sdes	return kbdintctxt->device ? 1 : 0;
17592555Sdes}
17692555Sdes
17776259Sgreen/*
17892555Sdes * try challenge-response, set authctxt->postponed if we have to
17976259Sgreen * wait for the response.
18076259Sgreen */
18176259Sgreenint
18276259Sgreenauth2_challenge(Authctxt *authctxt, char *devs)
18376259Sgreen{
18492555Sdes	debug("auth2_challenge: user=%s devs=%s",
18592555Sdes	    authctxt->user ? authctxt->user : "<nouser>",
18692555Sdes	    devs ? devs : "<no devs>");
18776259Sgreen
18892555Sdes	if (authctxt->user == NULL || !devs)
18976259Sgreen		return 0;
19092555Sdes	if (authctxt->kbdintctxt == NULL)
19192555Sdes		authctxt->kbdintctxt = kbdint_alloc(devs);
19292555Sdes	return auth2_challenge_start(authctxt);
19392555Sdes}
19492555Sdes
19592555Sdes/* unregister kbd-int callbacks and context */
19692555Sdesvoid
19792555Sdesauth2_challenge_stop(Authctxt *authctxt)
19892555Sdes{
19992555Sdes	/* unregister callback */
20092555Sdes	dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL);
20192555Sdes	if (authctxt->kbdintctxt != NULL)  {
20292555Sdes		kbdint_free(authctxt->kbdintctxt);
20392555Sdes		authctxt->kbdintctxt = NULL;
20492555Sdes	}
20592555Sdes}
20692555Sdes
20792555Sdes/* side effect: sets authctxt->postponed if a reply was sent*/
20892555Sdesstatic int
20992555Sdesauth2_challenge_start(Authctxt *authctxt)
21092555Sdes{
21192555Sdes	KbdintAuthctxt *kbdintctxt = authctxt->kbdintctxt;
21292555Sdes
21392555Sdes	debug2("auth2_challenge_start: devices %s",
21492555Sdes	    kbdintctxt->devices ?  kbdintctxt->devices : "<empty>");
21592555Sdes
21692555Sdes	if (kbdint_next_device(kbdintctxt) == 0) {
21792555Sdes		auth2_challenge_stop(authctxt);
21876259Sgreen		return 0;
21992555Sdes	}
22092555Sdes	debug("auth2_challenge_start: trying authentication method '%s'",
22192555Sdes	    kbdintctxt->device->name);
22292555Sdes
22392555Sdes	if ((kbdintctxt->ctxt = kbdintctxt->device->init_ctx(authctxt)) == NULL) {
22492555Sdes		auth2_challenge_stop(authctxt);
22592555Sdes		return 0;
22692555Sdes	}
22792555Sdes	if (send_userauth_info_request(authctxt) == 0) {
22892555Sdes		auth2_challenge_stop(authctxt);
22992555Sdes		return 0;
23092555Sdes	}
23176259Sgreen	dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE,
23276259Sgreen	    &input_userauth_info_response);
23392555Sdes
23476259Sgreen	authctxt->postponed = 1;
23576259Sgreen	return 0;
23676259Sgreen}
23776259Sgreen
23892555Sdesstatic int
23992555Sdessend_userauth_info_request(Authctxt *authctxt)
24076259Sgreen{
24192555Sdes	KbdintAuthctxt *kbdintctxt;
24292555Sdes	char *name, *instr, **prompts;
243149753Sdes	u_int i, *echo_on;
24476259Sgreen
24592555Sdes	kbdintctxt = authctxt->kbdintctxt;
24692555Sdes	if (kbdintctxt->device->query(kbdintctxt->ctxt,
24799063Sdes	    &name, &instr, &kbdintctxt->nreq, &prompts, &echo_on))
24892555Sdes		return 0;
24992555Sdes
25076259Sgreen	packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST);
25192555Sdes	packet_put_cstring(name);
25292555Sdes	packet_put_cstring(instr);
25398684Sdes	packet_put_cstring("");		/* language not used */
25499063Sdes	packet_put_int(kbdintctxt->nreq);
25599063Sdes	for (i = 0; i < kbdintctxt->nreq; i++) {
25692555Sdes		packet_put_cstring(prompts[i]);
25792555Sdes		packet_put_char(echo_on[i]);
25892555Sdes	}
25976259Sgreen	packet_send();
26076259Sgreen	packet_write_wait();
26192555Sdes
26299063Sdes	for (i = 0; i < kbdintctxt->nreq; i++)
26392555Sdes		xfree(prompts[i]);
26492555Sdes	xfree(prompts);
26592555Sdes	xfree(echo_on);
26692555Sdes	xfree(name);
26792555Sdes	xfree(instr);
26892555Sdes	return 1;
26976259Sgreen}
27076259Sgreen
27192555Sdesstatic void
27292555Sdesinput_userauth_info_response(int type, u_int32_t seq, void *ctxt)
27376259Sgreen{
27476259Sgreen	Authctxt *authctxt = ctxt;
27592555Sdes	KbdintAuthctxt *kbdintctxt;
276149753Sdes	int authenticated = 0, res, len;
277149753Sdes	u_int i, nresp;
27892555Sdes	char **response = NULL, *method;
27976259Sgreen
28076259Sgreen	if (authctxt == NULL)
28176259Sgreen		fatal("input_userauth_info_response: no authctxt");
28292555Sdes	kbdintctxt = authctxt->kbdintctxt;
28392555Sdes	if (kbdintctxt == NULL || kbdintctxt->ctxt == NULL)
28492555Sdes		fatal("input_userauth_info_response: no kbdintctxt");
28592555Sdes	if (kbdintctxt->device == NULL)
28692555Sdes		fatal("input_userauth_info_response: no device");
28776259Sgreen
28876259Sgreen	authctxt->postponed = 0;	/* reset */
28976259Sgreen	nresp = packet_get_int();
29099063Sdes	if (nresp != kbdintctxt->nreq)
29199063Sdes		fatal("input_userauth_info_response: wrong number of replies");
29299063Sdes	if (nresp > 100)
29399063Sdes		fatal("input_userauth_info_response: too many replies");
29492555Sdes	if (nresp > 0) {
295106130Sdes		response = xmalloc(nresp * sizeof(char *));
29692555Sdes		for (i = 0; i < nresp; i++)
29792555Sdes			response[i] = packet_get_string(NULL);
29892555Sdes	}
29992555Sdes	packet_check_eom();
30092555Sdes
301147005Sdes	res = kbdintctxt->device->respond(kbdintctxt->ctxt, nresp, response);
30292555Sdes
30392555Sdes	for (i = 0; i < nresp; i++) {
30492555Sdes		memset(response[i], 'r', strlen(response[i]));
30592555Sdes		xfree(response[i]);
30692555Sdes	}
30792555Sdes	if (response)
30876259Sgreen		xfree(response);
30992555Sdes
31092555Sdes	switch (res) {
31192555Sdes	case 0:
31292555Sdes		/* Success! */
313147005Sdes		authenticated = authctxt->valid ? 1 : 0;
31492555Sdes		break;
31592555Sdes	case 1:
31692555Sdes		/* Authentication needs further interaction */
31792555Sdes		if (send_userauth_info_request(authctxt) == 1)
31892555Sdes			authctxt->postponed = 1;
31992555Sdes		break;
32092555Sdes	default:
32192555Sdes		/* Failure! */
32292555Sdes		break;
32376259Sgreen	}
32476259Sgreen
32592555Sdes	len = strlen("keyboard-interactive") + 2 +
32692555Sdes		strlen(kbdintctxt->device->name);
32792555Sdes	method = xmalloc(len);
32892555Sdes	snprintf(method, len, "keyboard-interactive/%s",
32992555Sdes	    kbdintctxt->device->name);
33092555Sdes
33192555Sdes	if (!authctxt->postponed) {
33292555Sdes		if (authenticated) {
33392555Sdes			auth2_challenge_stop(authctxt);
33492555Sdes		} else {
33592555Sdes			/* start next device */
33692555Sdes			/* may set authctxt->postponed */
33792555Sdes			auth2_challenge_start(authctxt);
33892555Sdes		}
33992555Sdes	}
34076259Sgreen	userauth_finish(authctxt, authenticated, method);
34192555Sdes	xfree(method);
34276259Sgreen}
34398684Sdes
34498684Sdesvoid
34598684Sdesprivsep_challenge_enable(void)
34698684Sdes{
347124211Sdes#if defined(BSD_AUTH) || defined(USE_PAM) || defined(SKEY)
348124211Sdes	int n = 0;
349124211Sdes#endif
35098684Sdes#ifdef BSD_AUTH
35198684Sdes	extern KbdintDevice mm_bsdauth_device;
35298684Sdes#endif
35399052Sdes#ifdef USE_PAM
354124211Sdes	extern KbdintDevice mm_sshpam_device;
35599052Sdes#endif
35698684Sdes#ifdef SKEY
35798684Sdes	extern KbdintDevice mm_skey_device;
35898684Sdes#endif
35999052Sdes
36098684Sdes#ifdef BSD_AUTH
36199052Sdes	devices[n++] = &mm_bsdauth_device;
36298684Sdes#else
36399052Sdes#ifdef USE_PAM
364124211Sdes	devices[n++] = &mm_sshpam_device;
36599052Sdes#endif
36698684Sdes#ifdef SKEY
36799052Sdes	devices[n++] = &mm_skey_device;
36898684Sdes#endif
36998684Sdes#endif
37098684Sdes}
371