Coverage Report

Created: 2026-10-23 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssh/auth2-hostbased.c
Line
Count
Source
1
/* $OpenBSD: auth2-hostbased.c,v 1.59 2026/09/16 00:31:27 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 <stdlib.h>
31
#include <pwd.h>
32
#include <string.h>
33
#include <stdarg.h>
34
35
#include "xmalloc.h"
36
#include "ssh2.h"
37
#include "packet.h"
38
#include "kex.h"
39
#include "sshbuf.h"
40
#include "log.h"
41
#include "misc.h"
42
#include "servconf.h"
43
#include "sshkey.h"
44
#include "hostfile.h"
45
#include "auth.h"
46
#include "canohost.h"
47
#ifdef GSSAPI
48
#include "ssh-gss.h"
49
#endif
50
#include "monitor_wrap.h"
51
#include "pathnames.h"
52
#include "ssherr.h"
53
#include "match.h"
54
55
/* import */
56
extern ServerOptions options;
57
extern struct authmethod_cfg methodcfg_hostbased;
58
59
static int
60
userauth_hostbased(struct ssh *ssh, const char *method)
61
5
{
62
5
  Authctxt *authctxt = ssh->authctxt;
63
5
  struct sshbuf *keyblob = NULL, *b = NULL;
64
5
  struct sshkey *key = NULL;
65
5
  char *pkalg, *cuser, *chost;
66
5
  u_char *sig;
67
5
  size_t alen, slen;
68
5
  int r, pktype, authenticated = 0;
69
70
5
  if ((r = sshpkt_get_cstring(ssh, &pkalg, &alen)) != 0 ||
71
3
      (r = sshpkt_getb_froms(ssh, &keyblob)) != 0 ||
72
1
      (r = sshpkt_get_cstring(ssh, &chost, NULL)) != 0 ||
73
0
      (r = sshpkt_get_cstring(ssh, &cuser, NULL)) != 0 ||
74
0
      (r = sshpkt_get_string(ssh, &sig, &slen)) != 0)
75
5
    fatal_fr(r, "parse packet");
76
77
0
  debug_f("cuser %s chost %s pkalg %s slen %zu",
78
0
      cuser, chost, pkalg, slen);
79
#ifdef DEBUG_PK
80
  debug("signature:");
81
  sshbuf_dump_data(sig, slen, stderr);
82
#endif
83
0
  pktype = sshkey_type_from_name(pkalg);
84
0
  if (pktype == KEY_UNSPEC) {
85
    /* this is perfectly legal */
86
0
    logit_f("unsupported public key algorithm: %s",
87
0
        pkalg);
88
0
    goto done;
89
0
  }
90
0
  if (match_pattern_list(pkalg,
91
0
      options.hostbased_accepted_algos, 0) != 1) {
92
0
    logit_f("signature algorithm %s not in "
93
0
        "HostbasedAcceptedAlgorithms", pkalg);
94
0
    goto done;
95
0
  }
96
0
  if ((b = sshbuf_fromb(keyblob)) == NULL)
97
0
    fatal_f("sshbuf_fromb failed");
98
0
  switch ((r = sshkey_fromb_allowlist(b, &key,
99
0
      options.hostbased_accepted_algos, options.ca_sign_algorithms))) {
100
0
  case 0:
101
    /* ok */
102
0
    break;
103
0
  case SSH_ERR_KEY_ALG_UNSUPPORTED:
104
    /* This shouldn't happen unless the client is being weird */
105
0
    logit_f("key algorithm differs from signature algorithm %s and "
106
0
        "is not in HostbasedAcceptedAlgorithms", pkalg);
107
0
    goto done;
108
0
  case SSH_ERR_SIGN_ALG_UNSUPPORTED:
109
0
    logit_fr(r, "certificate signature algorithm not in "
110
0
        "CASignatureAlgorithms");
111
0
    goto done;
112
0
  default:
113
0
    error_fr(r, "parse key");
114
0
    goto done;
115
0
  }
116
0
  sshbuf_free(b);
117
0
  b = NULL;
118
119
0
  if (key->type != pktype || (sshkey_type_plain(pktype) == KEY_ECDSA &&
120
0
      sshkey_ecdsa_nid_from_name(pkalg) != key->ecdsa_nid)) {
121
0
    error_f("key type mismatch for decoded key "
122
0
        "(received %s, expected %s)", sshkey_ssh_name(key), pkalg);
123
0
    goto done;
124
0
  }
125
0
  if ((r = sshkey_check_rsa_length(key,
126
0
      options.required_rsa_size)) != 0) {
127
0
    logit_r(r, "refusing %s key", sshkey_type(key));
128
0
    goto done;
129
0
  }
130
131
0
  if (!authctxt->valid || authctxt->user == NULL) {
132
0
    debug2_f("disabled because of invalid user");
133
0
    goto done;
134
0
  }
135
136
0
  if ((b = sshbuf_new()) == NULL)
137
0
    fatal_f("sshbuf_new failed");
138
  /* reconstruct packet */
139
0
  if ((r = sshbuf_put_stringb(b, ssh->kex->session_id)) != 0 ||
140
0
      (r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
141
0
      (r = sshbuf_put_cstring(b, authctxt->user)) != 0 ||
142
0
      (r = sshbuf_put_cstring(b, authctxt->service)) != 0 ||
143
0
      (r = sshbuf_put_cstring(b, method)) != 0 ||
144
0
      (r = sshbuf_put_string(b, pkalg, alen)) != 0 ||
145
0
      (r = sshbuf_put_stringb(b, keyblob)) != 0 ||
146
0
      (r = sshbuf_put_cstring(b, chost)) != 0 ||
147
0
      (r = sshbuf_put_cstring(b, cuser)) != 0)
148
0
    fatal_fr(r, "reconstruct packet");
149
#ifdef DEBUG_PK
150
  sshbuf_dump(b, stderr);
151
#endif
152
153
0
  auth2_record_info(authctxt,
154
0
      "client user \"%.100s\", client host \"%.100s\"", cuser, chost);
155
156
  /* test for allowed key and correct signature */
157
0
  authenticated = 0;
158
0
  if (mm_hostbased_key_allowed(ssh, authctxt->pw, cuser,
159
0
      chost, key) &&
160
0
      mm_sshkey_verify(key, sig, slen,
161
0
      sshbuf_ptr(b), sshbuf_len(b), pkalg, ssh->compat, NULL) == 0)
162
0
    authenticated = 1;
163
164
0
  auth2_record_key(authctxt, authenticated, key);
165
0
done:
166
0
  debug2_f("authenticated %d", authenticated);
167
0
  sshkey_free(key);
168
0
  sshbuf_free(b);
169
0
  sshbuf_free(keyblob);
170
0
  free(pkalg);
171
0
  free(cuser);
172
0
  free(chost);
173
0
  free(sig);
174
0
  return authenticated;
175
0
}
176
177
/* return 1 if given hostkey is allowed */
178
int
179
hostbased_key_allowed(struct ssh *ssh, struct passwd *pw,
180
    const char *cuser, char *chost, struct sshkey *key)
181
0
{
182
0
  const char *resolvedname, *ipaddr, *lookup, *reason;
183
0
  HostStatus host_status;
184
0
  int len;
185
0
  char *fp;
186
187
0
  if (auth_key_is_revoked(key))
188
0
    return 0;
189
190
0
  resolvedname = auth_get_canonical_hostname(ssh, options.use_dns);
191
0
  ipaddr = ssh_remote_ipaddr(ssh);
192
193
0
  debug2_f("chost %s resolvedname %s ipaddr %s",
194
0
      chost, resolvedname, ipaddr);
195
196
0
  if (((len = strlen(chost)) > 0) && chost[len - 1] == '.') {
197
0
    debug2("stripping trailing dot from chost %s", chost);
198
0
    chost[len - 1] = '\0';
199
0
  }
200
201
0
  if (options.hostbased_uses_name_from_packet_only) {
202
0
    if (auth_rhosts2(pw, cuser, chost, chost) == 0) {
203
0
      debug2_f("auth_rhosts2 refused user \"%.100s\" "
204
0
          "host \"%.100s\" (from packet)", cuser, chost);
205
0
      return 0;
206
0
    }
207
0
    lookup = chost;
208
0
  } else {
209
0
    if (strcasecmp(resolvedname, chost) != 0)
210
0
      logit("userauth_hostbased mismatch: "
211
0
          "client sends %s, but we resolve %s to %s",
212
0
          chost, ipaddr, resolvedname);
213
0
    if (auth_rhosts2(pw, cuser, resolvedname, ipaddr) == 0) {
214
0
      debug2_f("auth_rhosts2 refused "
215
0
          "user \"%.100s\" host \"%.100s\" addr \"%.100s\"",
216
0
          cuser, resolvedname, ipaddr);
217
0
      return 0;
218
0
    }
219
0
    lookup = resolvedname;
220
0
  }
221
0
  debug2_f("access allowed by auth_rhosts2");
222
223
0
  if (sshkey_is_cert(key) && sshkey_cert_check_host(key, lookup,
224
0
       options.ca_sign_algorithms, &reason) != 0) {
225
0
    if ((fp = sshkey_fingerprint(key->cert->signature_key,
226
0
        options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
227
0
      fatal_f("sshkey_fingerprint fail");
228
0
    error("Refusing certificate ID \"%s\" serial=%llu signed by "
229
0
        "%s CA %s: %s", key->cert->key_id,
230
0
        (unsigned long long)key->cert->serial,
231
0
        sshkey_type(key->cert->signature_key), fp, reason);
232
0
    auth_debug_add("Refused Certificate ID \"%s\" serial=%llu: %s",
233
0
        key->cert->key_id, (unsigned long long)key->cert->serial,
234
0
        reason);
235
0
    free(fp);
236
0
    return 0;
237
0
  }
238
239
0
  host_status = check_key_in_hostfiles(pw, key, lookup,
240
0
      _PATH_SSH_SYSTEM_HOSTFILE,
241
0
      options.ignore_user_known_hosts ? NULL : _PATH_SSH_USER_HOSTFILE);
242
243
  /* backward compat if no key has been found. */
244
0
  if (host_status == HOST_NEW) {
245
0
    host_status = check_key_in_hostfiles(pw, key, lookup,
246
0
        _PATH_SSH_SYSTEM_HOSTFILE2,
247
0
        options.ignore_user_known_hosts ? NULL :
248
0
        _PATH_SSH_USER_HOSTFILE2);
249
0
  }
250
251
0
  if (host_status == HOST_OK) {
252
0
    if (sshkey_is_cert(key)) {
253
0
      if ((fp = sshkey_fingerprint(key->cert->signature_key,
254
0
          options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
255
0
        fatal_f("sshkey_fingerprint fail");
256
0
      verbose("Accepted certificate ID \"%s\" signed by "
257
0
          "%s CA %s from %s@%s", key->cert->key_id,
258
0
          sshkey_type(key->cert->signature_key), fp,
259
0
          cuser, lookup);
260
0
    } else {
261
0
      if ((fp = sshkey_fingerprint(key,
262
0
          options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
263
0
        fatal_f("sshkey_fingerprint fail");
264
0
      verbose("Accepted %s public key %s from %s@%s",
265
0
          sshkey_type(key), fp, cuser, lookup);
266
0
    }
267
0
    free(fp);
268
0
  }
269
270
0
  return (host_status == HOST_OK);
271
0
}
272
273
Authmethod method_hostbased = {
274
  &methodcfg_hostbased,
275
  userauth_hostbased,
276
};