auth2.c revision 65674
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"
2665674SkrisRCSID("$OpenBSD: auth2.c,v 1.14 2000/09/07 20:27:49 deraadt Exp $");
2765674SkrisRCSID("$FreeBSD: head/crypto/openssh/auth2.c 65674 2000-09-10 09:35:38Z kris $");
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 "cipher.h"
4060573Skris#include "servconf.h"
4160573Skris#include "compat.h"
4260573Skris#include "channels.h"
4360573Skris#include "bufaux.h"
4460573Skris#include "ssh2.h"
4560573Skris#include "auth.h"
4660573Skris#include "session.h"
4760573Skris#include "dispatch.h"
4860573Skris#include "auth.h"
4960573Skris#include "key.h"
5060573Skris#include "kex.h"
5160573Skris
5260573Skris#include "dsa.h"
5360573Skris#include "uidswap.h"
5465674Skris#include "auth-options.h"
5560573Skris
5660573Skris/* import */
5760573Skrisextern ServerOptions options;
5860573Skrisextern unsigned char *session_id2;
5960573Skrisextern int session_id2_len;
6060573Skris
6160573Skris/* protocol */
6260573Skris
6360573Skrisvoid	input_service_request(int type, int plen);
6460573Skrisvoid	input_userauth_request(int type, int plen);
6560573Skrisvoid	protocol_error(int type, int plen);
6660573Skris
6760573Skris/* auth */
6860573Skrisint	ssh2_auth_none(struct passwd *pw);
6960573Skrisint	ssh2_auth_password(struct passwd *pw);
7065674Skrisint  	ssh2_auth_pubkey(struct passwd *pw, char *service);
7160573Skris
7260573Skris/* helper */
7360573Skrisstruct passwd*	 auth_set_user(char *u, char *s);
7460573Skrisint	user_dsa_key_allowed(struct passwd *pw, Key *key);
7560573Skris
7660573Skristypedef struct Authctxt Authctxt;
7760573Skrisstruct Authctxt {
7860573Skris	char *user;
7960573Skris	char *service;
8060573Skris	struct passwd pw;
8160573Skris	int valid;
8260573Skris};
8360573Skrisstatic Authctxt	*authctxt = NULL;
8460573Skrisstatic int userauth_success = 0;
8560573Skris
8660573Skris/*
8760573Skris * loop until userauth_success == TRUE
8860573Skris */
8960573Skris
9060573Skrisvoid
9160573Skrisdo_authentication2()
9260573Skris{
9360573Skris	/* turn off skey/kerberos, not supported by SSH2 */
9460573Skris#ifdef SKEY
9560573Skris	options.skey_authentication = 0;
9660573Skris#endif
9760573Skris#ifdef KRB4
9860576Skris	options.krb4_authentication = 0;
9960573Skris#endif
10060573Skris
10160573Skris	dispatch_init(&protocol_error);
10260573Skris	dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request);
10360573Skris	dispatch_run(DISPATCH_BLOCK, &userauth_success);
10460573Skris	do_authenticated2();
10560573Skris}
10660573Skris
10760573Skrisvoid
10860573Skrisprotocol_error(int type, int plen)
10960573Skris{
11060573Skris	log("auth: protocol error: type %d plen %d", type, plen);
11160573Skris	packet_start(SSH2_MSG_UNIMPLEMENTED);
11260573Skris	packet_put_int(0);
11360573Skris	packet_send();
11460573Skris	packet_write_wait();
11560573Skris}
11660573Skris
11760573Skrisvoid
11860573Skrisinput_service_request(int type, int plen)
11960573Skris{
12060573Skris	unsigned int len;
12160573Skris	int accept = 0;
12260573Skris	char *service = packet_get_string(&len);
12360573Skris	packet_done();
12460573Skris
12560573Skris	if (strcmp(service, "ssh-userauth") == 0) {
12660573Skris		if (!userauth_success) {
12760573Skris			accept = 1;
12860573Skris			/* now we can handle user-auth requests */
12960573Skris			dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request);
13060573Skris		}
13160573Skris	}
13260573Skris	/* XXX all other service requests are denied */
13360573Skris
13460573Skris	if (accept) {
13560573Skris		packet_start(SSH2_MSG_SERVICE_ACCEPT);
13660573Skris		packet_put_cstring(service);
13760573Skris		packet_send();
13860573Skris		packet_write_wait();
13960573Skris	} else {
14060573Skris		debug("bad service request %s", service);
14160573Skris		packet_disconnect("bad service request %s", service);
14260573Skris	}
14360573Skris	xfree(service);
14460573Skris}
14560573Skris
14660573Skrisvoid
14760573Skrisinput_userauth_request(int type, int plen)
14860573Skris{
14960573Skris	static void (*authlog) (const char *fmt,...) = verbose;
15060573Skris	static int attempt = 0;
15165674Skris	unsigned int len;
15260573Skris	int authenticated = 0;
15365674Skris	char *user, *service, *method, *authmsg = NULL;
15460573Skris	struct passwd *pw;
15560573Skris
15660573Skris	if (++attempt == AUTH_FAIL_MAX)
15760573Skris		packet_disconnect("too many failed userauth_requests");
15860573Skris
15960573Skris	user = packet_get_string(&len);
16060573Skris	service = packet_get_string(&len);
16160573Skris	method = packet_get_string(&len);
16260573Skris	debug("userauth-request for user %s service %s method %s", user, service, method);
16360573Skris
16460573Skris	/* XXX we only allow the ssh-connection service */
16560573Skris	pw = auth_set_user(user, service);
16660573Skris	if (pw && strcmp(service, "ssh-connection")==0) {
16760573Skris		if (strcmp(method, "none") == 0) {
16860573Skris			authenticated =	ssh2_auth_none(pw);
16960573Skris		} else if (strcmp(method, "password") == 0) {
17060573Skris			authenticated =	ssh2_auth_password(pw);
17160573Skris		} else if (strcmp(method, "publickey") == 0) {
17265674Skris			authenticated =	ssh2_auth_pubkey(pw, service);
17360573Skris		}
17460573Skris	}
17560573Skris	if (authenticated && pw && pw->pw_uid == 0 && !options.permit_root_login) {
17660573Skris		authenticated = 0;
17760573Skris		log("ROOT LOGIN REFUSED FROM %.200s",
17860573Skris		    get_canonical_hostname());
17960573Skris	}
18060573Skris
18160573Skris	/* Raise logging level */
18260573Skris	if (authenticated == 1 ||
18360573Skris	    attempt == AUTH_FAIL_LOG ||
18460573Skris	    strcmp(method, "password") == 0)
18560573Skris		authlog = log;
18660573Skris
18760573Skris	/* Log before sending the reply */
18860573Skris	if (authenticated == 1) {
18960573Skris		authmsg = "Accepted";
19060573Skris	} else if (authenticated == 0) {
19160573Skris		authmsg = "Failed";
19260573Skris	} else {
19360573Skris		authmsg = "Postponed";
19460573Skris	}
19560573Skris	authlog("%s %s for %.200s from %.200s port %d ssh2",
19660573Skris		authmsg,
19760573Skris		method,
19860573Skris		pw && pw->pw_uid == 0 ? "ROOT" : user,
19960573Skris		get_remote_ipaddr(),
20060573Skris		get_remote_port());
20160573Skris
20260573Skris	/* XXX todo: check if multiple auth methods are needed */
20360573Skris	if (authenticated == 1) {
20460573Skris		/* turn off userauth */
20560573Skris		dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &protocol_error);
20660573Skris		packet_start(SSH2_MSG_USERAUTH_SUCCESS);
20760573Skris		packet_send();
20860573Skris		packet_write_wait();
20960573Skris		/* now we can break out */
21060573Skris		userauth_success = 1;
21160573Skris	} else if (authenticated == 0) {
21260573Skris		packet_start(SSH2_MSG_USERAUTH_FAILURE);
21360573Skris		packet_put_cstring("publickey,password");	/* XXX dynamic */
21460573Skris		packet_put_char(0);				/* XXX partial success, unused */
21560573Skris		packet_send();
21660573Skris		packet_write_wait();
21760573Skris	}
21860573Skris
21960573Skris	xfree(service);
22060573Skris	xfree(user);
22160573Skris	xfree(method);
22260573Skris}
22360573Skris
22460573Skrisint
22560573Skrisssh2_auth_none(struct passwd *pw)
22660573Skris{
22760573Skris	packet_done();
22860573Skris	return auth_password(pw, "");
22960573Skris}
23060573Skrisint
23160573Skrisssh2_auth_password(struct passwd *pw)
23260573Skris{
23360573Skris	char *password;
23460573Skris	int authenticated = 0;
23560573Skris	int change;
23660573Skris	unsigned int len;
23760573Skris	change = packet_get_char();
23860573Skris	if (change)
23960573Skris		log("password change not supported");
24060573Skris	password = packet_get_string(&len);
24160573Skris	packet_done();
24260573Skris	if (options.password_authentication &&
24360573Skris	    auth_password(pw, password) == 1)
24460573Skris		authenticated = 1;
24560573Skris	memset(password, 0, len);
24660573Skris	xfree(password);
24760573Skris	return authenticated;
24860573Skris}
24960573Skrisint
25065674Skrisssh2_auth_pubkey(struct passwd *pw, char *service)
25160573Skris{
25260573Skris	Buffer b;
25360573Skris	Key *key;
25460573Skris	char *pkalg, *pkblob, *sig;
25560573Skris	unsigned int alen, blen, slen;
25660573Skris	int have_sig;
25760573Skris	int authenticated = 0;
25860573Skris
25960573Skris	if (options.dsa_authentication == 0) {
26060573Skris		debug("pubkey auth disabled");
26160573Skris		return 0;
26260573Skris	}
26360573Skris	have_sig = packet_get_char();
26460573Skris	pkalg = packet_get_string(&alen);
26560573Skris	if (strcmp(pkalg, KEX_DSS) != 0) {
26660573Skris		xfree(pkalg);
26760573Skris		log("bad pkalg %s", pkalg);	/*XXX*/
26860573Skris		return 0;
26960573Skris	}
27060573Skris	pkblob = packet_get_string(&blen);
27160573Skris	key = dsa_key_from_blob(pkblob, blen);
27260573Skris	if (key != NULL) {
27360573Skris		if (have_sig) {
27460573Skris			sig = packet_get_string(&slen);
27560573Skris			packet_done();
27660573Skris			buffer_init(&b);
27765674Skris			if (datafellows & SSH_COMPAT_SESSIONID_ENCODING) {
27865674Skris				buffer_put_string(&b, session_id2, session_id2_len);
27965674Skris			} else {
28065674Skris				buffer_append(&b, session_id2, session_id2_len);
28165674Skris			}
28265674Skris			/* reconstruct packet */
28360573Skris			buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
28465674Skris			buffer_put_cstring(&b, pw->pw_name);
28565674Skris			buffer_put_cstring(&b,
28665674Skris			    datafellows & SSH_BUG_PUBKEYAUTH ?
28765674Skris			    "ssh-userauth" :
28865674Skris			    service);
28965674Skris			buffer_put_cstring(&b, "publickey");
29065674Skris			buffer_put_char(&b, have_sig);
29165674Skris			buffer_put_cstring(&b, KEX_DSS);
29265674Skris			buffer_put_string(&b, pkblob, blen);
29360573Skris#ifdef DEBUG_DSS
29460573Skris			buffer_dump(&b);
29560573Skris#endif
29660573Skris			/* test for correct signature */
29760573Skris			if (user_dsa_key_allowed(pw, key) &&
29860573Skris			    dsa_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1)
29960573Skris				authenticated = 1;
30060573Skris			buffer_clear(&b);
30160573Skris			xfree(sig);
30260573Skris		} else {
30360573Skris			packet_done();
30460573Skris			debug("test key...");
30560573Skris			/* test whether pkalg/pkblob are acceptable */
30660573Skris			/* XXX fake reply and always send PK_OK ? */
30760573Skris			/*
30860573Skris			 * XXX this allows testing whether a user is allowed
30960573Skris			 * to login: if you happen to have a valid pubkey this
31060573Skris			 * message is sent. the message is NEVER sent at all
31160573Skris			 * if a user is not allowed to login. is this an
31260573Skris			 * issue? -markus
31360573Skris			 */
31460573Skris			if (user_dsa_key_allowed(pw, key)) {
31560573Skris				packet_start(SSH2_MSG_USERAUTH_PK_OK);
31660573Skris				packet_put_string(pkalg, alen);
31760573Skris				packet_put_string(pkblob, blen);
31860573Skris				packet_send();
31960573Skris				packet_write_wait();
32060573Skris				authenticated = -1;
32160573Skris			}
32260573Skris		}
32360573Skris		key_free(key);
32460573Skris	}
32560573Skris	xfree(pkalg);
32660573Skris	xfree(pkblob);
32760573Skris	return authenticated;
32860573Skris}
32960573Skris
33060573Skris/* set and get current user */
33160573Skris
33260573Skrisstruct passwd*
33360573Skrisauth_get_user(void)
33460573Skris{
33560573Skris	return (authctxt != NULL && authctxt->valid) ? &authctxt->pw : NULL;
33660573Skris}
33760573Skris
33860573Skrisstruct passwd*
33960573Skrisauth_set_user(char *u, char *s)
34060573Skris{
34160573Skris	struct passwd *pw, *copy;
34260573Skris
34360573Skris	if (authctxt == NULL) {
34460573Skris		authctxt = xmalloc(sizeof(*authctxt));
34560573Skris		authctxt->valid = 0;
34660573Skris		authctxt->user = xstrdup(u);
34760573Skris		authctxt->service = xstrdup(s);
34860573Skris		setproctitle("%s", u);
34960573Skris		pw = getpwnam(u);
35060573Skris		if (!pw || !allowed_user(pw)) {
35160573Skris			log("auth_set_user: illegal user %s", u);
35260573Skris			return NULL;
35360573Skris		}
35460573Skris		copy = &authctxt->pw;
35560573Skris		memset(copy, 0, sizeof(*copy));
35660573Skris		copy->pw_name = xstrdup(pw->pw_name);
35760573Skris		copy->pw_passwd = xstrdup(pw->pw_passwd);
35860573Skris		copy->pw_uid = pw->pw_uid;
35960573Skris		copy->pw_gid = pw->pw_gid;
36065674Skris		copy->pw_class = xstrdup(pw->pw_class);
36160573Skris		copy->pw_dir = xstrdup(pw->pw_dir);
36260573Skris		copy->pw_shell = xstrdup(pw->pw_shell);
36362179Sgreen		copy->pw_class = xstrdup(pw->pw_class);
36462179Sgreen		copy->pw_expire = pw->pw_expire;
36562179Sgreen		copy->pw_change = pw->pw_change;
36660573Skris		authctxt->valid = 1;
36760573Skris	} else {
36860573Skris		if (strcmp(u, authctxt->user) != 0 ||
36960573Skris		    strcmp(s, authctxt->service) != 0) {
37060573Skris			log("auth_set_user: missmatch: (%s,%s)!=(%s,%s)",
37160573Skris			    u, s, authctxt->user, authctxt->service);
37260573Skris			return NULL;
37360573Skris		}
37460573Skris	}
37560573Skris	return auth_get_user();
37660573Skris}
37760573Skris
37860573Skris/* return 1 if user allows given key */
37960573Skrisint
38060573Skrisuser_dsa_key_allowed(struct passwd *pw, Key *key)
38160573Skris{
38260573Skris	char line[8192], file[1024];
38360573Skris	int found_key = 0;
38460573Skris	unsigned int bits = -1;
38560573Skris	FILE *f;
38660573Skris	unsigned long linenum = 0;
38760573Skris	struct stat st;
38860573Skris	Key *found;
38960573Skris
39060573Skris	/* Temporarily use the user's uid. */
39160573Skris	temporarily_use_uid(pw->pw_uid);
39260573Skris
39360573Skris	/* The authorized keys. */
39460573Skris	snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir,
39560573Skris	    SSH_USER_PERMITTED_KEYS2);
39660573Skris
39760573Skris	/* Fail quietly if file does not exist */
39860573Skris	if (stat(file, &st) < 0) {
39960573Skris		/* Restore the privileged uid. */
40060573Skris		restore_uid();
40160573Skris		return 0;
40260573Skris	}
40360573Skris	/* Open the file containing the authorized keys. */
40460573Skris	f = fopen(file, "r");
40560573Skris	if (!f) {
40660573Skris		/* Restore the privileged uid. */
40760573Skris		restore_uid();
40860573Skris		return 0;
40960573Skris	}
41060573Skris	if (options.strict_modes) {
41160573Skris		int fail = 0;
41260573Skris		char buf[1024];
41360573Skris		/* Check open file in order to avoid open/stat races */
41460573Skris		if (fstat(fileno(f), &st) < 0 ||
41560573Skris		    (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
41660573Skris		    (st.st_mode & 022) != 0) {
41760573Skris			snprintf(buf, sizeof buf, "DSA authentication refused for %.100s: "
41860573Skris			    "bad ownership or modes for '%s'.", pw->pw_name, file);
41960573Skris			fail = 1;
42060573Skris		} else {
42160573Skris			/* Check path to SSH_USER_PERMITTED_KEYS */
42260573Skris			int i;
42360573Skris			static const char *check[] = {
42460573Skris				"", SSH_USER_DIR, NULL
42560573Skris			};
42660573Skris			for (i = 0; check[i]; i++) {
42760573Skris				snprintf(line, sizeof line, "%.500s/%.100s",
42860573Skris				    pw->pw_dir, check[i]);
42960573Skris				if (stat(line, &st) < 0 ||
43060573Skris				    (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
43160573Skris				    (st.st_mode & 022) != 0) {
43260573Skris					snprintf(buf, sizeof buf,
43360573Skris					    "DSA authentication refused for %.100s: "
43460573Skris					    "bad ownership or modes for '%s'.",
43560573Skris					    pw->pw_name, line);
43660573Skris					fail = 1;
43760573Skris					break;
43860573Skris				}
43960573Skris			}
44060573Skris		}
44160573Skris		if (fail) {
44260573Skris			fclose(f);
44365674Skris			log("%s",buf);
44460573Skris			restore_uid();
44560573Skris			return 0;
44660573Skris		}
44760573Skris	}
44860573Skris	found_key = 0;
44960573Skris	found = key_new(KEY_DSA);
45060573Skris
45160573Skris	while (fgets(line, sizeof(line), f)) {
45265674Skris		char *cp, *options = NULL;
45360573Skris		linenum++;
45460573Skris		/* Skip leading whitespace, empty and comment lines. */
45560573Skris		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
45660573Skris			;
45760573Skris		if (!*cp || *cp == '\n' || *cp == '#')
45860573Skris			continue;
45965674Skris
46060573Skris		bits = key_read(found, &cp);
46165674Skris		if (bits == 0) {
46265674Skris			/* no key?  check if there are options for this key */
46365674Skris			int quoted = 0;
46465674Skris			options = cp;
46565674Skris			for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
46665674Skris				if (*cp == '\\' && cp[1] == '"')
46765674Skris					cp++;	/* Skip both */
46865674Skris				else if (*cp == '"')
46965674Skris					quoted = !quoted;
47065674Skris			}
47165674Skris			/* Skip remaining whitespace. */
47265674Skris			for (; *cp == ' ' || *cp == '\t'; cp++)
47365674Skris				;
47465674Skris			bits = key_read(found, &cp);
47565674Skris			if (bits == 0) {
47665674Skris				/* still no key?  advance to next line*/
47765674Skris				continue;
47865674Skris			}
47965674Skris		}
48065674Skris		if (key_equal(found, key) &&
48165674Skris		    auth_parse_options(pw, options, linenum) == 1) {
48260573Skris			found_key = 1;
48360573Skris			debug("matching key found: file %s, line %ld",
48460573Skris			    file, linenum);
48560573Skris			break;
48660573Skris		}
48760573Skris	}
48860573Skris	restore_uid();
48960573Skris	fclose(f);
49060573Skris	key_free(found);
49160573Skris	return found_key;
49260573Skris}
493