auth2-chall.c revision 192595
133965Sjdp/* $OpenBSD: auth2-chall.c,v 1.34 2008/12/09 04:32:22 djm Exp $ */
278828Sobrien/*
3218822Sdim * Copyright (c) 2001 Markus Friedl.  All rights reserved.
433965Sjdp * Copyright (c) 2001 Per Allansson.  All rights reserved.
533965Sjdp *
633965Sjdp * Redistribution and use in source and binary forms, with or without
733965Sjdp * modification, are permitted provided that the following conditions
8104834Sobrien * are met:
933965Sjdp * 1. Redistributions of source code must retain the above copyright
10104834Sobrien *    notice, this list of conditions and the following disclaimer.
11104834Sobrien * 2. Redistributions in binary form must reproduce the above copyright
12104834Sobrien *    notice, this list of conditions and the following disclaimer in the
13104834Sobrien *    documentation and/or other materials provided with the distribution.
1433965Sjdp *
15104834Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16104834Sobrien * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17104834Sobrien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18104834Sobrien * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1933965Sjdp * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20104834Sobrien * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21104834Sobrien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22218822Sdim * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2333965Sjdp * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24218822Sdim * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2533965Sjdp */
2633965Sjdp
2733965Sjdp#include "includes.h"
28130561Sobrien
2933965Sjdp#include <sys/types.h>
3033965Sjdp
3133965Sjdp#include <stdarg.h>
3233965Sjdp#include <stdio.h>
3333965Sjdp#include <string.h>
3433965Sjdp
3533965Sjdp#include "xmalloc.h"
3633965Sjdp#include "ssh2.h"
3733965Sjdp#include "key.h"
3833965Sjdp#include "hostfile.h"
3933965Sjdp#include "auth.h"
40130561Sobrien#include "buffer.h"
41130561Sobrien#include "packet.h"
42130561Sobrien#include "dispatch.h"
43130561Sobrien#include "log.h"
4433965Sjdp#include "servconf.h"
4533965Sjdp
4633965Sjdp/* import */
4733965Sjdpextern ServerOptions options;
4833965Sjdp
4933965Sjdpstatic int auth2_challenge_start(Authctxt *);
50130561Sobrienstatic int send_userauth_info_request(Authctxt *);
5133965Sjdpstatic void input_userauth_info_response(int, u_int32_t, void *);
5233965Sjdp
5333965Sjdp#ifdef BSD_AUTH
54130561Sobrienextern KbdintDevice bsdauth_device;
5533965Sjdp#else
5633965Sjdp#ifdef USE_PAM
5733965Sjdpextern KbdintDevice sshpam_device;
58130561Sobrien#endif
59130561Sobrien#ifdef SKEY
60130561Sobrienextern KbdintDevice skey_device;
6133965Sjdp#endif
6233965Sjdp#endif
6333965Sjdp
6489857SobrienKbdintDevice *devices[] = {
6533965Sjdp#ifdef BSD_AUTH
6633965Sjdp	&bsdauth_device,
6733965Sjdp#else
6833965Sjdp#ifdef USE_PAM
6933965Sjdp	&sshpam_device,
7033965Sjdp#endif
7133965Sjdp#ifdef SKEY
7233965Sjdp	&skey_device,
73130561Sobrien#endif
74218822Sdim#endif
7589857Sobrien	NULL
7689857Sobrien};
7789857Sobrien
7889857Sobrientypedef struct KbdintAuthctxt KbdintAuthctxt;
79130561Sobrienstruct KbdintAuthctxt
80218822Sdim{
8133965Sjdp	char *devices;
82130561Sobrien	void *ctxt;
8389857Sobrien	KbdintDevice *device;
84130561Sobrien	u_int nreq;
85130561Sobrien};
8633965Sjdp
87130561Sobrien#ifdef USE_PAM
88130561Sobrienvoid
8933965Sjdpremove_kbdint_device(const char *devname)
90130561Sobrien{
9133965Sjdp	int i, j;
9233965Sjdp
9333965Sjdp	for (i = 0; devices[i] != NULL; i++)
9433965Sjdp		if (strcmp(devices[i]->name, devname) == 0) {
9533965Sjdp			for (j = i; devices[j] != NULL; j++)
9633965Sjdp				devices[j] = devices[j+1];
9733965Sjdp			i--;
98130561Sobrien		}
9933965Sjdp}
10033965Sjdp#endif
10133965Sjdp
10233965Sjdpstatic KbdintAuthctxt *
10399461Sobrienkbdint_alloc(const char *devs)
10499461Sobrien{
10533965Sjdp	KbdintAuthctxt *kbdintctxt;
106218822Sdim	Buffer b;
10733965Sjdp	int i;
10833965Sjdp
10933965Sjdp#ifdef USE_PAM
11033965Sjdp	if (!options.use_pam)
11133965Sjdp		remove_kbdint_device("pam");
11233965Sjdp#endif
11389857Sobrien
11489857Sobrien	kbdintctxt = xmalloc(sizeof(KbdintAuthctxt));
11589857Sobrien	if (strcmp(devs, "") == 0) {
116130561Sobrien		buffer_init(&b);
11789857Sobrien		for (i = 0; devices[i]; i++) {
118218822Sdim			if (buffer_len(&b) > 0)
119218822Sdim				buffer_append(&b, ",", 1);
120218822Sdim			buffer_append(&b, devices[i]->name,
121218822Sdim			    strlen(devices[i]->name));
122218822Sdim		}
12389857Sobrien		buffer_append(&b, "\0", 1);
12489857Sobrien		kbdintctxt->devices = xstrdup(buffer_ptr(&b));
12589857Sobrien		buffer_free(&b);
126218822Sdim	} else {
127218822Sdim		kbdintctxt->devices = xstrdup(devs);
128218822Sdim	}
129218822Sdim	debug("kbdint_alloc: devices '%s'", kbdintctxt->devices);
130218822Sdim	kbdintctxt->ctxt = NULL;
131218822Sdim	kbdintctxt->device = NULL;
132218822Sdim	kbdintctxt->nreq = 0;
133218822Sdim
134218822Sdim	return kbdintctxt;
135218822Sdim}
136218822Sdimstatic void
137218822Sdimkbdint_reset_device(KbdintAuthctxt *kbdintctxt)
138218822Sdim{
139218822Sdim	if (kbdintctxt->ctxt) {
140218822Sdim		kbdintctxt->device->free_ctx(kbdintctxt->ctxt);
141218822Sdim		kbdintctxt->ctxt = NULL;
142218822Sdim	}
143218822Sdim	kbdintctxt->device = NULL;
144218822Sdim}
145218822Sdimstatic void
146218822Sdimkbdint_free(KbdintAuthctxt *kbdintctxt)
14733965Sjdp{
14833965Sjdp	if (kbdintctxt->device)
14933965Sjdp		kbdint_reset_device(kbdintctxt);
15033965Sjdp	if (kbdintctxt->devices) {
151218822Sdim		xfree(kbdintctxt->devices);
152218822Sdim		kbdintctxt->devices = NULL;
15333965Sjdp	}
15433965Sjdp	xfree(kbdintctxt);
15533965Sjdp}
15633965Sjdp/* get next device */
157218822Sdimstatic int
15833965Sjdpkbdint_next_device(KbdintAuthctxt *kbdintctxt)
15933965Sjdp{
160218822Sdim	size_t len;
161218822Sdim	char *t;
16233965Sjdp	int i;
16333965Sjdp
164218822Sdim	if (kbdintctxt->device)
165218822Sdim		kbdint_reset_device(kbdintctxt);
166218822Sdim	do {
167218822Sdim		len = kbdintctxt->devices ?
168218822Sdim		    strcspn(kbdintctxt->devices, ",") : 0;
16933965Sjdp
17033965Sjdp		if (len == 0)
17133965Sjdp			break;
17233965Sjdp		for (i = 0; devices[i]; i++)
173218822Sdim			if (strncmp(kbdintctxt->devices, devices[i]->name, len) == 0)
174218822Sdim				kbdintctxt->device = devices[i];
17533965Sjdp		t = kbdintctxt->devices;
176130561Sobrien		kbdintctxt->devices = t[len] ? xstrdup(t+len+1) : NULL;
177130561Sobrien		xfree(t);
17833965Sjdp		debug2("kbdint_next_device: devices %s", kbdintctxt->devices ?
17933965Sjdp		    kbdintctxt->devices : "<empty>");
18033965Sjdp	} while (kbdintctxt->devices && !kbdintctxt->device);
181218822Sdim
18233965Sjdp	return kbdintctxt->device ? 1 : 0;
18333965Sjdp}
18433965Sjdp
18533965Sjdp/*
18633965Sjdp * try challenge-response, set authctxt->postponed if we have to
18733965Sjdp * wait for the response.
18833965Sjdp */
18933965Sjdpint
19033965Sjdpauth2_challenge(Authctxt *authctxt, char *devs)
19133965Sjdp{
19233965Sjdp	debug("auth2_challenge: user=%s devs=%s",
19389857Sobrien	    authctxt->user ? authctxt->user : "<nouser>",
19433965Sjdp	    devs ? devs : "<no devs>");
19533965Sjdp
196218822Sdim	if (authctxt->user == NULL || !devs)
197218822Sdim		return 0;
198218822Sdim	if (authctxt->kbdintctxt == NULL)
199218822Sdim		authctxt->kbdintctxt = kbdint_alloc(devs);
200218822Sdim	return auth2_challenge_start(authctxt);
201218822Sdim}
202218822Sdim
203218822Sdim/* unregister kbd-int callbacks and context */
204218822Sdimvoid
205218822Sdimauth2_challenge_stop(Authctxt *authctxt)
206218822Sdim{
207218822Sdim	/* unregister callback */
208218822Sdim	dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL);
20933965Sjdp	if (authctxt->kbdintctxt != NULL) {
210218822Sdim		kbdint_free(authctxt->kbdintctxt);
21133965Sjdp		authctxt->kbdintctxt = NULL;
21233965Sjdp	}
213218822Sdim}
214218822Sdim
215218822Sdim/* side effect: sets authctxt->postponed if a reply was sent*/
216218822Sdimstatic int
217218822Sdimauth2_challenge_start(Authctxt *authctxt)
218218822Sdim{
219218822Sdim	KbdintAuthctxt *kbdintctxt = authctxt->kbdintctxt;
220218822Sdim
221218822Sdim	debug2("auth2_challenge_start: devices %s",
222218822Sdim	    kbdintctxt->devices ?  kbdintctxt->devices : "<empty>");
223218822Sdim
22433965Sjdp	if (kbdint_next_device(kbdintctxt) == 0) {
22589857Sobrien		auth2_challenge_stop(authctxt);
22633965Sjdp		return 0;
22733965Sjdp	}
228218822Sdim	debug("auth2_challenge_start: trying authentication method '%s'",
229218822Sdim	    kbdintctxt->device->name);
230218822Sdim
231218822Sdim	if ((kbdintctxt->ctxt = kbdintctxt->device->init_ctx(authctxt)) == NULL) {
232218822Sdim		auth2_challenge_stop(authctxt);
233218822Sdim		return 0;
234223262Sbenl	}
23533965Sjdp	if (send_userauth_info_request(authctxt) == 0) {
23633965Sjdp		auth2_challenge_stop(authctxt);
23733965Sjdp		return 0;
23833965Sjdp	}
239218822Sdim	dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE,
240218822Sdim	    &input_userauth_info_response);
241218822Sdim
242218822Sdim	authctxt->postponed = 1;
243218822Sdim	return 0;
244218822Sdim}
245218822Sdim
246218822Sdimstatic int
247218822Sdimsend_userauth_info_request(Authctxt *authctxt)
248218822Sdim{
249218822Sdim	KbdintAuthctxt *kbdintctxt;
250218822Sdim	char *name, *instr, **prompts;
251218822Sdim	u_int i, *echo_on;
252218822Sdim
253218822Sdim	kbdintctxt = authctxt->kbdintctxt;
254218822Sdim	if (kbdintctxt->device->query(kbdintctxt->ctxt,
255218822Sdim	    &name, &instr, &kbdintctxt->nreq, &prompts, &echo_on))
256218822Sdim		return 0;
257218822Sdim
258218822Sdim	packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST);
259218822Sdim	packet_put_cstring(name);
260218822Sdim	packet_put_cstring(instr);
261218822Sdim	packet_put_cstring("");		/* language not used */
262218822Sdim	packet_put_int(kbdintctxt->nreq);
263218822Sdim	for (i = 0; i < kbdintctxt->nreq; i++) {
26433965Sjdp		packet_put_cstring(prompts[i]);
26533965Sjdp		packet_put_char(echo_on[i]);
26633965Sjdp	}
26733965Sjdp	packet_send();
26833965Sjdp	packet_write_wait();
26933965Sjdp
270104834Sobrien	for (i = 0; i < kbdintctxt->nreq; i++)
27133965Sjdp		xfree(prompts[i]);
27233965Sjdp	xfree(prompts);
273130561Sobrien	xfree(echo_on);
27433965Sjdp	xfree(name);
27533965Sjdp	xfree(instr);
276130561Sobrien	return 1;
27733965Sjdp}
27833965Sjdp
279130561Sobrienstatic void
280130561Sobrieninput_userauth_info_response(int type, u_int32_t seq, void *ctxt)
281130561Sobrien{
28233965Sjdp	Authctxt *authctxt = ctxt;
283130561Sobrien	KbdintAuthctxt *kbdintctxt;
284130561Sobrien	int authenticated = 0, res;
285130561Sobrien	u_int i, nresp;
286130561Sobrien	char **response = NULL, *method;
287130561Sobrien
288130561Sobrien	if (authctxt == NULL)
289130561Sobrien		fatal("input_userauth_info_response: no authctxt");
290130561Sobrien	kbdintctxt = authctxt->kbdintctxt;
291130561Sobrien	if (kbdintctxt == NULL || kbdintctxt->ctxt == NULL)
29233965Sjdp		fatal("input_userauth_info_response: no kbdintctxt");
293130561Sobrien	if (kbdintctxt->device == NULL)
294130561Sobrien		fatal("input_userauth_info_response: no device");
29533965Sjdp
29633965Sjdp	authctxt->postponed = 0;	/* reset */
29733965Sjdp	nresp = packet_get_int();
298130561Sobrien	if (nresp != kbdintctxt->nreq)
29933965Sjdp		fatal("input_userauth_info_response: wrong number of replies");
300218822Sdim	if (nresp > 100)
301218822Sdim		fatal("input_userauth_info_response: too many replies");
30233965Sjdp	if (nresp > 0) {
303218822Sdim		response = xcalloc(nresp, sizeof(char *));
30433965Sjdp		for (i = 0; i < nresp; i++)
30533965Sjdp			response[i] = packet_get_string(NULL);
306218822Sdim	}
30733965Sjdp	packet_check_eom();
30833965Sjdp
309104834Sobrien	res = kbdintctxt->device->respond(kbdintctxt->ctxt, nresp, response);
31033965Sjdp
311218822Sdim	for (i = 0; i < nresp; i++) {
31233965Sjdp		memset(response[i], 'r', strlen(response[i]));
31333965Sjdp		xfree(response[i]);
31433965Sjdp	}
315104834Sobrien	if (response)
31638889Sjdp		xfree(response);
31738889Sjdp
318218822Sdim	switch (res) {
319218822Sdim	case 0:
320218822Sdim		/* Success! */
32138889Sjdp		authenticated = authctxt->valid ? 1 : 0;
32238889Sjdp		break;
32333965Sjdp	case 1:
32433965Sjdp		/* Authentication needs further interaction */
325218822Sdim		if (send_userauth_info_request(authctxt) == 1)
326218822Sdim			authctxt->postponed = 1;
327218822Sdim		break;
328218822Sdim	default:
329218822Sdim		/* Failure! */
330218822Sdim		break;
331218822Sdim	}
332218822Sdim
333218822Sdim	xasprintf(&method, "keyboard-interactive/%s", kbdintctxt->device->name);
334218822Sdim
335218822Sdim	if (!authctxt->postponed) {
336218822Sdim		if (authenticated) {
337218822Sdim			auth2_challenge_stop(authctxt);
338218822Sdim		} else {
339218822Sdim			/* start next device */
340218822Sdim			/* may set authctxt->postponed */
341218822Sdim			auth2_challenge_start(authctxt);
342218822Sdim		}
343218822Sdim	}
344218822Sdim	userauth_finish(authctxt, authenticated, method);
345218822Sdim	xfree(method);
346218822Sdim}
347218822Sdim
348218822Sdimvoid
349218822Sdimprivsep_challenge_enable(void)
350218822Sdim{
351218822Sdim#if defined(BSD_AUTH) || defined(USE_PAM) || defined(SKEY)
352218822Sdim	int n = 0;
353218822Sdim#endif
35433965Sjdp#ifdef BSD_AUTH
35589857Sobrien	extern KbdintDevice mm_bsdauth_device;
35633965Sjdp#endif
35733965Sjdp#ifdef USE_PAM
35833965Sjdp	extern KbdintDevice mm_sshpam_device;
359218822Sdim#endif
36033965Sjdp#ifdef SKEY
361218822Sdim	extern KbdintDevice mm_skey_device;
36233965Sjdp#endif
36333965Sjdp
36433965Sjdp#ifdef BSD_AUTH
36589857Sobrien	devices[n++] = &mm_bsdauth_device;
36633965Sjdp#else
36733965Sjdp#ifdef USE_PAM
36833965Sjdp	devices[n++] = &mm_sshpam_device;
36933965Sjdp#endif
37033965Sjdp#ifdef SKEY
37133965Sjdp	devices[n++] = &mm_skey_device;
37233965Sjdp#endif
37333965Sjdp#endif
374218822Sdim}
37533965Sjdp