auth2.c revision 92559
160573Skris/*
260573Skris * Copyright (c) 2000 Markus Friedl.  All rights reserved.
360573Skris *
460573Skris * Redistribution and use in source and binary forms, with or without
560573Skris * modification, are permitted provided that the following conditions
660573Skris * are met:
760573Skris * 1. Redistributions of source code must retain the above copyright
860573Skris *    notice, this list of conditions and the following disclaimer.
960573Skris * 2. Redistributions in binary form must reproduce the above copyright
1060573Skris *    notice, this list of conditions and the following disclaimer in the
1160573Skris *    documentation and/or other materials provided with the distribution.
1260573Skris *
1360573Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1460573Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1560573Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1660573Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1760573Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1860573Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1960573Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2060573Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2160573Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2260573Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2360573Skris */
2465674Skris
2560573Skris#include "includes.h"
2692559SdesRCSID("$OpenBSD: auth2.c,v 1.85 2002/02/24 19:14:59 markus Exp $");
2765674SkrisRCSID("$FreeBSD: head/crypto/openssh/auth2.c 92559 2002-03-18 10:09:43Z des $");
2860573Skris
2960573Skris#include <openssl/evp.h>
3060573Skris
3176262Sgreen#include "ssh2.h"
3260573Skris#include "xmalloc.h"
3360573Skris#include "rsa.h"
3476262Sgreen#include "sshpty.h"
3560573Skris#include "packet.h"
3660573Skris#include "buffer.h"
3776262Sgreen#include "log.h"
3860573Skris#include "servconf.h"
3960573Skris#include "compat.h"
4060573Skris#include "channels.h"
4160573Skris#include "bufaux.h"
4260573Skris#include "auth.h"
4360573Skris#include "session.h"
4460573Skris#include "dispatch.h"
4560573Skris#include "key.h"
4676262Sgreen#include "cipher.h"
4760573Skris#include "kex.h"
4876262Sgreen#include "pathnames.h"
4960573Skris#include "uidswap.h"
5065674Skris#include "auth-options.h"
5176262Sgreen#include "misc.h"
5276262Sgreen#include "hostfile.h"
5376262Sgreen#include "canohost.h"
5492559Sdes#include "match.h"
5560573Skris
5668704Sgreen#ifdef HAVE_LOGIN_CAP
5768704Sgreen#include <login_cap.h>
5868704Sgreen#endif /* HAVE_LOGIN_CAP */
5968704Sgreen
6060573Skris/* import */
6160573Skrisextern ServerOptions options;
6276262Sgreenextern u_char *session_id2;
6360573Skrisextern int session_id2_len;
6460573Skris
6569591Sgreenstatic Authctxt	*x_authctxt = NULL;
6669591Sgreenstatic int one = 1;
6769591Sgreen
6869591Sgreentypedef struct Authmethod Authmethod;
6969591Sgreenstruct Authmethod {
7069591Sgreen	char	*name;
7169591Sgreen	int	(*userauth)(Authctxt *authctxt);
7269591Sgreen	int	*enabled;
7369591Sgreen};
7469591Sgreen
7560573Skris/* protocol */
7660573Skris
7792559Sdesstatic void input_service_request(int, u_int32_t, void *);
7892559Sdesstatic void input_userauth_request(int, u_int32_t, void *);
7960573Skris
8060573Skris/* helper */
8192559Sdesstatic Authmethod *authmethod_lookup(const char *);
8292559Sdesstatic char *authmethods_get(void);
8392559Sdesstatic int user_key_allowed(struct passwd *, Key *);
8492559Sdesstatic int hostbased_key_allowed(struct passwd *, const char *, char *, Key *);
8560573Skris
8669591Sgreen/* auth */
8792559Sdesstatic void userauth_banner(void);
8892559Sdesstatic int userauth_none(Authctxt *);
8992559Sdesstatic int userauth_passwd(Authctxt *);
9092559Sdesstatic int userauth_pubkey(Authctxt *);
9192559Sdesstatic int userauth_hostbased(Authctxt *);
9292559Sdesstatic int userauth_kbdint(Authctxt *);
9369591Sgreen
9469591SgreenAuthmethod authmethods[] = {
9569591Sgreen	{"none",
9669591Sgreen		userauth_none,
9769591Sgreen		&one},
9869591Sgreen	{"publickey",
9969591Sgreen		userauth_pubkey,
10076262Sgreen		&options.pubkey_authentication},
10176262Sgreen	{"password",
10276262Sgreen		userauth_passwd,
10376262Sgreen		&options.password_authentication},
10469591Sgreen	{"keyboard-interactive",
10569591Sgreen		userauth_kbdint,
10669591Sgreen		&options.kbd_interactive_authentication},
10776262Sgreen	{"hostbased",
10876262Sgreen		userauth_hostbased,
10976262Sgreen		&options.hostbased_authentication},
11069591Sgreen	{NULL, NULL, NULL}
11160573Skris};
11260573Skris
11360573Skris/*
11469591Sgreen * loop until authctxt->success == TRUE
11560573Skris */
11660573Skris
11760573Skrisvoid
11892559Sdesdo_authentication2(void)
11960573Skris{
12076262Sgreen	Authctxt *authctxt = authctxt_new();
12176262Sgreen
12269591Sgreen	x_authctxt = authctxt;		/*XXX*/
12369591Sgreen
12492559Sdes	/* challenge-response is implemented via keyboard interactive */
12592559Sdes	if (options.challenge_response_authentication)
12676262Sgreen		options.kbd_interactive_authentication = 1;
12776262Sgreen
12892559Sdes	dispatch_init(&dispatch_protocol_error);
12960573Skris	dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request);
13069591Sgreen	dispatch_run(DISPATCH_BLOCK, &authctxt->success, authctxt);
13176262Sgreen	do_authenticated(authctxt);
13260573Skris}
13360573Skris
13492559Sdesstatic void
13592559Sdesinput_service_request(int type, u_int32_t seq, void *ctxt)
13660573Skris{
13769591Sgreen	Authctxt *authctxt = ctxt;
13876262Sgreen	u_int len;
13960573Skris	int accept = 0;
14060573Skris	char *service = packet_get_string(&len);
14192559Sdes	packet_check_eom();
14260573Skris
14369591Sgreen	if (authctxt == NULL)
14469591Sgreen		fatal("input_service_request: no authctxt");
14569591Sgreen
14660573Skris	if (strcmp(service, "ssh-userauth") == 0) {
14769591Sgreen		if (!authctxt->success) {
14860573Skris			accept = 1;
14960573Skris			/* now we can handle user-auth requests */
15060573Skris			dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request);
15160573Skris		}
15260573Skris	}
15360573Skris	/* XXX all other service requests are denied */
15460573Skris
15560573Skris	if (accept) {
15660573Skris		packet_start(SSH2_MSG_SERVICE_ACCEPT);
15760573Skris		packet_put_cstring(service);
15860573Skris		packet_send();
15960573Skris		packet_write_wait();
16060573Skris	} else {
16160573Skris		debug("bad service request %s", service);
16260573Skris		packet_disconnect("bad service request %s", service);
16360573Skris	}
16460573Skris	xfree(service);
16560573Skris}
16660573Skris
16792559Sdesstatic void
16892559Sdesinput_userauth_request(int type, u_int32_t seq, void *ctxt)
16960573Skris{
17069591Sgreen	Authctxt *authctxt = ctxt;
17169591Sgreen	Authmethod *m = NULL;
17276262Sgreen	char *user, *service, *method, *style = NULL;
17360573Skris	int authenticated = 0;
17468704Sgreen#ifdef HAVE_LOGIN_CAP
17568704Sgreen	login_cap_t *lc;
17668704Sgreen#endif /* HAVE_LOGIN_CAP */
17768704Sgreen#if defined(HAVE_LOGIN_CAP) || defined(LOGIN_ACCESS)
17868704Sgreen	const char *from_host, *from_ip;
17960573Skris
18092559Sdes	from_host = get_canonical_hostname(options.verify_reverse_mapping);
18168704Sgreen	from_ip = get_remote_ipaddr();
18268704Sgreen#endif /* HAVE_LOGIN_CAP || LOGIN_ACCESS */
18368704Sgreen
18469591Sgreen	if (authctxt == NULL)
18569591Sgreen		fatal("input_userauth_request: no authctxt");
18660573Skris
18769591Sgreen	user = packet_get_string(NULL);
18869591Sgreen	service = packet_get_string(NULL);
18969591Sgreen	method = packet_get_string(NULL);
19060573Skris	debug("userauth-request for user %s service %s method %s", user, service, method);
19176262Sgreen	debug("attempt %d failures %d", authctxt->attempt, authctxt->failures);
19260573Skris
19376262Sgreen	if ((style = strchr(user, ':')) != NULL)
19476262Sgreen		*style++ = 0;
19576262Sgreen
19676262Sgreen	if (authctxt->attempt++ == 0) {
19769591Sgreen		/* setup auth context */
19869591Sgreen		struct passwd *pw = NULL;
19969591Sgreen		pw = getpwnam(user);
20069591Sgreen		if (pw && allowed_user(pw) && strcmp(service, "ssh-connection")==0) {
20169591Sgreen			authctxt->pw = pwcopy(pw);
20269591Sgreen			authctxt->valid = 1;
20369591Sgreen			debug2("input_userauth_request: setting up authctxt for %s", user);
20469591Sgreen#ifdef USE_PAM
20569591Sgreen			start_pam(pw);
20669591Sgreen#endif
20769591Sgreen		} else {
20869591Sgreen			log("input_userauth_request: illegal user %s", user);
20974278Sgreen			authctxt->pw = NULL;
21060573Skris		}
21176262Sgreen		setproctitle("%s", pw ? user : "unknown");
21269591Sgreen		authctxt->user = xstrdup(user);
21369591Sgreen		authctxt->service = xstrdup(service);
21492559Sdes		authctxt->style = style ? xstrdup(style) : NULL;
21592559Sdes	} else if (strcmp(user, authctxt->user) != 0 ||
21692559Sdes	    strcmp(service, authctxt->service) != 0) {
21792559Sdes		packet_disconnect("Change of username or service not allowed: "
21892559Sdes		    "(%s,%s) -> (%s,%s)",
21992559Sdes		    authctxt->user, authctxt->service, user, service);
22060573Skris	}
22160573Skris
22268704Sgreen#ifdef HAVE_LOGIN_CAP
22369591Sgreen	if (authctxt->pw != NULL) {
22469591Sgreen		lc = login_getpwclass(authctxt->pw);
22569591Sgreen		if (lc == NULL)
22669591Sgreen			lc = login_getclassbyname(NULL, authctxt->pw);
22769591Sgreen		if (!auth_hostok(lc, from_host, from_ip)) {
22869591Sgreen			log("Denied connection for %.200s from %.200s [%.200s].",
22969591Sgreen			    authctxt->pw->pw_name, from_host, from_ip);
23069591Sgreen			packet_disconnect("Sorry, you are not allowed to connect.");
23169591Sgreen		}
23269591Sgreen		if (!auth_timeok(lc, time(NULL))) {
23369591Sgreen			log("LOGIN %.200s REFUSED (TIME) FROM %.200s",
23469591Sgreen			    authctxt->pw->pw_name, from_host);
23569591Sgreen			packet_disconnect("Logins not available right now.");
23669591Sgreen		}
23769591Sgreen		login_close(lc);
23869591Sgreen		lc = NULL;
23968704Sgreen	}
24068704Sgreen#endif  /* HAVE_LOGIN_CAP */
24168704Sgreen#ifdef LOGIN_ACCESS
24269591Sgreen	if (authctxt->pw != NULL &&
24369591Sgreen	    !login_access(authctxt->pw->pw_name, from_host)) {
24468704Sgreen		log("Denied connection for %.200s from %.200s [%.200s].",
24569591Sgreen		    authctxt->pw->pw_name, from_host, from_ip);
24668704Sgreen		packet_disconnect("Sorry, you are not allowed to connect.");
24768704Sgreen	}
24868704Sgreen#endif /* LOGIN_ACCESS */
24976262Sgreen	/* reset state */
25092559Sdes	auth2_challenge_stop(authctxt);
25176262Sgreen	authctxt->postponed = 0;
25268704Sgreen
25376262Sgreen	/* try to authenticate user */
25469591Sgreen	m = authmethod_lookup(method);
25569591Sgreen	if (m != NULL) {
25669591Sgreen		debug2("input_userauth_request: try method %s", method);
25769591Sgreen		authenticated =	m->userauth(authctxt);
25869591Sgreen	}
25969591Sgreen#ifdef USE_PAM
26069591Sgreen	if (authenticated && authctxt->user && !do_pam_account(authctxt->user, NULL))
26169591Sgreen		authenticated = 0;
26269591Sgreen#endif /* USE_PAM */
26376262Sgreen	userauth_finish(authctxt, authenticated, method);
26469591Sgreen
26569591Sgreen	xfree(service);
26669591Sgreen	xfree(user);
26769591Sgreen	xfree(method);
26869591Sgreen}
26969591Sgreen
27069591Sgreenvoid
27176262Sgreenuserauth_finish(Authctxt *authctxt, int authenticated, char *method)
27269591Sgreen{
27392559Sdes	char *methods;
27492559Sdes
27576262Sgreen	if (!authctxt->valid && authenticated)
27676262Sgreen		fatal("INTERNAL ERROR: authenticated invalid user %s",
27776262Sgreen		    authctxt->user);
27869591Sgreen
27976262Sgreen	/* Special handling for root */
28076262Sgreen	if (authenticated && authctxt->pw->pw_uid == 0 &&
28176262Sgreen	    !auth_root_allowed(method))
28276262Sgreen		authenticated = 0;
28360573Skris
28476262Sgreen	/* Log before sending the reply */
28576262Sgreen	auth_log(authctxt, authenticated, method, " ssh2");
28669591Sgreen
28792559Sdes	if (authctxt->postponed)
28892559Sdes		return;
28992559Sdes
29092559Sdes	/* XXX todo: check if multiple auth methods are needed */
29192559Sdes	if (authenticated == 1) {
29292559Sdes		/* turn off userauth */
29392559Sdes		dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore);
29492559Sdes		packet_start(SSH2_MSG_USERAUTH_SUCCESS);
29592559Sdes		packet_send();
29692559Sdes		packet_write_wait();
29792559Sdes		/* now we can break out */
29892559Sdes		authctxt->success = 1;
29992559Sdes	} else {
30092559Sdes		if (authctxt->failures++ > AUTH_FAIL_MAX)
30192559Sdes			packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
30292559Sdes		methods = authmethods_get();
30392559Sdes		packet_start(SSH2_MSG_USERAUTH_FAILURE);
30492559Sdes		packet_put_cstring(methods);
30592559Sdes		packet_put_char(0);	/* XXX partial success, unused */
30692559Sdes		packet_send();
30792559Sdes		packet_write_wait();
30892559Sdes		xfree(methods);
30992559Sdes	}
31076262Sgreen}
31169591Sgreen
31292559Sdesstatic void
31376262Sgreenuserauth_banner(void)
31476262Sgreen{
31576262Sgreen	struct stat st;
31676262Sgreen	char *banner = NULL;
31776262Sgreen	off_t len, n;
31876262Sgreen	int fd;
31976262Sgreen
32076262Sgreen	if (options.banner == NULL || (datafellows & SSH_BUG_BANNER))
32176262Sgreen		return;
32276262Sgreen	if ((fd = open(options.banner, O_RDONLY)) < 0)
32376262Sgreen		return;
32476262Sgreen	if (fstat(fd, &st) < 0)
32576262Sgreen		goto done;
32676262Sgreen	len = st.st_size;
32776262Sgreen	banner = xmalloc(len + 1);
32876262Sgreen	if ((n = read(fd, banner, len)) < 0)
32976262Sgreen		goto done;
33076262Sgreen	banner[n] = '\0';
33176262Sgreen	packet_start(SSH2_MSG_USERAUTH_BANNER);
33276262Sgreen	packet_put_cstring(banner);
33376262Sgreen	packet_put_cstring("");		/* language, unused */
33476262Sgreen	packet_send();
33576262Sgreen	debug("userauth_banner: sent");
33676262Sgreendone:
33776262Sgreen	if (banner)
33876262Sgreen		xfree(banner);
33976262Sgreen	close(fd);
34076262Sgreen	return;
34169591Sgreen}
34260573Skris
34392559Sdesstatic int
34469591Sgreenuserauth_none(Authctxt *authctxt)
34560573Skris{
34669591Sgreen	/* disable method "none", only allowed one time */
34769591Sgreen	Authmethod *m = authmethod_lookup("none");
34869591Sgreen	if (m != NULL)
34969591Sgreen		m->enabled = NULL;
35092559Sdes	packet_check_eom();
35176262Sgreen	userauth_banner();
35269591Sgreen#ifdef USE_PAM
35376262Sgreen	return authctxt->valid ? auth_pam_password(authctxt, "") : 0;
35469591Sgreen#else /* !USE_PAM */
35576262Sgreen	return authctxt->valid ? auth_password(authctxt, "") : 0;
35669591Sgreen#endif /* USE_PAM */
35760573Skris}
35869591Sgreen
35992559Sdesstatic int
36069591Sgreenuserauth_passwd(Authctxt *authctxt)
36160573Skris{
36260573Skris	char *password;
36360573Skris	int authenticated = 0;
36460573Skris	int change;
36576262Sgreen	u_int len;
36660573Skris	change = packet_get_char();
36760573Skris	if (change)
36860573Skris		log("password change not supported");
36960573Skris	password = packet_get_string(&len);
37092559Sdes	packet_check_eom();
37169591Sgreen	if (authctxt->valid &&
37269591Sgreen#ifdef USE_PAM
37392402Sdes	    auth_password(authctxt, password) == 1)
37469591Sgreen#else
37592402Sdes	    auth_password(authctxt, password) == 1)
37669591Sgreen#endif
37760573Skris		authenticated = 1;
37860573Skris	memset(password, 0, len);
37960573Skris	xfree(password);
38060573Skris	return authenticated;
38160573Skris}
38269591Sgreen
38392559Sdesstatic int
38469591Sgreenuserauth_kbdint(Authctxt *authctxt)
38560573Skris{
38669591Sgreen	int authenticated = 0;
38792559Sdes	char *lang, *devs;
38869591Sgreen
38969591Sgreen	lang = packet_get_string(NULL);
39069591Sgreen	devs = packet_get_string(NULL);
39192559Sdes	packet_check_eom();
39269591Sgreen
39392559Sdes	debug("keyboard-interactive devs %s", devs);
39476262Sgreen
39592559Sdes	if (options.challenge_response_authentication)
39676262Sgreen		authenticated = auth2_challenge(authctxt, devs);
39776262Sgreen
39892559Sdes	xfree(devs);
39969591Sgreen	xfree(lang);
40069591Sgreen	return authenticated;
40169591Sgreen}
40269591Sgreen
40392559Sdesstatic int
40469591Sgreenuserauth_pubkey(Authctxt *authctxt)
40569591Sgreen{
40660573Skris	Buffer b;
40792559Sdes	Key *key = NULL;
40892559Sdes	char *pkalg;
40992559Sdes	u_char *pkblob, *sig;
41076262Sgreen	u_int alen, blen, slen;
41176262Sgreen	int have_sig, pktype;
41260573Skris	int authenticated = 0;
41360573Skris
41469591Sgreen	if (!authctxt->valid) {
41569591Sgreen		debug2("userauth_pubkey: disabled because of invalid user");
41660573Skris		return 0;
41760573Skris	}
41860573Skris	have_sig = packet_get_char();
41976262Sgreen	if (datafellows & SSH_BUG_PKAUTH) {
42076262Sgreen		debug2("userauth_pubkey: SSH_BUG_PKAUTH");
42176262Sgreen		/* no explicit pkalg given */
42276262Sgreen		pkblob = packet_get_string(&blen);
42376262Sgreen		buffer_init(&b);
42476262Sgreen		buffer_append(&b, pkblob, blen);
42576262Sgreen		/* so we have to extract the pkalg from the pkblob */
42676262Sgreen		pkalg = buffer_get_string(&b, &alen);
42776262Sgreen		buffer_free(&b);
42876262Sgreen	} else {
42976262Sgreen		pkalg = packet_get_string(&alen);
43076262Sgreen		pkblob = packet_get_string(&blen);
43176262Sgreen	}
43276262Sgreen	pktype = key_type_from_name(pkalg);
43376262Sgreen	if (pktype == KEY_UNSPEC) {
43476262Sgreen		/* this is perfectly legal */
43592559Sdes		log("userauth_pubkey: unsupported public key algorithm: %s",
43692559Sdes		    pkalg);
43792559Sdes		goto done;
43860573Skris	}
43976262Sgreen	key = key_from_blob(pkblob, blen);
44092559Sdes	if (key == NULL) {
44192559Sdes		error("userauth_pubkey: cannot decode key: %s", pkalg);
44292559Sdes		goto done;
44392559Sdes	}
44492559Sdes	if (key->type != pktype) {
44592559Sdes		error("userauth_pubkey: type mismatch for decoded key "
44692559Sdes		    "(received %d, expected %d)", key->type, pktype);
44792559Sdes		goto done;
44892559Sdes	}
44992559Sdes	if (have_sig) {
45092559Sdes		sig = packet_get_string(&slen);
45192559Sdes		packet_check_eom();
45292559Sdes		buffer_init(&b);
45392559Sdes		if (datafellows & SSH_OLD_SESSIONID) {
45492559Sdes			buffer_append(&b, session_id2, session_id2_len);
45592559Sdes		} else {
45692559Sdes			buffer_put_string(&b, session_id2, session_id2_len);
45792559Sdes		}
45892559Sdes		/* reconstruct packet */
45992559Sdes		buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
46092559Sdes		buffer_put_cstring(&b, authctxt->user);
46192559Sdes		buffer_put_cstring(&b,
46292559Sdes		    datafellows & SSH_BUG_PKSERVICE ?
46392559Sdes		    "ssh-userauth" :
46492559Sdes		    authctxt->service);
46592559Sdes		if (datafellows & SSH_BUG_PKAUTH) {
46692559Sdes			buffer_put_char(&b, have_sig);
46792559Sdes		} else {
46892559Sdes			buffer_put_cstring(&b, "publickey");
46992559Sdes			buffer_put_char(&b, have_sig);
47092559Sdes			buffer_put_cstring(&b, pkalg);
47192559Sdes		}
47292559Sdes		buffer_put_string(&b, pkblob, blen);
47376262Sgreen#ifdef DEBUG_PK
47492559Sdes		buffer_dump(&b);
47560573Skris#endif
47692559Sdes		/* test for correct signature */
47792559Sdes		if (user_key_allowed(authctxt->pw, key) &&
47892559Sdes		    key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1)
47992559Sdes			authenticated = 1;
48092559Sdes		buffer_clear(&b);
48192559Sdes		xfree(sig);
48292559Sdes	} else {
48392559Sdes		debug("test whether pkalg/pkblob are acceptable");
48492559Sdes		packet_check_eom();
48569591Sgreen
48692559Sdes		/* XXX fake reply and always send PK_OK ? */
48792559Sdes		/*
48892559Sdes		 * XXX this allows testing whether a user is allowed
48992559Sdes		 * to login: if you happen to have a valid pubkey this
49092559Sdes		 * message is sent. the message is NEVER sent at all
49192559Sdes		 * if a user is not allowed to login. is this an
49292559Sdes		 * issue? -markus
49392559Sdes		 */
49492559Sdes		if (user_key_allowed(authctxt->pw, key)) {
49592559Sdes			packet_start(SSH2_MSG_USERAUTH_PK_OK);
49692559Sdes			packet_put_string(pkalg, alen);
49792559Sdes			packet_put_string(pkblob, blen);
49892559Sdes			packet_send();
49992559Sdes			packet_write_wait();
50092559Sdes			authctxt->postponed = 1;
50160573Skris		}
50260573Skris	}
50392559Sdes	if (authenticated != 1)
50492559Sdes		auth_clear_options();
50592559Sdesdone:
50676262Sgreen	debug2("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg);
50792559Sdes	if (key != NULL)
50892559Sdes		key_free(key);
50960573Skris	xfree(pkalg);
51060573Skris	xfree(pkblob);
51160573Skris	return authenticated;
51260573Skris}
51360573Skris
51492559Sdesstatic int
51576262Sgreenuserauth_hostbased(Authctxt *authctxt)
51676262Sgreen{
51776262Sgreen	Buffer b;
51892559Sdes	Key *key = NULL;
51992559Sdes	char *pkalg, *cuser, *chost, *service;
52092559Sdes	u_char *pkblob, *sig;
52176262Sgreen	u_int alen, blen, slen;
52276262Sgreen	int pktype;
52376262Sgreen	int authenticated = 0;
52476262Sgreen
52576262Sgreen	if (!authctxt->valid) {
52676262Sgreen		debug2("userauth_hostbased: disabled because of invalid user");
52776262Sgreen		return 0;
52876262Sgreen	}
52976262Sgreen	pkalg = packet_get_string(&alen);
53076262Sgreen	pkblob = packet_get_string(&blen);
53176262Sgreen	chost = packet_get_string(NULL);
53276262Sgreen	cuser = packet_get_string(NULL);
53376262Sgreen	sig = packet_get_string(&slen);
53476262Sgreen
53576262Sgreen	debug("userauth_hostbased: cuser %s chost %s pkalg %s slen %d",
53676262Sgreen	    cuser, chost, pkalg, slen);
53776262Sgreen#ifdef DEBUG_PK
53876262Sgreen	debug("signature:");
53976262Sgreen	buffer_init(&b);
54076262Sgreen	buffer_append(&b, sig, slen);
54176262Sgreen	buffer_dump(&b);
54276262Sgreen	buffer_free(&b);
54376262Sgreen#endif
54476262Sgreen	pktype = key_type_from_name(pkalg);
54576262Sgreen	if (pktype == KEY_UNSPEC) {
54676262Sgreen		/* this is perfectly legal */
54776262Sgreen		log("userauth_hostbased: unsupported "
54876262Sgreen		    "public key algorithm: %s", pkalg);
54976262Sgreen		goto done;
55076262Sgreen	}
55176262Sgreen	key = key_from_blob(pkblob, blen);
55276262Sgreen	if (key == NULL) {
55392559Sdes		error("userauth_hostbased: cannot decode key: %s", pkalg);
55476262Sgreen		goto done;
55576262Sgreen	}
55692559Sdes	if (key->type != pktype) {
55792559Sdes		error("userauth_hostbased: type mismatch for decoded key "
55892559Sdes		    "(received %d, expected %d)", key->type, pktype);
55992559Sdes		goto done;
56092559Sdes	}
56176262Sgreen	service = datafellows & SSH_BUG_HBSERVICE ? "ssh-userauth" :
56276262Sgreen	    authctxt->service;
56376262Sgreen	buffer_init(&b);
56476262Sgreen	buffer_put_string(&b, session_id2, session_id2_len);
56576262Sgreen	/* reconstruct packet */
56676262Sgreen	buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
56776262Sgreen	buffer_put_cstring(&b, authctxt->user);
56876262Sgreen	buffer_put_cstring(&b, service);
56976262Sgreen	buffer_put_cstring(&b, "hostbased");
57076262Sgreen	buffer_put_string(&b, pkalg, alen);
57176262Sgreen	buffer_put_string(&b, pkblob, blen);
57276262Sgreen	buffer_put_cstring(&b, chost);
57376262Sgreen	buffer_put_cstring(&b, cuser);
57476262Sgreen#ifdef DEBUG_PK
57576262Sgreen	buffer_dump(&b);
57676262Sgreen#endif
57776262Sgreen	/* test for allowed key and correct signature */
57876262Sgreen	if (hostbased_key_allowed(authctxt->pw, cuser, chost, key) &&
57976262Sgreen	    key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1)
58076262Sgreen		authenticated = 1;
58176262Sgreen
58276262Sgreen	buffer_clear(&b);
58376262Sgreendone:
58476262Sgreen	debug2("userauth_hostbased: authenticated %d", authenticated);
58592559Sdes	if (key != NULL)
58692559Sdes		key_free(key);
58776262Sgreen	xfree(pkalg);
58876262Sgreen	xfree(pkblob);
58976262Sgreen	xfree(cuser);
59076262Sgreen	xfree(chost);
59176262Sgreen	xfree(sig);
59276262Sgreen	return authenticated;
59376262Sgreen}
59476262Sgreen
59569591Sgreen/* get current user */
59660573Skris
59760573Skrisstruct passwd*
59860573Skrisauth_get_user(void)
59960573Skris{
60069591Sgreen	return (x_authctxt != NULL && x_authctxt->valid) ? x_authctxt->pw : NULL;
60160573Skris}
60260573Skris
60369591Sgreen#define	DELIM	","
60469591Sgreen
60592559Sdesstatic char *
60669591Sgreenauthmethods_get(void)
60760573Skris{
60869591Sgreen	Authmethod *method = NULL;
60992559Sdes	Buffer b;
61069591Sgreen	char *list;
61160573Skris
61292559Sdes	buffer_init(&b);
61369591Sgreen	for (method = authmethods; method->name != NULL; method++) {
61469591Sgreen		if (strcmp(method->name, "none") == 0)
61569591Sgreen			continue;
61669591Sgreen		if (method->enabled != NULL && *(method->enabled) != 0) {
61792559Sdes			if (buffer_len(&b) > 0)
61892559Sdes				buffer_append(&b, ",", 1);
61992559Sdes			buffer_append(&b, method->name, strlen(method->name));
62060573Skris		}
62169591Sgreen	}
62292559Sdes	buffer_append(&b, "\0", 1);
62392559Sdes	list = xstrdup(buffer_ptr(&b));
62492559Sdes	buffer_free(&b);
62569591Sgreen	return list;
62660573Skris}
62760573Skris
62892559Sdesstatic Authmethod *
62969591Sgreenauthmethod_lookup(const char *name)
63069591Sgreen{
63169591Sgreen	Authmethod *method = NULL;
63269591Sgreen	if (name != NULL)
63369591Sgreen		for (method = authmethods; method->name != NULL; method++)
63469591Sgreen			if (method->enabled != NULL &&
63569591Sgreen			    *(method->enabled) != 0 &&
63669591Sgreen			    strcmp(name, method->name) == 0)
63769591Sgreen				return method;
63869591Sgreen	debug2("Unrecognized authentication method name: %s", name ? name : "NULL");
63969591Sgreen	return NULL;
64069591Sgreen}
64169591Sgreen
64260573Skris/* return 1 if user allows given key */
64392559Sdesstatic int
64492559Sdesuser_key_allowed2(struct passwd *pw, Key *key, char *file)
64560573Skris{
64692559Sdes	char line[8192];
64760573Skris	int found_key = 0;
64860573Skris	FILE *f;
64976262Sgreen	u_long linenum = 0;
65060573Skris	struct stat st;
65160573Skris	Key *found;
65292559Sdes	char *fp;
65360573Skris
65469591Sgreen	if (pw == NULL)
65569591Sgreen		return 0;
65669591Sgreen
65760573Skris	/* Temporarily use the user's uid. */
65876262Sgreen	temporarily_use_uid(pw);
65960573Skris
66092559Sdes	debug("trying public key file %s", file);
66160573Skris
66260573Skris	/* Fail quietly if file does not exist */
66360573Skris	if (stat(file, &st) < 0) {
66460573Skris		/* Restore the privileged uid. */
66560573Skris		restore_uid();
66660573Skris		return 0;
66760573Skris	}
66860573Skris	/* Open the file containing the authorized keys. */
66960573Skris	f = fopen(file, "r");
67060573Skris	if (!f) {
67160573Skris		/* Restore the privileged uid. */
67260573Skris		restore_uid();
67360573Skris		return 0;
67460573Skris	}
67592559Sdes	if (options.strict_modes &&
67692559Sdes	    secure_filename(f, file, pw, line, sizeof(line)) != 0) {
67792559Sdes		fclose(f);
67892559Sdes		log("Authentication refused: %s", line);
67992559Sdes		restore_uid();
68092559Sdes		return 0;
68160573Skris	}
68292559Sdes
68360573Skris	found_key = 0;
68469591Sgreen	found = key_new(key->type);
68560573Skris
68660573Skris	while (fgets(line, sizeof(line), f)) {
68765674Skris		char *cp, *options = NULL;
68860573Skris		linenum++;
68960573Skris		/* Skip leading whitespace, empty and comment lines. */
69060573Skris		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
69160573Skris			;
69260573Skris		if (!*cp || *cp == '\n' || *cp == '#')
69360573Skris			continue;
69465674Skris
69592559Sdes		if (key_read(found, &cp) != 1) {
69665674Skris			/* no key?  check if there are options for this key */
69765674Skris			int quoted = 0;
69876262Sgreen			debug2("user_key_allowed: check options: '%s'", cp);
69965674Skris			options = cp;
70065674Skris			for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
70165674Skris				if (*cp == '\\' && cp[1] == '"')
70265674Skris					cp++;	/* Skip both */
70365674Skris				else if (*cp == '"')
70465674Skris					quoted = !quoted;
70565674Skris			}
70665674Skris			/* Skip remaining whitespace. */
70765674Skris			for (; *cp == ' ' || *cp == '\t'; cp++)
70865674Skris				;
70992559Sdes			if (key_read(found, &cp) != 1) {
71076262Sgreen				debug2("user_key_allowed: advance: '%s'", cp);
71165674Skris				/* still no key?  advance to next line*/
71265674Skris				continue;
71365674Skris			}
71465674Skris		}
71565674Skris		if (key_equal(found, key) &&
71676262Sgreen		    auth_parse_options(pw, options, file, linenum) == 1) {
71760573Skris			found_key = 1;
71892559Sdes			debug("matching key found: file %s, line %lu",
71960573Skris			    file, linenum);
72092559Sdes			fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX);
72192559Sdes			verbose("Found matching %s key: %s",
72292559Sdes			    key_type(found), fp);
72392559Sdes			xfree(fp);
72460573Skris			break;
72560573Skris		}
72660573Skris	}
72760573Skris	restore_uid();
72860573Skris	fclose(f);
72960573Skris	key_free(found);
73076262Sgreen	if (!found_key)
73176262Sgreen		debug2("key not found");
73260573Skris	return found_key;
73360573Skris}
73469591Sgreen
73592559Sdes/* check whether given key is in .ssh/authorized_keys* */
73692559Sdesstatic int
73792559Sdesuser_key_allowed(struct passwd *pw, Key *key)
73892559Sdes{
73992559Sdes	int success;
74092559Sdes	char *file;
74192559Sdes
74292559Sdes	file = authorized_keys_file(pw);
74392559Sdes	success = user_key_allowed2(pw, key, file);
74492559Sdes	xfree(file);
74592559Sdes	if (success)
74692559Sdes		return success;
74792559Sdes
74892559Sdes	/* try suffix "2" for backward compat, too */
74992559Sdes	file = authorized_keys_file2(pw);
75092559Sdes	success = user_key_allowed2(pw, key, file);
75192559Sdes	xfree(file);
75292559Sdes	return success;
75392559Sdes}
75492559Sdes
75576262Sgreen/* return 1 if given hostkey is allowed */
75692559Sdesstatic int
75776262Sgreenhostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost,
75876262Sgreen    Key *key)
75969591Sgreen{
76076262Sgreen	const char *resolvedname, *ipaddr, *lookup;
76192559Sdes	HostStatus host_status;
76292559Sdes	int len;
76376262Sgreen
76492559Sdes	resolvedname = get_canonical_hostname(options.verify_reverse_mapping);
76576262Sgreen	ipaddr = get_remote_ipaddr();
76676262Sgreen
76776262Sgreen	debug2("userauth_hostbased: chost %s resolvedname %s ipaddr %s",
76876262Sgreen	    chost, resolvedname, ipaddr);
76976262Sgreen
77076262Sgreen	if (options.hostbased_uses_name_from_packet_only) {
77176262Sgreen		if (auth_rhosts2(pw, cuser, chost, chost) == 0)
77276262Sgreen			return 0;
77376262Sgreen		lookup = chost;
77476262Sgreen	} else {
77576262Sgreen		if (((len = strlen(chost)) > 0) && chost[len - 1] == '.') {
77676262Sgreen			debug2("stripping trailing dot from chost %s", chost);
77776262Sgreen			chost[len - 1] = '\0';
77876262Sgreen		}
77976262Sgreen		if (strcasecmp(resolvedname, chost) != 0)
78076262Sgreen			log("userauth_hostbased mismatch: "
78176262Sgreen			    "client sends %s, but we resolve %s to %s",
78276262Sgreen			    chost, ipaddr, resolvedname);
78376262Sgreen		if (auth_rhosts2(pw, cuser, resolvedname, ipaddr) == 0)
78476262Sgreen			return 0;
78576262Sgreen		lookup = resolvedname;
78676262Sgreen	}
78776262Sgreen	debug2("userauth_hostbased: access allowed by auth_rhosts2");
78876262Sgreen
78992559Sdes	host_status = check_key_in_hostfiles(pw, key, lookup,
79092559Sdes	    _PATH_SSH_SYSTEM_HOSTFILE,
79192559Sdes	    options.ignore_user_known_hosts ? NULL : _PATH_SSH_USER_HOSTFILE);
79276262Sgreen
79392559Sdes	/* backward compat if no key has been found. */
79492559Sdes	if (host_status == HOST_NEW)
79592559Sdes		host_status = check_key_in_hostfiles(pw, key, lookup,
79692559Sdes		    _PATH_SSH_SYSTEM_HOSTFILE2,
79792559Sdes		    options.ignore_user_known_hosts ? NULL :
79892559Sdes		    _PATH_SSH_USER_HOSTFILE2);
79976262Sgreen
80076262Sgreen	return (host_status == HOST_OK);
80169591Sgreen}
802