auth2.c revision 69591
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"
2669591SgreenRCSID("$OpenBSD: auth2.c,v 1.20 2000/10/14 12:16:56 markus Exp $");
2765674SkrisRCSID("$FreeBSD: head/crypto/openssh/auth2.c 69591 2000-12-05 02:55:12Z green $");
2860573Skris
2960573Skris#include <openssl/dsa.h>
3060573Skris#include <openssl/rsa.h>
3160573Skris#include <openssl/evp.h>
3260573Skris
3360573Skris#include "xmalloc.h"
3460573Skris#include "rsa.h"
3560573Skris#include "ssh.h"
3660573Skris#include "pty.h"
3760573Skris#include "packet.h"
3860573Skris#include "buffer.h"
3960573Skris#include "servconf.h"
4060573Skris#include "compat.h"
4160573Skris#include "channels.h"
4260573Skris#include "bufaux.h"
4360573Skris#include "ssh2.h"
4460573Skris#include "auth.h"
4560573Skris#include "session.h"
4660573Skris#include "dispatch.h"
4760573Skris#include "auth.h"
4860573Skris#include "key.h"
4960573Skris#include "kex.h"
5060573Skris
5160573Skris#include "dsa.h"
5260573Skris#include "uidswap.h"
5365674Skris#include "auth-options.h"
5460573Skris
5568704Sgreen#ifdef HAVE_LOGIN_CAP
5668704Sgreen#include <login_cap.h>
5768704Sgreen#endif /* HAVE_LOGIN_CAP */
5868704Sgreen
5960573Skris/* import */
6060573Skrisextern ServerOptions options;
6160573Skrisextern unsigned char *session_id2;
6260573Skrisextern int session_id2_len;
6360573Skris
6469591Sgreenstatic Authctxt	*x_authctxt = NULL;
6569591Sgreenstatic int one = 1;
6669591Sgreen
6769591Sgreentypedef struct Authmethod Authmethod;
6869591Sgreenstruct Authmethod {
6969591Sgreen	char	*name;
7069591Sgreen	int	(*userauth)(Authctxt *authctxt);
7169591Sgreen	int	*enabled;
7269591Sgreen};
7369591Sgreen
7460573Skris/* protocol */
7560573Skris
7669591Sgreenvoid	input_service_request(int type, int plen, void *ctxt);
7769591Sgreenvoid	input_userauth_request(int type, int plen, void *ctxt);
7869591Sgreenvoid	protocol_error(int type, int plen, void *ctxt);
7960573Skris
8060573Skris
8160573Skris/* helper */
8269591SgreenAuthmethod	*authmethod_lookup(const char *name);
8369591Sgreenstruct passwd	*pwcopy(struct passwd *pw);
8460573Skrisint	user_dsa_key_allowed(struct passwd *pw, Key *key);
8569591Sgreenchar	*authmethods_get(void);
8660573Skris
8769591Sgreen/* auth */
8869591Sgreenint	userauth_none(Authctxt *authctxt);
8969591Sgreenint	userauth_passwd(Authctxt *authctxt);
9069591Sgreenint	userauth_pubkey(Authctxt *authctxt);
9169591Sgreenint	userauth_kbdint(Authctxt *authctxt);
9269591Sgreen
9369591SgreenAuthmethod authmethods[] = {
9469591Sgreen	{"none",
9569591Sgreen		userauth_none,
9669591Sgreen		&one},
9769591Sgreen	{"publickey",
9869591Sgreen		userauth_pubkey,
9969591Sgreen		&options.dsa_authentication},
10069591Sgreen	{"keyboard-interactive",
10169591Sgreen		userauth_kbdint,
10269591Sgreen		&options.kbd_interactive_authentication},
10369591Sgreen	{"password",
10469591Sgreen		userauth_passwd,
10569591Sgreen		&options.password_authentication},
10669591Sgreen	{NULL, NULL, NULL}
10760573Skris};
10860573Skris
10960573Skris/*
11069591Sgreen * loop until authctxt->success == TRUE
11160573Skris */
11260573Skris
11360573Skrisvoid
11460573Skrisdo_authentication2()
11560573Skris{
11669591Sgreen	Authctxt *authctxt = xmalloc(sizeof(*authctxt));
11769591Sgreen	memset(authctxt, 'a', sizeof(*authctxt));
11869591Sgreen	authctxt->valid = 0;
11969591Sgreen	authctxt->attempt = 0;
12069591Sgreen	authctxt->success = 0;
12169591Sgreen	x_authctxt = authctxt;		/*XXX*/
12269591Sgreen
12360573Skris#ifdef KRB4
12469591Sgreen	/* turn off kerberos, not supported by SSH2 */
12560576Skris	options.krb4_authentication = 0;
12660573Skris#endif
12760573Skris	dispatch_init(&protocol_error);
12860573Skris	dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request);
12969591Sgreen	dispatch_run(DISPATCH_BLOCK, &authctxt->success, authctxt);
13060573Skris	do_authenticated2();
13160573Skris}
13260573Skris
13360573Skrisvoid
13469591Sgreenprotocol_error(int type, int plen, void *ctxt)
13560573Skris{
13660573Skris	log("auth: protocol error: type %d plen %d", type, plen);
13760573Skris	packet_start(SSH2_MSG_UNIMPLEMENTED);
13860573Skris	packet_put_int(0);
13960573Skris	packet_send();
14060573Skris	packet_write_wait();
14160573Skris}
14260573Skris
14360573Skrisvoid
14469591Sgreeninput_service_request(int type, int plen, void *ctxt)
14560573Skris{
14669591Sgreen	Authctxt *authctxt = ctxt;
14760573Skris	unsigned int len;
14860573Skris	int accept = 0;
14960573Skris	char *service = packet_get_string(&len);
15060573Skris	packet_done();
15160573Skris
15269591Sgreen	if (authctxt == NULL)
15369591Sgreen		fatal("input_service_request: no authctxt");
15469591Sgreen
15560573Skris	if (strcmp(service, "ssh-userauth") == 0) {
15669591Sgreen		if (!authctxt->success) {
15760573Skris			accept = 1;
15860573Skris			/* now we can handle user-auth requests */
15960573Skris			dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request);
16060573Skris		}
16160573Skris	}
16260573Skris	/* XXX all other service requests are denied */
16360573Skris
16460573Skris	if (accept) {
16560573Skris		packet_start(SSH2_MSG_SERVICE_ACCEPT);
16660573Skris		packet_put_cstring(service);
16760573Skris		packet_send();
16860573Skris		packet_write_wait();
16960573Skris	} else {
17060573Skris		debug("bad service request %s", service);
17160573Skris		packet_disconnect("bad service request %s", service);
17260573Skris	}
17360573Skris	xfree(service);
17460573Skris}
17560573Skris
17660573Skrisvoid
17769591Sgreeninput_userauth_request(int type, int plen, void *ctxt)
17860573Skris{
17969591Sgreen	Authctxt *authctxt = ctxt;
18069591Sgreen	Authmethod *m = NULL;
18160573Skris	int authenticated = 0;
18265674Skris	char *user, *service, *method, *authmsg = NULL;
18368704Sgreen#ifdef HAVE_LOGIN_CAP
18468704Sgreen	login_cap_t *lc;
18568704Sgreen#endif /* HAVE_LOGIN_CAP */
18668704Sgreen#if defined(HAVE_LOGIN_CAP) || defined(LOGIN_ACCESS)
18768704Sgreen	const char *from_host, *from_ip;
18860573Skris
18968704Sgreen	from_host = get_canonical_hostname();
19068704Sgreen	from_ip = get_remote_ipaddr();
19168704Sgreen#endif /* HAVE_LOGIN_CAP || LOGIN_ACCESS */
19268704Sgreen
19369591Sgreen	if (authctxt == NULL)
19469591Sgreen		fatal("input_userauth_request: no authctxt");
19569591Sgreen	if (authctxt->attempt++ >= AUTH_FAIL_MAX)
19660573Skris		packet_disconnect("too many failed userauth_requests");
19760573Skris
19869591Sgreen	user = packet_get_string(NULL);
19969591Sgreen	service = packet_get_string(NULL);
20069591Sgreen	method = packet_get_string(NULL);
20160573Skris	debug("userauth-request for user %s service %s method %s", user, service, method);
20269591Sgreen	debug("attempt #%d", authctxt->attempt);
20360573Skris
20469591Sgreen	if (authctxt->attempt == 1) {
20569591Sgreen		/* setup auth context */
20669591Sgreen		struct passwd *pw = NULL;
20769591Sgreen		setproctitle("%s", user);
20869591Sgreen		pw = getpwnam(user);
20969591Sgreen		if (pw && allowed_user(pw) && strcmp(service, "ssh-connection")==0) {
21069591Sgreen			authctxt->pw = pwcopy(pw);
21169591Sgreen			authctxt->valid = 1;
21269591Sgreen			debug2("input_userauth_request: setting up authctxt for %s", user);
21369591Sgreen#ifdef USE_PAM
21469591Sgreen			start_pam(pw);
21569591Sgreen#endif
21669591Sgreen		} else {
21769591Sgreen			log("input_userauth_request: illegal user %s", user);
21860573Skris		}
21969591Sgreen		authctxt->user = xstrdup(user);
22069591Sgreen		authctxt->service = xstrdup(service);
22169591Sgreen	} else if (authctxt->valid) {
22269591Sgreen		if (strcmp(user, authctxt->user) != 0 ||
22369591Sgreen		    strcmp(service, authctxt->service) != 0) {
22469591Sgreen			log("input_userauth_request: missmatch: (%s,%s)!=(%s,%s)",
22569591Sgreen			    user, service, authctxt->user, authctxt->service);
22669591Sgreen			authctxt->valid = 0;
22769591Sgreen		}
22860573Skris	}
22960573Skris
23068704Sgreen#ifdef HAVE_LOGIN_CAP
23169591Sgreen	if (authctxt->pw != NULL) {
23269591Sgreen		lc = login_getpwclass(authctxt->pw);
23369591Sgreen		if (lc == NULL)
23469591Sgreen			lc = login_getclassbyname(NULL, authctxt->pw);
23569591Sgreen		if (!auth_hostok(lc, from_host, from_ip)) {
23669591Sgreen			log("Denied connection for %.200s from %.200s [%.200s].",
23769591Sgreen			    authctxt->pw->pw_name, from_host, from_ip);
23869591Sgreen			packet_disconnect("Sorry, you are not allowed to connect.");
23969591Sgreen		}
24069591Sgreen		if (!auth_timeok(lc, time(NULL))) {
24169591Sgreen			log("LOGIN %.200s REFUSED (TIME) FROM %.200s",
24269591Sgreen			    authctxt->pw->pw_name, from_host);
24369591Sgreen			packet_disconnect("Logins not available right now.");
24469591Sgreen		}
24569591Sgreen		login_close(lc);
24669591Sgreen		lc = NULL;
24768704Sgreen	}
24868704Sgreen#endif  /* HAVE_LOGIN_CAP */
24968704Sgreen#ifdef LOGIN_ACCESS
25069591Sgreen	if (authctxt->pw != NULL &&
25169591Sgreen	    !login_access(authctxt->pw->pw_name, from_host)) {
25268704Sgreen		log("Denied connection for %.200s from %.200s [%.200s].",
25369591Sgreen		    authctxt->pw->pw_name, from_host, from_ip);
25468704Sgreen		packet_disconnect("Sorry, you are not allowed to connect.");
25568704Sgreen	}
25668704Sgreen#endif /* LOGIN_ACCESS */
25768704Sgreen
25869591Sgreen	m = authmethod_lookup(method);
25969591Sgreen	if (m != NULL) {
26069591Sgreen		debug2("input_userauth_request: try method %s", method);
26169591Sgreen		authenticated =	m->userauth(authctxt);
26269591Sgreen	} else {
26369591Sgreen		debug2("input_userauth_request: unsupported method %s", method);
26469591Sgreen	}
26569591Sgreen	if (!authctxt->valid && authenticated == 1) {
26669591Sgreen		log("input_userauth_request: INTERNAL ERROR: authenticated invalid user %s service %s", user, method);
26769591Sgreen		authenticated = 0;
26869591Sgreen	}
26969591Sgreen
27069591Sgreen	/* Special handling for root */
27169591Sgreen	if (authenticated == 1 &&
27269591Sgreen	    authctxt->valid && authctxt->pw->pw_uid == 0 && !options.permit_root_login) {
27369591Sgreen		authenticated = 0;
27469591Sgreen		log("ROOT LOGIN REFUSED FROM %.200s", get_canonical_hostname());
27569591Sgreen	}
27669591Sgreen
27769591Sgreen#ifdef USE_PAM
27869591Sgreen	if (authenticated && authctxt->user && !do_pam_account(authctxt->user, NULL))
27969591Sgreen		authenticated = 0;
28069591Sgreen#endif /* USE_PAM */
28169591Sgreen
28269591Sgreen	/* Log before sending the reply */
28369591Sgreen	userauth_log(authctxt, authenticated, method);
28469591Sgreen	userauth_reply(authctxt, authenticated);
28569591Sgreen
28669591Sgreen	xfree(service);
28769591Sgreen	xfree(user);
28869591Sgreen	xfree(method);
28969591Sgreen}
29069591Sgreen
29169591Sgreen
29269591Sgreenvoid
29369591Sgreenuserauth_log(Authctxt *authctxt, int authenticated, char *method)
29469591Sgreen{
29569591Sgreen	void (*authlog) (const char *fmt,...) = verbose;
29669591Sgreen	char *user = NULL, *authmsg = NULL;
29769591Sgreen
29860573Skris	/* Raise logging level */
29960573Skris	if (authenticated == 1 ||
30069591Sgreen	    !authctxt->valid ||
30169591Sgreen	    authctxt->attempt >= AUTH_FAIL_LOG ||
30260573Skris	    strcmp(method, "password") == 0)
30360573Skris		authlog = log;
30460573Skris
30560573Skris	if (authenticated == 1) {
30660573Skris		authmsg = "Accepted";
30760573Skris	} else if (authenticated == 0) {
30860573Skris		authmsg = "Failed";
30960573Skris	} else {
31060573Skris		authmsg = "Postponed";
31160573Skris	}
31269591Sgreen
31369591Sgreen	if (authctxt->valid) {
31469591Sgreen		user = authctxt->pw->pw_uid == 0 ? "ROOT" : authctxt->user;
31569591Sgreen	} else {
31669591Sgreen		user = "NOUSER";
31769591Sgreen	}
31869591Sgreen
31960573Skris	authlog("%s %s for %.200s from %.200s port %d ssh2",
32069591Sgreen	    authmsg,
32169591Sgreen	    method,
32269591Sgreen	    user,
32369591Sgreen	    get_remote_ipaddr(),
32469591Sgreen	    get_remote_port());
32569591Sgreen}
32660573Skris
32769591Sgreenvoid
32869591Sgreenuserauth_reply(Authctxt *authctxt, int authenticated)
32969591Sgreen{
33060573Skris	/* XXX todo: check if multiple auth methods are needed */
33160573Skris	if (authenticated == 1) {
33260573Skris		/* turn off userauth */
33360573Skris		dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &protocol_error);
33460573Skris		packet_start(SSH2_MSG_USERAUTH_SUCCESS);
33560573Skris		packet_send();
33660573Skris		packet_write_wait();
33760573Skris		/* now we can break out */
33869591Sgreen		authctxt->success = 1;
33960573Skris	} else if (authenticated == 0) {
34069591Sgreen		char *methods = authmethods_get();
34160573Skris		packet_start(SSH2_MSG_USERAUTH_FAILURE);
34269591Sgreen		packet_put_cstring(methods);
34369591Sgreen		packet_put_char(0);	/* XXX partial success, unused */
34460573Skris		packet_send();
34560573Skris		packet_write_wait();
34669591Sgreen		xfree(methods);
34769591Sgreen	} else {
34869591Sgreen		/* do nothing, we did already send a reply */
34960573Skris	}
35060573Skris}
35160573Skris
35260573Skrisint
35369591Sgreenuserauth_none(Authctxt *authctxt)
35460573Skris{
35569591Sgreen	/* disable method "none", only allowed one time */
35669591Sgreen	Authmethod *m = authmethod_lookup("none");
35769591Sgreen	if (m != NULL)
35869591Sgreen		m->enabled = NULL;
35960573Skris	packet_done();
36069591Sgreen#ifdef USE_PAM
36169591Sgreen	return authctxt->valid ? auth_pam_password(authctxt->pw, "") : 0;
36269591Sgreen#else /* !USE_PAM */
36369591Sgreen	return authctxt->valid ? auth_password(authctxt->pw, "") : 0;
36469591Sgreen#endif /* USE_PAM */
36560573Skris}
36669591Sgreen
36760573Skrisint
36869591Sgreenuserauth_passwd(Authctxt *authctxt)
36960573Skris{
37060573Skris	char *password;
37160573Skris	int authenticated = 0;
37260573Skris	int change;
37360573Skris	unsigned int len;
37460573Skris	change = packet_get_char();
37560573Skris	if (change)
37660573Skris		log("password change not supported");
37760573Skris	password = packet_get_string(&len);
37860573Skris	packet_done();
37969591Sgreen	if (authctxt->valid &&
38069591Sgreen#ifdef USE_PAM
38169591Sgreen	    auth_pam_password(authctxt->pw, password) == 1
38269591Sgreen#else
38369591Sgreen	    auth_password(authctxt->pw, password) == 1
38469591Sgreen#endif
38569591Sgreen	    )
38660573Skris		authenticated = 1;
38760573Skris	memset(password, 0, len);
38860573Skris	xfree(password);
38960573Skris	return authenticated;
39060573Skris}
39169591Sgreen
39260573Skrisint
39369591Sgreenuserauth_kbdint(Authctxt *authctxt)
39460573Skris{
39569591Sgreen	int authenticated = 0;
39669591Sgreen	char *lang = NULL;
39769591Sgreen	char *devs = NULL;
39869591Sgreen
39969591Sgreen	lang = packet_get_string(NULL);
40069591Sgreen	devs = packet_get_string(NULL);
40169591Sgreen	packet_done();
40269591Sgreen
40369591Sgreen	debug("keyboard-interactive language %s devs %s", lang, devs);
40469591Sgreen#ifdef SKEY
40569591Sgreen	/* XXX hardcoded, we should look at devs */
40669591Sgreen	if (options.skey_authentication != 0)
40769591Sgreen		authenticated = auth2_skey(authctxt);
40869591Sgreen#endif
40969591Sgreen	xfree(lang);
41069591Sgreen	xfree(devs);
41169591Sgreen	return authenticated;
41269591Sgreen}
41369591Sgreen
41469591Sgreenint
41569591Sgreenuserauth_pubkey(Authctxt *authctxt)
41669591Sgreen{
41760573Skris	Buffer b;
41860573Skris	Key *key;
41960573Skris	char *pkalg, *pkblob, *sig;
42060573Skris	unsigned int alen, blen, slen;
42160573Skris	int have_sig;
42260573Skris	int authenticated = 0;
42360573Skris
42469591Sgreen	if (!authctxt->valid) {
42569591Sgreen		debug2("userauth_pubkey: disabled because of invalid user");
42660573Skris		return 0;
42760573Skris	}
42860573Skris	have_sig = packet_get_char();
42960573Skris	pkalg = packet_get_string(&alen);
43060573Skris	if (strcmp(pkalg, KEX_DSS) != 0) {
43169591Sgreen		log("bad pkalg %s", pkalg);	/*XXX*/
43260573Skris		xfree(pkalg);
43360573Skris		return 0;
43460573Skris	}
43560573Skris	pkblob = packet_get_string(&blen);
43660573Skris	key = dsa_key_from_blob(pkblob, blen);
43760573Skris	if (key != NULL) {
43860573Skris		if (have_sig) {
43960573Skris			sig = packet_get_string(&slen);
44060573Skris			packet_done();
44160573Skris			buffer_init(&b);
44269591Sgreen			if (datafellows & SSH_OLD_SESSIONID) {
44369591Sgreen				buffer_append(&b, session_id2, session_id2_len);
44469591Sgreen			} else {
44565674Skris				buffer_put_string(&b, session_id2, session_id2_len);
44665674Skris			}
44765674Skris			/* reconstruct packet */
44860573Skris			buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
44969591Sgreen			buffer_put_cstring(&b, authctxt->user);
45065674Skris			buffer_put_cstring(&b,
45165674Skris			    datafellows & SSH_BUG_PUBKEYAUTH ?
45265674Skris			    "ssh-userauth" :
45369591Sgreen			    authctxt->service);
45465674Skris			buffer_put_cstring(&b, "publickey");
45565674Skris			buffer_put_char(&b, have_sig);
45665674Skris			buffer_put_cstring(&b, KEX_DSS);
45765674Skris			buffer_put_string(&b, pkblob, blen);
45860573Skris#ifdef DEBUG_DSS
45960573Skris			buffer_dump(&b);
46060573Skris#endif
46160573Skris			/* test for correct signature */
46269591Sgreen			if (user_dsa_key_allowed(authctxt->pw, key) &&
46360573Skris			    dsa_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1)
46460573Skris				authenticated = 1;
46560573Skris			buffer_clear(&b);
46660573Skris			xfree(sig);
46760573Skris		} else {
46869591Sgreen			debug("test whether pkalg/pkblob are acceptable");
46960573Skris			packet_done();
47069591Sgreen
47160573Skris			/* XXX fake reply and always send PK_OK ? */
47260573Skris			/*
47360573Skris			 * XXX this allows testing whether a user is allowed
47460573Skris			 * to login: if you happen to have a valid pubkey this
47560573Skris			 * message is sent. the message is NEVER sent at all
47660573Skris			 * if a user is not allowed to login. is this an
47760573Skris			 * issue? -markus
47860573Skris			 */
47969591Sgreen			if (user_dsa_key_allowed(authctxt->pw, key)) {
48060573Skris				packet_start(SSH2_MSG_USERAUTH_PK_OK);
48160573Skris				packet_put_string(pkalg, alen);
48260573Skris				packet_put_string(pkblob, blen);
48360573Skris				packet_send();
48460573Skris				packet_write_wait();
48560573Skris				authenticated = -1;
48660573Skris			}
48760573Skris		}
48869591Sgreen		if (authenticated != 1)
48969591Sgreen			auth_clear_options();
49060573Skris		key_free(key);
49160573Skris	}
49260573Skris	xfree(pkalg);
49360573Skris	xfree(pkblob);
49460573Skris	return authenticated;
49560573Skris}
49660573Skris
49769591Sgreen/* get current user */
49860573Skris
49960573Skrisstruct passwd*
50060573Skrisauth_get_user(void)
50160573Skris{
50269591Sgreen	return (x_authctxt != NULL && x_authctxt->valid) ? x_authctxt->pw : NULL;
50360573Skris}
50460573Skris
50569591Sgreen#define	DELIM	","
50669591Sgreen
50769591Sgreenchar *
50869591Sgreenauthmethods_get(void)
50960573Skris{
51069591Sgreen	Authmethod *method = NULL;
51169591Sgreen	unsigned int size = 0;
51269591Sgreen	char *list;
51360573Skris
51469591Sgreen	for (method = authmethods; method->name != NULL; method++) {
51569591Sgreen		if (strcmp(method->name, "none") == 0)
51669591Sgreen			continue;
51769591Sgreen		if (method->enabled != NULL && *(method->enabled) != 0) {
51869591Sgreen			if (size != 0)
51969591Sgreen				size += strlen(DELIM);
52069591Sgreen			size += strlen(method->name);
52160573Skris		}
52269591Sgreen	}
52369591Sgreen	size++;			/* trailing '\0' */
52469591Sgreen	list = xmalloc(size);
52569591Sgreen	list[0] = '\0';
52669591Sgreen
52769591Sgreen	for (method = authmethods; method->name != NULL; method++) {
52869591Sgreen		if (strcmp(method->name, "none") == 0)
52969591Sgreen			continue;
53069591Sgreen		if (method->enabled != NULL && *(method->enabled) != 0) {
53169591Sgreen			if (list[0] != '\0')
53269591Sgreen				strlcat(list, DELIM, size);
53369591Sgreen			strlcat(list, method->name, size);
53460573Skris		}
53560573Skris	}
53669591Sgreen	return list;
53760573Skris}
53860573Skris
53969591SgreenAuthmethod *
54069591Sgreenauthmethod_lookup(const char *name)
54169591Sgreen{
54269591Sgreen	Authmethod *method = NULL;
54369591Sgreen	if (name != NULL)
54469591Sgreen		for (method = authmethods; method->name != NULL; method++)
54569591Sgreen			if (method->enabled != NULL &&
54669591Sgreen			    *(method->enabled) != 0 &&
54769591Sgreen			    strcmp(name, method->name) == 0)
54869591Sgreen				return method;
54969591Sgreen	debug2("Unrecognized authentication method name: %s", name ? name : "NULL");
55069591Sgreen	return NULL;
55169591Sgreen}
55269591Sgreen
55360573Skris/* return 1 if user allows given key */
55460573Skrisint
55560573Skrisuser_dsa_key_allowed(struct passwd *pw, Key *key)
55660573Skris{
55760573Skris	char line[8192], file[1024];
55860573Skris	int found_key = 0;
55960573Skris	unsigned int bits = -1;
56060573Skris	FILE *f;
56160573Skris	unsigned long linenum = 0;
56260573Skris	struct stat st;
56360573Skris	Key *found;
56460573Skris
56569591Sgreen	if (pw == NULL)
56669591Sgreen		return 0;
56769591Sgreen
56860573Skris	/* Temporarily use the user's uid. */
56960573Skris	temporarily_use_uid(pw->pw_uid);
57060573Skris
57160573Skris	/* The authorized keys. */
57260573Skris	snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir,
57360573Skris	    SSH_USER_PERMITTED_KEYS2);
57460573Skris
57560573Skris	/* Fail quietly if file does not exist */
57660573Skris	if (stat(file, &st) < 0) {
57760573Skris		/* Restore the privileged uid. */
57860573Skris		restore_uid();
57960573Skris		return 0;
58060573Skris	}
58160573Skris	/* Open the file containing the authorized keys. */
58260573Skris	f = fopen(file, "r");
58360573Skris	if (!f) {
58460573Skris		/* Restore the privileged uid. */
58560573Skris		restore_uid();
58660573Skris		return 0;
58760573Skris	}
58860573Skris	if (options.strict_modes) {
58960573Skris		int fail = 0;
59060573Skris		char buf[1024];
59160573Skris		/* Check open file in order to avoid open/stat races */
59260573Skris		if (fstat(fileno(f), &st) < 0 ||
59360573Skris		    (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
59460573Skris		    (st.st_mode & 022) != 0) {
59569591Sgreen			snprintf(buf, sizeof buf,
59669591Sgreen			    "%s authentication refused for %.100s: "
59769591Sgreen			    "bad ownership or modes for '%s'.",
59869591Sgreen			    key_type(key), pw->pw_name, file);
59960573Skris			fail = 1;
60060573Skris		} else {
60160573Skris			/* Check path to SSH_USER_PERMITTED_KEYS */
60260573Skris			int i;
60360573Skris			static const char *check[] = {
60460573Skris				"", SSH_USER_DIR, NULL
60560573Skris			};
60660573Skris			for (i = 0; check[i]; i++) {
60760573Skris				snprintf(line, sizeof line, "%.500s/%.100s",
60860573Skris				    pw->pw_dir, check[i]);
60960573Skris				if (stat(line, &st) < 0 ||
61060573Skris				    (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
61160573Skris				    (st.st_mode & 022) != 0) {
61260573Skris					snprintf(buf, sizeof buf,
61369591Sgreen					    "%s authentication refused for %.100s: "
61460573Skris					    "bad ownership or modes for '%s'.",
61569591Sgreen					    key_type(key), pw->pw_name, line);
61660573Skris					fail = 1;
61760573Skris					break;
61860573Skris				}
61960573Skris			}
62060573Skris		}
62160573Skris		if (fail) {
62260573Skris			fclose(f);
62365674Skris			log("%s",buf);
62460573Skris			restore_uid();
62560573Skris			return 0;
62660573Skris		}
62760573Skris	}
62860573Skris	found_key = 0;
62969591Sgreen	found = key_new(key->type);
63060573Skris
63160573Skris	while (fgets(line, sizeof(line), f)) {
63265674Skris		char *cp, *options = NULL;
63360573Skris		linenum++;
63460573Skris		/* Skip leading whitespace, empty and comment lines. */
63560573Skris		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
63660573Skris			;
63760573Skris		if (!*cp || *cp == '\n' || *cp == '#')
63860573Skris			continue;
63965674Skris
64060573Skris		bits = key_read(found, &cp);
64165674Skris		if (bits == 0) {
64265674Skris			/* no key?  check if there are options for this key */
64365674Skris			int quoted = 0;
64465674Skris			options = cp;
64565674Skris			for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
64665674Skris				if (*cp == '\\' && cp[1] == '"')
64765674Skris					cp++;	/* Skip both */
64865674Skris				else if (*cp == '"')
64965674Skris					quoted = !quoted;
65065674Skris			}
65165674Skris			/* Skip remaining whitespace. */
65265674Skris			for (; *cp == ' ' || *cp == '\t'; cp++)
65365674Skris				;
65465674Skris			bits = key_read(found, &cp);
65565674Skris			if (bits == 0) {
65665674Skris				/* still no key?  advance to next line*/
65765674Skris				continue;
65865674Skris			}
65965674Skris		}
66065674Skris		if (key_equal(found, key) &&
66165674Skris		    auth_parse_options(pw, options, linenum) == 1) {
66260573Skris			found_key = 1;
66360573Skris			debug("matching key found: file %s, line %ld",
66460573Skris			    file, linenum);
66560573Skris			break;
66660573Skris		}
66760573Skris	}
66860573Skris	restore_uid();
66960573Skris	fclose(f);
67060573Skris	key_free(found);
67160573Skris	return found_key;
67260573Skris}
67369591Sgreen
67469591Sgreenstruct passwd *
67569591Sgreenpwcopy(struct passwd *pw)
67669591Sgreen{
67769591Sgreen	struct passwd *copy = xmalloc(sizeof(*copy));
67869591Sgreen	memset(copy, 0, sizeof(*copy));
67969591Sgreen	copy->pw_name = xstrdup(pw->pw_name);
68069591Sgreen	copy->pw_passwd = xstrdup(pw->pw_passwd);
68169591Sgreen	copy->pw_uid = pw->pw_uid;
68269591Sgreen	copy->pw_gid = pw->pw_gid;
68369591Sgreen	copy->pw_class = xstrdup(pw->pw_class);
68469591Sgreen	copy->pw_dir = xstrdup(pw->pw_dir);
68569591Sgreen	copy->pw_shell = xstrdup(pw->pw_shell);
68669591Sgreen	copy->pw_expire = pw->pw_expire;
68769591Sgreen	copy->pw_change = pw->pw_change;
68869591Sgreen	return copy;
68969591Sgreen}
690