From 659ea2942e05881757b5714c701a438f34bc7002 Mon Sep 17 00:00:00 2001 From: purplerain Date: Mon, 18 Dec 2023 23:55:01 +0000 Subject: [PATCH] sync with OpenBSD -current --- lib/libcrypto/cmac/cmac.c | 71 ++--- lib/libcrypto/evp/evp_pbe.c | 43 ++- lib/libfido2/src/rs1.c | 31 +- lib/libfido2/src/rs256.c | 31 +- libexec/ld.so/loader.c | 6 +- regress/usr.bin/ssh/Makefile | 6 +- regress/usr.bin/ssh/agent-pkcs11-cert.sh | 92 ++++++ regress/usr.bin/ssh/agent-pkcs11-restrict.sh | 193 ++++++++++++ sbin/dump/main.c | 5 +- sys/kern/uipc_socket.c | 12 +- sys/kern/uipc_socket2.c | 10 +- sys/kern/uipc_syscalls.c | 6 +- sys/netinet/if_ether.c | 5 +- sys/netinet/in_pcb.h | 34 +- sys/sys/protosw.h | 8 +- usr.bin/ssh/PROTOCOL | 47 ++- usr.bin/ssh/PROTOCOL.agent | 33 +- usr.bin/ssh/auth2.c | 8 +- usr.bin/ssh/authfd.c | 40 ++- usr.bin/ssh/authfd.h | 5 +- usr.bin/ssh/channels.c | 19 +- usr.bin/ssh/channels.h | 3 +- usr.bin/ssh/kex.c | 313 ++++++++++++++----- usr.bin/ssh/kex.h | 8 +- usr.bin/ssh/monitor_wrap.c | 4 +- usr.bin/ssh/packet.c | 103 +++--- usr.bin/ssh/packet.h | 3 +- usr.bin/ssh/ssh-add.1 | 14 +- usr.bin/ssh/ssh-add.c | 92 ++++-- usr.bin/ssh/ssh-agent.c | 241 ++++++++++++-- usr.bin/ssh/ssh-pkcs11-client.c | 56 +++- usr.bin/ssh/ssh-pkcs11.h | 5 +- usr.bin/ssh/ssh.c | 41 ++- usr.bin/ssh/sshconnect2.c | 60 ++-- usr.bin/ssh/sshd.c | 4 +- usr.bin/ssh/version.h | 4 +- usr.sbin/rpki-client/version.h | 4 +- usr.sbin/snmpd/application_internal.c | 14 +- usr.sbin/snmpd/parse.y | 28 +- 39 files changed, 1318 insertions(+), 384 deletions(-) create mode 100644 regress/usr.bin/ssh/agent-pkcs11-cert.sh create mode 100644 regress/usr.bin/ssh/agent-pkcs11-restrict.sh diff --git a/lib/libcrypto/cmac/cmac.c b/lib/libcrypto/cmac/cmac.c index 0df40277b..9fe907609 100644 --- a/lib/libcrypto/cmac/cmac.c +++ b/lib/libcrypto/cmac/cmac.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmac.c,v 1.17 2023/12/15 13:45:05 tb Exp $ */ +/* $OpenBSD: cmac.c,v 1.18 2023/12/18 21:15:00 tb Exp $ */ /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL * project. */ @@ -90,21 +90,21 @@ struct CMAC_CTX_st { * and R64 = (1 << 64) | 0x1b for the only supported block sizes 128 and 64. */ static void -make_kn(unsigned char *kn, const unsigned char *l, int bl) +make_kn(unsigned char *kn, const unsigned char *l, int block_size) { unsigned char mask, Rb; int i; /* Choose Rb according to the block size in bytes. */ - Rb = bl == 16 ? 0x87 : 0x1b; + Rb = block_size == 16 ? 0x87 : 0x1b; /* Compute l << 1 up to last byte. */ - for (i = 0; i < bl - 1; i++) + for (i = 0; i < block_size - 1; i++) kn[i] = (l[i] << 1) | (l[i + 1] >> 7); /* Only xor with Rb if the MSB is one. */ mask = 0 - (l[0] >> 7); - kn[bl - 1] = (l[bl - 1] << 1) ^ (Rb & mask); + kn[block_size - 1] = (l[block_size - 1] << 1) ^ (Rb & mask); } CMAC_CTX * @@ -154,17 +154,17 @@ LCRYPTO_ALIAS(CMAC_CTX_free); int CMAC_CTX_copy(CMAC_CTX *out, const CMAC_CTX *in) { - int bl; + int block_size; if (in->nlast_block == -1) return 0; if (!EVP_CIPHER_CTX_copy(&out->cctx, &in->cctx)) return 0; - bl = EVP_CIPHER_CTX_block_size(&in->cctx); - memcpy(out->k1, in->k1, bl); - memcpy(out->k2, in->k2, bl); - memcpy(out->tbl, in->tbl, bl); - memcpy(out->last_block, in->last_block, bl); + block_size = EVP_CIPHER_CTX_block_size(&in->cctx); + memcpy(out->k1, in->k1, block_size); + memcpy(out->k2, in->k2, block_size); + memcpy(out->tbl, in->tbl, block_size); + memcpy(out->last_block, in->last_block, block_size); out->nlast_block = in->nlast_block; return 1; } @@ -175,7 +175,7 @@ CMAC_Init(CMAC_CTX *ctx, const void *key, size_t keylen, const EVP_CIPHER *cipher, ENGINE *impl) { static unsigned char zero_iv[EVP_MAX_BLOCK_LENGTH]; - int bl; + int block_size; /* All zeros means restart */ if (key == NULL && cipher == NULL && keylen == 0) { @@ -208,8 +208,8 @@ CMAC_Init(CMAC_CTX *ctx, const void *key, size_t keylen, return 0; /* make_kn() only supports block sizes of 8 and 16 bytes. */ - bl = EVP_CIPHER_CTX_block_size(&ctx->cctx); - if (bl != 8 && bl != 16) + block_size = EVP_CIPHER_CTX_block_size(&ctx->cctx); + if (block_size != 8 && block_size != 16) return 0; /* @@ -220,13 +220,13 @@ CMAC_Init(CMAC_CTX *ctx, const void *key, size_t keylen, return 0; if (!EVP_EncryptInit_ex(&ctx->cctx, NULL, NULL, key, zero_iv)) return 0; - if (!EVP_Cipher(&ctx->cctx, ctx->tbl, zero_iv, bl)) + if (!EVP_Cipher(&ctx->cctx, ctx->tbl, zero_iv, block_size)) return 0; /* Section 6.1, step 2: compute k1 from intermediate secret. */ - make_kn(ctx->k1, ctx->tbl, bl); + make_kn(ctx->k1, ctx->tbl, block_size); /* Section 6.1, step 3: compute k2 from k1. */ - make_kn(ctx->k2, ctx->k1, bl); + make_kn(ctx->k2, ctx->k1, block_size); /* Destroy intermediate secret and reset last block count. */ explicit_bzero(ctx->tbl, sizeof(ctx->tbl)); @@ -245,18 +245,18 @@ int CMAC_Update(CMAC_CTX *ctx, const void *in, size_t dlen) { const unsigned char *data = in; - size_t bl; + size_t block_size; if (ctx->nlast_block == -1) return 0; if (dlen == 0) return 1; - bl = EVP_CIPHER_CTX_block_size(&ctx->cctx); + block_size = EVP_CIPHER_CTX_block_size(&ctx->cctx); /* Copy into partial block if we need to */ if (ctx->nlast_block > 0) { size_t nleft; - nleft = bl - ctx->nlast_block; + nleft = block_size - ctx->nlast_block; if (dlen < nleft) nleft = dlen; memcpy(ctx->last_block + ctx->nlast_block, data, nleft); @@ -267,15 +267,16 @@ CMAC_Update(CMAC_CTX *ctx, const void *in, size_t dlen) return 1; data += nleft; /* Else not final block so encrypt it */ - if (!EVP_Cipher(&ctx->cctx, ctx->tbl, ctx->last_block, bl)) + if (!EVP_Cipher(&ctx->cctx, ctx->tbl, ctx->last_block, + block_size)) return 0; } /* Encrypt all but one of the complete blocks left */ - while (dlen > bl) { - if (!EVP_Cipher(&ctx->cctx, ctx->tbl, data, bl)) + while (dlen > block_size) { + if (!EVP_Cipher(&ctx->cctx, ctx->tbl, data, block_size)) return 0; - dlen -= bl; - data += bl; + dlen -= block_size; + data += block_size; } /* Copy any data left to last block buffer */ memcpy(ctx->last_block, data, dlen); @@ -287,28 +288,28 @@ LCRYPTO_ALIAS(CMAC_Update); int CMAC_Final(CMAC_CTX *ctx, unsigned char *out, size_t *poutlen) { - int i, bl, lb; + int i, block_size, lb; if (ctx->nlast_block == -1) return 0; - bl = EVP_CIPHER_CTX_block_size(&ctx->cctx); - *poutlen = (size_t)bl; + block_size = EVP_CIPHER_CTX_block_size(&ctx->cctx); + *poutlen = (size_t)block_size; if (!out) return 1; lb = ctx->nlast_block; /* Is last block complete? */ - if (lb == bl) { - for (i = 0; i < bl; i++) + if (lb == block_size) { + for (i = 0; i < block_size; i++) out[i] = ctx->last_block[i] ^ ctx->k1[i]; } else { ctx->last_block[lb] = 0x80; - if (bl - lb > 1) - memset(ctx->last_block + lb + 1, 0, bl - lb - 1); - for (i = 0; i < bl; i++) + if (block_size - lb > 1) + memset(ctx->last_block + lb + 1, 0, block_size - lb - 1); + for (i = 0; i < block_size; i++) out[i] = ctx->last_block[i] ^ ctx->k2[i]; } - if (!EVP_Cipher(&ctx->cctx, out, out, bl)) { - explicit_bzero(out, bl); + if (!EVP_Cipher(&ctx->cctx, out, out, block_size)) { + explicit_bzero(out, block_size); return 0; } return 1; diff --git a/lib/libcrypto/evp/evp_pbe.c b/lib/libcrypto/evp/evp_pbe.c index 94658f879..8553478bc 100644 --- a/lib/libcrypto/evp/evp_pbe.c +++ b/lib/libcrypto/evp/evp_pbe.c @@ -1,4 +1,4 @@ -/* $OpenBSD: evp_pbe.c,v 1.33 2023/12/16 14:09:33 tb Exp $ */ +/* $OpenBSD: evp_pbe.c,v 1.34 2023/12/18 13:12:43 tb Exp $ */ /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL * project 1999. */ @@ -269,43 +269,35 @@ int EVP_PBE_CipherInit(ASN1_OBJECT *pbe_obj, const char *pass, int passlen, ASN1_TYPE *param, EVP_CIPHER_CTX *ctx, int en_de) { - const EVP_CIPHER *cipher; - const EVP_MD *md; - int cipher_nid, md_nid; + const EVP_CIPHER *cipher = NULL; + const EVP_MD *md = NULL; + int pbe_nid, cipher_nid, md_nid; EVP_PBE_KEYGEN *keygen; - if (!EVP_PBE_find(EVP_PBE_TYPE_OUTER, OBJ_obj2nid(pbe_obj), - &cipher_nid, &md_nid, &keygen)) { - char obj_tmp[80]; + if ((pbe_nid = OBJ_obj2nid(pbe_obj)) == NID_undef) { EVPerror(EVP_R_UNKNOWN_PBE_ALGORITHM); - if (!pbe_obj) - strlcpy(obj_tmp, "NULL", sizeof obj_tmp); - else - i2t_ASN1_OBJECT(obj_tmp, sizeof obj_tmp, pbe_obj); - ERR_asprintf_error_data("TYPE=%s", obj_tmp); + return 0; + } + if (!EVP_PBE_find(EVP_PBE_TYPE_OUTER, pbe_nid, &cipher_nid, &md_nid, + &keygen)) { + EVPerror(EVP_R_UNKNOWN_PBE_ALGORITHM); + ERR_asprintf_error_data("NID=%d", pbe_nid); return 0; } - if (!pass) + if (pass == NULL) passlen = 0; - else if (passlen == -1) + if (passlen == -1) passlen = strlen(pass); - if (cipher_nid == -1) - cipher = NULL; - else { - cipher = EVP_get_cipherbynid(cipher_nid); - if (!cipher) { + if (cipher_nid != -1) { + if ((cipher = EVP_get_cipherbynid(cipher_nid)) == NULL) { EVPerror(EVP_R_UNKNOWN_CIPHER); return 0; } } - - if (md_nid == -1) - md = NULL; - else { - md = EVP_get_digestbynid(md_nid); - if (!md) { + if (md_nid != -1) { + if ((md = EVP_get_digestbynid(md_nid)) == NULL) { EVPerror(EVP_R_UNKNOWN_DIGEST); return 0; } @@ -315,6 +307,7 @@ EVP_PBE_CipherInit(ASN1_OBJECT *pbe_obj, const char *pass, int passlen, EVPerror(EVP_R_KEYGEN_FAILURE); return 0; } + return 1; } diff --git a/lib/libfido2/src/rs1.c b/lib/libfido2/src/rs1.c index 134068b16..326c84c35 100644 --- a/lib/libfido2/src/rs1.c +++ b/lib/libfido2/src/rs1.c @@ -9,25 +9,7 @@ #include "fido.h" -#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x3050200fL -static EVP_MD * -rs1_get_EVP_MD(void) -{ - const EVP_MD *from; - EVP_MD *to = NULL; - - if ((from = EVP_sha1()) != NULL && (to = malloc(sizeof(*to))) != NULL) - memcpy(to, from, sizeof(*to)); - - return (to); -} - -static void -rs1_free_EVP_MD(EVP_MD *md) -{ - freezero(md, sizeof(*md)); -} -#elif OPENSSL_VERSION_NUMBER >= 0x30000000 +#if OPENSSL_VERSION_NUMBER >= 0x30000000 static EVP_MD * rs1_get_EVP_MD(void) { @@ -43,20 +25,15 @@ rs1_free_EVP_MD(EVP_MD *md) static EVP_MD * rs1_get_EVP_MD(void) { - const EVP_MD *md; - - if ((md = EVP_sha1()) == NULL) - return (NULL); - - return (EVP_MD_meth_dup(md)); + return ((EVP_MD *)EVP_sha1()); } static void rs1_free_EVP_MD(EVP_MD *md) { - EVP_MD_meth_free(md); + (void)md; } -#endif /* LIBRESSL_VERSION_NUMBER */ +#endif /* OPENSSL_VERSION_NUMBER */ int rs1_verify_sig(const fido_blob_t *dgst, EVP_PKEY *pkey, diff --git a/lib/libfido2/src/rs256.c b/lib/libfido2/src/rs256.c index 95bae167a..400d06313 100644 --- a/lib/libfido2/src/rs256.c +++ b/lib/libfido2/src/rs256.c @@ -17,25 +17,7 @@ #define get0_RSA(x) EVP_PKEY_get0((x)) #endif -#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x3050200fL -static EVP_MD * -rs256_get_EVP_MD(void) -{ - const EVP_MD *from; - EVP_MD *to = NULL; - - if ((from = EVP_sha256()) != NULL && (to = malloc(sizeof(*to))) != NULL) - memcpy(to, from, sizeof(*to)); - - return (to); -} - -static void -rs256_free_EVP_MD(EVP_MD *md) -{ - freezero(md, sizeof(*md)); -} -#elif OPENSSL_VERSION_NUMBER >= 0x30000000 +#if OPENSSL_VERSION_NUMBER >= 0x30000000 static EVP_MD * rs256_get_EVP_MD(void) { @@ -51,20 +33,15 @@ rs256_free_EVP_MD(EVP_MD *md) static EVP_MD * rs256_get_EVP_MD(void) { - const EVP_MD *md; - - if ((md = EVP_sha256()) == NULL) - return (NULL); - - return (EVP_MD_meth_dup(md)); + return ((EVP_MD *)EVP_sha256()); } static void rs256_free_EVP_MD(EVP_MD *md) { - EVP_MD_meth_free(md); + (void)md; } -#endif /* LIBRESSL_VERSION_NUMBER */ +#endif /* OPENSSL_VERSION_NUMBER */ static int decode_bignum(const cbor_item_t *item, void *ptr, size_t len) diff --git a/libexec/ld.so/loader.c b/libexec/ld.so/loader.c index 016bfa336..d8be0dada 100644 --- a/libexec/ld.so/loader.c +++ b/libexec/ld.so/loader.c @@ -1,4 +1,4 @@ -/* $OpenBSD: loader.c,v 1.215 2023/12/12 15:44:00 deraadt Exp $ */ +/* $OpenBSD: loader.c,v 1.216 2023/12/18 17:19:07 deraadt Exp $ */ /* * Copyright (c) 1998 Per Fogelstrom, Opsycon AB @@ -476,13 +476,13 @@ _dl_self_relro(long loff) #define __STRINGIFY(x) #x #define STRINGIFY(x) __STRINGIFY(x) #ifdef __arm__ -__asm__(".pushsection openbsd.syscalls,\"\",%progbits;" +__asm__(".pushsection .openbsd.syscalls,\"\",%progbits;" ".p2align 2;" ".long 0;" ".long " STRINGIFY(SYS_kbind) ";" ".popsection"); #else -__asm__(".pushsection openbsd.syscalls,\"\",@progbits;" +__asm__(".pushsection .openbsd.syscalls,\"\",@progbits;" ".long 0;" ".p2align 2;" ".long " STRINGIFY(SYS_kbind) ";" diff --git a/regress/usr.bin/ssh/Makefile b/regress/usr.bin/ssh/Makefile index 78972c689..b8da40afc 100644 --- a/regress/usr.bin/ssh/Makefile +++ b/regress/usr.bin/ssh/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.129 2023/10/26 18:52:45 anton Exp $ +# $OpenBSD: Makefile,v 1.131 2023/12/18 14:50:08 djm Exp $ OPENSSL?= yes @@ -100,7 +100,9 @@ LTESTS= connect \ hostbased \ channel-timeout \ connection-timeout \ - match-subsystem + match-subsystem \ + agent-pkcs11-restrict \ + agent-pkcs11-cert INTEROP_TESTS= putty-transfer putty-ciphers putty-kex conch-ciphers INTEROP_TESTS+= dropbear-ciphers dropbear-kex diff --git a/regress/usr.bin/ssh/agent-pkcs11-cert.sh b/regress/usr.bin/ssh/agent-pkcs11-cert.sh new file mode 100644 index 000000000..4e8f74846 --- /dev/null +++ b/regress/usr.bin/ssh/agent-pkcs11-cert.sh @@ -0,0 +1,92 @@ +# $OpenBSD: agent-pkcs11-cert.sh,v 1.1 2023/12/18 14:50:08 djm Exp $ +# Placed in the Public Domain. + +tid="pkcs11 agent certificate test" + +SSH_AUTH_SOCK="$OBJ/agent.sock" +export SSH_AUTH_SOCK +LC_ALL=C +export LC_ALL +p11_setup || skip "No PKCS#11 library found" + +rm -f $SSH_AUTH_SOCK $OBJ/agent.log +rm -f $OBJ/output_* $OBJ/expect_* +rm -f $OBJ/ca* + +trace "generate CA key and certify keys" +$SSHKEYGEN -q -t ed25519 -C ca -N '' -f $OBJ/ca || fatal "ssh-keygen CA failed" +$SSHKEYGEN -qs $OBJ/ca -I "ecdsa_key" -n $USER -z 1 ${SSH_SOFTHSM_DIR}/EC.pub || + fatal "certify ECDSA key failed" +$SSHKEYGEN -qs $OBJ/ca -I "rsa_key" -n $USER -z 2 ${SSH_SOFTHSM_DIR}/RSA.pub || + fatal "certify RSA key failed" +$SSHKEYGEN -qs $OBJ/ca -I "ca_ca" -n $USER -z 3 $OBJ/ca.pub || + fatal "certify CA key failed" + +rm -f $SSH_AUTH_SOCK +trace "start agent" +${SSHAGENT} ${EXTRA_AGENT_ARGS} -d -a $SSH_AUTH_SOCK > $OBJ/agent.log 2>&1 & +AGENT_PID=$! +trap "kill $AGENT_PID" EXIT +for x in 0 1 2 3 4 ; do + # Give it a chance to start + ${SSHADD} -l > /dev/null 2>&1 + r=$? + test $r -eq 1 && break + sleep 1 +done +if [ $r -ne 1 ]; then + fatal "ssh-add -l did not fail with exit code 1 (got $r)" +fi + +trace "load pkcs11 keys and certs" +# Note: deliberately contains non-cert keys and non-matching cert on commandline +p11_ssh_add -qs ${TEST_SSH_PKCS11} \ + $OBJ/ca.pub \ + ${SSH_SOFTHSM_DIR}/EC.pub \ + ${SSH_SOFTHSM_DIR}/EC-cert.pub \ + ${SSH_SOFTHSM_DIR}/RSA.pub \ + ${SSH_SOFTHSM_DIR}/RSA-cert.pub || + fatal "failed to add keys" +# Verify their presence +cut -d' ' -f1-2 \ + ${SSH_SOFTHSM_DIR}/EC.pub \ + ${SSH_SOFTHSM_DIR}/RSA.pub \ + ${SSH_SOFTHSM_DIR}/EC-cert.pub \ + ${SSH_SOFTHSM_DIR}/RSA-cert.pub | sort > $OBJ/expect_list +$SSHADD -L | cut -d' ' -f1-2 | sort > $OBJ/output_list +diff $OBJ/expect_list $OBJ/output_list + +# Verify that all can perform signatures. +for x in ${SSH_SOFTHSM_DIR}/EC.pub ${SSH_SOFTHSM_DIR}/RSA.pub \ + ${SSH_SOFTHSM_DIR}/EC-cert.pub ${SSH_SOFTHSM_DIR}/RSA-cert.pub ; do + $SSHADD -T $x || fail "Signing failed for $x" +done + +# Delete plain keys. +$SSHADD -qd ${SSH_SOFTHSM_DIR}/EC.pub ${SSH_SOFTHSM_DIR}/RSA.pub +# Verify that certs can still perform signatures. +for x in ${SSH_SOFTHSM_DIR}/EC-cert.pub ${SSH_SOFTHSM_DIR}/RSA-cert.pub ; do + $SSHADD -T $x || fail "Signing failed for $x" +done + +$SSHADD -qD >/dev/null || fatal "clear agent failed" + +trace "load pkcs11 certs only" +p11_ssh_add -qCs ${TEST_SSH_PKCS11} \ + $OBJ/ca.pub \ + ${SSH_SOFTHSM_DIR}/EC.pub \ + ${SSH_SOFTHSM_DIR}/EC-cert.pub \ + ${SSH_SOFTHSM_DIR}/RSA.pub \ + ${SSH_SOFTHSM_DIR}/RSA-cert.pub || + fatal "failed to add keys" +# Verify their presence +cut -d' ' -f1-2 \ + ${SSH_SOFTHSM_DIR}/EC-cert.pub \ + ${SSH_SOFTHSM_DIR}/RSA-cert.pub | sort > $OBJ/expect_list +$SSHADD -L | cut -d' ' -f1-2 | sort > $OBJ/output_list +diff $OBJ/expect_list $OBJ/output_list + +# Verify that certs can perform signatures. +for x in ${SSH_SOFTHSM_DIR}/EC-cert.pub ${SSH_SOFTHSM_DIR}/RSA-cert.pub ; do + $SSHADD -T $x || fail "Signing failed for $x" +done diff --git a/regress/usr.bin/ssh/agent-pkcs11-restrict.sh b/regress/usr.bin/ssh/agent-pkcs11-restrict.sh new file mode 100644 index 000000000..867253211 --- /dev/null +++ b/regress/usr.bin/ssh/agent-pkcs11-restrict.sh @@ -0,0 +1,193 @@ +# $OpenBSD: agent-pkcs11-restrict.sh,v 1.1 2023/12/18 14:49:39 djm Exp $ +# Placed in the Public Domain. + +tid="pkcs11 agent constraint test" + +p11_setup || skip "No PKCS#11 library found" + +rm -f $SSH_AUTH_SOCK $OBJ/agent.log $OBJ/host_[abcx]* $OBJ/user_[abcx]* +rm -f $OBJ/sshd_proxy_host* $OBJ/ssh_output* $OBJ/expect_* +rm -f $OBJ/ssh_proxy[._]* $OBJ/command $OBJ/authorized_keys_* + +trace "generate host keys" +for h in a b x ca ; do + $SSHKEYGEN -q -t ed25519 -C host_$h -N '' -f $OBJ/host_$h || \ + fatal "ssh-keygen hostkey failed" +done + +# XXX test CA hostcerts too. + +key_for() { + case $h in + a) K="${SSH_SOFTHSM_DIR}/RSA.pub" ;; + b) K="${SSH_SOFTHSM_DIR}/EC.pub" ;; + *) K="" ;; + esac + export K +} + +SSH_AUTH_SOCK="$OBJ/agent.sock" +export SSH_AUTH_SOCK +rm -f $SSH_AUTH_SOCK +trace "start agent" +${SSHAGENT} ${EXTRA_AGENT_ARGS} -d -a $SSH_AUTH_SOCK > $OBJ/agent.log 2>&1 & +AGENT_PID=$! +trap "kill $AGENT_PID" EXIT +for x in 0 1 2 3 4 ; do + # Give it a chance to start + ${SSHADD} -l > /dev/null 2>&1 + r=$? + test $r -eq 1 && break + sleep 1 +done +if [ $r -ne 1 ]; then + fatal "ssh-add -l did not fail with exit code 1 (got $r)" +fi + +# XXX a lot of this is a copy of agent-restrict.sh, but I couldn't see a nice +# way to factor it out -djm + +trace "prepare client config" +egrep -vi '(identityfile|hostname|hostkeyalias|proxycommand)' \ + $OBJ/ssh_proxy > $OBJ/ssh_proxy.bak +cat << _EOF > $OBJ/ssh_proxy +IdentitiesOnly yes +ForwardAgent yes +ExitOnForwardFailure yes +_EOF +cp $OBJ/ssh_proxy $OBJ/ssh_proxy_noid +for h in a b ; do + key_for $h + cat << _EOF >> $OBJ/ssh_proxy +Host host_$h + Hostname host_$h + HostkeyAlias host_$h + IdentityFile $K + ProxyCommand ${SUDO} env SSH_SK_HELPER=\"$SSH_SK_HELPER\" ${OBJ}/sshd-log-wrapper.sh -i -f $OBJ/sshd_proxy_host_$h +_EOF + # Variant with no specified keys. + cat << _EOF >> $OBJ/ssh_proxy_noid +Host host_$h + Hostname host_$h + HostkeyAlias host_$h + ProxyCommand ${SUDO} env SSH_SK_HELPER=\"$SSH_SK_HELPER\" ${OBJ}/sshd-log-wrapper.sh -i -f $OBJ/sshd_proxy_host_$h +_EOF +done +cat $OBJ/ssh_proxy.bak >> $OBJ/ssh_proxy +cat $OBJ/ssh_proxy.bak >> $OBJ/ssh_proxy_noid + +LC_ALL=C +export LC_ALL +echo "SetEnv LC_ALL=${LC_ALL}" >> sshd_proxy + +trace "prepare known_hosts" +rm -f $OBJ/known_hosts +for h in a b x ; do + (printf "host_$h " ; cat $OBJ/host_${h}.pub) >> $OBJ/known_hosts +done + +trace "prepare server configs" +egrep -vi '(hostkey|pidfile)' $OBJ/sshd_proxy \ + > $OBJ/sshd_proxy.bak +for h in a b ; do + cp $OBJ/sshd_proxy.bak $OBJ/sshd_proxy_host_$h + cat << _EOF >> $OBJ/sshd_proxy_host_$h +ExposeAuthInfo yes +Hostkey $OBJ/host_$h +_EOF + cp $OBJ/sshd_proxy_host_$h $OBJ/sshd_proxy_host_${h}.bak +done + +trace "prepare authorized_keys" +cat >> $OBJ/command << EOF +#!/bin/sh +echo USERAUTH +cat \$SSH_USER_AUTH +echo AGENT +if $SSHADD -ql >/dev/null 2>&1 ; then + $SSHADD -L | cut -d' ' -f1-2 | sort +else + echo NONE +fi +EOF +chmod a+x $OBJ/command +>$OBJ/authorized_keys_$USER +for h in a b ; do + key_for $h + (printf "%s" "restrict,agent-forwarding,command=\"$OBJ/command\" "; + cat $K) >> $OBJ/authorized_keys_$USER +done + +trace "unrestricted keys" +$SSHADD -qD >/dev/null || fatal "clear agent failed" +p11_ssh_add -qs ${TEST_SSH_PKCS11} || + fatal "failed to add keys" +for h in a b ; do + key_for $h + echo USERAUTH > $OBJ/expect_$h + printf "publickey " >> $OBJ/expect_$h + cat $K >> $OBJ/expect_$h + echo AGENT >> $OBJ/expect_$h + $SSHADD -L | cut -d' ' -f1-2 | sort >> $OBJ/expect_$h + ${SSH} -F $OBJ/ssh_proxy -oIdentityFile=$K \ + host_$h true > $OBJ/ssh_output || fatal "test ssh $h failed" + cmp $OBJ/expect_$h $OBJ/ssh_output || fatal "unexpected output" +done + +trace "restricted to different host" +$SSHADD -qD >/dev/null || fatal "clear agent failed" +p11_ssh_add -q -h host_x -s ${TEST_SSH_PKCS11} -H $OBJ/known_hosts || + fatal "failed to add keys" +for h in a b ; do + key_for $h + ${SSH} -F $OBJ/ssh_proxy -oIdentityFile=$K \ + host_$h true > $OBJ/ssh_output && fatal "test ssh $h succeeded" +done + +trace "restricted to destination host" +$SSHADD -qD >/dev/null || fatal "clear agent failed" +p11_ssh_add -q -h host_a -h host_b -s ${TEST_SSH_PKCS11} -H $OBJ/known_hosts || + fatal "failed to add keys" +for h in a b ; do + key_for $h + echo USERAUTH > $OBJ/expect_$h + printf "publickey " >> $OBJ/expect_$h + cat $K >> $OBJ/expect_$h + echo AGENT >> $OBJ/expect_$h + echo NONE >> $OBJ/expect_$h + ${SSH} -F $OBJ/ssh_proxy -oIdentityFile=$K \ + host_$h true > $OBJ/ssh_output || fatal "test ssh $h failed" + cmp $OBJ/expect_$h $OBJ/ssh_output || fatal "unexpected output" +done + +trace "restricted multihop" +$SSHADD -qD >/dev/null || fatal "clear agent failed" +p11_ssh_add -q -h host_a -h "host_a>host_b" \ + -s ${TEST_SSH_PKCS11} -H $OBJ/known_hosts || fatal "failed to add keys" +key_for a +AK=$K +key_for b +BK=$K +# Prepare authorized_keys file to additionally ssh to host_b +_command="echo LOCAL ; ${OBJ}/command ; echo REMOTE; ${SSH} -AF $OBJ/ssh_proxy -oIdentityFile=$BK host_b" +(printf "%s" "restrict,agent-forwarding,command=\"$_command\" "; + cat $BK) > $OBJ/authorized_keys_a +grep -vi AuthorizedKeysFile $OBJ/sshd_proxy_host_a.bak > $OBJ/sshd_proxy_host_a +echo "AuthorizedKeysFile $OBJ/authorized_keys_a" >> $OBJ/sshd_proxy_host_a +# Prepare expected output from both hosts. +echo LOCAL > $OBJ/expect_a +echo USERAUTH >> $OBJ/expect_a +printf "publickey " >> $OBJ/expect_a +cat $AK >> $OBJ/expect_a +echo AGENT >> $OBJ/expect_a +$SSHADD -L | cut -d' ' -f1-2 | sort >> $OBJ/expect_a +echo REMOTE >> $OBJ/expect_a +echo USERAUTH >> $OBJ/expect_a +printf "publickey " >> $OBJ/expect_a +cat $BK >> $OBJ/expect_a +echo AGENT >> $OBJ/expect_a +echo NONE >> $OBJ/expect_a +${SSH} -AF $OBJ/ssh_proxy -oIdentityFile=$AK \ + host_a whatever > $OBJ/ssh_output || fatal "test ssh $h failed" +cmp $OBJ/expect_a $OBJ/ssh_output || fatal "unexpected output" + diff --git a/sbin/dump/main.c b/sbin/dump/main.c index 49952cb64..d513ad33c 100644 --- a/sbin/dump/main.c +++ b/sbin/dump/main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: main.c,v 1.63 2022/06/02 15:35:55 millert Exp $ */ +/* $OpenBSD: main.c,v 1.64 2023/12/18 13:23:52 otto Exp $ */ /* $NetBSD: main.c,v 1.14 1997/06/05 11:13:24 lukem Exp $ */ /*- @@ -465,6 +465,9 @@ main(int argc, char *argv[]) usedinomap = calloc((unsigned) mapsize, sizeof(char)); dumpdirmap = calloc((unsigned) mapsize, sizeof(char)); dumpinomap = calloc((unsigned) mapsize, sizeof(char)); + if (usedinomap == NULL || dumpdirmap == NULL || dumpinomap == NULL) + quit("Failed to allocate tables"); + tapesize = 3 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); nonodump = spcl.c_level < honorlevel; diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c index 6c2f93cda..7a3062cd8 100644 --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uipc_socket.c,v 1.309 2023/08/08 22:07:25 mvs Exp $ */ +/* $OpenBSD: uipc_socket.c,v 1.310 2023/12/18 13:11:20 bluhm Exp $ */ /* $NetBSD: uipc_socket.c,v 1.21 1996/02/04 02:17:52 christos Exp $ */ /* @@ -832,8 +832,10 @@ bad: *mp = NULL; solock_shared(so); + pru_lock(so); restart: if ((error = sblock(so, &so->so_rcv, SBLOCKWAIT(flags))) != 0) { + pru_unlock(so); sounlock_shared(so); return (error); } @@ -900,11 +902,13 @@ restart: SBLASTRECORDCHK(&so->so_rcv, "soreceive sbwait 1"); SBLASTMBUFCHK(&so->so_rcv, "soreceive sbwait 1"); sbunlock(so, &so->so_rcv); + pru_unlock(so); error = sbwait(so, &so->so_rcv); if (error) { sounlock_shared(so); return (error); } + pru_lock(so); goto restart; } dontblock: @@ -971,11 +975,13 @@ dontblock: sbsync(&so->so_rcv, nextrecord); if (controlp) { if (pr->pr_domain->dom_externalize) { + pru_unlock(so); sounlock_shared(so); error = (*pr->pr_domain->dom_externalize) (cm, controllen, flags); solock_shared(so); + pru_lock(so); } *controlp = cm; } else { @@ -1049,9 +1055,11 @@ dontblock: SBLASTRECORDCHK(&so->so_rcv, "soreceive uiomove"); SBLASTMBUFCHK(&so->so_rcv, "soreceive uiomove"); resid = uio->uio_resid; + pru_unlock(so); sounlock_shared(so); uio_error = uiomove(mtod(m, caddr_t) + moff, len, uio); solock_shared(so); + pru_lock(so); if (uio_error) uio->uio_resid = resid - len; } else @@ -1136,6 +1144,7 @@ dontblock: error = sbwait(so, &so->so_rcv); if (error) { sbunlock(so, &so->so_rcv); + pru_unlock(so); sounlock_shared(so); return (0); } @@ -1182,6 +1191,7 @@ dontblock: *flagsp |= flags; release: sbunlock(so, &so->so_rcv); + pru_unlock(so); sounlock_shared(so); return (error); } diff --git a/sys/kern/uipc_socket2.c b/sys/kern/uipc_socket2.c index f21e0e20a..18f7746f6 100644 --- a/sys/kern/uipc_socket2.c +++ b/sys/kern/uipc_socket2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uipc_socket2.c,v 1.138 2023/10/30 13:27:53 bluhm Exp $ */ +/* $OpenBSD: uipc_socket2.c,v 1.139 2023/12/18 13:11:20 bluhm Exp $ */ /* $NetBSD: uipc_socket2.c,v 1.11 1996/02/04 02:17:55 christos Exp $ */ /* @@ -368,7 +368,7 @@ solock_shared(struct socket *so) case PF_INET6: if (so->so_proto->pr_usrreqs->pru_lock != NULL) { NET_LOCK_SHARED(); - pru_lock(so); + rw_enter_write(&so->so_lock); } else NET_LOCK(); break; @@ -427,7 +427,7 @@ sounlock_shared(struct socket *so) case PF_INET: case PF_INET6: if (so->so_proto->pr_usrreqs->pru_unlock != NULL) { - pru_unlock(so); + rw_exit_write(&so->so_lock); NET_UNLOCK_SHARED(); } else NET_UNLOCK(); @@ -463,12 +463,12 @@ sosleep_nsec(struct socket *so, void *ident, int prio, const char *wmesg, case PF_INET6: if (so->so_proto->pr_usrreqs->pru_unlock != NULL && rw_status(&netlock) == RW_READ) { - pru_unlock(so); + rw_exit_write(&so->so_lock); } ret = rwsleep_nsec(ident, &netlock, prio, wmesg, nsecs); if (so->so_proto->pr_usrreqs->pru_lock != NULL && rw_status(&netlock) == RW_READ) { - pru_lock(so); + rw_enter_write(&so->so_lock); } break; default: diff --git a/sys/kern/uipc_syscalls.c b/sys/kern/uipc_syscalls.c index 2919c1c96..0a58664ad 100644 --- a/sys/kern/uipc_syscalls.c +++ b/sys/kern/uipc_syscalls.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uipc_syscalls.c,v 1.214 2023/09/23 09:17:21 jan Exp $ */ +/* $OpenBSD: uipc_syscalls.c,v 1.215 2023/12/18 13:11:20 bluhm Exp $ */ /* $NetBSD: uipc_syscalls.c,v 1.19 1996/02/09 19:00:48 christos Exp $ */ /* @@ -185,9 +185,9 @@ sys_bind(struct proc *p, void *v, register_t *retval) if (KTRPOINT(p, KTR_STRUCT)) ktrsockaddr(p, mtod(nam, caddr_t), SCARG(uap, namelen)); #endif - solock(so); + solock_shared(so); error = sobind(so, nam, p); - sounlock(so); + sounlock_shared(so); m_freem(nam); out: FRELE(fp, p); diff --git a/sys/netinet/if_ether.c b/sys/netinet/if_ether.c index 5ba3bcece..0d6c3d930 100644 --- a/sys/netinet/if_ether.c +++ b/sys/netinet/if_ether.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_ether.c,v 1.266 2023/11/09 21:45:18 bluhm Exp $ */ +/* $OpenBSD: if_ether.c,v 1.267 2023/12/18 13:30:44 bluhm Exp $ */ /* $NetBSD: if_ether.c,v 1.31 1996/05/11 12:59:58 mycroft Exp $ */ /* @@ -756,7 +756,8 @@ arptfree(struct rtentry *rt) arpinvalidate(rt); ifp = if_get(rt->rt_ifidx); - KASSERT(ifp != NULL); + if (ifp == NULL) + return; if (!ISSET(rt->rt_flags, RTF_STATIC|RTF_CACHED)) rtdeletemsg(rt, ifp, ifp->if_rdomain); if_put(ifp); diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h index b618a2e80..16d1ce324 100644 --- a/sys/netinet/in_pcb.h +++ b/sys/netinet/in_pcb.h @@ -1,4 +1,4 @@ -/* $OpenBSD: in_pcb.h,v 1.144 2023/12/15 00:24:56 bluhm Exp $ */ +/* $OpenBSD: in_pcb.h,v 1.145 2023/12/18 13:11:20 bluhm Exp $ */ /* $NetBSD: in_pcb.h,v 1.14 1996/02/13 23:42:00 christos Exp $ */ /* @@ -84,6 +84,38 @@ * p inpcb_mtx pcb mutex */ +/* + * The pcb table mutex guarantees that all inpcb are consistent and + * that bind(2) and connect(2) create unique combinations of + * laddr/faddr/lport/fport/rtalbleid. This mutex is used to protect + * both address consistency and inpcb lookup during protocol input. + * All writes to inp_[lf]addr take table mutex. A per socket lock is + * needed, so that socket layer input have a consistent view at these + * values. + * + * In soconnect() and sosend() pcb mutex cannot be used. They eventually + * can call IP output which takes pf lock which is a sleeping lock. + * Also connect(2) does a route lookup for source selection. There + * route resolve happens, which creates a route, which sends a route + * message, which needs route lock, which is a rw-lock. + * + * On the other hand a mutex should be used in protocol input. It + * does not make sense to do a process switch per packet. Better spin + * until the packet can be processed. + * + * So there are three locks. Table mutex is for writing inp_[lf]addr/port + * and lookup, socket rw-lock to separate sockets in system calls, and + * pcb mutex to protect socket receive buffer. Changing inp_[lf]addr/port + * takes both per socket rw-lock and global table mutex. Protocol + * input only reads inp_[lf]addr/port during lookup and is safe. System + * call only reads when holding socket rw-lock and is safe. The socket + * layer needs pcb mutex only in soreceive(). + * + * Function pru_lock() grabs the pcb mutex and its existence indicates + * that a protocol is MP safe. Otherwise the exclusive net lock is + * used. + */ + struct pf_state_key; union inpaddru { diff --git a/sys/sys/protosw.h b/sys/sys/protosw.h index 2c585e758..a53364757 100644 --- a/sys/sys/protosw.h +++ b/sys/sys/protosw.h @@ -1,4 +1,4 @@ -/* $OpenBSD: protosw.h,v 1.62 2023/05/18 09:59:44 mvs Exp $ */ +/* $OpenBSD: protosw.h,v 1.63 2023/12/18 13:11:20 bluhm Exp $ */ /* $NetBSD: protosw.h,v 1.10 1996/04/09 20:55:32 cgd Exp $ */ /*- @@ -284,13 +284,15 @@ pru_detach(struct socket *so) static inline void pru_lock(struct socket *so) { - (*so->so_proto->pr_usrreqs->pru_lock)(so); + if (so->so_proto->pr_usrreqs->pru_lock) + (*so->so_proto->pr_usrreqs->pru_lock)(so); } static inline void pru_unlock(struct socket *so) { - (*so->so_proto->pr_usrreqs->pru_unlock)(so); + if (so->so_proto->pr_usrreqs->pru_unlock) + (*so->so_proto->pr_usrreqs->pru_unlock)(so); } static inline int diff --git a/usr.bin/ssh/PROTOCOL b/usr.bin/ssh/PROTOCOL index 9e15c11ab..b81d0ef53 100644 --- a/usr.bin/ssh/PROTOCOL +++ b/usr.bin/ssh/PROTOCOL @@ -137,6 +137,51 @@ than as a named global or channel request to allow pings with very short packet lengths, which would not be possible with other approaches. +1.9 transport: strict key exchange extension + +OpenSSH supports a number of transport-layer hardening measures under +a "strict KEX" feature. This feature is signalled similarly to the +RFC8308 ext-info feature: by including a additional algorithm in the +initiial SSH2_MSG_KEXINIT kex_algorithms field. The client may append +"kex-strict-c-v00@openssh.com" to its kex_algorithms and the server +may append "kex-strict-s-v00@openssh.com". These pseudo-algorithms +are only valid in the initial SSH2_MSG_KEXINIT and MUST be ignored +if they are present in subsequent SSH2_MSG_KEXINIT packets. + +When an endpoint that supports this extension observes this algorithm +name in a peer's KEXINIT packet, it MUST make the following changes to +the the protocol: + +a) During initial KEX, terminate the connection if any unexpected or + out-of-sequence packet is received. This includes terminating the + connection if the first packet received is not SSH2_MSG_KEXINIT. + Unexpected packets for the purpose of strict KEX include messages + that are otherwise valid at any time during the connection such as + SSH2_MSG_DEBUG and SSH2_MSG_IGNORE. +b) After sending or receiving a SSH2_MSG_NEWKEYS message, reset the + packet sequence number to zero. This behaviour persists for the + duration of the connection (i.e. not just the first + SSH2_MSG_NEWKEYS). + +1.10 transport: SSH2_MSG_EXT_INFO during user authentication + +This protocol extension allows the SSH2_MSG_EXT_INFO to be sent +during user authentication. RFC8308 does allow a second +SSH2_MSG_EXT_INFO notification, but it may only be sent at the end +of user authentication and this is too late to signal per-user +server signature algorithms. + +Support for receiving the SSH2_MSG_EXT_INFO message during user +authentication is signalled by the client including a +"ext-info-in-auth@openssh.com" key via its initial SSH2_MSG_EXT_INFO +set after the SSH2_MSG_NEWKEYS message. + +A server that supports this extension MAY send a second +SSH2_MSG_EXT_INFO message any time after the client's first +SSH2_MSG_USERAUTH_REQUEST, regardless of whether it succeed or fails. +The client SHOULD be prepared to update the server-sig-algs that +it received during an earlier SSH2_MSG_EXT_INFO with the later one. + 2. Connection protocol changes 2.1. connection: Channel write close extension "eow@openssh.com" @@ -745,4 +790,4 @@ master instance and later clients. OpenSSH extends the usual agent protocol. These changes are documented in the PROTOCOL.agent file. -$OpenBSD: PROTOCOL,v 1.49 2023/08/28 03:28:43 djm Exp $ +$OpenBSD: PROTOCOL,v 1.51 2023/12/18 14:45:49 djm Exp $ diff --git a/usr.bin/ssh/PROTOCOL.agent b/usr.bin/ssh/PROTOCOL.agent index 1c4841147..e4a6b74c5 100644 --- a/usr.bin/ssh/PROTOCOL.agent +++ b/usr.bin/ssh/PROTOCOL.agent @@ -81,4 +81,35 @@ the constraint is: This option is only valid for XMSS keys. -$OpenBSD: PROTOCOL.agent,v 1.20 2023/10/03 23:56:10 djm Exp $ +3. associated-certs-v00@openssh.com key constraint extension + +The key constraint extension allows certificates to be associated +with private keys as they are loaded from a PKCS#11 token. + + byte SSH_AGENT_CONSTRAIN_EXTENSION (0xff) + string associated-certs-v00@openssh.com + bool certs_only + string certsblob + +Where "certsblob" constists of one or more certificates encoded as public +key blobs: + + string[] certificates + +This extension is only valid for SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED +requests. When an agent receives this extension, it will attempt to match +each certificate in the request with a corresponding private key loaded +from the requested PKCS#11 token. When a matching key is found, the +agent will graft the certificate contents to the token-hosted private key +and store the result for subsequent use by regular agent operations. + +If the "certs_only" flag is set, then this extension will cause ONLY +the resultant certificates to be loaded to the agent. The default +behaviour is to load the PKCS#11-hosted private key as well as the +resultant certificate. + +A SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED will return SSH_AGENT_SUCCESS +if any key (plain private or certificate) was successfully loaded, or +SSH_AGENT_FAILURE if no key was loaded. + +$OpenBSD: PROTOCOL.agent,v 1.21 2023/12/18 14:46:56 djm Exp $ diff --git a/usr.bin/ssh/auth2.c b/usr.bin/ssh/auth2.c index 2d5dbcb5b..40bd28a47 100644 --- a/usr.bin/ssh/auth2.c +++ b/usr.bin/ssh/auth2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2.c,v 1.167 2023/08/28 09:48:11 djm Exp $ */ +/* $OpenBSD: auth2.c,v 1.168 2023/12/18 14:45:49 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -55,6 +55,7 @@ #include "monitor_wrap.h" #include "ssherr.h" #include "digest.h" +#include "kex.h" /* import */ extern ServerOptions options; @@ -162,6 +163,8 @@ do_authentication2(struct ssh *ssh) Authctxt *authctxt = ssh->authctxt; ssh_dispatch_init(ssh, &dispatch_protocol_error); + if (ssh->kex->ext_info_c) + ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_input_ext_info); ssh_dispatch_set(ssh, SSH2_MSG_SERVICE_REQUEST, &input_service_request); ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &authctxt->success); ssh->authctxt = NULL; @@ -201,6 +204,7 @@ input_service_request(int type, u_int32_t seq, struct ssh *ssh) debug("bad service request %s", service); ssh_packet_disconnect(ssh, "bad service request %s", service); } + ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &dispatch_protocol_error); r = 0; out: free(service); @@ -296,6 +300,8 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh) if (use_privsep) mm_inform_authserv(service, style); userauth_banner(ssh); + if ((r = kex_server_update_ext_info(ssh)) != 0) + fatal_fr(r, "kex_server_update_ext_info failed"); if (auth2_setup_methods_lists(authctxt) != 0) ssh_packet_disconnect(ssh, "no authentication methods enabled"); diff --git a/usr.bin/ssh/authfd.c b/usr.bin/ssh/authfd.c index 4e6763126..20b461e89 100644 --- a/usr.bin/ssh/authfd.c +++ b/usr.bin/ssh/authfd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: authfd.c,v 1.133 2023/03/09 21:06:24 jcs Exp $ */ +/* $OpenBSD: authfd.c,v 1.134 2023/12/18 14:46:56 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -503,9 +503,10 @@ encode_dest_constraint(struct sshbuf *m, const struct dest_constraint *dc) } static int -encode_constraints(struct sshbuf *m, u_int life, u_int confirm, u_int maxsign, - const char *provider, struct dest_constraint **dest_constraints, - size_t ndest_constraints) +encode_constraints(struct sshbuf *m, u_int life, u_int confirm, + u_int maxsign, const char *provider, + struct dest_constraint **dest_constraints, size_t ndest_constraints, + int cert_only, struct sshkey **certs, size_t ncerts) { int r; struct sshbuf *b = NULL; @@ -549,6 +550,27 @@ encode_constraints(struct sshbuf *m, u_int life, u_int confirm, u_int maxsign, "restrict-destination-v00@openssh.com")) != 0 || (r = sshbuf_put_stringb(m, b)) != 0) goto out; + sshbuf_free(b); + b = NULL; + } + if (ncerts != 0) { + if ((b = sshbuf_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + for (i = 0; i < ncerts; i++) { + if ((r = sshkey_puts(certs[i], b)) != 0) + goto out; + } + if ((r = sshbuf_put_u8(m, + SSH_AGENT_CONSTRAIN_EXTENSION)) != 0 || + (r = sshbuf_put_cstring(m, + "associated-certs-v00@openssh.com")) != 0 || + (r = sshbuf_put_u8(m, cert_only != 0)) != 0 || + (r = sshbuf_put_stringb(m, b)) != 0) + goto out; + sshbuf_free(b); + b = NULL; } r = 0; out: @@ -606,7 +628,7 @@ ssh_add_identity_constrained(int sock, struct sshkey *key, } if (constrained && (r = encode_constraints(msg, life, confirm, maxsign, - provider, dest_constraints, ndest_constraints)) != 0) + provider, dest_constraints, ndest_constraints, 0, NULL, 0)) != 0) goto out; if ((r = ssh_request_reply_decode(sock, msg)) != 0) goto out; @@ -661,10 +683,11 @@ ssh_remove_identity(int sock, const struct sshkey *key) int ssh_update_card(int sock, int add, const char *reader_id, const char *pin, u_int life, u_int confirm, - struct dest_constraint **dest_constraints, size_t ndest_constraints) + struct dest_constraint **dest_constraints, size_t ndest_constraints, + int cert_only, struct sshkey **certs, size_t ncerts) { struct sshbuf *msg; - int r, constrained = (life || confirm || dest_constraints); + int r, constrained = (life || confirm || dest_constraints || certs); u_char type; if (add) { @@ -682,7 +705,8 @@ ssh_update_card(int sock, int add, const char *reader_id, const char *pin, goto out; if (constrained && (r = encode_constraints(msg, life, confirm, 0, NULL, - dest_constraints, ndest_constraints)) != 0) + dest_constraints, ndest_constraints, + cert_only, certs, ncerts)) != 0) goto out; if ((r = ssh_request_reply_decode(sock, msg)) != 0) goto out; diff --git a/usr.bin/ssh/authfd.h b/usr.bin/ssh/authfd.h index 7a1c0ddff..c1e4b405c 100644 --- a/usr.bin/ssh/authfd.h +++ b/usr.bin/ssh/authfd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: authfd.h,v 1.51 2021/12/19 22:10:24 djm Exp $ */ +/* $OpenBSD: authfd.h,v 1.52 2023/12/18 14:46:56 djm Exp $ */ /* * Author: Tatu Ylonen @@ -56,7 +56,8 @@ int ssh_remove_identity(int sock, const struct sshkey *key); int ssh_update_card(int sock, int add, const char *reader_id, const char *pin, u_int life, u_int confirm, struct dest_constraint **dest_constraints, - size_t ndest_constraints); + size_t ndest_constraints, + int cert_only, struct sshkey **certs, size_t ncerts); int ssh_remove_all_identities(int sock, int version); int ssh_agent_sign(int sock, const struct sshkey *key, diff --git a/usr.bin/ssh/channels.c b/usr.bin/ssh/channels.c index 7c611bc37..0431b8c38 100644 --- a/usr.bin/ssh/channels.c +++ b/usr.bin/ssh/channels.c @@ -1,4 +1,4 @@ -/* $OpenBSD: channels.c,v 1.434 2023/11/15 22:51:49 djm Exp $ */ +/* $OpenBSD: channels.c,v 1.435 2023/12/18 14:47:20 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -3365,11 +3365,20 @@ channel_input_data(int type, u_int32_t seq, struct ssh *ssh) return 0; } if (win_len > c->local_window) { - logit("channel %d: rcvd too much data %zu, win %u", - c->self, win_len, c->local_window); - return 0; + c->local_window_exceeded += win_len - c->local_window; + logit("channel %d: rcvd too much data %zu, win %u/%u " + "(excess %u)", c->self, win_len, c->local_window, + c->local_window_max, c->local_window_exceeded); + c->local_window = 0; + /* Allow 10% grace before bringing the hammer down */ + if (c->local_window_exceeded > (c->local_window_max / 10)) { + ssh_packet_disconnect(ssh, "channel %d: peer ignored " + "channel window", c->self); + } + } else { + c->local_window -= win_len; + c->local_window_exceeded = 0; } - c->local_window -= win_len; if (c->datagram) { if ((r = sshbuf_put_string(c->output, data, data_len)) != 0) diff --git a/usr.bin/ssh/channels.h b/usr.bin/ssh/channels.h index b8c888358..e0893ef86 100644 --- a/usr.bin/ssh/channels.h +++ b/usr.bin/ssh/channels.h @@ -1,4 +1,4 @@ -/* $OpenBSD: channels.h,v 1.153 2023/11/15 22:51:49 djm Exp $ */ +/* $OpenBSD: channels.h,v 1.154 2023/12/18 14:47:20 djm Exp $ */ /* * Author: Tatu Ylonen @@ -167,6 +167,7 @@ struct Channel { u_int remote_window; u_int remote_maxpacket; u_int local_window; + u_int local_window_exceeded; u_int local_window_max; u_int local_consumed; u_int local_maxpacket; diff --git a/usr.bin/ssh/kex.c b/usr.bin/ssh/kex.c index f0178efda..050d0df10 100644 --- a/usr.bin/ssh/kex.c +++ b/usr.bin/ssh/kex.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.c,v 1.182 2023/10/11 04:46:29 djm Exp $ */ +/* $OpenBSD: kex.c,v 1.184 2023/12/18 14:45:49 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * @@ -60,7 +60,7 @@ #include "xmalloc.h" /* prototype */ -static int kex_choose_conf(struct ssh *); +static int kex_choose_conf(struct ssh *, uint32_t seq); static int kex_input_newkeys(int, u_int32_t, struct ssh *); static const char * const proposal_names[PROPOSAL_MAX] = { @@ -162,6 +162,18 @@ kex_names_valid(const char *names) return 1; } +/* returns non-zero if proposal contains any algorithm from algs */ +static int +has_any_alg(const char *proposal, const char *algs) +{ + char *cp; + + if ((cp = match_list(proposal, algs, NULL)) == NULL) + return 0; + free(cp); + return 1; +} + /* * Concatenate algorithm names, avoiding duplicates in the process. * Caller must free returned string. @@ -169,7 +181,7 @@ kex_names_valid(const char *names) char * kex_names_cat(const char *a, const char *b) { - char *ret = NULL, *tmp = NULL, *cp, *p, *m; + char *ret = NULL, *tmp = NULL, *cp, *p; size_t len; if (a == NULL || *a == '\0') @@ -186,10 +198,8 @@ kex_names_cat(const char *a, const char *b) } strlcpy(ret, a, len); for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) { - if ((m = match_list(ret, p, NULL)) != NULL) { - free(m); + if (has_any_alg(ret, p)) continue; /* Algorithm already present */ - } if (strlcat(ret, ",", len) >= len || strlcat(ret, p, len) >= len) { free(tmp); @@ -319,15 +329,23 @@ kex_proposal_populate_entries(struct ssh *ssh, char *prop[PROPOSAL_MAX], const char *defpropclient[PROPOSAL_MAX] = { KEX_CLIENT }; const char **defprop = ssh->kex->server ? defpropserver : defpropclient; u_int i; + char *cp; if (prop == NULL) fatal_f("proposal missing"); + /* Append EXT_INFO signalling to KexAlgorithms */ + if (kexalgos == NULL) + kexalgos = defprop[PROPOSAL_KEX_ALGS]; + if ((cp = kex_names_cat(kexalgos, ssh->kex->server ? + "ext-info-s,kex-strict-s-v00@openssh.com" : + "ext-info-c,kex-strict-c-v00@openssh.com")) == NULL) + fatal_f("kex_names_cat"); + for (i = 0; i < PROPOSAL_MAX; i++) { switch(i) { case PROPOSAL_KEX_ALGS: - prop[i] = compat_kex_proposal(ssh, - kexalgos ? kexalgos : defprop[i]); + prop[i] = compat_kex_proposal(ssh, cp); break; case PROPOSAL_ENC_ALGS_CTOS: case PROPOSAL_ENC_ALGS_STOC: @@ -348,6 +366,7 @@ kex_proposal_populate_entries(struct ssh *ssh, char *prop[PROPOSAL_MAX], prop[i] = xstrdup(defprop[i]); } } + free(cp); } void @@ -451,7 +470,12 @@ kex_protocol_error(int type, u_int32_t seq, struct ssh *ssh) { int r; - error("kex protocol error: type %d seq %u", type, seq); + /* If in strict mode, any unexpected message is an error */ + if ((ssh->kex->flags & KEX_INITIAL) && ssh->kex->kex_strict) { + ssh_packet_disconnect(ssh, "strict KEX violation: " + "unexpected packet type %u (seqnr %u)", type, seq); + } + error_f("type %u seq %u", type, seq); if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 || (r = sshpkt_put_u32(ssh, seq)) != 0 || (r = sshpkt_send(ssh)) != 0) @@ -466,36 +490,138 @@ kex_reset_dispatch(struct ssh *ssh) SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error); } +void +kex_set_server_sig_algs(struct ssh *ssh, const char *allowed_algs) +{ + char *alg, *oalgs, *algs, *sigalgs; + const char *sigalg; + + /* + * NB. allowed algorithms may contain certificate algorithms that + * map to a specific plain signature type, e.g. + * rsa-sha2-512-cert-v01@openssh.com => rsa-sha2-512 + * We need to be careful here to match these, retain the mapping + * and only add each signature algorithm once. + */ + if ((sigalgs = sshkey_alg_list(0, 1, 1, ',')) == NULL) + fatal_f("sshkey_alg_list failed"); + oalgs = algs = xstrdup(allowed_algs); + free(ssh->kex->server_sig_algs); + ssh->kex->server_sig_algs = NULL; + for ((alg = strsep(&algs, ",")); alg != NULL && *alg != '\0'; + (alg = strsep(&algs, ","))) { + if ((sigalg = sshkey_sigalg_by_name(alg)) == NULL) + continue; + if (!has_any_alg(sigalg, sigalgs)) + continue; + /* Don't add an algorithm twice. */ + if (ssh->kex->server_sig_algs != NULL && + has_any_alg(sigalg, ssh->kex->server_sig_algs)) + continue; + xextendf(&ssh->kex->server_sig_algs, ",", "%s", sigalg); + } + free(oalgs); + free(sigalgs); + if (ssh->kex->server_sig_algs == NULL) + ssh->kex->server_sig_algs = xstrdup(""); +} + static int -kex_send_ext_info(struct ssh *ssh) +kex_compose_ext_info_server(struct ssh *ssh, struct sshbuf *m) { int r; - char *algs; - debug("Sending SSH2_MSG_EXT_INFO"); - if ((algs = sshkey_alg_list(0, 1, 1, ',')) == NULL) + if (ssh->kex->server_sig_algs == NULL && + (ssh->kex->server_sig_algs = sshkey_alg_list(0, 1, 1, ',')) == NULL) return SSH_ERR_ALLOC_FAIL; - /* XXX filter algs list by allowed pubkey/hostbased types */ - if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 || - (r = sshpkt_put_u32(ssh, 3)) != 0 || - (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 || - (r = sshpkt_put_cstring(ssh, algs)) != 0 || - (r = sshpkt_put_cstring(ssh, + if ((r = sshbuf_put_u32(m, 3)) != 0 || + (r = sshbuf_put_cstring(m, "server-sig-algs")) != 0 || + (r = sshbuf_put_cstring(m, ssh->kex->server_sig_algs)) != 0 || + (r = sshbuf_put_cstring(m, "publickey-hostbound@openssh.com")) != 0 || - (r = sshpkt_put_cstring(ssh, "0")) != 0 || - (r = sshpkt_put_cstring(ssh, "ping@openssh.com")) != 0 || - (r = sshpkt_put_cstring(ssh, "0")) != 0 || - (r = sshpkt_send(ssh)) != 0) { + (r = sshbuf_put_cstring(m, "0")) != 0 || + (r = sshbuf_put_cstring(m, "ping@openssh.com")) != 0 || + (r = sshbuf_put_cstring(m, "0")) != 0) { + error_fr(r, "compose"); + return r; + } + return 0; +} + +static int +kex_compose_ext_info_client(struct ssh *ssh, struct sshbuf *m) +{ + int r; + + if ((r = sshbuf_put_u32(m, 1)) != 0 || + (r = sshbuf_put_cstring(m, "ext-info-in-auth@openssh.com")) != 0 || + (r = sshbuf_put_cstring(m, "0")) != 0) { error_fr(r, "compose"); goto out; } /* success */ r = 0; out: - free(algs); return r; } +static int +kex_maybe_send_ext_info(struct ssh *ssh) +{ + int r; + struct sshbuf *m = NULL; + + if ((ssh->kex->flags & KEX_INITIAL) == 0) + return 0; + if (!ssh->kex->ext_info_c && !ssh->kex->ext_info_s) + return 0; + + /* Compose EXT_INFO packet. */ + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if (ssh->kex->ext_info_c && + (r = kex_compose_ext_info_server(ssh, m)) != 0) + goto fail; + if (ssh->kex->ext_info_s && + (r = kex_compose_ext_info_client(ssh, m)) != 0) + goto fail; + + /* Send the actual KEX_INFO packet */ + debug("Sending SSH2_MSG_EXT_INFO"); + if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 || + (r = sshpkt_putb(ssh, m)) != 0 || + (r = sshpkt_send(ssh)) != 0) { + error_f("send EXT_INFO"); + goto fail; + } + + r = 0; + + fail: + sshbuf_free(m); + return r; +} + +int +kex_server_update_ext_info(struct ssh *ssh) +{ + int r; + + if ((ssh->kex->flags & KEX_HAS_EXT_INFO_IN_AUTH) == 0) + return 0; + + debug_f("Sending SSH2_MSG_EXT_INFO"); + if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 || + (r = sshpkt_put_u32(ssh, 1)) != 0 || + (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 || + (r = sshpkt_put_cstring(ssh, ssh->kex->server_sig_algs)) != 0 || + (r = sshpkt_send(ssh)) != 0) { + error_f("send EXT_INFO"); + return r; + } + return 0; +} + int kex_send_newkeys(struct ssh *ssh) { @@ -507,9 +633,8 @@ kex_send_newkeys(struct ssh *ssh) return r; debug("SSH2_MSG_NEWKEYS sent"); ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys); - if (ssh->kex->ext_info_c && (ssh->kex->flags & KEX_INITIAL) != 0) - if ((r = kex_send_ext_info(ssh)) != 0) - return r; + if ((r = kex_maybe_send_ext_info(ssh)) != 0) + return r; debug("expecting SSH2_MSG_NEWKEYS"); return 0; } @@ -531,10 +656,61 @@ kex_ext_info_check_ver(struct kex *kex, const char *name, return 0; } +static int +kex_ext_info_client_parse(struct ssh *ssh, const char *name, + const u_char *value, size_t vlen) +{ + int r; + + /* NB. some messages are only accepted in the initial EXT_INFO */ + if (strcmp(name, "server-sig-algs") == 0) { + /* Ensure no \0 lurking in value */ + if (memchr(value, '\0', vlen) != NULL) { + error_f("nul byte in %s", name); + return SSH_ERR_INVALID_FORMAT; + } + debug_f("%s=<%s>", name, value); + free(ssh->kex->server_sig_algs); + ssh->kex->server_sig_algs = xstrdup((const char *)value); + } else if (ssh->kex->ext_info_received == 1 && + strcmp(name, "publickey-hostbound@openssh.com") == 0) { + if ((r = kex_ext_info_check_ver(ssh->kex, name, value, vlen, + "0", KEX_HAS_PUBKEY_HOSTBOUND)) != 0) { + return r; + } + } else if (ssh->kex->ext_info_received == 1 && + strcmp(name, "ping@openssh.com") == 0) { + if ((r = kex_ext_info_check_ver(ssh->kex, name, value, vlen, + "0", KEX_HAS_PING)) != 0) { + return r; + } + } else + debug_f("%s (unrecognised)", name); + + return 0; +} + +static int +kex_ext_info_server_parse(struct ssh *ssh, const char *name, + const u_char *value, size_t vlen) +{ + int r; + + if (strcmp(name, "ext-info-in-auth@openssh.com") == 0) { + if ((r = kex_ext_info_check_ver(ssh->kex, name, value, vlen, + "0", KEX_HAS_EXT_INFO_IN_AUTH)) != 0) { + return r; + } + } else + debug_f("%s (unrecognised)", name); + return 0; +} + int kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh) { struct kex *kex = ssh->kex; + const int max_ext_info = kex->server ? 1 : 2; u_int32_t i, ninfo; char *name; u_char *val; @@ -542,13 +718,17 @@ kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh) int r; debug("SSH2_MSG_EXT_INFO received"); + if (++kex->ext_info_received > max_ext_info) { + error("too many SSH2_MSG_EXT_INFO messages sent by peer"); + return dispatch_protocol_error(type, seq, ssh); + } ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error); if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0) return r; if (ninfo >= 1024) { error("SSH2_MSG_EXT_INFO with too many entries, expected " "<=1024, received %u", ninfo); - return SSH_ERR_INVALID_FORMAT; + return dispatch_protocol_error(type, seq, ssh); } for (i = 0; i < ninfo; i++) { if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0) @@ -557,34 +737,16 @@ kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh) free(name); return r; } - if (strcmp(name, "server-sig-algs") == 0) { - /* Ensure no \0 lurking in value */ - if (memchr(val, '\0', vlen) != NULL) { - error_f("nul byte in %s", name); - free(name); - free(val); - return SSH_ERR_INVALID_FORMAT; - } - debug_f("%s=<%s>", name, val); - kex->server_sig_algs = val; - val = NULL; - } else if (strcmp(name, - "publickey-hostbound@openssh.com") == 0) { - if ((r = kex_ext_info_check_ver(kex, name, val, vlen, - "0", KEX_HAS_PUBKEY_HOSTBOUND)) != 0) { - free(name); - free(val); + debug3_f("extension %s", name); + if (kex->server) { + if ((r = kex_ext_info_server_parse(ssh, name, + val, vlen)) != 0) return r; - } - } else if (strcmp(name, "ping@openssh.com") == 0) { - if ((r = kex_ext_info_check_ver(kex, name, val, vlen, - "0", KEX_HAS_PING)) != 0) { - free(name); - free(val); + } else { + if ((r = kex_ext_info_client_parse(ssh, name, + val, vlen)) != 0) return r; - } - } else - debug_f("%s (unrecognised)", name); + } free(name); free(val); } @@ -598,6 +760,8 @@ kex_input_newkeys(int type, u_int32_t seq, struct ssh *ssh) int r; debug("SSH2_MSG_NEWKEYS received"); + if (kex->ext_info_c && (kex->flags & KEX_INITIAL) != 0) + ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_input_ext_info); ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error); ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); if ((r = sshpkt_get_end(ssh)) != 0) @@ -666,7 +830,7 @@ kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh) error_f("no kex"); return SSH_ERR_INTERNAL_ERROR; } - ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, NULL); + ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_protocol_error); ptr = sshpkt_ptr(ssh, &dlen); if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0) return r; @@ -702,7 +866,7 @@ kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh) if (!(kex->flags & KEX_INIT_SENT)) if ((r = kex_send_kexinit(ssh)) != 0) return r; - if ((r = kex_choose_conf(ssh)) != 0) + if ((r = kex_choose_conf(ssh, seq)) != 0) return r; if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL) @@ -964,20 +1128,14 @@ proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX]) return (1); } -/* returns non-zero if proposal contains any algorithm from algs */ static int -has_any_alg(const char *proposal, const char *algs) +kexalgs_contains(char **peer, const char *ext) { - char *cp; - - if ((cp = match_list(proposal, algs, NULL)) == NULL) - return 0; - free(cp); - return 1; + return has_any_alg(peer[PROPOSAL_KEX_ALGS], ext); } static int -kex_choose_conf(struct ssh *ssh) +kex_choose_conf(struct ssh *ssh, uint32_t seq) { struct kex *kex = ssh->kex; struct newkeys *newkeys; @@ -1002,13 +1160,24 @@ kex_choose_conf(struct ssh *ssh) sprop=peer; } - /* Check whether client supports ext_info_c */ - if (kex->server && (kex->flags & KEX_INITIAL)) { - char *ext; - - ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL); - kex->ext_info_c = (ext != NULL); - free(ext); + /* Check whether peer supports ext_info/kex_strict */ + if ((kex->flags & KEX_INITIAL) != 0) { + if (kex->server) { + kex->ext_info_c = kexalgs_contains(peer, "ext-info-c"); + kex->kex_strict = kexalgs_contains(peer, + "kex-strict-c-v00@openssh.com"); + } else { + kex->ext_info_s = kexalgs_contains(peer, "ext-info-s"); + kex->kex_strict = kexalgs_contains(peer, + "kex-strict-s-v00@openssh.com"); + } + if (kex->kex_strict) { + debug3_f("will use strict KEX ordering"); + if (seq != 0) + ssh_packet_disconnect(ssh, + "strict KEX violation: " + "KEXINIT was not the first packet"); + } } /* Check whether client supports rsa-sha2 algorithms */ diff --git a/usr.bin/ssh/kex.h b/usr.bin/ssh/kex.h index 50738b18d..cac066b4f 100644 --- a/usr.bin/ssh/kex.h +++ b/usr.bin/ssh/kex.h @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.h,v 1.119 2023/08/28 03:28:43 djm Exp $ */ +/* $OpenBSD: kex.h,v 1.121 2023/12/18 14:45:49 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -105,6 +105,7 @@ enum kex_exchange { #define KEX_RSA_SHA2_256_SUPPORTED 0x0008 /* only set in server for now */ #define KEX_RSA_SHA2_512_SUPPORTED 0x0010 /* only set in server for now */ #define KEX_HAS_PING 0x0020 +#define KEX_HAS_EXT_INFO_IN_AUTH 0x0040 struct sshenc { char *name; @@ -142,6 +143,9 @@ struct kex { u_int kex_type; char *server_sig_algs; int ext_info_c; + int ext_info_s; + int kex_strict; + int ext_info_received; struct sshbuf *my; struct sshbuf *peer; struct sshbuf *client_version; @@ -201,6 +205,8 @@ int kex_protocol_error(int, u_int32_t, struct ssh *); int kex_derive_keys(struct ssh *, u_char *, u_int, const struct sshbuf *); int kex_send_newkeys(struct ssh *); int kex_start_rekex(struct ssh *); +int kex_server_update_ext_info(struct ssh *); +void kex_set_server_sig_algs(struct ssh *, const char *); int kexgex_client(struct ssh *); int kexgex_server(struct ssh *); diff --git a/usr.bin/ssh/monitor_wrap.c b/usr.bin/ssh/monitor_wrap.c index 301b21610..3115dfee9 100644 --- a/usr.bin/ssh/monitor_wrap.c +++ b/usr.bin/ssh/monitor_wrap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor_wrap.c,v 1.128 2023/03/31 00:44:29 dtucker Exp $ */ +/* $OpenBSD: monitor_wrap.c,v 1.129 2023/12/18 14:45:49 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -327,8 +327,8 @@ out: log_verbose_add(options.log_verbose[i]); process_permitopen(ssh, &options); process_channel_timeouts(ssh, &options); + kex_set_server_sig_algs(ssh, options.pubkey_accepted_algos); free(newopts); - sshbuf_free(m); return (pw); diff --git a/usr.bin/ssh/packet.c b/usr.bin/ssh/packet.c index 0ef57d4e9..4ac450d3a 100644 --- a/usr.bin/ssh/packet.c +++ b/usr.bin/ssh/packet.c @@ -1,4 +1,4 @@ -/* $OpenBSD: packet.c,v 1.312 2023/08/28 03:31:16 djm Exp $ */ +/* $OpenBSD: packet.c,v 1.313 2023/12/18 14:45:17 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1187,8 +1187,13 @@ ssh_packet_send2_wrapped(struct ssh *ssh) sshbuf_dump(state->output, stderr); #endif /* increment sequence number for outgoing packets */ - if (++state->p_send.seqnr == 0) + if (++state->p_send.seqnr == 0) { + if ((ssh->kex->flags & KEX_INITIAL) != 0) { + ssh_packet_disconnect(ssh, "outgoing sequence number " + "wrapped during initial key exchange"); + } logit("outgoing seqnr wraps around"); + } if (++state->p_send.packets == 0) if (!(ssh->compat & SSH_BUG_NOREKEY)) return SSH_ERR_NEED_REKEY; @@ -1196,6 +1201,11 @@ ssh_packet_send2_wrapped(struct ssh *ssh) state->p_send.bytes += len; sshbuf_reset(state->outgoing_packet); + if (type == SSH2_MSG_NEWKEYS && ssh->kex->kex_strict) { + debug_f("resetting send seqnr %u", state->p_send.seqnr); + state->p_send.seqnr = 0; + } + if (type == SSH2_MSG_NEWKEYS) r = ssh_set_newkeys(ssh, MODE_OUT); else if (type == SSH2_MSG_USERAUTH_SUCCESS && state->server_side) @@ -1324,8 +1334,7 @@ ssh_packet_read_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) /* Stay in the loop until we have received a complete packet. */ for (;;) { /* Try to read a packet from the buffer. */ - r = ssh_packet_read_poll_seqnr(ssh, typep, seqnr_p); - if (r != 0) + if ((r = ssh_packet_read_poll_seqnr(ssh, typep, seqnr_p)) != 0) break; /* If we got a packet, return it. */ if (*typep != SSH_MSG_NONE) @@ -1395,29 +1404,6 @@ ssh_packet_read(struct ssh *ssh) return type; } -/* - * Waits until a packet has been received, verifies that its type matches - * that given, and gives a fatal error and exits if there is a mismatch. - */ - -int -ssh_packet_read_expect(struct ssh *ssh, u_int expected_type) -{ - int r; - u_char type; - - if ((r = ssh_packet_read_seqnr(ssh, &type, NULL)) != 0) - return r; - if (type != expected_type) { - if ((r = sshpkt_disconnect(ssh, - "Protocol error: expected packet type %d, got %d", - expected_type, type)) != 0) - return r; - return SSH_ERR_PROTOCOL_ERROR; - } - return 0; -} - static int ssh_packet_read_poll2_mux(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) { @@ -1608,10 +1594,16 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) if ((r = sshbuf_consume(state->input, mac->mac_len)) != 0) goto out; } + if (seqnr_p != NULL) *seqnr_p = state->p_read.seqnr; - if (++state->p_read.seqnr == 0) + if (++state->p_read.seqnr == 0) { + if ((ssh->kex->flags & KEX_INITIAL) != 0) { + ssh_packet_disconnect(ssh, "incoming sequence number " + "wrapped during initial key exchange"); + } logit("incoming seqnr wraps around"); + } if (++state->p_read.packets == 0) if (!(ssh->compat & SSH_BUG_NOREKEY)) return SSH_ERR_NEED_REKEY; @@ -1677,6 +1669,10 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) #endif /* reset for next packet */ state->packlen = 0; + if (*typep == SSH2_MSG_NEWKEYS && ssh->kex->kex_strict) { + debug_f("resetting read seqnr %u", state->p_read.seqnr); + state->p_read.seqnr = 0; + } if ((r = ssh_packet_check_rekey(ssh)) != 0) return r; @@ -1699,10 +1695,39 @@ ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) r = ssh_packet_read_poll2(ssh, typep, seqnr_p); if (r != 0) return r; - if (*typep) { - state->keep_alive_timeouts = 0; - DBG(debug("received packet type %d", *typep)); + if (*typep == 0) { + /* no message ready */ + return 0; } + state->keep_alive_timeouts = 0; + DBG(debug("received packet type %d", *typep)); + + /* Always process disconnect messages */ + if (*typep == SSH2_MSG_DISCONNECT) { + if ((r = sshpkt_get_u32(ssh, &reason)) != 0 || + (r = sshpkt_get_string(ssh, &msg, NULL)) != 0) + return r; + /* Ignore normal client exit notifications */ + do_log2(ssh->state->server_side && + reason == SSH2_DISCONNECT_BY_APPLICATION ? + SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_ERROR, + "Received disconnect from %s port %d:" + "%u: %.400s", ssh_remote_ipaddr(ssh), + ssh_remote_port(ssh), reason, msg); + free(msg); + return SSH_ERR_DISCONNECTED; + } + + /* + * Do not implicitly handle any messages here during initial + * KEX when in strict mode. They will be need to be allowed + * explicitly by the KEX dispatch table or they will generate + * protocol errors. + */ + if (ssh->kex != NULL && + (ssh->kex->flags & KEX_INITIAL) && ssh->kex->kex_strict) + return 0; + /* Implicitly handle transport-level messages */ switch (*typep) { case SSH2_MSG_IGNORE: debug3("Received SSH2_MSG_IGNORE"); @@ -1717,19 +1742,6 @@ ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) debug("Remote: %.900s", msg); free(msg); break; - case SSH2_MSG_DISCONNECT: - if ((r = sshpkt_get_u32(ssh, &reason)) != 0 || - (r = sshpkt_get_string(ssh, &msg, NULL)) != 0) - return r; - /* Ignore normal client exit notifications */ - do_log2(ssh->state->server_side && - reason == SSH2_DISCONNECT_BY_APPLICATION ? - SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_ERROR, - "Received disconnect from %s port %d:" - "%u: %.400s", ssh_remote_ipaddr(ssh), - ssh_remote_port(ssh), reason, msg); - free(msg); - return SSH_ERR_DISCONNECTED; case SSH2_MSG_UNIMPLEMENTED: if ((r = sshpkt_get_u32(ssh, &seqnr)) != 0) return r; @@ -2219,6 +2231,7 @@ kex_to_blob(struct sshbuf *m, struct kex *kex) (r = sshbuf_put_u32(m, kex->hostkey_type)) != 0 || (r = sshbuf_put_u32(m, kex->hostkey_nid)) != 0 || (r = sshbuf_put_u32(m, kex->kex_type)) != 0 || + (r = sshbuf_put_u32(m, kex->kex_strict)) != 0 || (r = sshbuf_put_stringb(m, kex->my)) != 0 || (r = sshbuf_put_stringb(m, kex->peer)) != 0 || (r = sshbuf_put_stringb(m, kex->client_version)) != 0 || @@ -2381,6 +2394,7 @@ kex_from_blob(struct sshbuf *m, struct kex **kexp) (r = sshbuf_get_u32(m, (u_int *)&kex->hostkey_type)) != 0 || (r = sshbuf_get_u32(m, (u_int *)&kex->hostkey_nid)) != 0 || (r = sshbuf_get_u32(m, &kex->kex_type)) != 0 || + (r = sshbuf_get_u32(m, &kex->kex_strict)) != 0 || (r = sshbuf_get_stringb(m, kex->my)) != 0 || (r = sshbuf_get_stringb(m, kex->peer)) != 0 || (r = sshbuf_get_stringb(m, kex->client_version)) != 0 || @@ -2705,6 +2719,7 @@ sshpkt_disconnect(struct ssh *ssh, const char *fmt,...) vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); + debug2_f("sending SSH2_MSG_DISCONNECT: %s", buf); if ((r = sshpkt_start(ssh, SSH2_MSG_DISCONNECT)) != 0 || (r = sshpkt_put_u32(ssh, SSH2_DISCONNECT_PROTOCOL_ERROR)) != 0 || (r = sshpkt_put_cstring(ssh, buf)) != 0 || diff --git a/usr.bin/ssh/packet.h b/usr.bin/ssh/packet.h index 39ee0b5ff..7dbe682af 100644 --- a/usr.bin/ssh/packet.h +++ b/usr.bin/ssh/packet.h @@ -1,4 +1,4 @@ -/* $OpenBSD: packet.h,v 1.95 2023/08/28 03:31:16 djm Exp $ */ +/* $OpenBSD: packet.h,v 1.96 2023/12/18 14:45:17 djm Exp $ */ /* * Author: Tatu Ylonen @@ -118,7 +118,6 @@ int ssh_packet_send2_wrapped(struct ssh *); int ssh_packet_send2(struct ssh *); int ssh_packet_read(struct ssh *); -int ssh_packet_read_expect(struct ssh *, u_int type); int ssh_packet_read_poll(struct ssh *); int ssh_packet_read_poll2(struct ssh *, u_char *, u_int32_t *seqnr_p); int ssh_packet_process_incoming(struct ssh *, const char *buf, u_int len); diff --git a/usr.bin/ssh/ssh-add.1 b/usr.bin/ssh/ssh-add.1 index 4601f5981..f0186cd5f 100644 --- a/usr.bin/ssh/ssh-add.1 +++ b/usr.bin/ssh/ssh-add.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ssh-add.1,v 1.84 2022/02/04 02:49:17 dtucker Exp $ +.\" $OpenBSD: ssh-add.1,v 1.85 2023/12/18 14:46:56 djm Exp $ .\" .\" Author: Tatu Ylonen .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -35,7 +35,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd $Mdocdate: February 4 2022 $ +.Dd $Mdocdate: December 18 2023 $ .Dt SSH-ADD 1 .Os .Sh NAME @@ -43,7 +43,7 @@ .Nd adds private key identities to the OpenSSH authentication agent .Sh SYNOPSIS .Nm ssh-add -.Op Fl cDdKkLlqvXx +.Op Fl cCDdKkLlqvXx .Op Fl E Ar fingerprint_hash .Op Fl H Ar hostkey_file .Op Fl h Ar destination_constraint @@ -52,6 +52,8 @@ .Op Ar .Nm ssh-add .Fl s Ar pkcs11 +.Op Fl vC +.Op Ar certificate ... .Nm ssh-add .Fl e Ar pkcs11 .Nm ssh-add @@ -100,6 +102,9 @@ Confirmation is performed by Successful confirmation is signaled by a zero exit status from .Xr ssh-askpass 1 , rather than text entered into the requester. +.It Fl C +When loading keys into or deleting keys from the agent, process +certificates only and skip plain keys. .It Fl D Deletes all identities from the agent. .It Fl d @@ -228,6 +233,9 @@ internal USB HID support. .It Fl s Ar pkcs11 Add keys provided by the PKCS#11 shared library .Ar pkcs11 . +Certificate files may optionally be listed as command-line arguments. +If these are present, then they will be loaded into the agent using any +corresponding private keys loaded from the PKCS#11 token. .It Fl T Ar pubkey ... Tests whether the private keys that correspond to the specified .Ar pubkey diff --git a/usr.bin/ssh/ssh-add.c b/usr.bin/ssh/ssh-add.c index ab92a540f..ef321ea95 100644 --- a/usr.bin/ssh/ssh-add.c +++ b/usr.bin/ssh/ssh-add.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-add.c,v 1.168 2023/07/06 22:17:59 dtucker Exp $ */ +/* $OpenBSD: ssh-add.c,v 1.169 2023/12/18 14:46:56 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -124,7 +124,7 @@ delete_one(int agent_fd, const struct sshkey *key, const char *comment, } static int -delete_stdin(int agent_fd, int qflag) +delete_stdin(int agent_fd, int qflag, int key_only, int cert_only) { char *line = NULL, *cp; size_t linesize = 0; @@ -145,8 +145,13 @@ delete_stdin(int agent_fd, int qflag) error_r(r, "(stdin):%d: invalid key", lnum); continue; } - if (delete_one(agent_fd, key, cp, "(stdin)", qflag) == 0) - ret = 0; + if ((!key_only && !cert_only) || + (key_only && !sshkey_is_cert(key)) || + (cert_only && sshkey_is_cert(key))) { + if (delete_one(agent_fd, key, cp, + "(stdin)", qflag) == 0) + ret = 0; + } } sshkey_free(key); free(line); @@ -154,21 +159,26 @@ delete_stdin(int agent_fd, int qflag) } static int -delete_file(int agent_fd, const char *filename, int key_only, int qflag) +delete_file(int agent_fd, const char *filename, int key_only, + int cert_only, int qflag) { struct sshkey *public, *cert = NULL; char *certpath = NULL, *comment = NULL; int r, ret = -1; if (strcmp(filename, "-") == 0) - return delete_stdin(agent_fd, qflag); + return delete_stdin(agent_fd, qflag, key_only, cert_only); if ((r = sshkey_load_public(filename, &public, &comment)) != 0) { printf("Bad key file %s: %s\n", filename, ssh_err(r)); return -1; } - if (delete_one(agent_fd, public, comment, filename, qflag) == 0) - ret = 0; + if ((!key_only && !cert_only) || + (key_only && !sshkey_is_cert(public)) || + (cert_only && sshkey_is_cert(public))) { + if (delete_one(agent_fd, public, comment, filename, qflag) == 0) + ret = 0; + } if (key_only) goto out; @@ -224,8 +234,9 @@ delete_all(int agent_fd, int qflag) } static int -add_file(int agent_fd, const char *filename, int key_only, int qflag, - const char *skprovider, struct dest_constraint **dest_constraints, +add_file(int agent_fd, const char *filename, int key_only, int cert_only, + int qflag, const char *skprovider, + struct dest_constraint **dest_constraints, size_t ndest_constraints) { struct sshkey *private, *cert; @@ -354,7 +365,8 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag, skprovider = NULL; } - if ((r = ssh_add_identity_constrained(agent_fd, private, comment, + if (!cert_only && + (r = ssh_add_identity_constrained(agent_fd, private, comment, lifetime, confirm, maxsign, skprovider, dest_constraints, ndest_constraints)) == 0) { ret = 0; @@ -383,7 +395,8 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag, xasprintf(&certpath, "%s-cert.pub", filename); if ((r = sshkey_load_public(certpath, &cert, NULL)) != 0) { if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT) - error_r(r, "Failed to load certificate \"%s\"", certpath); + error_r(r, "Failed to load certificate \"%s\"", + certpath); goto out; } @@ -438,11 +451,16 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag, static int update_card(int agent_fd, int add, const char *id, int qflag, - struct dest_constraint **dest_constraints, size_t ndest_constraints) + int key_only, int cert_only, + struct dest_constraint **dest_constraints, size_t ndest_constraints, + struct sshkey **certs, size_t ncerts) { char *pin = NULL; int r, ret = -1; + if (key_only) + ncerts = 0; + if (add) { if ((pin = read_passphrase("Enter passphrase for PKCS#11: ", RP_ALLOW_STDIN)) == NULL) @@ -450,7 +468,8 @@ update_card(int agent_fd, int add, const char *id, int qflag, } if ((r = ssh_update_card(agent_fd, add, id, pin == NULL ? "" : pin, - lifetime, confirm, dest_constraints, ndest_constraints)) == 0) { + lifetime, confirm, dest_constraints, ndest_constraints, + cert_only, certs, ncerts)) == 0) { ret = 0; if (!qflag) { fprintf(stderr, "Card %s: %s\n", @@ -626,16 +645,17 @@ load_resident_keys(int agent_fd, const char *skprovider, int qflag, } static int -do_file(int agent_fd, int deleting, int key_only, char *file, int qflag, - const char *skprovider, struct dest_constraint **dest_constraints, - size_t ndest_constraints) +do_file(int agent_fd, int deleting, int key_only, int cert_only, + char *file, int qflag, const char *skprovider, + struct dest_constraint **dest_constraints, size_t ndest_constraints) { if (deleting) { - if (delete_file(agent_fd, file, key_only, qflag) == -1) + if (delete_file(agent_fd, file, key_only, + cert_only, qflag) == -1) return -1; } else { - if (add_file(agent_fd, file, key_only, qflag, skprovider, - dest_constraints, ndest_constraints) == -1) + if (add_file(agent_fd, file, key_only, cert_only, qflag, + skprovider, dest_constraints, ndest_constraints) == -1) return -1; } return 0; @@ -783,12 +803,14 @@ main(int argc, char **argv) int agent_fd; char *pkcs11provider = NULL, *skprovider = NULL; char **dest_constraint_strings = NULL, **hostkey_files = NULL; - int r, i, ch, deleting = 0, ret = 0, key_only = 0, do_download = 0; - int xflag = 0, lflag = 0, Dflag = 0, qflag = 0, Tflag = 0; + int r, i, ch, deleting = 0, ret = 0, key_only = 0, cert_only = 0; + int do_download = 0, xflag = 0, lflag = 0, Dflag = 0; + int qflag = 0, Tflag = 0; SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; LogLevel log_level = SYSLOG_LEVEL_INFO; + struct sshkey *k, **certs = NULL; struct dest_constraint **dest_constraints = NULL; - size_t ndest_constraints = 0; + size_t ndest_constraints = 0i, ncerts = 0; /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); @@ -815,7 +837,7 @@ main(int argc, char **argv) skprovider = getenv("SSH_SK_PROVIDER"); - while ((ch = getopt(argc, argv, "vkKlLcdDTxXE:e:h:H:M:m:qs:S:t:")) != -1) { + while ((ch = getopt(argc, argv, "vkKlLCcdDTxXE:e:h:H:M:m:qs:S:t:")) != -1) { switch (ch) { case 'v': if (log_level == SYSLOG_LEVEL_INFO) @@ -837,6 +859,9 @@ main(int argc, char **argv) case 'k': key_only = 1; break; + case 'C': + cert_only = 1; + break; case 'K': do_download = 1; break; @@ -952,8 +977,19 @@ main(int argc, char **argv) goto done; } if (pkcs11provider != NULL) { + for (i = 0; i < argc; i++) { + if ((r = sshkey_load_public(argv[i], &k, NULL)) != 0) + fatal_fr(r, "load certificate %s", argv[i]); + certs = xrecallocarray(certs, ncerts, ncerts + 1, + sizeof(*certs)); + debug2("%s: %s", argv[i], sshkey_ssh_name(k)); + certs[ncerts++] = k; + } + debug2_f("loaded %zu certificates", ncerts); if (update_card(agent_fd, !deleting, pkcs11provider, - qflag, dest_constraints, ndest_constraints) == -1) + qflag, key_only, cert_only, + dest_constraints, ndest_constraints, + certs, ncerts) == -1) ret = 1; goto done; } @@ -983,8 +1019,8 @@ main(int argc, char **argv) default_files[i]); if (stat(buf, &st) == -1) continue; - if (do_file(agent_fd, deleting, key_only, buf, - qflag, skprovider, + if (do_file(agent_fd, deleting, key_only, cert_only, + buf, qflag, skprovider, dest_constraints, ndest_constraints) == -1) ret = 1; else @@ -994,7 +1030,7 @@ main(int argc, char **argv) ret = 1; } else { for (i = 0; i < argc; i++) { - if (do_file(agent_fd, deleting, key_only, + if (do_file(agent_fd, deleting, key_only, cert_only, argv[i], qflag, skprovider, dest_constraints, ndest_constraints) == -1) ret = 1; diff --git a/usr.bin/ssh/ssh-agent.c b/usr.bin/ssh/ssh-agent.c index 0b2ee9712..1790106cf 100644 --- a/usr.bin/ssh/ssh-agent.c +++ b/usr.bin/ssh/ssh-agent.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-agent.c,v 1.300 2023/07/19 13:56:33 djm Exp $ */ +/* $OpenBSD: ssh-agent.c,v 1.304 2023/12/18 15:58:56 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -92,6 +92,8 @@ #define AGENT_MAX_SID_LEN 128 /* Maximum number of destination constraints to accept on a key */ #define AGENT_MAX_DEST_CONSTRAINTS 1024 +/* Maximum number of associated certificate constraints to accept on a key */ +#define AGENT_MAX_EXT_CERTS 1024 /* XXX store hostkey_sid in a refcounted tree */ @@ -115,6 +117,7 @@ typedef struct socket_entry { struct sshbuf *request; size_t nsession_ids; struct hostkey_sid *session_ids; + int session_bind_attempted; } SocketEntry; u_int sockets_alloc = 0; @@ -234,6 +237,91 @@ free_dest_constraints(struct dest_constraint *dcs, size_t ndcs) free(dcs); } +static void +dup_dest_constraint_hop(const struct dest_constraint_hop *dch, + struct dest_constraint_hop *out) +{ + u_int i; + int r; + + out->user = dch->user == NULL ? NULL : xstrdup(dch->user); + out->hostname = dch->hostname == NULL ? NULL : xstrdup(dch->hostname); + out->is_ca = dch->is_ca; + out->nkeys = dch->nkeys; + out->keys = out->nkeys == 0 ? NULL : + xcalloc(out->nkeys, sizeof(*out->keys)); + out->key_is_ca = out->nkeys == 0 ? NULL : + xcalloc(out->nkeys, sizeof(*out->key_is_ca)); + for (i = 0; i < dch->nkeys; i++) { + if (dch->keys[i] != NULL && + (r = sshkey_from_private(dch->keys[i], + &(out->keys[i]))) != 0) + fatal_fr(r, "copy key"); + out->key_is_ca[i] = dch->key_is_ca[i]; + } +} + +static struct dest_constraint * +dup_dest_constraints(const struct dest_constraint *dcs, size_t ndcs) +{ + size_t i; + struct dest_constraint *ret; + + if (ndcs == 0) + return NULL; + ret = xcalloc(ndcs, sizeof(*ret)); + for (i = 0; i < ndcs; i++) { + dup_dest_constraint_hop(&dcs[i].from, &ret[i].from); + dup_dest_constraint_hop(&dcs[i].to, &ret[i].to); + } + return ret; +} + +#ifdef DEBUG_CONSTRAINTS +static void +dump_dest_constraint_hop(const struct dest_constraint_hop *dch) +{ + u_int i; + char *fp; + + debug_f("user %s hostname %s is_ca %d nkeys %u", + dch->user == NULL ? "(null)" : dch->user, + dch->hostname == NULL ? "(null)" : dch->hostname, + dch->is_ca, dch->nkeys); + for (i = 0; i < dch->nkeys; i++) { + fp = NULL; + if (dch->keys[i] != NULL && + (fp = sshkey_fingerprint(dch->keys[i], + SSH_FP_HASH_DEFAULT, SSH_FP_DEFAULT)) == NULL) + fatal_f("fingerprint failed"); + debug_f("key %u/%u: %s%s%s key_is_ca %d", i, dch->nkeys, + dch->keys[i] == NULL ? "" : sshkey_ssh_name(dch->keys[i]), + dch->keys[i] == NULL ? "" : " ", + dch->keys[i] == NULL ? "none" : fp, + dch->key_is_ca[i]); + free(fp); + } +} +#endif /* DEBUG_CONSTRAINTS */ + +static void +dump_dest_constraints(const char *context, + const struct dest_constraint *dcs, size_t ndcs) +{ +#ifdef DEBUG_CONSTRAINTS + size_t i; + + debug_f("%s: %zu constraints", context, ndcs); + for (i = 0; i < ndcs; i++) { + debug_f("constraint %zu / %zu: from: ", i, ndcs); + dump_dest_constraint_hop(&dcs[i].from); + debug_f("constraint %zu / %zu: to: ", i, ndcs); + dump_dest_constraint_hop(&dcs[i].to); + } + debug_f("done for %s", context); +#endif /* DEBUG_CONSTRAINTS */ +} + static void free_identity(Identity *id) { @@ -377,6 +465,10 @@ identity_permitted(Identity *id, SocketEntry *e, char *user, e->nsession_ids, id->ndest_constraints); if (id->ndest_constraints == 0) return 0; /* unconstrained */ + if (e->session_bind_attempted && e->nsession_ids == 0) { + error_f("previous session bind failed on socket"); + return -1; + } if (e->nsession_ids == 0) return 0; /* local use */ /* @@ -456,6 +548,12 @@ identity_permitted(Identity *id, SocketEntry *e, char *user, return 0; } +static int +socket_is_remote(SocketEntry *e) +{ + return e->session_bind_attempted || (e->nsession_ids != 0); +} + /* return matching private key for given public key */ static Identity * lookup_identity(struct sshkey *key) @@ -505,13 +603,22 @@ process_request_identities(SocketEntry *e) Identity *id; struct sshbuf *msg, *keys; int r; - u_int nentries = 0; + u_int i = 0, nentries = 0; + char *fp; debug2_f("entering"); if ((msg = sshbuf_new()) == NULL || (keys = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); TAILQ_FOREACH(id, &idtab->idlist, next) { + if ((fp = sshkey_fingerprint(id->key, SSH_FP_HASH_DEFAULT, + SSH_FP_DEFAULT)) == NULL) + fatal_f("fingerprint failed"); + debug_f("key %u / %u: %s %s", i++, idtab->nentries, + sshkey_ssh_name(id->key), fp); + dump_dest_constraints(__func__, + id->dest_constraints, id->ndest_constraints); + free(fp); /* identity not visible, don't include in response */ if (identity_permitted(id, e, NULL, NULL, NULL) != 0) continue; @@ -1051,11 +1158,14 @@ parse_dest_constraint(struct sshbuf *m, struct dest_constraint *dc) static int parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp, - struct dest_constraint **dcsp, size_t *ndcsp) + struct dest_constraint **dcsp, size_t *ndcsp, int *cert_onlyp, + struct sshkey ***certs, size_t *ncerts) { char *ext_name = NULL; int r; struct sshbuf *b = NULL; + u_char v; + struct sshkey *k; if ((r = sshbuf_get_cstring(m, &ext_name, NULL)) != 0) { error_fr(r, "parse constraint extension"); @@ -1098,6 +1208,36 @@ parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp, *dcsp + (*ndcsp)++)) != 0) goto out; /* error already logged */ } + } else if (strcmp(ext_name, + "associated-certs-v00@openssh.com") == 0) { + if (certs == NULL || ncerts == NULL || cert_onlyp == NULL) { + error_f("%s not valid here", ext_name); + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (*certs != NULL) { + error_f("%s already set", ext_name); + goto out; + } + if ((r = sshbuf_get_u8(m, &v)) != 0 || + (r = sshbuf_froms(m, &b)) != 0) { + error_fr(r, "parse %s", ext_name); + goto out; + } + *cert_onlyp = v != 0; + while (sshbuf_len(b) != 0) { + if (*ncerts >= AGENT_MAX_EXT_CERTS) { + error_f("too many %s constraints", ext_name); + goto out; + } + *certs = xrecallocarray(*certs, *ncerts, *ncerts + 1, + sizeof(**certs)); + if ((r = sshkey_froms(b, &k)) != 0) { + error_fr(r, "parse key"); + goto out; + } + (*certs)[(*ncerts)++] = k; + } } else { error_f("unsupported constraint \"%s\"", ext_name); r = SSH_ERR_FEATURE_UNSUPPORTED; @@ -1114,7 +1254,8 @@ parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp, static int parse_key_constraints(struct sshbuf *m, struct sshkey *k, time_t *deathp, u_int *secondsp, int *confirmp, char **sk_providerp, - struct dest_constraint **dcsp, size_t *ndcsp) + struct dest_constraint **dcsp, size_t *ndcsp, + int *cert_onlyp, size_t *ncerts, struct sshkey ***certs) { u_char ctype; int r; @@ -1169,7 +1310,8 @@ parse_key_constraints(struct sshbuf *m, struct sshkey *k, time_t *deathp, break; case SSH_AGENT_CONSTRAIN_EXTENSION: if ((r = parse_key_constraint_extension(m, - sk_providerp, dcsp, ndcsp)) != 0) + sk_providerp, dcsp, ndcsp, + cert_onlyp, certs, ncerts)) != 0) goto out; /* error already logged */ break; default: @@ -1206,11 +1348,13 @@ process_add_identity(SocketEntry *e) goto out; } if (parse_key_constraints(e->request, k, &death, &seconds, &confirm, - &sk_provider, &dest_constraints, &ndest_constraints) != 0) { + &sk_provider, &dest_constraints, &ndest_constraints, + NULL, NULL, NULL) != 0) { error_f("failed to parse constraints"); sshbuf_reset(e->request); goto out; } + dump_dest_constraints(__func__, dest_constraints, ndest_constraints); if (sk_provider != NULL) { if (!sshkey_is_sk(k)) { @@ -1221,7 +1365,7 @@ process_add_identity(SocketEntry *e) if (strcasecmp(sk_provider, "internal") == 0) { debug_f("internal provider"); } else { - if (e->nsession_ids != 0 && !remote_add_provider) { + if (socket_is_remote(e) && !remote_add_provider) { verbose("failed add of SK provider \"%.100s\": " "remote addition of providers is disabled", sk_provider); @@ -1365,6 +1509,32 @@ no_identities(SocketEntry *e) sshbuf_free(msg); } +/* Add an identity to idlist; takes ownership of 'key' and 'comment' */ +static void +add_p11_identity(struct sshkey *key, char *comment, const char *provider, + time_t death, u_int confirm, struct dest_constraint *dest_constraints, + size_t ndest_constraints) +{ + Identity *id; + + if (lookup_identity(key) != NULL) { + sshkey_free(key); + free(comment); + return; + } + id = xcalloc(1, sizeof(Identity)); + id->key = key; + id->comment = comment; + id->provider = xstrdup(provider); + id->death = death; + id->confirm = confirm; + id->dest_constraints = dup_dest_constraints(dest_constraints, + ndest_constraints); + id->ndest_constraints = ndest_constraints; + TAILQ_INSERT_TAIL(&idtab->idlist, id, next); + idtab->nentries++; +} + #ifdef ENABLE_PKCS11 static void process_add_smartcard_key(SocketEntry *e) @@ -1375,9 +1545,10 @@ process_add_smartcard_key(SocketEntry *e) u_int seconds = 0; time_t death = 0; struct sshkey **keys = NULL, *k; - Identity *id; struct dest_constraint *dest_constraints = NULL; - size_t ndest_constraints = 0; + size_t j, ndest_constraints = 0, ncerts = 0; + struct sshkey **certs = NULL; + int cert_only = 0; debug2_f("entering"); if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 || @@ -1386,11 +1557,13 @@ process_add_smartcard_key(SocketEntry *e) goto send; } if (parse_key_constraints(e->request, NULL, &death, &seconds, &confirm, - NULL, &dest_constraints, &ndest_constraints) != 0) { + NULL, &dest_constraints, &ndest_constraints, &cert_only, + &ncerts, &certs) != 0) { error_f("failed to parse constraints"); goto send; } - if (e->nsession_ids != 0 && !remote_add_provider) { + dump_dest_constraints(__func__, dest_constraints, ndest_constraints); + if (socket_is_remote(e) && !remote_add_provider) { verbose("failed PKCS#11 add of \"%.100s\": remote addition of " "providers is disabled", provider); goto send; @@ -1411,26 +1584,28 @@ process_add_smartcard_key(SocketEntry *e) count = pkcs11_add_provider(canonical_provider, pin, &keys, &comments); for (i = 0; i < count; i++) { - k = keys[i]; - if (lookup_identity(k) == NULL) { - id = xcalloc(1, sizeof(Identity)); - id->key = k; - keys[i] = NULL; /* transferred */ - id->provider = xstrdup(canonical_provider); - if (*comments[i] != '\0') { - id->comment = comments[i]; - comments[i] = NULL; /* transferred */ - } else { - id->comment = xstrdup(canonical_provider); - } - id->death = death; - id->confirm = confirm; - id->dest_constraints = dest_constraints; - id->ndest_constraints = ndest_constraints; - dest_constraints = NULL; /* transferred */ - ndest_constraints = 0; - TAILQ_INSERT_TAIL(&idtab->idlist, id, next); - idtab->nentries++; + if (comments[i] == NULL || comments[i][0] == '\0') { + free(comments[i]); + comments[i] = xstrdup(canonical_provider); + } + for (j = 0; j < ncerts; j++) { + if (!sshkey_is_cert(certs[j])) + continue; + if (!sshkey_equal_public(keys[i], certs[j])) + continue; + if (pkcs11_make_cert(keys[i], certs[j], &k) != 0) + continue; + add_p11_identity(k, xstrdup(comments[i]), + canonical_provider, death, confirm, + dest_constraints, ndest_constraints); + success = 1; + } + if (!cert_only && lookup_identity(keys[i]) == NULL) { + add_p11_identity(keys[i], comments[i], + canonical_provider, death, confirm, + dest_constraints, ndest_constraints); + keys[i] = NULL; /* transferred */ + comments[i] = NULL; /* transferred */ success = 1; } /* XXX update constraints for existing keys */ @@ -1443,6 +1618,9 @@ send: free(keys); free(comments); free_dest_constraints(dest_constraints, ndest_constraints); + for (j = 0; j < ncerts; j++) + sshkey_free(certs[j]); + free(certs); send_status(e, success); } @@ -1500,6 +1678,7 @@ process_ext_session_bind(SocketEntry *e) u_char fwd = 0; debug2_f("entering"); + e->session_bind_attempted = 1; if ((r = sshkey_froms(e->request, &key)) != 0 || (r = sshbuf_froms(e->request, &sid)) != 0 || (r = sshbuf_froms(e->request, &sig)) != 0 || diff --git a/usr.bin/ssh/ssh-pkcs11-client.c b/usr.bin/ssh/ssh-pkcs11-client.c index d4221644c..af9ad1b88 100644 --- a/usr.bin/ssh/ssh-pkcs11-client.c +++ b/usr.bin/ssh/ssh-pkcs11-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-pkcs11-client.c,v 1.18 2023/07/19 14:03:45 djm Exp $ */ +/* $OpenBSD: ssh-pkcs11-client.c,v 1.19 2023/12/18 14:46:56 djm Exp $ */ /* * Copyright (c) 2010 Markus Friedl. All rights reserved. * Copyright (c) 2014 Pedro Martelletto. All rights reserved. @@ -405,6 +405,60 @@ wrap_key(struct helper *helper, struct sshkey *k) helper->path, helper->nrsa, helper->nec); } +/* + * Make a private PKCS#11-backed certificate by grafting a previously-loaded + * PKCS#11 private key and a public certificate key. + */ +int +pkcs11_make_cert(const struct sshkey *priv, + const struct sshkey *certpub, struct sshkey **certprivp) +{ + struct helper *helper = NULL; + struct sshkey *ret; + int r; + + debug3_f("private key type %s cert type %s", sshkey_type(priv), + sshkey_type(certpub)); + *certprivp = NULL; + if (!sshkey_is_cert(certpub) || sshkey_is_cert(priv) || + !sshkey_equal_public(priv, certpub)) { + error_f("private key %s doesn't match cert %s", + sshkey_type(priv), sshkey_type(certpub)); + return SSH_ERR_INVALID_ARGUMENT; + } + *certprivp = NULL; + if (priv->type == KEY_RSA) { + if ((helper = helper_by_rsa(priv->rsa)) == NULL || + helper->fd == -1) + fatal_f("no helper for PKCS11 RSA key"); + if ((r = sshkey_from_private(priv, &ret)) != 0) + fatal_fr(r, "copy key"); + RSA_set_method(ret->rsa, helper->rsa_meth); + if (helper->nrsa++ >= INT_MAX) + fatal_f("RSA refcount error"); + } else if (priv->type == KEY_ECDSA) { + if ((helper = helper_by_ec(priv->ecdsa)) == NULL || + helper->fd == -1) + fatal_f("no helper for PKCS11 EC key"); + if ((r = sshkey_from_private(priv, &ret)) != 0) + fatal_fr(r, "copy key"); + EC_KEY_set_method(ret->ecdsa, helper->ec_meth); + if (helper->nec++ >= INT_MAX) + fatal_f("EC refcount error"); + } else + fatal_f("unknown key type %s", sshkey_type(priv)); + + ret->flags |= SSHKEY_FLAG_EXT; + if ((r = sshkey_to_certified(ret)) != 0 || + (r = sshkey_cert_copy(certpub, ret)) != 0) + fatal_fr(r, "graft certificate"); + debug3_f("provider %s remaining keys: %zu RSA %zu ECDSA", + helper->path, helper->nrsa, helper->nec); + /* success */ + *certprivp = ret; + return 0; +} + static int pkcs11_start_helper_methods(struct helper *helper) { diff --git a/usr.bin/ssh/ssh-pkcs11.h b/usr.bin/ssh/ssh-pkcs11.h index 81f1d7c5d..526022319 100644 --- a/usr.bin/ssh/ssh-pkcs11.h +++ b/usr.bin/ssh/ssh-pkcs11.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-pkcs11.h,v 1.6 2020/01/25 00:03:36 djm Exp $ */ +/* $OpenBSD: ssh-pkcs11.h,v 1.7 2023/12/18 14:46:56 djm Exp $ */ /* * Copyright (c) 2010 Markus Friedl. All rights reserved. * @@ -35,6 +35,9 @@ struct sshkey * u_int32_t *); #endif +/* Only available in ssh-pkcs11-client.c so far */ +int pkcs11_make_cert(const struct sshkey *, + const struct sshkey *, struct sshkey **); #if !defined(WITH_OPENSSL) && defined(ENABLE_PKCS11) #undef ENABLE_PKCS11 #endif diff --git a/usr.bin/ssh/ssh.c b/usr.bin/ssh/ssh.c index 8b0bd6060..e6b1241ff 100644 --- a/usr.bin/ssh/ssh.c +++ b/usr.bin/ssh/ssh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.598 2023/10/12 02:48:43 djm Exp $ */ +/* $OpenBSD: ssh.c,v 1.599 2023/12/18 14:47:44 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -610,6 +610,41 @@ ssh_conn_info_free(struct ssh_conn_info *cinfo) free(cinfo); } +static int +valid_hostname(const char *s) +{ + size_t i; + + if (*s == '-') + return 0; + for (i = 0; s[i] != 0; i++) { + if (strchr("'`\"$\\;&<>|(){}", s[i]) != NULL || + isspace((u_char)s[i]) || iscntrl((u_char)s[i])) + return 0; + } + return 1; +} + +static int +valid_ruser(const char *s) +{ + size_t i; + + if (*s == '-') + return 0; + for (i = 0; s[i] != 0; i++) { + if (strchr("'`\";&<>|(){}", s[i]) != NULL) + return 0; + /* Disallow '-' after whitespace */ + if (isspace((u_char)s[i]) && s[i + 1] == '-') + return 0; + /* Disallow \ in last position */ + if (s[i] == '\\' && s[i + 1] == '\0') + return 0; + } + return 1; +} + /* * Main program for the ssh client. */ @@ -1092,6 +1127,10 @@ main(int ac, char **av) if (!host) usage(); + if (!valid_hostname(host)) + fatal("hostname contains invalid characters"); + if (options.user != NULL && !valid_ruser(options.user)) + fatal("remote username contains invalid characters"); options.host_arg = xstrdup(host); #ifdef WITH_OPENSSL diff --git a/usr.bin/ssh/sshconnect2.c b/usr.bin/ssh/sshconnect2.c index a7198755f..dead8d5d1 100644 --- a/usr.bin/ssh/sshconnect2.c +++ b/usr.bin/ssh/sshconnect2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect2.c,v 1.369 2023/12/13 03:28:19 djm Exp $ */ +/* $OpenBSD: sshconnect2.c,v 1.371 2023/12/18 14:45:49 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2008 Damien Miller. All rights reserved. @@ -351,7 +351,6 @@ struct cauthmethod { }; static int input_userauth_service_accept(int, u_int32_t, struct ssh *); -static int input_userauth_ext_info(int, u_int32_t, struct ssh *); static int input_userauth_success(int, u_int32_t, struct ssh *); static int input_userauth_failure(int, u_int32_t, struct ssh *); static int input_userauth_banner(int, u_int32_t, struct ssh *); @@ -453,10 +452,8 @@ ssh_userauth2(struct ssh *ssh, const char *local_user, authctxt.mech_tried = 0; #endif authctxt.agent_fd = -1; - pubkey_prepare(ssh, &authctxt); - if (authctxt.method == NULL) { + if (authctxt.method == NULL) fatal_f("internal error: cannot send userauth none request"); - } if ((r = sshpkt_start(ssh, SSH2_MSG_SERVICE_REQUEST)) != 0 || (r = sshpkt_put_cstring(ssh, "ssh-userauth")) != 0 || @@ -465,7 +462,7 @@ ssh_userauth2(struct ssh *ssh, const char *local_user, ssh->authctxt = &authctxt; ssh_dispatch_init(ssh, &input_userauth_error); - ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &input_userauth_ext_info); + ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, kex_input_ext_info); ssh_dispatch_set(ssh, SSH2_MSG_SERVICE_ACCEPT, &input_userauth_service_accept); ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &authctxt.success); /* loop until success */ pubkey_cleanup(ssh); @@ -515,7 +512,9 @@ input_userauth_service_accept(int type, u_int32_t seq, struct ssh *ssh) /* initial userauth request */ userauth_none(ssh); - ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &input_userauth_error); + /* accept EXT_INFO at any time during userauth */ + ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, ssh->kex->ext_info_s ? + &kex_input_ext_info : &input_userauth_error); ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success); ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure); ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner); @@ -524,12 +523,6 @@ input_userauth_service_accept(int type, u_int32_t seq, struct ssh *ssh) return r; } -static int -input_userauth_ext_info(int type, u_int32_t seqnr, struct ssh *ssh) -{ - return kex_input_ext_info(type, seqnr, ssh); -} - void userauth(struct ssh *ssh, char *authlist) { @@ -608,6 +601,7 @@ input_userauth_success(int type, u_int32_t seq, struct ssh *ssh) free(authctxt->methoddata); authctxt->methoddata = NULL; authctxt->success = 1; /* break out */ + ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, dispatch_protocol_error); return 0; } @@ -1677,10 +1671,10 @@ pubkey_prepare(struct ssh *ssh, Authctxt *authctxt) struct identity *id, *id2, *tmp; struct idlist agent, files, *preferred; struct sshkey *key; - int agent_fd = -1, i, r, found; + int disallowed, agent_fd = -1, i, r, found; size_t j; struct ssh_identitylist *idlist; - char *ident; + char *cp, *ident; TAILQ_INIT(&agent); /* keys from the agent */ TAILQ_INIT(&files); /* keys from the config file */ @@ -1798,16 +1792,30 @@ pubkey_prepare(struct ssh *ssh, Authctxt *authctxt) TAILQ_CONCAT(preferred, &files, next); /* finally, filter by PubkeyAcceptedAlgorithms */ TAILQ_FOREACH_SAFE(id, preferred, next, id2) { - if (id->key != NULL && !key_type_allowed_by_config(id->key)) { - debug("Skipping %s key %s - " - "corresponding algo not in PubkeyAcceptedAlgorithms", - sshkey_ssh_name(id->key), id->filename); - TAILQ_REMOVE(preferred, id, next); - sshkey_free(id->key); - free(id->filename); - memset(id, 0, sizeof(*id)); + disallowed = 0; + cp = NULL; + if (id->key == NULL) continue; + if (!key_type_allowed_by_config(id->key)) { + debug("Skipping %s key %s - corresponding algorithm " + "not in PubkeyAcceptedAlgorithms", + sshkey_ssh_name(id->key), id->filename); + disallowed = 1; + } else if (ssh->kex->server_sig_algs != NULL && + (cp = key_sig_algorithm(ssh, id->key)) == NULL) { + debug("Skipping %s key %s - corresponding algorithm " + "not supported by server", + sshkey_ssh_name(id->key), id->filename); + disallowed = 1; } + free(cp); + if (!disallowed) + continue; + /* remove key */ + TAILQ_REMOVE(preferred, id, next); + sshkey_free(id->key); + free(id->filename); + memset(id, 0, sizeof(*id)); } /* List the keys we plan on using */ TAILQ_FOREACH_SAFE(id, preferred, next, id2) { @@ -1853,6 +1861,12 @@ userauth_pubkey(struct ssh *ssh) Identity *id; int sent = 0; char *ident; + static int prepared; + + if (!prepared) { + pubkey_prepare(ssh, authctxt); + prepared = 1; + } while ((id = TAILQ_FIRST(&authctxt->keys))) { if (id->tried++) diff --git a/usr.bin/ssh/sshd.c b/usr.bin/ssh/sshd.c index df701f6bb..04b450efd 100644 --- a/usr.bin/ssh/sshd.c +++ b/usr.bin/ssh/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.600 2023/03/08 04:43:12 guenther Exp $ */ +/* $OpenBSD: sshd.c,v 1.601 2023/12/18 14:45:49 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -2240,7 +2240,9 @@ do_ssh2_kex(struct ssh *ssh) /* start key exchange */ if ((r = kex_setup(ssh, myproposal)) != 0) fatal_r(r, "kex_setup"); + kex_set_server_sig_algs(ssh, options.pubkey_accepted_algos); kex = ssh->kex; + #ifdef WITH_OPENSSL kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_server; kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_server; diff --git a/usr.bin/ssh/version.h b/usr.bin/ssh/version.h index 83ddf1d68..9134b5fd3 100644 --- a/usr.bin/ssh/version.h +++ b/usr.bin/ssh/version.h @@ -1,3 +1,3 @@ -/* $OpenBSD: version.h,v 1.99 2023/10/04 04:04:09 djm Exp $ */ +/* $OpenBSD: version.h,v 1.100 2023/12/18 14:48:44 djm Exp $ */ -#define SSH_VERSION "OpenSSH_9.5" +#define SSH_VERSION "OpenSSH_9.6" diff --git a/usr.sbin/rpki-client/version.h b/usr.sbin/rpki-client/version.h index c0a3187cd..6491446cd 100644 --- a/usr.sbin/rpki-client/version.h +++ b/usr.sbin/rpki-client/version.h @@ -1,3 +1,3 @@ -/* $OpenBSD: version.h,v 1.18 2023/10/02 13:31:32 claudio Exp $ */ +/* $OpenBSD: version.h,v 1.19 2023/12/18 09:51:06 benno Exp $ */ -#define RPKI_VERSION "8.6" +#define RPKI_VERSION "8.7" diff --git a/usr.sbin/snmpd/application_internal.c b/usr.sbin/snmpd/application_internal.c index 11fec82d6..a9c637b23 100644 --- a/usr.sbin/snmpd/application_internal.c +++ b/usr.sbin/snmpd/application_internal.c @@ -1,4 +1,4 @@ -/* $OpenBSD: application_internal.c,v 1.9 2023/12/12 20:15:49 martijn Exp $ */ +/* $OpenBSD: application_internal.c,v 1.10 2023/12/18 09:42:57 martijn Exp $ */ /* * Copyright (c) 2023 Martijn van Duren @@ -130,8 +130,6 @@ appl_internal_init(void) NULL); appl_internal_object(&OID(MIB_snmpInReadOnlys), appl_internal_snmp, NULL); - appl_internal_object(&OID(MIB_snmpInReadOnlys), appl_internal_snmp, - NULL); appl_internal_object(&OID(MIB_snmpInGenErrs), appl_internal_snmp, NULL); appl_internal_object(&OID(MIB_snmpInTotalReqVars), appl_internal_snmp, NULL); @@ -253,6 +251,7 @@ appl_internal_object(struct ber_oid *oid, struct ber_element *(*getnext)(int8_t, struct ber_oid *)) { struct appl_internal_object *obj; + char buf[1024]; if ((obj = calloc(1, sizeof(*obj))) == NULL) fatal(NULL); @@ -261,7 +260,10 @@ appl_internal_object(struct ber_oid *oid, obj->getnext = getnext; obj->stringval = NULL; - RB_INSERT(appl_internal_objects, &appl_internal_objects, obj); + if (RB_INSERT(appl_internal_objects, + &appl_internal_objects, obj) != NULL) + fatalx("%s: %s already registered", __func__, + smi_oid2string(oid, buf, sizeof(buf), 0)); } const char * @@ -351,6 +353,8 @@ appl_internal_get(struct appl_backend *backend, __unused int32_t transactionid, resp[i - 1].av_next = NULL; appl_response(backend, requestid, APPL_ERROR_NOERROR, 0, resp); + + free(resp); return; fail: @@ -434,6 +438,8 @@ appl_internal_getnext(struct appl_backend *backend, resp[i - 1].av_next = NULL; appl_response(backend, requestid, APPL_ERROR_NOERROR, 0, resp); + + free(resp); return; fail: diff --git a/usr.sbin/snmpd/parse.y b/usr.sbin/snmpd/parse.y index 849277557..7d001774e 100644 --- a/usr.sbin/snmpd/parse.y +++ b/usr.sbin/snmpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.83 2023/11/21 08:47:04 martijn Exp $ */ +/* $OpenBSD: parse.y,v 1.85 2023/12/18 16:58:26 martijn Exp $ */ /* * Copyright (c) 2007, 2008, 2012 Reyk Floeter @@ -105,7 +105,7 @@ static uint8_t engineid[SNMPD_MAXENGINEIDLEN]; static int32_t enginepen; static size_t engineidlen; -int host(const char *, const char *, int, +int host(const char *, const char *, int, int, struct sockaddr_storage *, int); int listen_add(struct sockaddr_storage *, int, int); @@ -395,8 +395,8 @@ listen_udptcp : listenproto STRING port listenflags { } for (i = 0; i < addresslen; i++) { - nhosts = host(address[i], port, $1, ss, nitems(ss)); - if (nhosts < 1) { + if ((nhosts = host(address[i], port, AF_UNSPEC, + $1, ss, nitems(ss))) < 1) { yyerror("invalid address: %s", $2); free($2); free($3); @@ -1021,7 +1021,8 @@ hostdef : STRING hostoid hostauth srcaddr { YYERROR; } - if (host($1, SNMPTRAP_PORT, SOCK_DGRAM, &ss, 1) <= 0) { + if (host($1, SNMPTRAP_PORT, AF_UNSPEC, SOCK_DGRAM, + &ss, 1) <= 0) { yyerror("invalid host: %s", $1); free($1); free($2); @@ -1033,8 +1034,10 @@ hostdef : STRING hostoid hostauth srcaddr { free($1); memcpy(&(tr->ta_ss), &ss, sizeof(ss)); if ($4 != NULL) { - if (host($1, "0", SOCK_DGRAM, &ss, 1) <= 0) { - yyerror("invalid host: %s", $1); + if (host($4, "0", ss.ss_family, SOCK_DGRAM, + &ss, 1) <= 0) { + yyerror("invalid source-address: %s", + $4); free($2); free($3.data); free($4); @@ -1702,11 +1705,12 @@ parse_config(const char *filename, u_int flags) /* Setup default listen addresses */ if (TAILQ_EMPTY(&conf->sc_addresses)) { - if (host("0.0.0.0", SNMP_PORT, SOCK_DGRAM, &ss, 1) != 1) + if (host("0.0.0.0", SNMP_PORT, AF_INET, SOCK_DGRAM, + &ss, 1) != 1) fatal("Unexpected resolving of 0.0.0.0"); if (listen_add(&ss, SOCK_DGRAM, 0) == -1) fatal("calloc"); - if (host("::", SNMP_PORT, SOCK_DGRAM, &ss, 1) != 1) + if (host("::", SNMP_PORT, AF_INET6, SOCK_DGRAM, &ss, 1) != 1) fatal("Unexpected resolving of ::"); if (listen_add(&ss, SOCK_DGRAM, 0) == -1) fatal("calloc"); @@ -1843,14 +1847,14 @@ symget(const char *nam) } int -host(const char *s, const char *port, int type, struct sockaddr_storage *ss, - int max) +host(const char *s, const char *port, int family, int type, + struct sockaddr_storage *ss, int max) { struct addrinfo hints, *res0, *res; int error, i; bzero(&hints, sizeof(hints)); - hints.ai_family = PF_UNSPEC; + hints.ai_family = family; hints.ai_socktype = type; /* * Without AI_NUMERICHOST getaddrinfo might not resolve ip addresses