]>
Commit | Line | Data |
---|---|---|
fc7f8f21 | 1 | /* $OpenBSD: auth2-hostbased.c,v 1.52 2023/03/05 05:34:09 dtucker Exp $ */ |
855bf3ac BL |
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" | |
855bf3ac | 27 | |
9f2abc47 DM |
28 | #include <sys/types.h> |
29 | ||
be02d7cb | 30 | #include <stdlib.h> |
9f2abc47 | 31 | #include <pwd.h> |
e3476ed0 | 32 | #include <string.h> |
d7834353 | 33 | #include <stdarg.h> |
9f2abc47 | 34 | |
855bf3ac | 35 | #include "xmalloc.h" |
d7834353 | 36 | #include "ssh2.h" |
855bf3ac | 37 | #include "packet.h" |
39be3dc2 | 38 | #include "kex.h" |
c7d39ac8 | 39 | #include "sshbuf.h" |
855bf3ac | 40 | #include "log.h" |
7acefbbc | 41 | #include "misc.h" |
855bf3ac | 42 | #include "servconf.h" |
eb76698b | 43 | #include "sshkey.h" |
d7834353 DM |
44 | #include "hostfile.h" |
45 | #include "auth.h" | |
855bf3ac | 46 | #include "canohost.h" |
d7834353 DM |
47 | #ifdef GSSAPI |
48 | #include "ssh-gss.h" | |
49 | #endif | |
855bf3ac BL |
50 | #include "monitor_wrap.h" |
51 | #include "pathnames.h" | |
eb76698b | 52 | #include "ssherr.h" |
1f729f06 | 53 | #include "match.h" |
855bf3ac BL |
54 | |
55 | /* import */ | |
56 | extern ServerOptions options; | |
855bf3ac BL |
57 | |
58 | static int | |
dbb339f0 | 59 | userauth_hostbased(struct ssh *ssh, const char *method) |
855bf3ac | 60 | { |
eb272ea4 | 61 | Authctxt *authctxt = ssh->authctxt; |
eb76698b | 62 | struct sshbuf *b; |
54d90ace | 63 | struct sshkey *key = NULL; |
14b5c635 | 64 | char *pkalg, *cuser, *chost; |
855bf3ac | 65 | u_char *pkblob, *sig; |
eb76698b | 66 | size_t alen, blen, slen; |
67 | int r, pktype, authenticated = 0; | |
855bf3ac | 68 | |
eb76698b | 69 | /* XXX use sshkey_froms() */ |
70 | if ((r = sshpkt_get_cstring(ssh, &pkalg, &alen)) != 0 || | |
71 | (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0 || | |
72 | (r = sshpkt_get_cstring(ssh, &chost, NULL)) != 0 || | |
73 | (r = sshpkt_get_cstring(ssh, &cuser, NULL)) != 0 || | |
74 | (r = sshpkt_get_string(ssh, &sig, &slen)) != 0) | |
816036f1 | 75 | fatal_fr(r, "parse packet"); |
855bf3ac | 76 | |
816036f1 | 77 | debug_f("cuser %s chost %s pkalg %s slen %zu", |
855bf3ac BL |
78 | cuser, chost, pkalg, slen); |
79 | #ifdef DEBUG_PK | |
80 | debug("signature:"); | |
086cc614 | 81 | sshbuf_dump_data(sig, slen, stderr); |
855bf3ac | 82 | #endif |
eb76698b | 83 | pktype = sshkey_type_from_name(pkalg); |
855bf3ac BL |
84 | if (pktype == KEY_UNSPEC) { |
85 | /* this is perfectly legal */ | |
816036f1 | 86 | logit_f("unsupported public key algorithm: %s", |
87 | pkalg); | |
eb76698b | 88 | goto done; |
89 | } | |
90 | if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) { | |
816036f1 | 91 | error_fr(r, "key_from_blob"); |
855bf3ac BL |
92 | goto done; |
93 | } | |
855bf3ac | 94 | if (key == NULL) { |
816036f1 | 95 | error_f("cannot decode key: %s", pkalg); |
855bf3ac BL |
96 | goto done; |
97 | } | |
98 | if (key->type != pktype) { | |
816036f1 | 99 | error_f("type mismatch for decoded key " |
100 | "(received %d, expected %d)", key->type, pktype); | |
855bf3ac BL |
101 | goto done; |
102 | } | |
e9f78d6b | 103 | if (match_pattern_list(pkalg, options.hostbased_accepted_algos, 0) != 1) { |
c2d9ced1 | 104 | logit_f("signature algorithm %s not in " |
105 | "HostbasedAcceptedAlgorithms", pkalg); | |
1f729f06 | 106 | goto done; |
107 | } | |
86e5737c | 108 | if ((r = sshkey_check_cert_sigtype(key, |
109 | options.ca_sign_algorithms)) != 0) { | |
816036f1 | 110 | logit_fr(r, "certificate signature algorithm %s", |
86e5737c | 111 | (key->cert == NULL || key->cert->signature_type == NULL) ? |
816036f1 | 112 | "(null)" : key->cert->signature_type); |
86e5737c | 113 | goto done; |
114 | } | |
1875042c | 115 | if ((r = sshkey_check_rsa_length(key, |
116 | options.required_rsa_size)) != 0) { | |
117 | logit_r(r, "refusing %s key", sshkey_type(key)); | |
118 | goto done; | |
119 | } | |
1f729f06 | 120 | |
74287f5d | 121 | if (!authctxt->valid || authctxt->user == NULL) { |
816036f1 | 122 | debug2_f("disabled because of invalid user"); |
74287f5d | 123 | goto done; |
124 | } | |
125 | ||
eb76698b | 126 | if ((b = sshbuf_new()) == NULL) |
816036f1 | 127 | fatal_f("sshbuf_new failed"); |
855bf3ac | 128 | /* reconstruct packet */ |
39be3dc2 | 129 | if ((r = sshbuf_put_stringb(b, ssh->kex->session_id)) != 0 || |
eb76698b | 130 | (r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 || |
131 | (r = sshbuf_put_cstring(b, authctxt->user)) != 0 || | |
14b5c635 | 132 | (r = sshbuf_put_cstring(b, authctxt->service)) != 0 || |
dbb339f0 | 133 | (r = sshbuf_put_cstring(b, method)) != 0 || |
eb76698b | 134 | (r = sshbuf_put_string(b, pkalg, alen)) != 0 || |
135 | (r = sshbuf_put_string(b, pkblob, blen)) != 0 || | |
136 | (r = sshbuf_put_cstring(b, chost)) != 0 || | |
137 | (r = sshbuf_put_cstring(b, cuser)) != 0) | |
816036f1 | 138 | fatal_fr(r, "reconstruct packet"); |
855bf3ac | 139 | #ifdef DEBUG_PK |
eb76698b | 140 | sshbuf_dump(b, stderr); |
855bf3ac | 141 | #endif |
20bdcd72 | 142 | |
8f574959 | 143 | auth2_record_info(authctxt, |
20bdcd72 DM |
144 | "client user \"%.100s\", client host \"%.100s\"", cuser, chost); |
145 | ||
855bf3ac BL |
146 | /* test for allowed key and correct signature */ |
147 | authenticated = 0; | |
04c091fc | 148 | if (PRIVSEP(hostbased_key_allowed(ssh, authctxt->pw, cuser, |
149 | chost, key)) && | |
eb76698b | 150 | PRIVSEP(sshkey_verify(key, sig, slen, |
b7e74ea0 | 151 | sshbuf_ptr(b), sshbuf_len(b), pkalg, ssh->compat, NULL)) == 0) |
855bf3ac BL |
152 | authenticated = 1; |
153 | ||
8f574959 | 154 | auth2_record_key(authctxt, authenticated, key); |
eb76698b | 155 | sshbuf_free(b); |
855bf3ac | 156 | done: |
816036f1 | 157 | debug2_f("authenticated %d", authenticated); |
8f574959 | 158 | sshkey_free(key); |
a627d42e DT |
159 | free(pkalg); |
160 | free(pkblob); | |
161 | free(cuser); | |
162 | free(chost); | |
163 | free(sig); | |
855bf3ac BL |
164 | return authenticated; |
165 | } | |
166 | ||
167 | /* return 1 if given hostkey is allowed */ | |
168 | int | |
04c091fc | 169 | hostbased_key_allowed(struct ssh *ssh, struct passwd *pw, |
170 | const char *cuser, char *chost, struct sshkey *key) | |
855bf3ac | 171 | { |
c158331f | 172 | const char *resolvedname, *ipaddr, *lookup, *reason; |
855bf3ac BL |
173 | HostStatus host_status; |
174 | int len; | |
c158331f | 175 | char *fp; |
855bf3ac | 176 | |
1aed65eb DM |
177 | if (auth_key_is_revoked(key)) |
178 | return 0; | |
179 | ||
95767262 | 180 | resolvedname = auth_get_canonical_hostname(ssh, options.use_dns); |
181 | ipaddr = ssh_remote_ipaddr(ssh); | |
855bf3ac | 182 | |
816036f1 | 183 | debug2_f("chost %s resolvedname %s ipaddr %s", |
855bf3ac BL |
184 | chost, resolvedname, ipaddr); |
185 | ||
a1d03a50 DM |
186 | if (((len = strlen(chost)) > 0) && chost[len - 1] == '.') { |
187 | debug2("stripping trailing dot from chost %s", chost); | |
188 | chost[len - 1] = '\0'; | |
189 | } | |
190 | ||
855bf3ac | 191 | if (options.hostbased_uses_name_from_packet_only) { |
5191df92 | 192 | if (auth_rhosts2(pw, cuser, chost, chost) == 0) { |
816036f1 | 193 | debug2_f("auth_rhosts2 refused user \"%.100s\" " |
194 | "host \"%.100s\" (from packet)", cuser, chost); | |
855bf3ac | 195 | return 0; |
5191df92 | 196 | } |
855bf3ac BL |
197 | lookup = chost; |
198 | } else { | |
855bf3ac | 199 | if (strcasecmp(resolvedname, chost) != 0) |
996acd24 | 200 | logit("userauth_hostbased mismatch: " |
855bf3ac BL |
201 | "client sends %s, but we resolve %s to %s", |
202 | chost, ipaddr, resolvedname); | |
5191df92 | 203 | if (auth_rhosts2(pw, cuser, resolvedname, ipaddr) == 0) { |
816036f1 | 204 | debug2_f("auth_rhosts2 refused " |
5191df92 | 205 | "user \"%.100s\" host \"%.100s\" addr \"%.100s\"", |
816036f1 | 206 | cuser, resolvedname, ipaddr); |
855bf3ac | 207 | return 0; |
5191df92 | 208 | } |
855bf3ac BL |
209 | lookup = resolvedname; |
210 | } | |
816036f1 | 211 | debug2_f("access allowed by auth_rhosts2"); |
855bf3ac | 212 | |
eb76698b | 213 | if (sshkey_is_cert(key) && |
d0bb1ce7 | 214 | sshkey_cert_check_authority_now(key, 1, 0, 0, lookup, &reason)) { |
c158331f DM |
215 | error("%s", reason); |
216 | auth_debug_add("%s", reason); | |
217 | return 0; | |
218 | } | |
219 | ||
855bf3ac BL |
220 | host_status = check_key_in_hostfiles(pw, key, lookup, |
221 | _PATH_SSH_SYSTEM_HOSTFILE, | |
222 | options.ignore_user_known_hosts ? NULL : _PATH_SSH_USER_HOSTFILE); | |
223 | ||
224 | /* backward compat if no key has been found. */ | |
c158331f | 225 | if (host_status == HOST_NEW) { |
855bf3ac BL |
226 | host_status = check_key_in_hostfiles(pw, key, lookup, |
227 | _PATH_SSH_SYSTEM_HOSTFILE2, | |
228 | options.ignore_user_known_hosts ? NULL : | |
229 | _PATH_SSH_USER_HOSTFILE2); | |
c158331f DM |
230 | } |
231 | ||
232 | if (host_status == HOST_OK) { | |
eb76698b | 233 | if (sshkey_is_cert(key)) { |
9ce86c92 | 234 | if ((fp = sshkey_fingerprint(key->cert->signature_key, |
235 | options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) | |
816036f1 | 236 | fatal_f("sshkey_fingerprint fail"); |
c158331f DM |
237 | verbose("Accepted certificate ID \"%s\" signed by " |
238 | "%s CA %s from %s@%s", key->cert->key_id, | |
eb76698b | 239 | sshkey_type(key->cert->signature_key), fp, |
c158331f DM |
240 | cuser, lookup); |
241 | } else { | |
9ce86c92 | 242 | if ((fp = sshkey_fingerprint(key, |
243 | options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) | |
816036f1 | 244 | fatal_f("sshkey_fingerprint fail"); |
c158331f | 245 | verbose("Accepted %s public key %s from %s@%s", |
eb76698b | 246 | sshkey_type(key), fp, cuser, lookup); |
c158331f | 247 | } |
a627d42e | 248 | free(fp); |
c158331f | 249 | } |
855bf3ac BL |
250 | |
251 | return (host_status == HOST_OK); | |
252 | } | |
253 | ||
254 | Authmethod method_hostbased = { | |
255 | "hostbased", | |
dbb339f0 | 256 | NULL, |
855bf3ac BL |
257 | userauth_hostbased, |
258 | &options.hostbased_authentication | |
259 | }; |