auth2-hostbased.c revision 323129
1/* $OpenBSD: auth2-hostbased.c,v 1.26 2016/03/07 19:02:43 djm Exp $ */
2/*
3 * Copyright (c) 2000 Markus Friedl.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "includes.h"
27
28#include <sys/types.h>
29
30#include <pwd.h>
31#include <string.h>
32#include <stdarg.h>
33
34#include "xmalloc.h"
35#include "ssh2.h"
36#include "packet.h"
37#include "buffer.h"
38#include "log.h"
39#include "misc.h"
40#include "servconf.h"
41#include "compat.h"
42#include "key.h"
43#include "hostfile.h"
44#include "auth.h"
45#include "canohost.h"
46#ifdef GSSAPI
47#include "ssh-gss.h"
48#endif
49#include "monitor_wrap.h"
50#include "pathnames.h"
51#include "match.h"
52
53/* import */
54extern ServerOptions options;
55extern u_char *session_id2;
56extern u_int session_id2_len;
57
58static int
59userauth_hostbased(Authctxt *authctxt)
60{
61	Buffer b;
62	Key *key = NULL;
63	char *pkalg, *cuser, *chost, *service;
64	u_char *pkblob, *sig;
65	u_int alen, blen, slen;
66	int pktype;
67	int authenticated = 0;
68
69	if (!authctxt->valid) {
70		debug2("userauth_hostbased: disabled because of invalid user");
71		return 0;
72	}
73	pkalg = packet_get_string(&alen);
74	pkblob = packet_get_string(&blen);
75	chost = packet_get_string(NULL);
76	cuser = packet_get_string(NULL);
77	sig = packet_get_string(&slen);
78
79	debug("userauth_hostbased: cuser %s chost %s pkalg %s slen %d",
80	    cuser, chost, pkalg, slen);
81#ifdef DEBUG_PK
82	debug("signature:");
83	buffer_init(&b);
84	buffer_append(&b, sig, slen);
85	buffer_dump(&b);
86	buffer_free(&b);
87#endif
88	pktype = key_type_from_name(pkalg);
89	if (pktype == KEY_UNSPEC) {
90		/* this is perfectly legal */
91		logit("userauth_hostbased: unsupported "
92		    "public key algorithm: %s", pkalg);
93		goto done;
94	}
95	key = key_from_blob(pkblob, blen);
96	if (key == NULL) {
97		error("userauth_hostbased: cannot decode key: %s", pkalg);
98		goto done;
99	}
100	if (key->type != pktype) {
101		error("userauth_hostbased: type mismatch for decoded key "
102		    "(received %d, expected %d)", key->type, pktype);
103		goto done;
104	}
105	if (key_type_plain(key->type) == KEY_RSA &&
106	    (datafellows & SSH_BUG_RSASIGMD5) != 0) {
107		error("Refusing RSA key because peer uses unsafe "
108		    "signature format");
109		goto done;
110	}
111	if (match_pattern_list(sshkey_ssh_name(key),
112	    options.hostbased_key_types, 0) != 1) {
113		logit("%s: key type %s not in HostbasedAcceptedKeyTypes",
114		    __func__, sshkey_type(key));
115		goto done;
116	}
117
118	service = datafellows & SSH_BUG_HBSERVICE ? "ssh-userauth" :
119	    authctxt->service;
120	buffer_init(&b);
121	buffer_put_string(&b, session_id2, session_id2_len);
122	/* reconstruct packet */
123	buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
124	buffer_put_cstring(&b, authctxt->user);
125	buffer_put_cstring(&b, service);
126	buffer_put_cstring(&b, "hostbased");
127	buffer_put_string(&b, pkalg, alen);
128	buffer_put_string(&b, pkblob, blen);
129	buffer_put_cstring(&b, chost);
130	buffer_put_cstring(&b, cuser);
131#ifdef DEBUG_PK
132	buffer_dump(&b);
133#endif
134
135	pubkey_auth_info(authctxt, key,
136	    "client user \"%.100s\", client host \"%.100s\"", cuser, chost);
137
138	/* test for allowed key and correct signature */
139	authenticated = 0;
140	if (PRIVSEP(hostbased_key_allowed(authctxt->pw, cuser, chost, key)) &&
141	    PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b),
142			buffer_len(&b))) == 1)
143		authenticated = 1;
144
145	buffer_free(&b);
146done:
147	debug2("userauth_hostbased: authenticated %d", authenticated);
148	if (key != NULL)
149		key_free(key);
150	free(pkalg);
151	free(pkblob);
152	free(cuser);
153	free(chost);
154	free(sig);
155	return authenticated;
156}
157
158/* return 1 if given hostkey is allowed */
159int
160hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost,
161    Key *key)
162{
163	struct ssh *ssh = active_state; /* XXX */
164	const char *resolvedname, *ipaddr, *lookup, *reason;
165	HostStatus host_status;
166	int len;
167	char *fp;
168
169	if (auth_key_is_revoked(key))
170		return 0;
171
172	resolvedname = auth_get_canonical_hostname(ssh, options.use_dns);
173	ipaddr = ssh_remote_ipaddr(ssh);
174
175	debug2("%s: chost %s resolvedname %s ipaddr %s", __func__,
176	    chost, resolvedname, ipaddr);
177
178	if (((len = strlen(chost)) > 0) && chost[len - 1] == '.') {
179		debug2("stripping trailing dot from chost %s", chost);
180		chost[len - 1] = '\0';
181	}
182
183	if (options.hostbased_uses_name_from_packet_only) {
184		if (auth_rhosts2(pw, cuser, chost, chost) == 0) {
185			debug2("%s: auth_rhosts2 refused "
186			    "user \"%.100s\" host \"%.100s\" (from packet)",
187			    __func__, cuser, chost);
188			return 0;
189		}
190		lookup = chost;
191	} else {
192		if (strcasecmp(resolvedname, chost) != 0)
193			logit("userauth_hostbased mismatch: "
194			    "client sends %s, but we resolve %s to %s",
195			    chost, ipaddr, resolvedname);
196		if (auth_rhosts2(pw, cuser, resolvedname, ipaddr) == 0) {
197			debug2("%s: auth_rhosts2 refused "
198			    "user \"%.100s\" host \"%.100s\" addr \"%.100s\"",
199			    __func__, cuser, resolvedname, ipaddr);
200			return 0;
201		}
202		lookup = resolvedname;
203	}
204	debug2("%s: access allowed by auth_rhosts2", __func__);
205
206	if (key_is_cert(key) &&
207	    key_cert_check_authority(key, 1, 0, lookup, &reason)) {
208		error("%s", reason);
209		auth_debug_add("%s", reason);
210		return 0;
211	}
212
213	host_status = check_key_in_hostfiles(pw, key, lookup,
214	    _PATH_SSH_SYSTEM_HOSTFILE,
215	    options.ignore_user_known_hosts ? NULL : _PATH_SSH_USER_HOSTFILE);
216
217	/* backward compat if no key has been found. */
218	if (host_status == HOST_NEW) {
219		host_status = check_key_in_hostfiles(pw, key, lookup,
220		    _PATH_SSH_SYSTEM_HOSTFILE2,
221		    options.ignore_user_known_hosts ? NULL :
222		    _PATH_SSH_USER_HOSTFILE2);
223	}
224
225	if (host_status == HOST_OK) {
226		if (key_is_cert(key)) {
227			if ((fp = sshkey_fingerprint(key->cert->signature_key,
228			    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
229				fatal("%s: sshkey_fingerprint fail", __func__);
230			verbose("Accepted certificate ID \"%s\" signed by "
231			    "%s CA %s from %s@%s", key->cert->key_id,
232			    key_type(key->cert->signature_key), fp,
233			    cuser, lookup);
234		} else {
235			if ((fp = sshkey_fingerprint(key,
236			    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
237				fatal("%s: sshkey_fingerprint fail", __func__);
238			verbose("Accepted %s public key %s from %s@%s",
239			    key_type(key), fp, cuser, lookup);
240		}
241		free(fp);
242	}
243
244	return (host_status == HOST_OK);
245}
246
247Authmethod method_hostbased = {
248	"hostbased",
249	userauth_hostbased,
250	&options.hostbased_authentication
251};
252