From fcee231f9e4cc51d443b8267223452f6bc4143db Mon Sep 17 00:00:00 2001 From: djm Date: Sun, 19 Dec 2021 22:12:30 +0000 Subject: [PATCH] sshd side of hostbound public key auth This is identical to the standard "publickey" method, but it also includes the initial server hostkey in the message signed by the client. feedback / ok markus@ --- usr.bin/ssh/auth2-pubkey.c | 37 +++++++++++++++++++++++++++++-------- usr.bin/ssh/monitor.c | 26 +++++++++++++++++++++----- 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/usr.bin/ssh/auth2-pubkey.c b/usr.bin/ssh/auth2-pubkey.c index 54f6f043c36..afde053dba5 100644 --- a/usr.bin/ssh/auth2-pubkey.c +++ b/usr.bin/ssh/auth2-pubkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-pubkey.c,v 1.111 2021/12/19 22:12:07 djm Exp $ */ +/* $OpenBSD: auth2-pubkey.c,v 1.112 2021/12/19 22:12:30 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -64,6 +64,7 @@ #include "authfile.h" #include "match.h" #include "ssherr.h" +#include "kex.h" #include "channels.h" /* XXX for session.h */ #include "session.h" /* XXX for child_set_env(); refactor? */ #include "sk-api.h" @@ -88,19 +89,34 @@ userauth_pubkey(struct ssh *ssh, const char *method) Authctxt *authctxt = ssh->authctxt; struct passwd *pw = authctxt->pw; struct sshbuf *b = NULL; - struct sshkey *key = NULL; + struct sshkey *key = NULL, *hostkey = NULL; char *pkalg = NULL, *userstyle = NULL, *key_s = NULL, *ca_s = NULL; u_char *pkblob = NULL, *sig = NULL, have_sig; size_t blen, slen; - int r, pktype; + int hostbound, r, pktype; int req_presence = 0, req_verify = 0, authenticated = 0; struct sshauthopt *authopts = NULL; struct sshkey_sig_details *sig_details = NULL; + hostbound = strcmp(method, "publickey-hostbound-v00@openssh.com") == 0; + if ((r = sshpkt_get_u8(ssh, &have_sig)) != 0 || (r = sshpkt_get_cstring(ssh, &pkalg, NULL)) != 0 || (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0) - fatal_fr(r, "parse packet"); + fatal_fr(r, "parse %s packet", method); + + /* hostbound auth includes the hostkey offered at initial KEX */ + if (hostbound) { + if ((r = sshpkt_getb_froms(ssh, &b)) != 0 || + (r = sshkey_fromb(b, &hostkey)) != 0) + fatal_fr(r, "parse %s hostkey", method); + if (ssh->kex->initial_hostkey == NULL) + fatal_f("internal error: initial hostkey not recorded"); + if (!sshkey_equal(hostkey, ssh->kex->initial_hostkey)) + fatal_f("%s packet contained wrong host key", method); + sshbuf_free(b); + b = NULL; + } if (log_level_get() >= SYSLOG_LEVEL_DEBUG2) { char *keystring; @@ -163,7 +179,8 @@ userauth_pubkey(struct ssh *ssh, const char *method) ca_s = format_key(key->cert->signature_key); if (have_sig) { - debug3_f("have %s signature for %s%s%s", pkalg, key_s, + debug3_f("%s have %s signature for %s%s%s", + method, pkalg, key_s, ca_s == NULL ? "" : " CA ", ca_s == NULL ? "" : ca_s); if ((r = sshpkt_get_string(ssh, &sig, &slen)) != 0 || (r = sshpkt_get_end(ssh)) != 0) @@ -193,7 +210,10 @@ userauth_pubkey(struct ssh *ssh, const char *method) (r = sshbuf_put_u8(b, have_sig)) != 0 || (r = sshbuf_put_cstring(b, pkalg)) != 0 || (r = sshbuf_put_string(b, pkblob, blen)) != 0) - fatal_fr(r, "reconstruct packet"); + fatal_fr(r, "reconstruct %s packet", method); + if (hostbound && + (r = sshkey_puts(ssh->kex->initial_hostkey, b)) != 0) + fatal_fr(r, "reconstruct %s packet", method); #ifdef DEBUG_PK sshbuf_dump(b, stderr); #endif @@ -243,7 +263,7 @@ userauth_pubkey(struct ssh *ssh, const char *method) } auth2_record_key(authctxt, authenticated, key); } else { - debug_f("test pkalg %s pkblob %s%s%s", pkalg, key_s, + debug_f("%s test pkalg %s pkblob %s%s%s", method, pkalg, key_s, ca_s == NULL ? "" : " CA ", ca_s == NULL ? "" : ca_s); if ((r = sshpkt_get_end(ssh)) != 0) @@ -282,6 +302,7 @@ done: sshbuf_free(b); sshauthopt_free(authopts); sshkey_free(key); + sshkey_free(hostkey); free(userstyle); free(pkalg); free(pkblob); @@ -1064,7 +1085,7 @@ user_key_allowed(struct ssh *ssh, struct passwd *pw, struct sshkey *key, Authmethod method_pubkey = { "publickey", - NULL, + "publickey-hostbound-v00@openssh.com", userauth_pubkey, &options.pubkey_authentication }; diff --git a/usr.bin/ssh/monitor.c b/usr.bin/ssh/monitor.c index 3318e124397..f44e0549f3d 100644 --- a/usr.bin/ssh/monitor.c +++ b/usr.bin/ssh/monitor.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor.c,v 1.228 2021/08/11 05:20:17 djm Exp $ */ +/* $OpenBSD: monitor.c,v 1.229 2021/12/19 22:12:30 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -997,11 +997,12 @@ static int monitor_valid_userblob(struct ssh *ssh, const u_char *data, u_int datalen) { struct sshbuf *b; + struct sshkey *hostkey = NULL; const u_char *p; char *userstyle, *cp; size_t len; u_char type; - int r, fail = 0; + int hostbound = 0, r, fail = 0; if ((b = sshbuf_from(data, datalen)) == NULL) fatal_f("sshbuf_from"); @@ -1042,19 +1043,34 @@ monitor_valid_userblob(struct ssh *ssh, const u_char *data, u_int datalen) if ((r = sshbuf_skip_string(b)) != 0 || /* service */ (r = sshbuf_get_cstring(b, &cp, NULL)) != 0) fatal_fr(r, "parse method"); - if (strcmp("publickey", cp) != 0) - fail++; + if (strcmp("publickey", cp) != 0) { + if (strcmp("publickey-hostbound-v00@openssh.com", cp) == 0) + hostbound = 1; + else + fail++; + } free(cp); if ((r = sshbuf_get_u8(b, &type)) != 0) fatal_fr(r, "parse pktype"); if (type == 0) fail++; if ((r = sshbuf_skip_string(b)) != 0 || /* pkalg */ - (r = sshbuf_skip_string(b)) != 0) /* pkblob */ + (r = sshbuf_skip_string(b)) != 0 || /* pkblob */ + (hostbound && (r = sshkey_froms(b, &hostkey)) != 0)) fatal_fr(r, "parse pk"); if (sshbuf_len(b) != 0) fail++; sshbuf_free(b); + if (hostkey != NULL) { + /* + * Ensure this is actually one of our hostkeys; unfortunately + * can't check ssh->kex->initial_hostkey directly at this point + * as packet state has not yet been exported to monitor. + */ + if (get_hostkey_index(hostkey, 1, ssh) == -1) + fatal_f("hostbound hostkey does not match"); + sshkey_free(hostkey); + } return (fail == 0); } -- 2.20.1