auth2.c revision 113911
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"
26113911SdesRCSID("$OpenBSD: auth2.c,v 1.96 2003/02/06 21:22:43 markus Exp $");
2799053SdesRCSID("$FreeBSD: head/crypto/openssh/auth2.c 113911 2003-04-23 17:13:13Z 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
4160573Skris/* import */
4260573Skrisextern ServerOptions options;
4376262Sgreenextern u_char *session_id2;
4460573Skrisextern int session_id2_len;
4560573Skris
4698684SdesAuthctxt *x_authctxt = NULL;
4769591Sgreen
4898684Sdes/* methods */
4998684Sdes
5098684Sdesextern Authmethod method_none;
5198684Sdesextern Authmethod method_pubkey;
5298684Sdesextern Authmethod method_passwd;
5398684Sdesextern Authmethod method_kbdint;
5498684Sdesextern Authmethod method_hostbased;
5598684Sdes
5698684SdesAuthmethod *authmethods[] = {
5798684Sdes	&method_none,
5898684Sdes	&method_pubkey,
5998684Sdes	&method_passwd,
6098684Sdes	&method_kbdint,
6198684Sdes	&method_hostbased,
6298684Sdes	NULL
6369591Sgreen};
6469591Sgreen
6560573Skris/* protocol */
6660573Skris
6792559Sdesstatic void input_service_request(int, u_int32_t, void *);
6892559Sdesstatic void input_userauth_request(int, u_int32_t, void *);
6960573Skris
7060573Skris/* helper */
7192559Sdesstatic Authmethod *authmethod_lookup(const char *);
7292559Sdesstatic char *authmethods_get(void);
7398684Sdesint user_key_allowed(struct passwd *, Key *);
7498684Sdesint hostbased_key_allowed(struct passwd *, const char *, char *, Key *);
7560573Skris
7660573Skris/*
7769591Sgreen * loop until authctxt->success == TRUE
7860573Skris */
7960573Skris
8098684SdesAuthctxt *
8192559Sdesdo_authentication2(void)
8260573Skris{
8376262Sgreen	Authctxt *authctxt = authctxt_new();
8476262Sgreen
8569591Sgreen	x_authctxt = authctxt;		/*XXX*/
8669591Sgreen
8792559Sdes	/* challenge-response is implemented via keyboard interactive */
8892559Sdes	if (options.challenge_response_authentication)
8976262Sgreen		options.kbd_interactive_authentication = 1;
9098941Sdes	if (options.pam_authentication_via_kbd_int)
9198941Sdes		options.kbd_interactive_authentication = 1;
9298941Sdes	if (use_privsep)
9398941Sdes		options.pam_authentication_via_kbd_int = 0;
9476262Sgreen
9592559Sdes	dispatch_init(&dispatch_protocol_error);
9660573Skris	dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request);
9769591Sgreen	dispatch_run(DISPATCH_BLOCK, &authctxt->success, authctxt);
9898684Sdes
9998684Sdes	return (authctxt);
10060573Skris}
10160573Skris
10292559Sdesstatic void
10392559Sdesinput_service_request(int type, u_int32_t seq, void *ctxt)
10460573Skris{
10569591Sgreen	Authctxt *authctxt = ctxt;
10676262Sgreen	u_int len;
107106130Sdes	int acceptit = 0;
10860573Skris	char *service = packet_get_string(&len);
10992559Sdes	packet_check_eom();
11060573Skris
11169591Sgreen	if (authctxt == NULL)
11269591Sgreen		fatal("input_service_request: no authctxt");
11369591Sgreen
11460573Skris	if (strcmp(service, "ssh-userauth") == 0) {
11569591Sgreen		if (!authctxt->success) {
116106130Sdes			acceptit = 1;
11760573Skris			/* now we can handle user-auth requests */
11860573Skris			dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request);
11960573Skris		}
12060573Skris	}
12160573Skris	/* XXX all other service requests are denied */
12260573Skris
123106130Sdes	if (acceptit) {
12460573Skris		packet_start(SSH2_MSG_SERVICE_ACCEPT);
12560573Skris		packet_put_cstring(service);
12660573Skris		packet_send();
12760573Skris		packet_write_wait();
12860573Skris	} else {
12960573Skris		debug("bad service request %s", service);
13060573Skris		packet_disconnect("bad service request %s", service);
13160573Skris	}
13260573Skris	xfree(service);
13360573Skris}
13460573Skris
13592559Sdesstatic void
13692559Sdesinput_userauth_request(int type, u_int32_t seq, void *ctxt)
13760573Skris{
13869591Sgreen	Authctxt *authctxt = ctxt;
13969591Sgreen	Authmethod *m = NULL;
14076262Sgreen	char *user, *service, *method, *style = NULL;
14160573Skris	int authenticated = 0;
14299053Sdes#ifdef HAVE_LOGIN_CAP
14399053Sdes	login_cap_t *lc;
14499053Sdes	const char *from_host, *from_ip;
14560573Skris
14699053Sdes        from_host = get_canonical_hostname(options.verify_reverse_mapping);
14799053Sdes        from_ip = get_remote_ipaddr();
14899053Sdes#endif
14999053Sdes
15069591Sgreen	if (authctxt == NULL)
15169591Sgreen		fatal("input_userauth_request: no authctxt");
15260573Skris
15369591Sgreen	user = packet_get_string(NULL);
15469591Sgreen	service = packet_get_string(NULL);
15569591Sgreen	method = packet_get_string(NULL);
15660573Skris	debug("userauth-request for user %s service %s method %s", user, service, method);
15776262Sgreen	debug("attempt %d failures %d", authctxt->attempt, authctxt->failures);
15860573Skris
15976262Sgreen	if ((style = strchr(user, ':')) != NULL)
16076262Sgreen		*style++ = 0;
16176262Sgreen
16276262Sgreen	if (authctxt->attempt++ == 0) {
16369591Sgreen		/* setup auth context */
16498684Sdes		authctxt->pw = PRIVSEP(getpwnamallow(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#ifdef USE_PAM
16998941Sdes			PRIVSEP(start_pam(authctxt->pw->pw_name));
17069591Sgreen#endif
17169591Sgreen		} else {
17269591Sgreen			log("input_userauth_request: illegal user %s", user);
17398941Sdes#ifdef USE_PAM
17498941Sdes			PRIVSEP(start_pam("NOUSER"));
17598941Sdes#endif
17660573Skris		}
17798684Sdes		setproctitle("%s%s", authctxt->pw ? user : "unknown",
17898684Sdes		    use_privsep ? " [net]" : "");
17969591Sgreen		authctxt->user = xstrdup(user);
18069591Sgreen		authctxt->service = xstrdup(service);
18192559Sdes		authctxt->style = style ? xstrdup(style) : NULL;
18298684Sdes		if (use_privsep)
18398684Sdes			mm_inform_authserv(service, style);
18492559Sdes	} else if (strcmp(user, authctxt->user) != 0 ||
18592559Sdes	    strcmp(service, authctxt->service) != 0) {
18692559Sdes		packet_disconnect("Change of username or service not allowed: "
18792559Sdes		    "(%s,%s) -> (%s,%s)",
18892559Sdes		    authctxt->user, authctxt->service, user, service);
18960573Skris	}
19099053Sdes
19199053Sdes#ifdef HAVE_LOGIN_CAP
19299053Sdes        if (authctxt->pw != NULL) {
19399053Sdes                lc = login_getpwclass(authctxt->pw);
19499053Sdes                if (lc == NULL)
19599053Sdes                        lc = login_getclassbyname(NULL, authctxt->pw);
19699053Sdes                if (!auth_hostok(lc, from_host, from_ip)) {
19799053Sdes                        log("Denied connection for %.200s from %.200s [%.200s].",
19899053Sdes                            authctxt->pw->pw_name, from_host, from_ip);
19999053Sdes                        packet_disconnect("Sorry, you are not allowed to connect.");
20099053Sdes                }
20199053Sdes                if (!auth_timeok(lc, time(NULL))) {
20299053Sdes                        log("LOGIN %.200s REFUSED (TIME) FROM %.200s",
20399053Sdes                            authctxt->pw->pw_name, from_host);
20499053Sdes                        packet_disconnect("Logins not available right now.");
20599053Sdes                }
20699053Sdes                login_close(lc);
20799053Sdes                lc = NULL;
20899053Sdes        }
20999053Sdes#endif  /* HAVE_LOGIN_CAP */
21099053Sdes
21176262Sgreen	/* reset state */
21292559Sdes	auth2_challenge_stop(authctxt);
21376262Sgreen	authctxt->postponed = 0;
21468704Sgreen
21576262Sgreen	/* try to authenticate user */
21669591Sgreen	m = authmethod_lookup(method);
21769591Sgreen	if (m != NULL) {
21869591Sgreen		debug2("input_userauth_request: try method %s", method);
21969591Sgreen		authenticated =	m->userauth(authctxt);
22069591Sgreen	}
22176262Sgreen	userauth_finish(authctxt, authenticated, method);
22269591Sgreen
22369591Sgreen	xfree(service);
22469591Sgreen	xfree(user);
22569591Sgreen	xfree(method);
22669591Sgreen}
22769591Sgreen
22869591Sgreenvoid
22976262Sgreenuserauth_finish(Authctxt *authctxt, int authenticated, char *method)
23069591Sgreen{
23192559Sdes	char *methods;
23292559Sdes
23376262Sgreen	if (!authctxt->valid && authenticated)
23476262Sgreen		fatal("INTERNAL ERROR: authenticated invalid user %s",
23576262Sgreen		    authctxt->user);
23669591Sgreen
23776262Sgreen	/* Special handling for root */
238113911Sdes	if (authenticated && authctxt->pw->pw_uid == 0 &&
23976262Sgreen	    !auth_root_allowed(method))
24076262Sgreen		authenticated = 0;
24160573Skris
24298941Sdes#ifdef USE_PAM
24398941Sdes	if (!use_privsep && authenticated && authctxt->user &&
24498941Sdes	    !do_pam_account(authctxt->user, NULL))
24598941Sdes		authenticated = 0;
24698941Sdes#endif /* USE_PAM */
24798941Sdes
248106130Sdes#ifdef _UNICOS
249106130Sdes	if (authenticated && cray_access_denied(authctxt->user)) {
250106130Sdes		authenticated = 0;
251106130Sdes		fatal("Access denied for user %s.",authctxt->user);
252106130Sdes	}
253106130Sdes#endif /* _UNICOS */
254106130Sdes
25576262Sgreen	/* Log before sending the reply */
25676262Sgreen	auth_log(authctxt, authenticated, method, " ssh2");
25769591Sgreen
25892559Sdes	if (authctxt->postponed)
25992559Sdes		return;
26092559Sdes
26192559Sdes	/* XXX todo: check if multiple auth methods are needed */
26292559Sdes	if (authenticated == 1) {
26392559Sdes		/* turn off userauth */
26492559Sdes		dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore);
26592559Sdes		packet_start(SSH2_MSG_USERAUTH_SUCCESS);
26692559Sdes		packet_send();
26792559Sdes		packet_write_wait();
26892559Sdes		/* now we can break out */
26992559Sdes		authctxt->success = 1;
27092559Sdes	} else {
27198941Sdes		if (authctxt->failures++ > AUTH_FAIL_MAX) {
27292559Sdes			packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
27398941Sdes		}
274106130Sdes#ifdef _UNICOS
275106130Sdes		if (strcmp(method, "password") == 0)
276106130Sdes			cray_login_failure(authctxt->user, IA_UDBERR);
277106130Sdes#endif /* _UNICOS */
27892559Sdes		methods = authmethods_get();
27992559Sdes		packet_start(SSH2_MSG_USERAUTH_FAILURE);
28092559Sdes		packet_put_cstring(methods);
28192559Sdes		packet_put_char(0);	/* XXX partial success, unused */
28292559Sdes		packet_send();
28392559Sdes		packet_write_wait();
28492559Sdes		xfree(methods);
28592559Sdes	}
28676262Sgreen}
28769591Sgreen
28869591Sgreen/* get current user */
28960573Skris
29060573Skrisstruct passwd*
29160573Skrisauth_get_user(void)
29260573Skris{
29369591Sgreen	return (x_authctxt != NULL && x_authctxt->valid) ? x_authctxt->pw : NULL;
29460573Skris}
29560573Skris
29669591Sgreen#define	DELIM	","
29769591Sgreen
29892559Sdesstatic char *
29969591Sgreenauthmethods_get(void)
30060573Skris{
30192559Sdes	Buffer b;
30269591Sgreen	char *list;
30398684Sdes	int i;
30460573Skris
30592559Sdes	buffer_init(&b);
30698684Sdes	for (i = 0; authmethods[i] != NULL; i++) {
30798684Sdes		if (strcmp(authmethods[i]->name, "none") == 0)
30869591Sgreen			continue;
30998684Sdes		if (authmethods[i]->enabled != NULL &&
31098684Sdes		    *(authmethods[i]->enabled) != 0) {
31192559Sdes			if (buffer_len(&b) > 0)
31292559Sdes				buffer_append(&b, ",", 1);
31398684Sdes			buffer_append(&b, authmethods[i]->name,
31498684Sdes			    strlen(authmethods[i]->name));
31560573Skris		}
31669591Sgreen	}
31792559Sdes	buffer_append(&b, "\0", 1);
31892559Sdes	list = xstrdup(buffer_ptr(&b));
31992559Sdes	buffer_free(&b);
32069591Sgreen	return list;
32160573Skris}
32260573Skris
32392559Sdesstatic Authmethod *
32469591Sgreenauthmethod_lookup(const char *name)
32569591Sgreen{
32698684Sdes	int i;
32798684Sdes
32869591Sgreen	if (name != NULL)
32998684Sdes		for (i = 0; authmethods[i] != NULL; i++)
33098684Sdes			if (authmethods[i]->enabled != NULL &&
33198684Sdes			    *(authmethods[i]->enabled) != 0 &&
33298684Sdes			    strcmp(name, authmethods[i]->name) == 0)
33398684Sdes				return authmethods[i];
33498684Sdes	debug2("Unrecognized authentication method name: %s",
33598684Sdes	    name ? name : "NULL");
33669591Sgreen	return NULL;
33769591Sgreen}
338