auth2.c revision 124211
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"
26124211SdesRCSID("$OpenBSD: auth2.c,v 1.102 2003/08/26 09:58:43 markus Exp $");
2799053SdesRCSID("$FreeBSD: head/crypto/openssh/auth2.c 124211 2004-01-07 11:16:27Z 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"
4060573Skris
41124211Sdes#ifdef GSSAPI
42124211Sdes#include "ssh-gss.h"
43124211Sdes#endif
44124211Sdes
4560573Skris/* import */
4660573Skrisextern ServerOptions options;
4776262Sgreenextern u_char *session_id2;
48124211Sdesextern u_int session_id2_len;
4960573Skris
5098684SdesAuthctxt *x_authctxt = NULL;
5169591Sgreen
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 *);
8498684Sdesint hostbased_key_allowed(struct passwd *, const char *, char *, Key *);
8560573Skris
8660573Skris/*
8769591Sgreen * loop until authctxt->success == TRUE
8860573Skris */
8960573Skris
9098684SdesAuthctxt *
9192559Sdesdo_authentication2(void)
9260573Skris{
9376262Sgreen	Authctxt *authctxt = authctxt_new();
9476262Sgreen
9569591Sgreen	x_authctxt = authctxt;		/*XXX*/
9669591Sgreen
9792559Sdes	/* challenge-response is implemented via keyboard interactive */
9892559Sdes	if (options.challenge_response_authentication)
9976262Sgreen		options.kbd_interactive_authentication = 1;
10076262Sgreen
10192559Sdes	dispatch_init(&dispatch_protocol_error);
10260573Skris	dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request);
10369591Sgreen	dispatch_run(DISPATCH_BLOCK, &authctxt->success, authctxt);
10498684Sdes
10598684Sdes	return (authctxt);
10660573Skris}
10760573Skris
10892559Sdesstatic void
10992559Sdesinput_service_request(int type, u_int32_t seq, void *ctxt)
11060573Skris{
11169591Sgreen	Authctxt *authctxt = ctxt;
11276262Sgreen	u_int len;
113106130Sdes	int acceptit = 0;
11460573Skris	char *service = packet_get_string(&len);
11592559Sdes	packet_check_eom();
11660573Skris
11769591Sgreen	if (authctxt == NULL)
11869591Sgreen		fatal("input_service_request: no authctxt");
11969591Sgreen
12060573Skris	if (strcmp(service, "ssh-userauth") == 0) {
12169591Sgreen		if (!authctxt->success) {
122106130Sdes			acceptit = 1;
12360573Skris			/* now we can handle user-auth requests */
12460573Skris			dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request);
12560573Skris		}
12660573Skris	}
12760573Skris	/* XXX all other service requests are denied */
12860573Skris
129106130Sdes	if (acceptit) {
13060573Skris		packet_start(SSH2_MSG_SERVICE_ACCEPT);
13160573Skris		packet_put_cstring(service);
13260573Skris		packet_send();
13360573Skris		packet_write_wait();
13460573Skris	} else {
13560573Skris		debug("bad service request %s", service);
13660573Skris		packet_disconnect("bad service request %s", service);
13760573Skris	}
13860573Skris	xfree(service);
13960573Skris}
14060573Skris
14192559Sdesstatic void
14292559Sdesinput_userauth_request(int type, u_int32_t seq, void *ctxt)
14360573Skris{
14469591Sgreen	Authctxt *authctxt = ctxt;
14569591Sgreen	Authmethod *m = NULL;
14676262Sgreen	char *user, *service, *method, *style = NULL;
14760573Skris	int authenticated = 0;
14899053Sdes#ifdef HAVE_LOGIN_CAP
14999053Sdes	login_cap_t *lc;
15099053Sdes	const char *from_host, *from_ip;
15160573Skris
152124211Sdes        from_host = get_canonical_hostname(options.use_dns);
15399053Sdes        from_ip = get_remote_ipaddr();
15499053Sdes#endif
15599053Sdes
15669591Sgreen	if (authctxt == NULL)
15769591Sgreen		fatal("input_userauth_request: no authctxt");
15860573Skris
15969591Sgreen	user = packet_get_string(NULL);
16069591Sgreen	service = packet_get_string(NULL);
16169591Sgreen	method = packet_get_string(NULL);
16260573Skris	debug("userauth-request for user %s service %s method %s", user, service, method);
16376262Sgreen	debug("attempt %d failures %d", authctxt->attempt, authctxt->failures);
16460573Skris
16576262Sgreen	if ((style = strchr(user, ':')) != NULL)
16676262Sgreen		*style++ = 0;
16776262Sgreen
16876262Sgreen	if (authctxt->attempt++ == 0) {
16969591Sgreen		/* setup auth context */
17098684Sdes		authctxt->pw = PRIVSEP(getpwnamallow(user));
17198684Sdes		if (authctxt->pw && strcmp(service, "ssh-connection")==0) {
17269591Sgreen			authctxt->valid = 1;
17369591Sgreen			debug2("input_userauth_request: setting up authctxt for %s", user);
17469591Sgreen#ifdef USE_PAM
175124211Sdes			if (options.use_pam)
176124211Sdes				PRIVSEP(start_pam(authctxt->pw->pw_name));
17769591Sgreen#endif
17869591Sgreen		} else {
179124211Sdes			logit("input_userauth_request: illegal user %s", user);
180124211Sdes			authctxt->pw = fakepw();
18198941Sdes#ifdef USE_PAM
182124211Sdes			if (options.use_pam)
183124211Sdes				PRIVSEP(start_pam(user));
18498941Sdes#endif
18560573Skris		}
18698684Sdes		setproctitle("%s%s", authctxt->pw ? user : "unknown",
18798684Sdes		    use_privsep ? " [net]" : "");
18869591Sgreen		authctxt->user = xstrdup(user);
18969591Sgreen		authctxt->service = xstrdup(service);
19092559Sdes		authctxt->style = style ? xstrdup(style) : NULL;
19198684Sdes		if (use_privsep)
19298684Sdes			mm_inform_authserv(service, style);
19392559Sdes	} else if (strcmp(user, authctxt->user) != 0 ||
19492559Sdes	    strcmp(service, authctxt->service) != 0) {
19592559Sdes		packet_disconnect("Change of username or service not allowed: "
19692559Sdes		    "(%s,%s) -> (%s,%s)",
19792559Sdes		    authctxt->user, authctxt->service, user, service);
19860573Skris	}
19999053Sdes
20099053Sdes#ifdef HAVE_LOGIN_CAP
20199053Sdes        if (authctxt->pw != NULL) {
20299053Sdes                lc = login_getpwclass(authctxt->pw);
20399053Sdes                if (lc == NULL)
20499053Sdes                        lc = login_getclassbyname(NULL, authctxt->pw);
20599053Sdes                if (!auth_hostok(lc, from_host, from_ip)) {
206124211Sdes                        logit("Denied connection for %.200s from %.200s [%.200s].",
20799053Sdes                            authctxt->pw->pw_name, from_host, from_ip);
20899053Sdes                        packet_disconnect("Sorry, you are not allowed to connect.");
20999053Sdes                }
21099053Sdes                if (!auth_timeok(lc, time(NULL))) {
211124211Sdes                        logit("LOGIN %.200s REFUSED (TIME) FROM %.200s",
21299053Sdes                            authctxt->pw->pw_name, from_host);
21399053Sdes                        packet_disconnect("Logins not available right now.");
21499053Sdes                }
21599053Sdes                login_close(lc);
21699053Sdes                lc = NULL;
21799053Sdes        }
21899053Sdes#endif  /* HAVE_LOGIN_CAP */
21999053Sdes
22076262Sgreen	/* reset state */
22192559Sdes	auth2_challenge_stop(authctxt);
222124211Sdes
223124211Sdes#ifdef GSSAPI
224124211Sdes	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
225124211Sdes	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
226124211Sdes#endif
227124211Sdes
22876262Sgreen	authctxt->postponed = 0;
22968704Sgreen
23076262Sgreen	/* try to authenticate user */
23169591Sgreen	m = authmethod_lookup(method);
23269591Sgreen	if (m != NULL) {
23369591Sgreen		debug2("input_userauth_request: try method %s", method);
23469591Sgreen		authenticated =	m->userauth(authctxt);
23569591Sgreen	}
23676262Sgreen	userauth_finish(authctxt, authenticated, method);
23769591Sgreen
23869591Sgreen	xfree(service);
23969591Sgreen	xfree(user);
24069591Sgreen	xfree(method);
24169591Sgreen}
24269591Sgreen
24369591Sgreenvoid
24476262Sgreenuserauth_finish(Authctxt *authctxt, int authenticated, char *method)
24569591Sgreen{
24692559Sdes	char *methods;
24792559Sdes
24876262Sgreen	if (!authctxt->valid && authenticated)
24976262Sgreen		fatal("INTERNAL ERROR: authenticated invalid user %s",
25076262Sgreen		    authctxt->user);
25169591Sgreen
25276262Sgreen	/* Special handling for root */
253113911Sdes	if (authenticated && authctxt->pw->pw_uid == 0 &&
25476262Sgreen	    !auth_root_allowed(method))
25576262Sgreen		authenticated = 0;
25660573Skris
25798941Sdes#ifdef USE_PAM
258124211Sdes	if (options.use_pam && authenticated && !PRIVSEP(do_pam_account()))
25998941Sdes		authenticated = 0;
260124211Sdes#endif
26198941Sdes
262106130Sdes#ifdef _UNICOS
263106130Sdes	if (authenticated && cray_access_denied(authctxt->user)) {
264106130Sdes		authenticated = 0;
265106130Sdes		fatal("Access denied for user %s.",authctxt->user);
266106130Sdes	}
267106130Sdes#endif /* _UNICOS */
268106130Sdes
26976262Sgreen	/* Log before sending the reply */
27076262Sgreen	auth_log(authctxt, authenticated, method, " ssh2");
27169591Sgreen
27292559Sdes	if (authctxt->postponed)
27392559Sdes		return;
27492559Sdes
27592559Sdes	/* XXX todo: check if multiple auth methods are needed */
27692559Sdes	if (authenticated == 1) {
27792559Sdes		/* turn off userauth */
27892559Sdes		dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore);
27992559Sdes		packet_start(SSH2_MSG_USERAUTH_SUCCESS);
28092559Sdes		packet_send();
28192559Sdes		packet_write_wait();
28292559Sdes		/* now we can break out */
28392559Sdes		authctxt->success = 1;
28492559Sdes	} else {
285124211Sdes		if (authctxt->failures++ > AUTH_FAIL_MAX)
28692559Sdes			packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
28792559Sdes		methods = authmethods_get();
28892559Sdes		packet_start(SSH2_MSG_USERAUTH_FAILURE);
28992559Sdes		packet_put_cstring(methods);
29092559Sdes		packet_put_char(0);	/* XXX partial success, unused */
29192559Sdes		packet_send();
29292559Sdes		packet_write_wait();
29392559Sdes		xfree(methods);
29492559Sdes	}
29576262Sgreen}
29669591Sgreen
29769591Sgreen/* get current user */
29860573Skris
29960573Skrisstruct passwd*
30060573Skrisauth_get_user(void)
30160573Skris{
30269591Sgreen	return (x_authctxt != NULL && x_authctxt->valid) ? x_authctxt->pw : NULL;
30360573Skris}
30460573Skris
30569591Sgreen#define	DELIM	","
30669591Sgreen
30792559Sdesstatic char *
30869591Sgreenauthmethods_get(void)
30960573Skris{
31092559Sdes	Buffer b;
31169591Sgreen	char *list;
31298684Sdes	int i;
31360573Skris
31492559Sdes	buffer_init(&b);
31598684Sdes	for (i = 0; authmethods[i] != NULL; i++) {
31698684Sdes		if (strcmp(authmethods[i]->name, "none") == 0)
31769591Sgreen			continue;
31898684Sdes		if (authmethods[i]->enabled != NULL &&
31998684Sdes		    *(authmethods[i]->enabled) != 0) {
32092559Sdes			if (buffer_len(&b) > 0)
32192559Sdes				buffer_append(&b, ",", 1);
32298684Sdes			buffer_append(&b, authmethods[i]->name,
32398684Sdes			    strlen(authmethods[i]->name));
32460573Skris		}
32569591Sgreen	}
32692559Sdes	buffer_append(&b, "\0", 1);
32792559Sdes	list = xstrdup(buffer_ptr(&b));
32892559Sdes	buffer_free(&b);
32969591Sgreen	return list;
33060573Skris}
33160573Skris
33292559Sdesstatic Authmethod *
33369591Sgreenauthmethod_lookup(const char *name)
33469591Sgreen{
33598684Sdes	int i;
33698684Sdes
33769591Sgreen	if (name != NULL)
33898684Sdes		for (i = 0; authmethods[i] != NULL; i++)
33998684Sdes			if (authmethods[i]->enabled != NULL &&
34098684Sdes			    *(authmethods[i]->enabled) != 0 &&
34198684Sdes			    strcmp(name, authmethods[i]->name) == 0)
34298684Sdes				return authmethods[i];
34398684Sdes	debug2("Unrecognized authentication method name: %s",
34498684Sdes	    name ? name : "NULL");
34569591Sgreen	return NULL;
34669591Sgreen}
347