1323124Sdes/* $OpenBSD: auth2-hostbased.c,v 1.26 2016/03/07 19:02:43 djm Exp $ */
298675Sdes/*
398675Sdes * Copyright (c) 2000 Markus Friedl.  All rights reserved.
498675Sdes *
598675Sdes * Redistribution and use in source and binary forms, with or without
698675Sdes * modification, are permitted provided that the following conditions
798675Sdes * are met:
898675Sdes * 1. Redistributions of source code must retain the above copyright
998675Sdes *    notice, this list of conditions and the following disclaimer.
1098675Sdes * 2. Redistributions in binary form must reproduce the above copyright
1198675Sdes *    notice, this list of conditions and the following disclaimer in the
1298675Sdes *    documentation and/or other materials provided with the distribution.
1398675Sdes *
1498675Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1598675Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1698675Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1798675Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1898675Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1998675Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2098675Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2198675Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2298675Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2398675Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2498675Sdes */
2598675Sdes
2698675Sdes#include "includes.h"
2798675Sdes
28162852Sdes#include <sys/types.h>
29162852Sdes
30162852Sdes#include <pwd.h>
31162852Sdes#include <string.h>
32162852Sdes#include <stdarg.h>
33162852Sdes
34162852Sdes#include "xmalloc.h"
3598675Sdes#include "ssh2.h"
3698675Sdes#include "packet.h"
3798675Sdes#include "buffer.h"
3898675Sdes#include "log.h"
39295367Sdes#include "misc.h"
4098675Sdes#include "servconf.h"
4198675Sdes#include "compat.h"
42162852Sdes#include "key.h"
43162852Sdes#include "hostfile.h"
4498675Sdes#include "auth.h"
4598675Sdes#include "canohost.h"
46162852Sdes#ifdef GSSAPI
47162852Sdes#include "ssh-gss.h"
48162852Sdes#endif
4998675Sdes#include "monitor_wrap.h"
5098675Sdes#include "pathnames.h"
51295367Sdes#include "match.h"
5298675Sdes
5398675Sdes/* import */
5498675Sdesextern ServerOptions options;
5598675Sdesextern u_char *session_id2;
56124208Sdesextern u_int session_id2_len;
5798675Sdes
5898675Sdesstatic int
5998675Sdesuserauth_hostbased(Authctxt *authctxt)
6098675Sdes{
6198675Sdes	Buffer b;
6298675Sdes	Key *key = NULL;
6398675Sdes	char *pkalg, *cuser, *chost, *service;
6498675Sdes	u_char *pkblob, *sig;
6598675Sdes	u_int alen, blen, slen;
6698675Sdes	int pktype;
6798675Sdes	int authenticated = 0;
6898675Sdes
6998675Sdes	if (!authctxt->valid) {
7098675Sdes		debug2("userauth_hostbased: disabled because of invalid user");
7198675Sdes		return 0;
7298675Sdes	}
7398675Sdes	pkalg = packet_get_string(&alen);
7498675Sdes	pkblob = packet_get_string(&blen);
7598675Sdes	chost = packet_get_string(NULL);
7698675Sdes	cuser = packet_get_string(NULL);
7798675Sdes	sig = packet_get_string(&slen);
7898675Sdes
7998675Sdes	debug("userauth_hostbased: cuser %s chost %s pkalg %s slen %d",
8098675Sdes	    cuser, chost, pkalg, slen);
8198675Sdes#ifdef DEBUG_PK
8298675Sdes	debug("signature:");
8398675Sdes	buffer_init(&b);
8498675Sdes	buffer_append(&b, sig, slen);
8598675Sdes	buffer_dump(&b);
8698675Sdes	buffer_free(&b);
8798675Sdes#endif
8898675Sdes	pktype = key_type_from_name(pkalg);
8998675Sdes	if (pktype == KEY_UNSPEC) {
9098675Sdes		/* this is perfectly legal */
91124208Sdes		logit("userauth_hostbased: unsupported "
9298675Sdes		    "public key algorithm: %s", pkalg);
9398675Sdes		goto done;
9498675Sdes	}
9598675Sdes	key = key_from_blob(pkblob, blen);
9698675Sdes	if (key == NULL) {
9798675Sdes		error("userauth_hostbased: cannot decode key: %s", pkalg);
9898675Sdes		goto done;
9998675Sdes	}
10098675Sdes	if (key->type != pktype) {
10198675Sdes		error("userauth_hostbased: type mismatch for decoded key "
10298675Sdes		    "(received %d, expected %d)", key->type, pktype);
10398675Sdes		goto done;
10498675Sdes	}
105262566Sdes	if (key_type_plain(key->type) == KEY_RSA &&
106262566Sdes	    (datafellows & SSH_BUG_RSASIGMD5) != 0) {
107262566Sdes		error("Refusing RSA key because peer uses unsafe "
108262566Sdes		    "signature format");
109262566Sdes		goto done;
110262566Sdes	}
111295367Sdes	if (match_pattern_list(sshkey_ssh_name(key),
112295367Sdes	    options.hostbased_key_types, 0) != 1) {
113295367Sdes		logit("%s: key type %s not in HostbasedAcceptedKeyTypes",
114295367Sdes		    __func__, sshkey_type(key));
115295367Sdes		goto done;
116295367Sdes	}
117295367Sdes
11898675Sdes	service = datafellows & SSH_BUG_HBSERVICE ? "ssh-userauth" :
11998675Sdes	    authctxt->service;
12098675Sdes	buffer_init(&b);
12198675Sdes	buffer_put_string(&b, session_id2, session_id2_len);
12298675Sdes	/* reconstruct packet */
12398675Sdes	buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
12498675Sdes	buffer_put_cstring(&b, authctxt->user);
12598675Sdes	buffer_put_cstring(&b, service);
12698675Sdes	buffer_put_cstring(&b, "hostbased");
12798675Sdes	buffer_put_string(&b, pkalg, alen);
12898675Sdes	buffer_put_string(&b, pkblob, blen);
12998675Sdes	buffer_put_cstring(&b, chost);
13098675Sdes	buffer_put_cstring(&b, cuser);
13198675Sdes#ifdef DEBUG_PK
13298675Sdes	buffer_dump(&b);
13398675Sdes#endif
134255767Sdes
135255767Sdes	pubkey_auth_info(authctxt, key,
136255767Sdes	    "client user \"%.100s\", client host \"%.100s\"", cuser, chost);
137255767Sdes
13898675Sdes	/* test for allowed key and correct signature */
13998675Sdes	authenticated = 0;
14098675Sdes	if (PRIVSEP(hostbased_key_allowed(authctxt->pw, cuser, chost, key)) &&
14198675Sdes	    PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b),
14298675Sdes			buffer_len(&b))) == 1)
14398675Sdes		authenticated = 1;
14498675Sdes
145126274Sdes	buffer_free(&b);
14698675Sdesdone:
14798675Sdes	debug2("userauth_hostbased: authenticated %d", authenticated);
14898675Sdes	if (key != NULL)
14998675Sdes		key_free(key);
150255767Sdes	free(pkalg);
151255767Sdes	free(pkblob);
152255767Sdes	free(cuser);
153255767Sdes	free(chost);
154255767Sdes	free(sig);
15598675Sdes	return authenticated;
15698675Sdes}
15798675Sdes
15898675Sdes/* return 1 if given hostkey is allowed */
15998675Sdesint
16098675Sdeshostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost,
16198675Sdes    Key *key)
16298675Sdes{
163323124Sdes	struct ssh *ssh = active_state; /* XXX */
164215116Sdes	const char *resolvedname, *ipaddr, *lookup, *reason;
16598675Sdes	HostStatus host_status;
16698675Sdes	int len;
167215116Sdes	char *fp;
16898675Sdes
169204917Sdes	if (auth_key_is_revoked(key))
170204917Sdes		return 0;
171204917Sdes
172323124Sdes	resolvedname = auth_get_canonical_hostname(ssh, options.use_dns);
173323124Sdes	ipaddr = ssh_remote_ipaddr(ssh);
17498675Sdes
175295367Sdes	debug2("%s: chost %s resolvedname %s ipaddr %s", __func__,
17698675Sdes	    chost, resolvedname, ipaddr);
17798675Sdes
178181111Sdes	if (((len = strlen(chost)) > 0) && chost[len - 1] == '.') {
179181111Sdes		debug2("stripping trailing dot from chost %s", chost);
180181111Sdes		chost[len - 1] = '\0';
181181111Sdes	}
182181111Sdes
18398675Sdes	if (options.hostbased_uses_name_from_packet_only) {
184295367Sdes		if (auth_rhosts2(pw, cuser, chost, chost) == 0) {
185295367Sdes			debug2("%s: auth_rhosts2 refused "
186295367Sdes			    "user \"%.100s\" host \"%.100s\" (from packet)",
187295367Sdes			    __func__, cuser, chost);
18898675Sdes			return 0;
189295367Sdes		}
19098675Sdes		lookup = chost;
19198675Sdes	} else {
19298675Sdes		if (strcasecmp(resolvedname, chost) != 0)
193124208Sdes			logit("userauth_hostbased mismatch: "
19498675Sdes			    "client sends %s, but we resolve %s to %s",
19598675Sdes			    chost, ipaddr, resolvedname);
196295367Sdes		if (auth_rhosts2(pw, cuser, resolvedname, ipaddr) == 0) {
197295367Sdes			debug2("%s: auth_rhosts2 refused "
198295367Sdes			    "user \"%.100s\" host \"%.100s\" addr \"%.100s\"",
199295367Sdes			    __func__, cuser, resolvedname, ipaddr);
20098675Sdes			return 0;
201295367Sdes		}
20298675Sdes		lookup = resolvedname;
20398675Sdes	}
204295367Sdes	debug2("%s: access allowed by auth_rhosts2", __func__);
20598675Sdes
206215116Sdes	if (key_is_cert(key) &&
207215116Sdes	    key_cert_check_authority(key, 1, 0, lookup, &reason)) {
208215116Sdes		error("%s", reason);
209215116Sdes		auth_debug_add("%s", reason);
210215116Sdes		return 0;
211215116Sdes	}
212215116Sdes
21398675Sdes	host_status = check_key_in_hostfiles(pw, key, lookup,
21498675Sdes	    _PATH_SSH_SYSTEM_HOSTFILE,
21598675Sdes	    options.ignore_user_known_hosts ? NULL : _PATH_SSH_USER_HOSTFILE);
21698675Sdes
21798675Sdes	/* backward compat if no key has been found. */
218215116Sdes	if (host_status == HOST_NEW) {
21998675Sdes		host_status = check_key_in_hostfiles(pw, key, lookup,
22098675Sdes		    _PATH_SSH_SYSTEM_HOSTFILE2,
22198675Sdes		    options.ignore_user_known_hosts ? NULL :
22298675Sdes		    _PATH_SSH_USER_HOSTFILE2);
223215116Sdes	}
22498675Sdes
225215116Sdes	if (host_status == HOST_OK) {
226215116Sdes		if (key_is_cert(key)) {
227295367Sdes			if ((fp = sshkey_fingerprint(key->cert->signature_key,
228295367Sdes			    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
229295367Sdes				fatal("%s: sshkey_fingerprint fail", __func__);
230215116Sdes			verbose("Accepted certificate ID \"%s\" signed by "
231215116Sdes			    "%s CA %s from %s@%s", key->cert->key_id,
232215116Sdes			    key_type(key->cert->signature_key), fp,
233215116Sdes			    cuser, lookup);
234215116Sdes		} else {
235295367Sdes			if ((fp = sshkey_fingerprint(key,
236295367Sdes			    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
237295367Sdes				fatal("%s: sshkey_fingerprint fail", __func__);
238215116Sdes			verbose("Accepted %s public key %s from %s@%s",
239215116Sdes			    key_type(key), fp, cuser, lookup);
240215116Sdes		}
241255767Sdes		free(fp);
242215116Sdes	}
243215116Sdes
24498675Sdes	return (host_status == HOST_OK);
24598675Sdes}
24698675Sdes
24798675SdesAuthmethod method_hostbased = {
24898675Sdes	"hostbased",
24998675Sdes	userauth_hostbased,
25098675Sdes	&options.hostbased_authentication
25198675Sdes};
252