auth2.c revision 157019
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"
26137019SdesRCSID("$OpenBSD: auth2.c,v 1.107 2004/07/28 09:40:29 markus Exp $");
2799053SdesRCSID("$FreeBSD: head/crypto/openssh/auth2.c 157019 2006-03-22 20:41:37Z des $");
2860573Skris
29107860Sdes#include "canohost.h"
3076262Sgreen#include "ssh2.h"
3160573Skris#include "xmalloc.h"
3260573Skris#include "packet.h"
3376262Sgreen#include "log.h"
3460573Skris#include "servconf.h"
3560573Skris#include "compat.h"
3660573Skris#include "auth.h"
3760573Skris#include "dispatch.h"
3876262Sgreen#include "pathnames.h"
3998684Sdes#include "monitor_wrap.h"
40147005Sdes#include "buffer.h"
4160573Skris
42124211Sdes#ifdef GSSAPI
43124211Sdes#include "ssh-gss.h"
44124211Sdes#endif
45124211Sdes
4660573Skris/* import */
4760573Skrisextern ServerOptions options;
4876262Sgreenextern u_char *session_id2;
49124211Sdesextern u_int session_id2_len;
50147005Sdesextern Buffer loginmsg;
5160573Skris
5298684Sdes/* methods */
5398684Sdes
5498684Sdesextern Authmethod method_none;
5598684Sdesextern Authmethod method_pubkey;
5698684Sdesextern Authmethod method_passwd;
5798684Sdesextern Authmethod method_kbdint;
5898684Sdesextern Authmethod method_hostbased;
59124211Sdes#ifdef GSSAPI
60124211Sdesextern Authmethod method_gssapi;
61124211Sdes#endif
6298684Sdes
6398684SdesAuthmethod *authmethods[] = {
6498684Sdes	&method_none,
6598684Sdes	&method_pubkey,
66124211Sdes#ifdef GSSAPI
67124211Sdes	&method_gssapi,
68124211Sdes#endif
6998684Sdes	&method_passwd,
7098684Sdes	&method_kbdint,
7198684Sdes	&method_hostbased,
7298684Sdes	NULL
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);
8398684Sdesint user_key_allowed(struct passwd *, Key *);
8460573Skris
8560573Skris/*
8669591Sgreen * loop until authctxt->success == TRUE
8760573Skris */
8860573Skris
89126277Sdesvoid
90126277Sdesdo_authentication2(Authctxt *authctxt)
9160573Skris{
9292559Sdes	/* challenge-response is implemented via keyboard interactive */
9392559Sdes	if (options.challenge_response_authentication)
9476262Sgreen		options.kbd_interactive_authentication = 1;
9576262Sgreen
9692559Sdes	dispatch_init(&dispatch_protocol_error);
9760573Skris	dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request);
9869591Sgreen	dispatch_run(DISPATCH_BLOCK, &authctxt->success, authctxt);
9960573Skris}
10060573Skris
10192559Sdesstatic void
10292559Sdesinput_service_request(int type, u_int32_t seq, void *ctxt)
10360573Skris{
10469591Sgreen	Authctxt *authctxt = ctxt;
10576262Sgreen	u_int len;
106106130Sdes	int acceptit = 0;
10760573Skris	char *service = packet_get_string(&len);
10892559Sdes	packet_check_eom();
10960573Skris
11069591Sgreen	if (authctxt == NULL)
11169591Sgreen		fatal("input_service_request: no authctxt");
11269591Sgreen
11360573Skris	if (strcmp(service, "ssh-userauth") == 0) {
11469591Sgreen		if (!authctxt->success) {
115106130Sdes			acceptit = 1;
11660573Skris			/* now we can handle user-auth requests */
11760573Skris			dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request);
11860573Skris		}
11960573Skris	}
12060573Skris	/* XXX all other service requests are denied */
12160573Skris
122106130Sdes	if (acceptit) {
12360573Skris		packet_start(SSH2_MSG_SERVICE_ACCEPT);
12460573Skris		packet_put_cstring(service);
12560573Skris		packet_send();
12660573Skris		packet_write_wait();
12760573Skris	} else {
12860573Skris		debug("bad service request %s", service);
12960573Skris		packet_disconnect("bad service request %s", service);
13060573Skris	}
13160573Skris	xfree(service);
13260573Skris}
13360573Skris
13492559Sdesstatic void
13592559Sdesinput_userauth_request(int type, u_int32_t seq, void *ctxt)
13660573Skris{
13769591Sgreen	Authctxt *authctxt = ctxt;
13869591Sgreen	Authmethod *m = NULL;
13976262Sgreen	char *user, *service, *method, *style = NULL;
14060573Skris	int authenticated = 0;
14199053Sdes#ifdef HAVE_LOGIN_CAP
14299053Sdes	login_cap_t *lc;
14399053Sdes	const char *from_host, *from_ip;
14460573Skris
145124211Sdes        from_host = get_canonical_hostname(options.use_dns);
14699053Sdes        from_ip = get_remote_ipaddr();
14799053Sdes#endif
14899053Sdes
14969591Sgreen	if (authctxt == NULL)
15069591Sgreen		fatal("input_userauth_request: no authctxt");
15160573Skris
15269591Sgreen	user = packet_get_string(NULL);
15369591Sgreen	service = packet_get_string(NULL);
15469591Sgreen	method = packet_get_string(NULL);
15560573Skris	debug("userauth-request for user %s service %s method %s", user, service, method);
15676262Sgreen	debug("attempt %d failures %d", authctxt->attempt, authctxt->failures);
15760573Skris
15876262Sgreen	if ((style = strchr(user, ':')) != NULL)
15976262Sgreen		*style++ = 0;
16076262Sgreen
16176262Sgreen	if (authctxt->attempt++ == 0) {
16269591Sgreen		/* setup auth context */
16398684Sdes		authctxt->pw = PRIVSEP(getpwnamallow(user));
164128460Sdes		authctxt->user = xstrdup(user);
16598684Sdes		if (authctxt->pw && strcmp(service, "ssh-connection")==0) {
16669591Sgreen			authctxt->valid = 1;
16769591Sgreen			debug2("input_userauth_request: setting up authctxt for %s", user);
16869591Sgreen		} else {
169137019Sdes			logit("input_userauth_request: invalid user %s", user);
170124211Sdes			authctxt->pw = fakepw();
171147005Sdes#ifdef SSH_AUDIT_EVENTS
172147005Sdes			PRIVSEP(audit_event(SSH_INVALID_USER));
173147005Sdes#endif
17460573Skris		}
175157019Sdes#ifdef USE_PAM
176157019Sdes		if (options.use_pam)
177157019Sdes			PRIVSEP(start_pam(authctxt));
178157019Sdes#endif
179137019Sdes		setproctitle("%s%s", authctxt->valid ? user : "unknown",
18098684Sdes		    use_privsep ? " [net]" : "");
18169591Sgreen		authctxt->service = xstrdup(service);
18292559Sdes		authctxt->style = style ? xstrdup(style) : NULL;
18398684Sdes		if (use_privsep)
18498684Sdes			mm_inform_authserv(service, style);
18592559Sdes	} else if (strcmp(user, authctxt->user) != 0 ||
18692559Sdes	    strcmp(service, authctxt->service) != 0) {
18792559Sdes		packet_disconnect("Change of username or service not allowed: "
18892559Sdes		    "(%s,%s) -> (%s,%s)",
18992559Sdes		    authctxt->user, authctxt->service, user, service);
19060573Skris	}
19199053Sdes
19299053Sdes#ifdef HAVE_LOGIN_CAP
19399053Sdes        if (authctxt->pw != NULL) {
19499053Sdes                lc = login_getpwclass(authctxt->pw);
19599053Sdes                if (lc == NULL)
19699053Sdes                        lc = login_getclassbyname(NULL, authctxt->pw);
19799053Sdes                if (!auth_hostok(lc, from_host, from_ip)) {
198124211Sdes                        logit("Denied connection for %.200s from %.200s [%.200s].",
19999053Sdes                            authctxt->pw->pw_name, from_host, from_ip);
20099053Sdes                        packet_disconnect("Sorry, you are not allowed to connect.");
20199053Sdes                }
20299053Sdes                if (!auth_timeok(lc, time(NULL))) {
203124211Sdes                        logit("LOGIN %.200s REFUSED (TIME) FROM %.200s",
20499053Sdes                            authctxt->pw->pw_name, from_host);
20599053Sdes                        packet_disconnect("Logins not available right now.");
20699053Sdes                }
20799053Sdes                login_close(lc);
20899053Sdes                lc = NULL;
20999053Sdes        }
21099053Sdes#endif  /* HAVE_LOGIN_CAP */
21199053Sdes
21276262Sgreen	/* reset state */
21392559Sdes	auth2_challenge_stop(authctxt);
214124211Sdes
215124211Sdes#ifdef GSSAPI
216124211Sdes	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
217124211Sdes	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
218124211Sdes#endif
219124211Sdes
22076262Sgreen	authctxt->postponed = 0;
22168704Sgreen
22276262Sgreen	/* try to authenticate user */
22369591Sgreen	m = authmethod_lookup(method);
22469591Sgreen	if (m != NULL) {
22569591Sgreen		debug2("input_userauth_request: try method %s", method);
22669591Sgreen		authenticated =	m->userauth(authctxt);
22769591Sgreen	}
22876262Sgreen	userauth_finish(authctxt, authenticated, method);
22969591Sgreen
23069591Sgreen	xfree(service);
23169591Sgreen	xfree(user);
23269591Sgreen	xfree(method);
23369591Sgreen}
23469591Sgreen
23569591Sgreenvoid
23676262Sgreenuserauth_finish(Authctxt *authctxt, int authenticated, char *method)
23769591Sgreen{
23892559Sdes	char *methods;
23992559Sdes
24076262Sgreen	if (!authctxt->valid && authenticated)
24176262Sgreen		fatal("INTERNAL ERROR: authenticated invalid user %s",
24276262Sgreen		    authctxt->user);
24369591Sgreen
24476262Sgreen	/* Special handling for root */
245113911Sdes	if (authenticated && authctxt->pw->pw_uid == 0 &&
246147005Sdes	    !auth_root_allowed(method)) {
24776262Sgreen		authenticated = 0;
248147005Sdes#ifdef SSH_AUDIT_EVENTS
249147005Sdes		PRIVSEP(audit_event(SSH_LOGIN_ROOT_DENIED));
250147005Sdes#endif
251147005Sdes	}
25260573Skris
25398941Sdes#ifdef USE_PAM
254147005Sdes	if (options.use_pam && authenticated) {
255147005Sdes		if (!PRIVSEP(do_pam_account())) {
256147005Sdes			/* if PAM returned a message, send it to the user */
257147005Sdes			if (buffer_len(&loginmsg) > 0) {
258147005Sdes				buffer_append(&loginmsg, "\0", 1);
259147005Sdes				userauth_send_banner(buffer_ptr(&loginmsg));
260147005Sdes				packet_write_wait();
261147005Sdes			}
262147005Sdes			fatal("Access denied for user %s by PAM account "
263149753Sdes			    "configuration", authctxt->user);
264147005Sdes		}
265147005Sdes	}
266124211Sdes#endif
26798941Sdes
268106130Sdes#ifdef _UNICOS
269106130Sdes	if (authenticated && cray_access_denied(authctxt->user)) {
270106130Sdes		authenticated = 0;
271106130Sdes		fatal("Access denied for user %s.",authctxt->user);
272106130Sdes	}
273106130Sdes#endif /* _UNICOS */
274106130Sdes
27576262Sgreen	/* Log before sending the reply */
27676262Sgreen	auth_log(authctxt, authenticated, method, " ssh2");
27769591Sgreen
27892559Sdes	if (authctxt->postponed)
27992559Sdes		return;
28092559Sdes
28192559Sdes	/* XXX todo: check if multiple auth methods are needed */
28292559Sdes	if (authenticated == 1) {
28392559Sdes		/* turn off userauth */
28492559Sdes		dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore);
28592559Sdes		packet_start(SSH2_MSG_USERAUTH_SUCCESS);
28692559Sdes		packet_send();
28792559Sdes		packet_write_wait();
28892559Sdes		/* now we can break out */
28992559Sdes		authctxt->success = 1;
29092559Sdes	} else {
291147005Sdes		if (authctxt->failures++ > options.max_authtries) {
292147005Sdes#ifdef SSH_AUDIT_EVENTS
293147005Sdes			PRIVSEP(audit_event(SSH_LOGIN_EXCEED_MAXTRIES));
294147005Sdes#endif
29592559Sdes			packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
296147005Sdes		}
29792559Sdes		methods = authmethods_get();
29892559Sdes		packet_start(SSH2_MSG_USERAUTH_FAILURE);
29992559Sdes		packet_put_cstring(methods);
30092559Sdes		packet_put_char(0);	/* XXX partial success, unused */
30192559Sdes		packet_send();
30292559Sdes		packet_write_wait();
30392559Sdes		xfree(methods);
30492559Sdes	}
30576262Sgreen}
30669591Sgreen
30769591Sgreen#define	DELIM	","
30869591Sgreen
30992559Sdesstatic char *
31069591Sgreenauthmethods_get(void)
31160573Skris{
31292559Sdes	Buffer b;
31369591Sgreen	char *list;
31498684Sdes	int i;
31560573Skris
31692559Sdes	buffer_init(&b);
31798684Sdes	for (i = 0; authmethods[i] != NULL; i++) {
31898684Sdes		if (strcmp(authmethods[i]->name, "none") == 0)
31969591Sgreen			continue;
32098684Sdes		if (authmethods[i]->enabled != NULL &&
32198684Sdes		    *(authmethods[i]->enabled) != 0) {
32292559Sdes			if (buffer_len(&b) > 0)
32392559Sdes				buffer_append(&b, ",", 1);
32498684Sdes			buffer_append(&b, authmethods[i]->name,
32598684Sdes			    strlen(authmethods[i]->name));
32660573Skris		}
32769591Sgreen	}
32892559Sdes	buffer_append(&b, "\0", 1);
32992559Sdes	list = xstrdup(buffer_ptr(&b));
33092559Sdes	buffer_free(&b);
33169591Sgreen	return list;
33260573Skris}
33360573Skris
33492559Sdesstatic Authmethod *
33569591Sgreenauthmethod_lookup(const char *name)
33669591Sgreen{
33798684Sdes	int i;
33898684Sdes
33969591Sgreen	if (name != NULL)
34098684Sdes		for (i = 0; authmethods[i] != NULL; i++)
34198684Sdes			if (authmethods[i]->enabled != NULL &&
34298684Sdes			    *(authmethods[i]->enabled) != 0 &&
34398684Sdes			    strcmp(name, authmethods[i]->name) == 0)
34498684Sdes				return authmethods[i];
34598684Sdes	debug2("Unrecognized authentication method name: %s",
34698684Sdes	    name ? name : "NULL");
34769591Sgreen	return NULL;
34869591Sgreen}
349