auth2.c revision 60573
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 * 3. All advertising materials mentioning features or use of this software
1360573Skris *    must display the following acknowledgement:
1460573Skris *      This product includes software developed by Markus Friedl.
1560573Skris * 4. The name of the author may not be used to endorse or promote products
1660573Skris *    derived from this software without specific prior written permission.
1760573Skris *
1860573Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1960573Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2060573Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2160573Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2260573Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2360573Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2460573Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2560573Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2660573Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2760573Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2860573Skris */
2960573Skris#include "includes.h"
3060573SkrisRCSID("$OpenBSD: auth2.c,v 1.8 2000/05/08 17:42:24 markus Exp $");
3160573Skris
3260573Skris#include <openssl/dsa.h>
3360573Skris#include <openssl/rsa.h>
3460573Skris#include <openssl/evp.h>
3560573Skris
3660573Skris#include "xmalloc.h"
3760573Skris#include "rsa.h"
3860573Skris#include "ssh.h"
3960573Skris#include "pty.h"
4060573Skris#include "packet.h"
4160573Skris#include "buffer.h"
4260573Skris#include "cipher.h"
4360573Skris#include "servconf.h"
4460573Skris#include "compat.h"
4560573Skris#include "channels.h"
4660573Skris#include "bufaux.h"
4760573Skris#include "ssh2.h"
4860573Skris#include "auth.h"
4960573Skris#include "session.h"
5060573Skris#include "dispatch.h"
5160573Skris#include "auth.h"
5260573Skris#include "key.h"
5360573Skris#include "kex.h"
5460573Skris
5560573Skris#include "dsa.h"
5660573Skris#include "uidswap.h"
5760573Skris
5860573Skris/* import */
5960573Skrisextern ServerOptions options;
6060573Skrisextern unsigned char *session_id2;
6160573Skrisextern int session_id2_len;
6260573Skris
6360573Skris/* protocol */
6460573Skris
6560573Skrisvoid	input_service_request(int type, int plen);
6660573Skrisvoid	input_userauth_request(int type, int plen);
6760573Skrisvoid	protocol_error(int type, int plen);
6860573Skris
6960573Skris/* auth */
7060573Skrisint	ssh2_auth_none(struct passwd *pw);
7160573Skrisint	ssh2_auth_password(struct passwd *pw);
7260573Skrisint	ssh2_auth_pubkey(struct passwd *pw, unsigned char *raw, unsigned int rlen);
7360573Skris
7460573Skris/* helper */
7560573Skrisstruct passwd*	 auth_set_user(char *u, char *s);
7660573Skrisint	user_dsa_key_allowed(struct passwd *pw, Key *key);
7760573Skris
7860573Skristypedef struct Authctxt Authctxt;
7960573Skrisstruct Authctxt {
8060573Skris	char *user;
8160573Skris	char *service;
8260573Skris	struct passwd pw;
8360573Skris	int valid;
8460573Skris};
8560573Skrisstatic Authctxt	*authctxt = NULL;
8660573Skrisstatic int userauth_success = 0;
8760573Skris
8860573Skris/*
8960573Skris * loop until userauth_success == TRUE
9060573Skris */
9160573Skris
9260573Skrisvoid
9360573Skrisdo_authentication2()
9460573Skris{
9560573Skris	/* turn off skey/kerberos, not supported by SSH2 */
9660573Skris#ifdef SKEY
9760573Skris	options.skey_authentication = 0;
9860573Skris#endif
9960573Skris#ifdef KRB4
10060573Skris	options.kerberos_authentication = 0;
10160573Skris#endif
10260573Skris
10360573Skris	dispatch_init(&protocol_error);
10460573Skris	dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request);
10560573Skris	dispatch_run(DISPATCH_BLOCK, &userauth_success);
10660573Skris	do_authenticated2();
10760573Skris}
10860573Skris
10960573Skrisvoid
11060573Skrisprotocol_error(int type, int plen)
11160573Skris{
11260573Skris	log("auth: protocol error: type %d plen %d", type, plen);
11360573Skris	packet_start(SSH2_MSG_UNIMPLEMENTED);
11460573Skris	packet_put_int(0);
11560573Skris	packet_send();
11660573Skris	packet_write_wait();
11760573Skris}
11860573Skris
11960573Skrisvoid
12060573Skrisinput_service_request(int type, int plen)
12160573Skris{
12260573Skris	unsigned int len;
12360573Skris	int accept = 0;
12460573Skris	char *service = packet_get_string(&len);
12560573Skris	packet_done();
12660573Skris
12760573Skris	if (strcmp(service, "ssh-userauth") == 0) {
12860573Skris		if (!userauth_success) {
12960573Skris			accept = 1;
13060573Skris			/* now we can handle user-auth requests */
13160573Skris			dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request);
13260573Skris		}
13360573Skris	}
13460573Skris	/* XXX all other service requests are denied */
13560573Skris
13660573Skris	if (accept) {
13760573Skris		packet_start(SSH2_MSG_SERVICE_ACCEPT);
13860573Skris		packet_put_cstring(service);
13960573Skris		packet_send();
14060573Skris		packet_write_wait();
14160573Skris	} else {
14260573Skris		debug("bad service request %s", service);
14360573Skris		packet_disconnect("bad service request %s", service);
14460573Skris	}
14560573Skris	xfree(service);
14660573Skris}
14760573Skris
14860573Skrisvoid
14960573Skrisinput_userauth_request(int type, int plen)
15060573Skris{
15160573Skris	static void (*authlog) (const char *fmt,...) = verbose;
15260573Skris	static int attempt = 0;
15360573Skris	unsigned int len, rlen;
15460573Skris	int authenticated = 0;
15560573Skris	char *raw, *user, *service, *method, *authmsg = NULL;
15660573Skris	struct passwd *pw;
15760573Skris
15860573Skris	if (++attempt == AUTH_FAIL_MAX)
15960573Skris		packet_disconnect("too many failed userauth_requests");
16060573Skris
16160573Skris	raw = packet_get_raw(&rlen);
16260573Skris	if (plen != rlen)
16360573Skris		fatal("plen != rlen");
16460573Skris	user = packet_get_string(&len);
16560573Skris	service = packet_get_string(&len);
16660573Skris	method = packet_get_string(&len);
16760573Skris	debug("userauth-request for user %s service %s method %s", user, service, method);
16860573Skris
16960573Skris	/* XXX we only allow the ssh-connection service */
17060573Skris	pw = auth_set_user(user, service);
17160573Skris	if (pw && strcmp(service, "ssh-connection")==0) {
17260573Skris		if (strcmp(method, "none") == 0) {
17360573Skris			authenticated =	ssh2_auth_none(pw);
17460573Skris		} else if (strcmp(method, "password") == 0) {
17560573Skris			authenticated =	ssh2_auth_password(pw);
17660573Skris		} else if (strcmp(method, "publickey") == 0) {
17760573Skris			authenticated =	ssh2_auth_pubkey(pw, raw, rlen);
17860573Skris		}
17960573Skris	}
18060573Skris	if (authenticated && pw && pw->pw_uid == 0 && !options.permit_root_login) {
18160573Skris		authenticated = 0;
18260573Skris		log("ROOT LOGIN REFUSED FROM %.200s",
18360573Skris		    get_canonical_hostname());
18460573Skris	}
18560573Skris
18660573Skris	/* Raise logging level */
18760573Skris	if (authenticated == 1 ||
18860573Skris	    attempt == AUTH_FAIL_LOG ||
18960573Skris	    strcmp(method, "password") == 0)
19060573Skris		authlog = log;
19160573Skris
19260573Skris	/* Log before sending the reply */
19360573Skris	if (authenticated == 1) {
19460573Skris		authmsg = "Accepted";
19560573Skris	} else if (authenticated == 0) {
19660573Skris		authmsg = "Failed";
19760573Skris	} else {
19860573Skris		authmsg = "Postponed";
19960573Skris	}
20060573Skris	authlog("%s %s for %.200s from %.200s port %d ssh2",
20160573Skris		authmsg,
20260573Skris		method,
20360573Skris		pw && pw->pw_uid == 0 ? "ROOT" : user,
20460573Skris		get_remote_ipaddr(),
20560573Skris		get_remote_port());
20660573Skris
20760573Skris	/* XXX todo: check if multiple auth methods are needed */
20860573Skris	if (authenticated == 1) {
20960573Skris		/* turn off userauth */
21060573Skris		dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &protocol_error);
21160573Skris		packet_start(SSH2_MSG_USERAUTH_SUCCESS);
21260573Skris		packet_send();
21360573Skris		packet_write_wait();
21460573Skris		/* now we can break out */
21560573Skris		userauth_success = 1;
21660573Skris	} else if (authenticated == 0) {
21760573Skris		packet_start(SSH2_MSG_USERAUTH_FAILURE);
21860573Skris		packet_put_cstring("publickey,password");	/* XXX dynamic */
21960573Skris		packet_put_char(0);				/* XXX partial success, unused */
22060573Skris		packet_send();
22160573Skris		packet_write_wait();
22260573Skris	}
22360573Skris
22460573Skris	xfree(service);
22560573Skris	xfree(user);
22660573Skris	xfree(method);
22760573Skris}
22860573Skris
22960573Skrisint
23060573Skrisssh2_auth_none(struct passwd *pw)
23160573Skris{
23260573Skris	packet_done();
23360573Skris	return auth_password(pw, "");
23460573Skris}
23560573Skrisint
23660573Skrisssh2_auth_password(struct passwd *pw)
23760573Skris{
23860573Skris	char *password;
23960573Skris	int authenticated = 0;
24060573Skris	int change;
24160573Skris	unsigned int len;
24260573Skris	change = packet_get_char();
24360573Skris	if (change)
24460573Skris		log("password change not supported");
24560573Skris	password = packet_get_string(&len);
24660573Skris	packet_done();
24760573Skris	if (options.password_authentication &&
24860573Skris	    auth_password(pw, password) == 1)
24960573Skris		authenticated = 1;
25060573Skris	memset(password, 0, len);
25160573Skris	xfree(password);
25260573Skris	return authenticated;
25360573Skris}
25460573Skrisint
25560573Skrisssh2_auth_pubkey(struct passwd *pw, unsigned char *raw, unsigned int rlen)
25660573Skris{
25760573Skris	Buffer b;
25860573Skris	Key *key;
25960573Skris	char *pkalg, *pkblob, *sig;
26060573Skris	unsigned int alen, blen, slen;
26160573Skris	int have_sig;
26260573Skris	int authenticated = 0;
26360573Skris
26460573Skris	if (options.dsa_authentication == 0) {
26560573Skris		debug("pubkey auth disabled");
26660573Skris		return 0;
26760573Skris	}
26860573Skris	if (datafellows & SSH_BUG_PUBKEYAUTH) {
26960573Skris		log("bug compatibility with ssh-2.0.13 pubkey not implemented");
27060573Skris		return 0;
27160573Skris	}
27260573Skris	have_sig = packet_get_char();
27360573Skris	pkalg = packet_get_string(&alen);
27460573Skris	if (strcmp(pkalg, KEX_DSS) != 0) {
27560573Skris		xfree(pkalg);
27660573Skris		log("bad pkalg %s", pkalg);	/*XXX*/
27760573Skris		return 0;
27860573Skris	}
27960573Skris	pkblob = packet_get_string(&blen);
28060573Skris	key = dsa_key_from_blob(pkblob, blen);
28160573Skris	if (key != NULL) {
28260573Skris		if (have_sig) {
28360573Skris			sig = packet_get_string(&slen);
28460573Skris			packet_done();
28560573Skris			buffer_init(&b);
28660573Skris			buffer_append(&b, session_id2, session_id2_len);
28760573Skris			buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
28860573Skris			if (slen + 4 > rlen)
28960573Skris				fatal("bad rlen/slen");
29060573Skris			buffer_append(&b, raw, rlen - slen - 4);
29160573Skris#ifdef DEBUG_DSS
29260573Skris			buffer_dump(&b);
29360573Skris#endif
29460573Skris			/* test for correct signature */
29560573Skris			if (user_dsa_key_allowed(pw, key) &&
29660573Skris			    dsa_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1)
29760573Skris				authenticated = 1;
29860573Skris			buffer_clear(&b);
29960573Skris			xfree(sig);
30060573Skris		} else {
30160573Skris			packet_done();
30260573Skris			debug("test key...");
30360573Skris			/* test whether pkalg/pkblob are acceptable */
30460573Skris			/* XXX fake reply and always send PK_OK ? */
30560573Skris			/*
30660573Skris			 * XXX this allows testing whether a user is allowed
30760573Skris			 * to login: if you happen to have a valid pubkey this
30860573Skris			 * message is sent. the message is NEVER sent at all
30960573Skris			 * if a user is not allowed to login. is this an
31060573Skris			 * issue? -markus
31160573Skris			 */
31260573Skris			if (user_dsa_key_allowed(pw, key)) {
31360573Skris				packet_start(SSH2_MSG_USERAUTH_PK_OK);
31460573Skris				packet_put_string(pkalg, alen);
31560573Skris				packet_put_string(pkblob, blen);
31660573Skris				packet_send();
31760573Skris				packet_write_wait();
31860573Skris				authenticated = -1;
31960573Skris			}
32060573Skris		}
32160573Skris		key_free(key);
32260573Skris	}
32360573Skris	xfree(pkalg);
32460573Skris	xfree(pkblob);
32560573Skris	return authenticated;
32660573Skris}
32760573Skris
32860573Skris/* set and get current user */
32960573Skris
33060573Skrisstruct passwd*
33160573Skrisauth_get_user(void)
33260573Skris{
33360573Skris	return (authctxt != NULL && authctxt->valid) ? &authctxt->pw : NULL;
33460573Skris}
33560573Skris
33660573Skrisstruct passwd*
33760573Skrisauth_set_user(char *u, char *s)
33860573Skris{
33960573Skris	struct passwd *pw, *copy;
34060573Skris
34160573Skris	if (authctxt == NULL) {
34260573Skris		authctxt = xmalloc(sizeof(*authctxt));
34360573Skris		authctxt->valid = 0;
34460573Skris		authctxt->user = xstrdup(u);
34560573Skris		authctxt->service = xstrdup(s);
34660573Skris		setproctitle("%s", u);
34760573Skris		pw = getpwnam(u);
34860573Skris		if (!pw || !allowed_user(pw)) {
34960573Skris			log("auth_set_user: illegal user %s", u);
35060573Skris			return NULL;
35160573Skris		}
35260573Skris		copy = &authctxt->pw;
35360573Skris		memset(copy, 0, sizeof(*copy));
35460573Skris		copy->pw_name = xstrdup(pw->pw_name);
35560573Skris		copy->pw_passwd = xstrdup(pw->pw_passwd);
35660573Skris		copy->pw_uid = pw->pw_uid;
35760573Skris		copy->pw_gid = pw->pw_gid;
35860573Skris		copy->pw_dir = xstrdup(pw->pw_dir);
35960573Skris		copy->pw_shell = xstrdup(pw->pw_shell);
36060573Skris		authctxt->valid = 1;
36160573Skris	} else {
36260573Skris		if (strcmp(u, authctxt->user) != 0 ||
36360573Skris		    strcmp(s, authctxt->service) != 0) {
36460573Skris			log("auth_set_user: missmatch: (%s,%s)!=(%s,%s)",
36560573Skris			    u, s, authctxt->user, authctxt->service);
36660573Skris			return NULL;
36760573Skris		}
36860573Skris	}
36960573Skris	return auth_get_user();
37060573Skris}
37160573Skris
37260573Skris/* return 1 if user allows given key */
37360573Skrisint
37460573Skrisuser_dsa_key_allowed(struct passwd *pw, Key *key)
37560573Skris{
37660573Skris	char line[8192], file[1024];
37760573Skris	int found_key = 0;
37860573Skris	unsigned int bits = -1;
37960573Skris	FILE *f;
38060573Skris	unsigned long linenum = 0;
38160573Skris	struct stat st;
38260573Skris	Key *found;
38360573Skris
38460573Skris	/* Temporarily use the user's uid. */
38560573Skris	temporarily_use_uid(pw->pw_uid);
38660573Skris
38760573Skris	/* The authorized keys. */
38860573Skris	snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir,
38960573Skris	    SSH_USER_PERMITTED_KEYS2);
39060573Skris
39160573Skris	/* Fail quietly if file does not exist */
39260573Skris	if (stat(file, &st) < 0) {
39360573Skris		/* Restore the privileged uid. */
39460573Skris		restore_uid();
39560573Skris		return 0;
39660573Skris	}
39760573Skris	/* Open the file containing the authorized keys. */
39860573Skris	f = fopen(file, "r");
39960573Skris	if (!f) {
40060573Skris		/* Restore the privileged uid. */
40160573Skris		restore_uid();
40260573Skris		return 0;
40360573Skris	}
40460573Skris	if (options.strict_modes) {
40560573Skris		int fail = 0;
40660573Skris		char buf[1024];
40760573Skris		/* Check open file in order to avoid open/stat races */
40860573Skris		if (fstat(fileno(f), &st) < 0 ||
40960573Skris		    (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
41060573Skris		    (st.st_mode & 022) != 0) {
41160573Skris			snprintf(buf, sizeof buf, "DSA authentication refused for %.100s: "
41260573Skris			    "bad ownership or modes for '%s'.", pw->pw_name, file);
41360573Skris			fail = 1;
41460573Skris		} else {
41560573Skris			/* Check path to SSH_USER_PERMITTED_KEYS */
41660573Skris			int i;
41760573Skris			static const char *check[] = {
41860573Skris				"", SSH_USER_DIR, NULL
41960573Skris			};
42060573Skris			for (i = 0; check[i]; i++) {
42160573Skris				snprintf(line, sizeof line, "%.500s/%.100s",
42260573Skris				    pw->pw_dir, check[i]);
42360573Skris				if (stat(line, &st) < 0 ||
42460573Skris				    (st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
42560573Skris				    (st.st_mode & 022) != 0) {
42660573Skris					snprintf(buf, sizeof buf,
42760573Skris					    "DSA authentication refused for %.100s: "
42860573Skris					    "bad ownership or modes for '%s'.",
42960573Skris					    pw->pw_name, line);
43060573Skris					fail = 1;
43160573Skris					break;
43260573Skris				}
43360573Skris			}
43460573Skris		}
43560573Skris		if (fail) {
43660573Skris			log(buf);
43760573Skris			fclose(f);
43860573Skris			restore_uid();
43960573Skris			return 0;
44060573Skris		}
44160573Skris	}
44260573Skris	found_key = 0;
44360573Skris	found = key_new(KEY_DSA);
44460573Skris
44560573Skris	while (fgets(line, sizeof(line), f)) {
44660573Skris		char *cp;
44760573Skris		linenum++;
44860573Skris		/* Skip leading whitespace, empty and comment lines. */
44960573Skris		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
45060573Skris			;
45160573Skris		if (!*cp || *cp == '\n' || *cp == '#')
45260573Skris			continue;
45360573Skris		bits = key_read(found, &cp);
45460573Skris		if (bits == 0)
45560573Skris			continue;
45660573Skris		if (key_equal(found, key)) {
45760573Skris			found_key = 1;
45860573Skris			debug("matching key found: file %s, line %ld",
45960573Skris			    file, linenum);
46060573Skris			break;
46160573Skris		}
46260573Skris	}
46360573Skris	restore_uid();
46460573Skris	fclose(f);
46560573Skris	key_free(found);
46660573Skris	return found_key;
46760573Skris}
468