sync with OpenBSD -current

This commit is contained in:
purplerain 2023-12-18 23:55:01 +00:00
parent da785accdf
commit 659ea2942e
Signed by: purplerain
GPG key ID: F42C07F07E2E35B7
39 changed files with 1318 additions and 384 deletions

View file

@ -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 /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
* project. * project.
*/ */
@ -90,21 +90,21 @@ struct CMAC_CTX_st {
* and R64 = (1 << 64) | 0x1b for the only supported block sizes 128 and 64. * and R64 = (1 << 64) | 0x1b for the only supported block sizes 128 and 64.
*/ */
static void 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; unsigned char mask, Rb;
int i; int i;
/* Choose Rb according to the block size in bytes. */ /* 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. */ /* 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); kn[i] = (l[i] << 1) | (l[i + 1] >> 7);
/* Only xor with Rb if the MSB is one. */ /* Only xor with Rb if the MSB is one. */
mask = 0 - (l[0] >> 7); 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 * CMAC_CTX *
@ -154,17 +154,17 @@ LCRYPTO_ALIAS(CMAC_CTX_free);
int int
CMAC_CTX_copy(CMAC_CTX *out, const CMAC_CTX *in) CMAC_CTX_copy(CMAC_CTX *out, const CMAC_CTX *in)
{ {
int bl; int block_size;
if (in->nlast_block == -1) if (in->nlast_block == -1)
return 0; return 0;
if (!EVP_CIPHER_CTX_copy(&out->cctx, &in->cctx)) if (!EVP_CIPHER_CTX_copy(&out->cctx, &in->cctx))
return 0; return 0;
bl = EVP_CIPHER_CTX_block_size(&in->cctx); block_size = EVP_CIPHER_CTX_block_size(&in->cctx);
memcpy(out->k1, in->k1, bl); memcpy(out->k1, in->k1, block_size);
memcpy(out->k2, in->k2, bl); memcpy(out->k2, in->k2, block_size);
memcpy(out->tbl, in->tbl, bl); memcpy(out->tbl, in->tbl, block_size);
memcpy(out->last_block, in->last_block, bl); memcpy(out->last_block, in->last_block, block_size);
out->nlast_block = in->nlast_block; out->nlast_block = in->nlast_block;
return 1; return 1;
} }
@ -175,7 +175,7 @@ CMAC_Init(CMAC_CTX *ctx, const void *key, size_t keylen,
const EVP_CIPHER *cipher, ENGINE *impl) const EVP_CIPHER *cipher, ENGINE *impl)
{ {
static unsigned char zero_iv[EVP_MAX_BLOCK_LENGTH]; static unsigned char zero_iv[EVP_MAX_BLOCK_LENGTH];
int bl; int block_size;
/* All zeros means restart */ /* All zeros means restart */
if (key == NULL && cipher == NULL && keylen == 0) { if (key == NULL && cipher == NULL && keylen == 0) {
@ -208,8 +208,8 @@ CMAC_Init(CMAC_CTX *ctx, const void *key, size_t keylen,
return 0; return 0;
/* make_kn() only supports block sizes of 8 and 16 bytes. */ /* make_kn() only supports block sizes of 8 and 16 bytes. */
bl = EVP_CIPHER_CTX_block_size(&ctx->cctx); block_size = EVP_CIPHER_CTX_block_size(&ctx->cctx);
if (bl != 8 && bl != 16) if (block_size != 8 && block_size != 16)
return 0; return 0;
/* /*
@ -220,13 +220,13 @@ CMAC_Init(CMAC_CTX *ctx, const void *key, size_t keylen,
return 0; return 0;
if (!EVP_EncryptInit_ex(&ctx->cctx, NULL, NULL, key, zero_iv)) if (!EVP_EncryptInit_ex(&ctx->cctx, NULL, NULL, key, zero_iv))
return 0; 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; return 0;
/* Section 6.1, step 2: compute k1 from intermediate secret. */ /* 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. */ /* 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. */ /* Destroy intermediate secret and reset last block count. */
explicit_bzero(ctx->tbl, sizeof(ctx->tbl)); explicit_bzero(ctx->tbl, sizeof(ctx->tbl));
@ -245,18 +245,18 @@ int
CMAC_Update(CMAC_CTX *ctx, const void *in, size_t dlen) CMAC_Update(CMAC_CTX *ctx, const void *in, size_t dlen)
{ {
const unsigned char *data = in; const unsigned char *data = in;
size_t bl; size_t block_size;
if (ctx->nlast_block == -1) if (ctx->nlast_block == -1)
return 0; return 0;
if (dlen == 0) if (dlen == 0)
return 1; 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 */ /* Copy into partial block if we need to */
if (ctx->nlast_block > 0) { if (ctx->nlast_block > 0) {
size_t nleft; size_t nleft;
nleft = bl - ctx->nlast_block; nleft = block_size - ctx->nlast_block;
if (dlen < nleft) if (dlen < nleft)
nleft = dlen; nleft = dlen;
memcpy(ctx->last_block + ctx->nlast_block, data, nleft); 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; return 1;
data += nleft; data += nleft;
/* Else not final block so encrypt it */ /* 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; return 0;
} }
/* Encrypt all but one of the complete blocks left */ /* Encrypt all but one of the complete blocks left */
while (dlen > bl) { while (dlen > block_size) {
if (!EVP_Cipher(&ctx->cctx, ctx->tbl, data, bl)) if (!EVP_Cipher(&ctx->cctx, ctx->tbl, data, block_size))
return 0; return 0;
dlen -= bl; dlen -= block_size;
data += bl; data += block_size;
} }
/* Copy any data left to last block buffer */ /* Copy any data left to last block buffer */
memcpy(ctx->last_block, data, dlen); memcpy(ctx->last_block, data, dlen);
@ -287,28 +288,28 @@ LCRYPTO_ALIAS(CMAC_Update);
int int
CMAC_Final(CMAC_CTX *ctx, unsigned char *out, size_t *poutlen) 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) if (ctx->nlast_block == -1)
return 0; return 0;
bl = EVP_CIPHER_CTX_block_size(&ctx->cctx); block_size = EVP_CIPHER_CTX_block_size(&ctx->cctx);
*poutlen = (size_t)bl; *poutlen = (size_t)block_size;
if (!out) if (!out)
return 1; return 1;
lb = ctx->nlast_block; lb = ctx->nlast_block;
/* Is last block complete? */ /* Is last block complete? */
if (lb == bl) { if (lb == block_size) {
for (i = 0; i < bl; i++) for (i = 0; i < block_size; i++)
out[i] = ctx->last_block[i] ^ ctx->k1[i]; out[i] = ctx->last_block[i] ^ ctx->k1[i];
} else { } else {
ctx->last_block[lb] = 0x80; ctx->last_block[lb] = 0x80;
if (bl - lb > 1) if (block_size - lb > 1)
memset(ctx->last_block + lb + 1, 0, bl - lb - 1); memset(ctx->last_block + lb + 1, 0, block_size - lb - 1);
for (i = 0; i < bl; i++) for (i = 0; i < block_size; i++)
out[i] = ctx->last_block[i] ^ ctx->k2[i]; out[i] = ctx->last_block[i] ^ ctx->k2[i];
} }
if (!EVP_Cipher(&ctx->cctx, out, out, bl)) { if (!EVP_Cipher(&ctx->cctx, out, out, block_size)) {
explicit_bzero(out, bl); explicit_bzero(out, block_size);
return 0; return 0;
} }
return 1; return 1;

View file

@ -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 /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
* project 1999. * project 1999.
*/ */
@ -269,43 +269,35 @@ int
EVP_PBE_CipherInit(ASN1_OBJECT *pbe_obj, const char *pass, int passlen, EVP_PBE_CipherInit(ASN1_OBJECT *pbe_obj, const char *pass, int passlen,
ASN1_TYPE *param, EVP_CIPHER_CTX *ctx, int en_de) ASN1_TYPE *param, EVP_CIPHER_CTX *ctx, int en_de)
{ {
const EVP_CIPHER *cipher; const EVP_CIPHER *cipher = NULL;
const EVP_MD *md; const EVP_MD *md = NULL;
int cipher_nid, md_nid; int pbe_nid, cipher_nid, md_nid;
EVP_PBE_KEYGEN *keygen; EVP_PBE_KEYGEN *keygen;
if (!EVP_PBE_find(EVP_PBE_TYPE_OUTER, OBJ_obj2nid(pbe_obj), if ((pbe_nid = OBJ_obj2nid(pbe_obj)) == NID_undef) {
&cipher_nid, &md_nid, &keygen)) {
char obj_tmp[80];
EVPerror(EVP_R_UNKNOWN_PBE_ALGORITHM); EVPerror(EVP_R_UNKNOWN_PBE_ALGORITHM);
if (!pbe_obj) return 0;
strlcpy(obj_tmp, "NULL", sizeof obj_tmp); }
else if (!EVP_PBE_find(EVP_PBE_TYPE_OUTER, pbe_nid, &cipher_nid, &md_nid,
i2t_ASN1_OBJECT(obj_tmp, sizeof obj_tmp, pbe_obj); &keygen)) {
ERR_asprintf_error_data("TYPE=%s", obj_tmp); EVPerror(EVP_R_UNKNOWN_PBE_ALGORITHM);
ERR_asprintf_error_data("NID=%d", pbe_nid);
return 0; return 0;
} }
if (!pass) if (pass == NULL)
passlen = 0; passlen = 0;
else if (passlen == -1) if (passlen == -1)
passlen = strlen(pass); passlen = strlen(pass);
if (cipher_nid == -1) if (cipher_nid != -1) {
cipher = NULL; if ((cipher = EVP_get_cipherbynid(cipher_nid)) == NULL) {
else {
cipher = EVP_get_cipherbynid(cipher_nid);
if (!cipher) {
EVPerror(EVP_R_UNKNOWN_CIPHER); EVPerror(EVP_R_UNKNOWN_CIPHER);
return 0; return 0;
} }
} }
if (md_nid != -1) {
if (md_nid == -1) if ((md = EVP_get_digestbynid(md_nid)) == NULL) {
md = NULL;
else {
md = EVP_get_digestbynid(md_nid);
if (!md) {
EVPerror(EVP_R_UNKNOWN_DIGEST); EVPerror(EVP_R_UNKNOWN_DIGEST);
return 0; return 0;
} }
@ -315,6 +307,7 @@ EVP_PBE_CipherInit(ASN1_OBJECT *pbe_obj, const char *pass, int passlen,
EVPerror(EVP_R_KEYGEN_FAILURE); EVPerror(EVP_R_KEYGEN_FAILURE);
return 0; return 0;
} }
return 1; return 1;
} }

View file

@ -9,25 +9,7 @@
#include "fido.h" #include "fido.h"
#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x3050200fL #if OPENSSL_VERSION_NUMBER >= 0x30000000
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
static EVP_MD * static EVP_MD *
rs1_get_EVP_MD(void) rs1_get_EVP_MD(void)
{ {
@ -43,20 +25,15 @@ rs1_free_EVP_MD(EVP_MD *md)
static EVP_MD * static EVP_MD *
rs1_get_EVP_MD(void) rs1_get_EVP_MD(void)
{ {
const EVP_MD *md; return ((EVP_MD *)EVP_sha1());
if ((md = EVP_sha1()) == NULL)
return (NULL);
return (EVP_MD_meth_dup(md));
} }
static void static void
rs1_free_EVP_MD(EVP_MD *md) rs1_free_EVP_MD(EVP_MD *md)
{ {
EVP_MD_meth_free(md); (void)md;
} }
#endif /* LIBRESSL_VERSION_NUMBER */ #endif /* OPENSSL_VERSION_NUMBER */
int int
rs1_verify_sig(const fido_blob_t *dgst, EVP_PKEY *pkey, rs1_verify_sig(const fido_blob_t *dgst, EVP_PKEY *pkey,

View file

@ -17,25 +17,7 @@
#define get0_RSA(x) EVP_PKEY_get0((x)) #define get0_RSA(x) EVP_PKEY_get0((x))
#endif #endif
#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x3050200fL #if OPENSSL_VERSION_NUMBER >= 0x30000000
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
static EVP_MD * static EVP_MD *
rs256_get_EVP_MD(void) rs256_get_EVP_MD(void)
{ {
@ -51,20 +33,15 @@ rs256_free_EVP_MD(EVP_MD *md)
static EVP_MD * static EVP_MD *
rs256_get_EVP_MD(void) rs256_get_EVP_MD(void)
{ {
const EVP_MD *md; return ((EVP_MD *)EVP_sha256());
if ((md = EVP_sha256()) == NULL)
return (NULL);
return (EVP_MD_meth_dup(md));
} }
static void static void
rs256_free_EVP_MD(EVP_MD *md) rs256_free_EVP_MD(EVP_MD *md)
{ {
EVP_MD_meth_free(md); (void)md;
} }
#endif /* LIBRESSL_VERSION_NUMBER */ #endif /* OPENSSL_VERSION_NUMBER */
static int static int
decode_bignum(const cbor_item_t *item, void *ptr, size_t len) decode_bignum(const cbor_item_t *item, void *ptr, size_t len)

View file

@ -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 * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
@ -476,13 +476,13 @@ _dl_self_relro(long loff)
#define __STRINGIFY(x) #x #define __STRINGIFY(x) #x
#define STRINGIFY(x) __STRINGIFY(x) #define STRINGIFY(x) __STRINGIFY(x)
#ifdef __arm__ #ifdef __arm__
__asm__(".pushsection openbsd.syscalls,\"\",%progbits;" __asm__(".pushsection .openbsd.syscalls,\"\",%progbits;"
".p2align 2;" ".p2align 2;"
".long 0;" ".long 0;"
".long " STRINGIFY(SYS_kbind) ";" ".long " STRINGIFY(SYS_kbind) ";"
".popsection"); ".popsection");
#else #else
__asm__(".pushsection openbsd.syscalls,\"\",@progbits;" __asm__(".pushsection .openbsd.syscalls,\"\",@progbits;"
".long 0;" ".long 0;"
".p2align 2;" ".p2align 2;"
".long " STRINGIFY(SYS_kbind) ";" ".long " STRINGIFY(SYS_kbind) ";"

View file

@ -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 OPENSSL?= yes
@ -100,7 +100,9 @@ LTESTS= connect \
hostbased \ hostbased \
channel-timeout \ channel-timeout \
connection-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= putty-transfer putty-ciphers putty-kex conch-ciphers
INTEROP_TESTS+= dropbear-ciphers dropbear-kex INTEROP_TESTS+= dropbear-ciphers dropbear-kex

View file

@ -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

View file

@ -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"

View file

@ -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 $ */ /* $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)); usedinomap = calloc((unsigned) mapsize, sizeof(char));
dumpdirmap = calloc((unsigned) mapsize, sizeof(char)); dumpdirmap = calloc((unsigned) mapsize, sizeof(char));
dumpinomap = 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); tapesize = 3 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1);
nonodump = spcl.c_level < honorlevel; nonodump = spcl.c_level < honorlevel;

View file

@ -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 $ */ /* $NetBSD: uipc_socket.c,v 1.21 1996/02/04 02:17:52 christos Exp $ */
/* /*
@ -832,8 +832,10 @@ bad:
*mp = NULL; *mp = NULL;
solock_shared(so); solock_shared(so);
pru_lock(so);
restart: restart:
if ((error = sblock(so, &so->so_rcv, SBLOCKWAIT(flags))) != 0) { if ((error = sblock(so, &so->so_rcv, SBLOCKWAIT(flags))) != 0) {
pru_unlock(so);
sounlock_shared(so); sounlock_shared(so);
return (error); return (error);
} }
@ -900,11 +902,13 @@ restart:
SBLASTRECORDCHK(&so->so_rcv, "soreceive sbwait 1"); SBLASTRECORDCHK(&so->so_rcv, "soreceive sbwait 1");
SBLASTMBUFCHK(&so->so_rcv, "soreceive sbwait 1"); SBLASTMBUFCHK(&so->so_rcv, "soreceive sbwait 1");
sbunlock(so, &so->so_rcv); sbunlock(so, &so->so_rcv);
pru_unlock(so);
error = sbwait(so, &so->so_rcv); error = sbwait(so, &so->so_rcv);
if (error) { if (error) {
sounlock_shared(so); sounlock_shared(so);
return (error); return (error);
} }
pru_lock(so);
goto restart; goto restart;
} }
dontblock: dontblock:
@ -971,11 +975,13 @@ dontblock:
sbsync(&so->so_rcv, nextrecord); sbsync(&so->so_rcv, nextrecord);
if (controlp) { if (controlp) {
if (pr->pr_domain->dom_externalize) { if (pr->pr_domain->dom_externalize) {
pru_unlock(so);
sounlock_shared(so); sounlock_shared(so);
error = error =
(*pr->pr_domain->dom_externalize) (*pr->pr_domain->dom_externalize)
(cm, controllen, flags); (cm, controllen, flags);
solock_shared(so); solock_shared(so);
pru_lock(so);
} }
*controlp = cm; *controlp = cm;
} else { } else {
@ -1049,9 +1055,11 @@ dontblock:
SBLASTRECORDCHK(&so->so_rcv, "soreceive uiomove"); SBLASTRECORDCHK(&so->so_rcv, "soreceive uiomove");
SBLASTMBUFCHK(&so->so_rcv, "soreceive uiomove"); SBLASTMBUFCHK(&so->so_rcv, "soreceive uiomove");
resid = uio->uio_resid; resid = uio->uio_resid;
pru_unlock(so);
sounlock_shared(so); sounlock_shared(so);
uio_error = uiomove(mtod(m, caddr_t) + moff, len, uio); uio_error = uiomove(mtod(m, caddr_t) + moff, len, uio);
solock_shared(so); solock_shared(so);
pru_lock(so);
if (uio_error) if (uio_error)
uio->uio_resid = resid - len; uio->uio_resid = resid - len;
} else } else
@ -1136,6 +1144,7 @@ dontblock:
error = sbwait(so, &so->so_rcv); error = sbwait(so, &so->so_rcv);
if (error) { if (error) {
sbunlock(so, &so->so_rcv); sbunlock(so, &so->so_rcv);
pru_unlock(so);
sounlock_shared(so); sounlock_shared(so);
return (0); return (0);
} }
@ -1182,6 +1191,7 @@ dontblock:
*flagsp |= flags; *flagsp |= flags;
release: release:
sbunlock(so, &so->so_rcv); sbunlock(so, &so->so_rcv);
pru_unlock(so);
sounlock_shared(so); sounlock_shared(so);
return (error); return (error);
} }

View file

@ -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 $ */ /* $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: case PF_INET6:
if (so->so_proto->pr_usrreqs->pru_lock != NULL) { if (so->so_proto->pr_usrreqs->pru_lock != NULL) {
NET_LOCK_SHARED(); NET_LOCK_SHARED();
pru_lock(so); rw_enter_write(&so->so_lock);
} else } else
NET_LOCK(); NET_LOCK();
break; break;
@ -427,7 +427,7 @@ sounlock_shared(struct socket *so)
case PF_INET: case PF_INET:
case PF_INET6: case PF_INET6:
if (so->so_proto->pr_usrreqs->pru_unlock != NULL) { if (so->so_proto->pr_usrreqs->pru_unlock != NULL) {
pru_unlock(so); rw_exit_write(&so->so_lock);
NET_UNLOCK_SHARED(); NET_UNLOCK_SHARED();
} else } else
NET_UNLOCK(); NET_UNLOCK();
@ -463,12 +463,12 @@ sosleep_nsec(struct socket *so, void *ident, int prio, const char *wmesg,
case PF_INET6: case PF_INET6:
if (so->so_proto->pr_usrreqs->pru_unlock != NULL && if (so->so_proto->pr_usrreqs->pru_unlock != NULL &&
rw_status(&netlock) == RW_READ) { rw_status(&netlock) == RW_READ) {
pru_unlock(so); rw_exit_write(&so->so_lock);
} }
ret = rwsleep_nsec(ident, &netlock, prio, wmesg, nsecs); ret = rwsleep_nsec(ident, &netlock, prio, wmesg, nsecs);
if (so->so_proto->pr_usrreqs->pru_lock != NULL && if (so->so_proto->pr_usrreqs->pru_lock != NULL &&
rw_status(&netlock) == RW_READ) { rw_status(&netlock) == RW_READ) {
pru_lock(so); rw_enter_write(&so->so_lock);
} }
break; break;
default: default:

View file

@ -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 $ */ /* $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)) if (KTRPOINT(p, KTR_STRUCT))
ktrsockaddr(p, mtod(nam, caddr_t), SCARG(uap, namelen)); ktrsockaddr(p, mtod(nam, caddr_t), SCARG(uap, namelen));
#endif #endif
solock(so); solock_shared(so);
error = sobind(so, nam, p); error = sobind(so, nam, p);
sounlock(so); sounlock_shared(so);
m_freem(nam); m_freem(nam);
out: out:
FRELE(fp, p); FRELE(fp, p);

View file

@ -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 $ */ /* $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); arpinvalidate(rt);
ifp = if_get(rt->rt_ifidx); ifp = if_get(rt->rt_ifidx);
KASSERT(ifp != NULL); if (ifp == NULL)
return;
if (!ISSET(rt->rt_flags, RTF_STATIC|RTF_CACHED)) if (!ISSET(rt->rt_flags, RTF_STATIC|RTF_CACHED))
rtdeletemsg(rt, ifp, ifp->if_rdomain); rtdeletemsg(rt, ifp, ifp->if_rdomain);
if_put(ifp); if_put(ifp);

View file

@ -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 $ */ /* $NetBSD: in_pcb.h,v 1.14 1996/02/13 23:42:00 christos Exp $ */
/* /*
@ -84,6 +84,38 @@
* p inpcb_mtx pcb mutex * 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; struct pf_state_key;
union inpaddru { union inpaddru {

View file

@ -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 $ */ /* $NetBSD: protosw.h,v 1.10 1996/04/09 20:55:32 cgd Exp $ */
/*- /*-
@ -284,12 +284,14 @@ pru_detach(struct socket *so)
static inline void static inline void
pru_lock(struct socket *so) pru_lock(struct socket *so)
{ {
if (so->so_proto->pr_usrreqs->pru_lock)
(*so->so_proto->pr_usrreqs->pru_lock)(so); (*so->so_proto->pr_usrreqs->pru_lock)(so);
} }
static inline void static inline void
pru_unlock(struct socket *so) pru_unlock(struct socket *so)
{ {
if (so->so_proto->pr_usrreqs->pru_unlock)
(*so->so_proto->pr_usrreqs->pru_unlock)(so); (*so->so_proto->pr_usrreqs->pru_unlock)(so);
} }

View file

@ -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 short packet lengths, which would not be possible with other
approaches. 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. Connection protocol changes
2.1. connection: Channel write close extension "eow@openssh.com" 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 OpenSSH extends the usual agent protocol. These changes are documented
in the PROTOCOL.agent file. 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 $

View file

@ -81,4 +81,35 @@ the constraint is:
This option is only valid for XMSS keys. 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 $

View file

@ -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. * Copyright (c) 2000 Markus Friedl. All rights reserved.
* *
@ -55,6 +55,7 @@
#include "monitor_wrap.h" #include "monitor_wrap.h"
#include "ssherr.h" #include "ssherr.h"
#include "digest.h" #include "digest.h"
#include "kex.h"
/* import */ /* import */
extern ServerOptions options; extern ServerOptions options;
@ -162,6 +163,8 @@ do_authentication2(struct ssh *ssh)
Authctxt *authctxt = ssh->authctxt; Authctxt *authctxt = ssh->authctxt;
ssh_dispatch_init(ssh, &dispatch_protocol_error); 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_set(ssh, SSH2_MSG_SERVICE_REQUEST, &input_service_request);
ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &authctxt->success); ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &authctxt->success);
ssh->authctxt = NULL; 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); debug("bad service request %s", service);
ssh_packet_disconnect(ssh, "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; r = 0;
out: out:
free(service); free(service);
@ -296,6 +300,8 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
if (use_privsep) if (use_privsep)
mm_inform_authserv(service, style); mm_inform_authserv(service, style);
userauth_banner(ssh); 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) if (auth2_setup_methods_lists(authctxt) != 0)
ssh_packet_disconnect(ssh, ssh_packet_disconnect(ssh,
"no authentication methods enabled"); "no authentication methods enabled");

View file

@ -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 <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -503,9 +503,10 @@ encode_dest_constraint(struct sshbuf *m, const struct dest_constraint *dc)
} }
static int static int
encode_constraints(struct sshbuf *m, u_int life, u_int confirm, u_int maxsign, encode_constraints(struct sshbuf *m, u_int life, u_int confirm,
const char *provider, struct dest_constraint **dest_constraints, u_int maxsign, const char *provider,
size_t ndest_constraints) struct dest_constraint **dest_constraints, size_t ndest_constraints,
int cert_only, struct sshkey **certs, size_t ncerts)
{ {
int r; int r;
struct sshbuf *b = NULL; 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 || "restrict-destination-v00@openssh.com")) != 0 ||
(r = sshbuf_put_stringb(m, b)) != 0) (r = sshbuf_put_stringb(m, b)) != 0)
goto out; 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; r = 0;
out: out:
@ -606,7 +628,7 @@ ssh_add_identity_constrained(int sock, struct sshkey *key,
} }
if (constrained && if (constrained &&
(r = encode_constraints(msg, life, confirm, maxsign, (r = encode_constraints(msg, life, confirm, maxsign,
provider, dest_constraints, ndest_constraints)) != 0) provider, dest_constraints, ndest_constraints, 0, NULL, 0)) != 0)
goto out; goto out;
if ((r = ssh_request_reply_decode(sock, msg)) != 0) if ((r = ssh_request_reply_decode(sock, msg)) != 0)
goto out; goto out;
@ -661,10 +683,11 @@ ssh_remove_identity(int sock, const struct sshkey *key)
int int
ssh_update_card(int sock, int add, const char *reader_id, const char *pin, ssh_update_card(int sock, int add, const char *reader_id, const char *pin,
u_int life, u_int confirm, 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; struct sshbuf *msg;
int r, constrained = (life || confirm || dest_constraints); int r, constrained = (life || confirm || dest_constraints || certs);
u_char type; u_char type;
if (add) { if (add) {
@ -682,7 +705,8 @@ ssh_update_card(int sock, int add, const char *reader_id, const char *pin,
goto out; goto out;
if (constrained && if (constrained &&
(r = encode_constraints(msg, life, confirm, 0, NULL, (r = encode_constraints(msg, life, confirm, 0, NULL,
dest_constraints, ndest_constraints)) != 0) dest_constraints, ndest_constraints,
cert_only, certs, ncerts)) != 0)
goto out; goto out;
if ((r = ssh_request_reply_decode(sock, msg)) != 0) if ((r = ssh_request_reply_decode(sock, msg)) != 0)
goto out; goto out;

View file

@ -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 <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -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, int ssh_update_card(int sock, int add, const char *reader_id,
const char *pin, u_int life, u_int confirm, const char *pin, u_int life, u_int confirm,
struct dest_constraint **dest_constraints, 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_remove_all_identities(int sock, int version);
int ssh_agent_sign(int sock, const struct sshkey *key, int ssh_agent_sign(int sock, const struct sshkey *key,

View file

@ -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 <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -3365,11 +3365,20 @@ channel_input_data(int type, u_int32_t seq, struct ssh *ssh)
return 0; return 0;
} }
if (win_len > c->local_window) { if (win_len > c->local_window) {
logit("channel %d: rcvd too much data %zu, win %u", c->local_window_exceeded += win_len - c->local_window;
c->self, win_len, c->local_window); logit("channel %d: rcvd too much data %zu, win %u/%u "
return 0; "(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 -= win_len;
c->local_window_exceeded = 0;
}
if (c->datagram) { if (c->datagram) {
if ((r = sshbuf_put_string(c->output, data, data_len)) != 0) if ((r = sshbuf_put_string(c->output, data, data_len)) != 0)

View file

@ -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 <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -167,6 +167,7 @@ struct Channel {
u_int remote_window; u_int remote_window;
u_int remote_maxpacket; u_int remote_maxpacket;
u_int local_window; u_int local_window;
u_int local_window_exceeded;
u_int local_window_max; u_int local_window_max;
u_int local_consumed; u_int local_consumed;
u_int local_maxpacket; u_int local_maxpacket;

View file

@ -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. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
* *
@ -60,7 +60,7 @@
#include "xmalloc.h" #include "xmalloc.h"
/* prototype */ /* 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 int kex_input_newkeys(int, u_int32_t, struct ssh *);
static const char * const proposal_names[PROPOSAL_MAX] = { static const char * const proposal_names[PROPOSAL_MAX] = {
@ -162,6 +162,18 @@ kex_names_valid(const char *names)
return 1; 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. * Concatenate algorithm names, avoiding duplicates in the process.
* Caller must free returned string. * Caller must free returned string.
@ -169,7 +181,7 @@ kex_names_valid(const char *names)
char * char *
kex_names_cat(const char *a, const char *b) 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; size_t len;
if (a == NULL || *a == '\0') if (a == NULL || *a == '\0')
@ -186,10 +198,8 @@ kex_names_cat(const char *a, const char *b)
} }
strlcpy(ret, a, len); strlcpy(ret, a, len);
for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) { for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) {
if ((m = match_list(ret, p, NULL)) != NULL) { if (has_any_alg(ret, p))
free(m);
continue; /* Algorithm already present */ continue; /* Algorithm already present */
}
if (strlcat(ret, ",", len) >= len || if (strlcat(ret, ",", len) >= len ||
strlcat(ret, p, len) >= len) { strlcat(ret, p, len) >= len) {
free(tmp); 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 *defpropclient[PROPOSAL_MAX] = { KEX_CLIENT };
const char **defprop = ssh->kex->server ? defpropserver : defpropclient; const char **defprop = ssh->kex->server ? defpropserver : defpropclient;
u_int i; u_int i;
char *cp;
if (prop == NULL) if (prop == NULL)
fatal_f("proposal missing"); 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++) { for (i = 0; i < PROPOSAL_MAX; i++) {
switch(i) { switch(i) {
case PROPOSAL_KEX_ALGS: case PROPOSAL_KEX_ALGS:
prop[i] = compat_kex_proposal(ssh, prop[i] = compat_kex_proposal(ssh, cp);
kexalgos ? kexalgos : defprop[i]);
break; break;
case PROPOSAL_ENC_ALGS_CTOS: case PROPOSAL_ENC_ALGS_CTOS:
case PROPOSAL_ENC_ALGS_STOC: 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]); prop[i] = xstrdup(defprop[i]);
} }
} }
free(cp);
} }
void void
@ -451,7 +470,12 @@ kex_protocol_error(int type, u_int32_t seq, struct ssh *ssh)
{ {
int r; 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 || if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 ||
(r = sshpkt_put_u32(ssh, seq)) != 0 || (r = sshpkt_put_u32(ssh, seq)) != 0 ||
(r = sshpkt_send(ssh)) != 0) (r = sshpkt_send(ssh)) != 0)
@ -466,36 +490,138 @@ kex_reset_dispatch(struct ssh *ssh)
SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error); 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 static int
kex_send_ext_info(struct ssh *ssh) kex_compose_ext_info_server(struct ssh *ssh, struct sshbuf *m)
{ {
int r; int r;
char *algs;
debug("Sending SSH2_MSG_EXT_INFO"); if (ssh->kex->server_sig_algs == NULL &&
if ((algs = sshkey_alg_list(0, 1, 1, ',')) == NULL) (ssh->kex->server_sig_algs = sshkey_alg_list(0, 1, 1, ',')) == NULL)
return SSH_ERR_ALLOC_FAIL; return SSH_ERR_ALLOC_FAIL;
/* XXX filter algs list by allowed pubkey/hostbased types */ if ((r = sshbuf_put_u32(m, 3)) != 0 ||
if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 || (r = sshbuf_put_cstring(m, "server-sig-algs")) != 0 ||
(r = sshpkt_put_u32(ssh, 3)) != 0 || (r = sshbuf_put_cstring(m, ssh->kex->server_sig_algs)) != 0 ||
(r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 || (r = sshbuf_put_cstring(m,
(r = sshpkt_put_cstring(ssh, algs)) != 0 ||
(r = sshpkt_put_cstring(ssh,
"publickey-hostbound@openssh.com")) != 0 || "publickey-hostbound@openssh.com")) != 0 ||
(r = sshpkt_put_cstring(ssh, "0")) != 0 || (r = sshbuf_put_cstring(m, "0")) != 0 ||
(r = sshpkt_put_cstring(ssh, "ping@openssh.com")) != 0 || (r = sshbuf_put_cstring(m, "ping@openssh.com")) != 0 ||
(r = sshpkt_put_cstring(ssh, "0")) != 0 || (r = sshbuf_put_cstring(m, "0")) != 0) {
(r = sshpkt_send(ssh)) != 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"); error_fr(r, "compose");
goto out; goto out;
} }
/* success */ /* success */
r = 0; r = 0;
out: out:
free(algs);
return r; 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 int
kex_send_newkeys(struct ssh *ssh) kex_send_newkeys(struct ssh *ssh)
{ {
@ -507,8 +633,7 @@ kex_send_newkeys(struct ssh *ssh)
return r; return r;
debug("SSH2_MSG_NEWKEYS sent"); debug("SSH2_MSG_NEWKEYS sent");
ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys); 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_maybe_send_ext_info(ssh)) != 0)
if ((r = kex_send_ext_info(ssh)) != 0)
return r; return r;
debug("expecting SSH2_MSG_NEWKEYS"); debug("expecting SSH2_MSG_NEWKEYS");
return 0; return 0;
@ -531,10 +656,61 @@ kex_ext_info_check_ver(struct kex *kex, const char *name,
return 0; 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 int
kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh) kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
{ {
struct kex *kex = ssh->kex; struct kex *kex = ssh->kex;
const int max_ext_info = kex->server ? 1 : 2;
u_int32_t i, ninfo; u_int32_t i, ninfo;
char *name; char *name;
u_char *val; u_char *val;
@ -542,13 +718,17 @@ kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)
int r; int r;
debug("SSH2_MSG_EXT_INFO received"); 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); ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error);
if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0) if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0)
return r; return r;
if (ninfo >= 1024) { if (ninfo >= 1024) {
error("SSH2_MSG_EXT_INFO with too many entries, expected " error("SSH2_MSG_EXT_INFO with too many entries, expected "
"<=1024, received %u", ninfo); "<=1024, received %u", ninfo);
return SSH_ERR_INVALID_FORMAT; return dispatch_protocol_error(type, seq, ssh);
} }
for (i = 0; i < ninfo; i++) { for (i = 0; i < ninfo; i++) {
if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0) 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); free(name);
return r; return r;
} }
if (strcmp(name, "server-sig-algs") == 0) { debug3_f("extension %s", name);
/* Ensure no \0 lurking in value */ if (kex->server) {
if (memchr(val, '\0', vlen) != NULL) { if ((r = kex_ext_info_server_parse(ssh, name,
error_f("nul byte in %s", name); val, vlen)) != 0)
free(name); return r;
free(val); } else {
return SSH_ERR_INVALID_FORMAT; if ((r = kex_ext_info_client_parse(ssh, name,
} val, vlen)) != 0)
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);
return r; 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);
return r;
}
} else
debug_f("%s (unrecognised)", name);
free(name); free(name);
free(val); free(val);
} }
@ -598,6 +760,8 @@ kex_input_newkeys(int type, u_int32_t seq, struct ssh *ssh)
int r; int r;
debug("SSH2_MSG_NEWKEYS received"); 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_NEWKEYS, &kex_protocol_error);
ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit);
if ((r = sshpkt_get_end(ssh)) != 0) 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"); error_f("no kex");
return SSH_ERR_INTERNAL_ERROR; 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); ptr = sshpkt_ptr(ssh, &dlen);
if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0) if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0)
return r; 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 (!(kex->flags & KEX_INIT_SENT))
if ((r = kex_send_kexinit(ssh)) != 0) if ((r = kex_send_kexinit(ssh)) != 0)
return r; return r;
if ((r = kex_choose_conf(ssh)) != 0) if ((r = kex_choose_conf(ssh, seq)) != 0)
return r; return r;
if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL) 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); return (1);
} }
/* returns non-zero if proposal contains any algorithm from algs */
static int static int
has_any_alg(const char *proposal, const char *algs) kexalgs_contains(char **peer, const char *ext)
{ {
char *cp; return has_any_alg(peer[PROPOSAL_KEX_ALGS], ext);
if ((cp = match_list(proposal, algs, NULL)) == NULL)
return 0;
free(cp);
return 1;
} }
static int static int
kex_choose_conf(struct ssh *ssh) kex_choose_conf(struct ssh *ssh, uint32_t seq)
{ {
struct kex *kex = ssh->kex; struct kex *kex = ssh->kex;
struct newkeys *newkeys; struct newkeys *newkeys;
@ -1002,13 +1160,24 @@ kex_choose_conf(struct ssh *ssh)
sprop=peer; sprop=peer;
} }
/* Check whether client supports ext_info_c */ /* Check whether peer supports ext_info/kex_strict */
if (kex->server && (kex->flags & KEX_INITIAL)) { if ((kex->flags & KEX_INITIAL) != 0) {
char *ext; if (kex->server) {
kex->ext_info_c = kexalgs_contains(peer, "ext-info-c");
ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL); kex->kex_strict = kexalgs_contains(peer,
kex->ext_info_c = (ext != NULL); "kex-strict-c-v00@openssh.com");
free(ext); } 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 */ /* Check whether client supports rsa-sha2 algorithms */

View file

@ -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. * 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_256_SUPPORTED 0x0008 /* only set in server for now */
#define KEX_RSA_SHA2_512_SUPPORTED 0x0010 /* 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_PING 0x0020
#define KEX_HAS_EXT_INFO_IN_AUTH 0x0040
struct sshenc { struct sshenc {
char *name; char *name;
@ -142,6 +143,9 @@ struct kex {
u_int kex_type; u_int kex_type;
char *server_sig_algs; char *server_sig_algs;
int ext_info_c; int ext_info_c;
int ext_info_s;
int kex_strict;
int ext_info_received;
struct sshbuf *my; struct sshbuf *my;
struct sshbuf *peer; struct sshbuf *peer;
struct sshbuf *client_version; 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_derive_keys(struct ssh *, u_char *, u_int, const struct sshbuf *);
int kex_send_newkeys(struct ssh *); int kex_send_newkeys(struct ssh *);
int kex_start_rekex(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_client(struct ssh *);
int kexgex_server(struct ssh *); int kexgex_server(struct ssh *);

View file

@ -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 <provos@citi.umich.edu> * Copyright 2002 Niels Provos <provos@citi.umich.edu>
* Copyright 2002 Markus Friedl <markus@openbsd.org> * Copyright 2002 Markus Friedl <markus@openbsd.org>
@ -327,8 +327,8 @@ out:
log_verbose_add(options.log_verbose[i]); log_verbose_add(options.log_verbose[i]);
process_permitopen(ssh, &options); process_permitopen(ssh, &options);
process_channel_timeouts(ssh, &options); process_channel_timeouts(ssh, &options);
kex_set_server_sig_algs(ssh, options.pubkey_accepted_algos);
free(newopts); free(newopts);
sshbuf_free(m); sshbuf_free(m);
return (pw); return (pw);

View file

@ -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 <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -1187,8 +1187,13 @@ ssh_packet_send2_wrapped(struct ssh *ssh)
sshbuf_dump(state->output, stderr); sshbuf_dump(state->output, stderr);
#endif #endif
/* increment sequence number for outgoing packets */ /* 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"); logit("outgoing seqnr wraps around");
}
if (++state->p_send.packets == 0) if (++state->p_send.packets == 0)
if (!(ssh->compat & SSH_BUG_NOREKEY)) if (!(ssh->compat & SSH_BUG_NOREKEY))
return SSH_ERR_NEED_REKEY; return SSH_ERR_NEED_REKEY;
@ -1196,6 +1201,11 @@ ssh_packet_send2_wrapped(struct ssh *ssh)
state->p_send.bytes += len; state->p_send.bytes += len;
sshbuf_reset(state->outgoing_packet); 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) if (type == SSH2_MSG_NEWKEYS)
r = ssh_set_newkeys(ssh, MODE_OUT); r = ssh_set_newkeys(ssh, MODE_OUT);
else if (type == SSH2_MSG_USERAUTH_SUCCESS && state->server_side) 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. */ /* Stay in the loop until we have received a complete packet. */
for (;;) { for (;;) {
/* Try to read a packet from the buffer. */ /* Try to read a packet from the buffer. */
r = ssh_packet_read_poll_seqnr(ssh, typep, seqnr_p); if ((r = ssh_packet_read_poll_seqnr(ssh, typep, seqnr_p)) != 0)
if (r != 0)
break; break;
/* If we got a packet, return it. */ /* If we got a packet, return it. */
if (*typep != SSH_MSG_NONE) if (*typep != SSH_MSG_NONE)
@ -1395,29 +1404,6 @@ ssh_packet_read(struct ssh *ssh)
return type; 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 static int
ssh_packet_read_poll2_mux(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) 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) if ((r = sshbuf_consume(state->input, mac->mac_len)) != 0)
goto out; goto out;
} }
if (seqnr_p != NULL) if (seqnr_p != NULL)
*seqnr_p = state->p_read.seqnr; *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"); logit("incoming seqnr wraps around");
}
if (++state->p_read.packets == 0) if (++state->p_read.packets == 0)
if (!(ssh->compat & SSH_BUG_NOREKEY)) if (!(ssh->compat & SSH_BUG_NOREKEY))
return SSH_ERR_NEED_REKEY; 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 #endif
/* reset for next packet */ /* reset for next packet */
state->packlen = 0; 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) if ((r = ssh_packet_check_rekey(ssh)) != 0)
return r; 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); r = ssh_packet_read_poll2(ssh, typep, seqnr_p);
if (r != 0) if (r != 0)
return r; return r;
if (*typep) { if (*typep == 0) {
/* no message ready */
return 0;
}
state->keep_alive_timeouts = 0; state->keep_alive_timeouts = 0;
DBG(debug("received packet type %d", *typep)); 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) { switch (*typep) {
case SSH2_MSG_IGNORE: case SSH2_MSG_IGNORE:
debug3("Received 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); debug("Remote: %.900s", msg);
free(msg); free(msg);
break; 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: case SSH2_MSG_UNIMPLEMENTED:
if ((r = sshpkt_get_u32(ssh, &seqnr)) != 0) if ((r = sshpkt_get_u32(ssh, &seqnr)) != 0)
return r; 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_type)) != 0 ||
(r = sshbuf_put_u32(m, kex->hostkey_nid)) != 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_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->my)) != 0 ||
(r = sshbuf_put_stringb(m, kex->peer)) != 0 || (r = sshbuf_put_stringb(m, kex->peer)) != 0 ||
(r = sshbuf_put_stringb(m, kex->client_version)) != 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_type)) != 0 ||
(r = sshbuf_get_u32(m, (u_int *)&kex->hostkey_nid)) != 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_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->my)) != 0 ||
(r = sshbuf_get_stringb(m, kex->peer)) != 0 || (r = sshbuf_get_stringb(m, kex->peer)) != 0 ||
(r = sshbuf_get_stringb(m, kex->client_version)) != 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); vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args); va_end(args);
debug2_f("sending SSH2_MSG_DISCONNECT: %s", buf);
if ((r = sshpkt_start(ssh, SSH2_MSG_DISCONNECT)) != 0 || if ((r = sshpkt_start(ssh, SSH2_MSG_DISCONNECT)) != 0 ||
(r = sshpkt_put_u32(ssh, SSH2_DISCONNECT_PROTOCOL_ERROR)) != 0 || (r = sshpkt_put_u32(ssh, SSH2_DISCONNECT_PROTOCOL_ERROR)) != 0 ||
(r = sshpkt_put_cstring(ssh, buf)) != 0 || (r = sshpkt_put_cstring(ssh, buf)) != 0 ||

View file

@ -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 <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -118,7 +118,6 @@ int ssh_packet_send2_wrapped(struct ssh *);
int ssh_packet_send2(struct ssh *); int ssh_packet_send2(struct ssh *);
int ssh_packet_read(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_poll(struct ssh *);
int ssh_packet_read_poll2(struct ssh *, u_char *, u_int32_t *seqnr_p); 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); int ssh_packet_process_incoming(struct ssh *, const char *buf, u_int len);

View file

@ -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 <ylo@cs.hut.fi> .\" Author: Tatu Ylonen <ylo@cs.hut.fi>
.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland .\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -35,7 +35,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" 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 .Dt SSH-ADD 1
.Os .Os
.Sh NAME .Sh NAME
@ -43,7 +43,7 @@
.Nd adds private key identities to the OpenSSH authentication agent .Nd adds private key identities to the OpenSSH authentication agent
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm ssh-add .Nm ssh-add
.Op Fl cDdKkLlqvXx .Op Fl cCDdKkLlqvXx
.Op Fl E Ar fingerprint_hash .Op Fl E Ar fingerprint_hash
.Op Fl H Ar hostkey_file .Op Fl H Ar hostkey_file
.Op Fl h Ar destination_constraint .Op Fl h Ar destination_constraint
@ -52,6 +52,8 @@
.Op Ar .Op Ar
.Nm ssh-add .Nm ssh-add
.Fl s Ar pkcs11 .Fl s Ar pkcs11
.Op Fl vC
.Op Ar certificate ...
.Nm ssh-add .Nm ssh-add
.Fl e Ar pkcs11 .Fl e Ar pkcs11
.Nm ssh-add .Nm ssh-add
@ -100,6 +102,9 @@ Confirmation is performed by
Successful confirmation is signaled by a zero exit status from Successful confirmation is signaled by a zero exit status from
.Xr ssh-askpass 1 , .Xr ssh-askpass 1 ,
rather than text entered into the requester. 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 .It Fl D
Deletes all identities from the agent. Deletes all identities from the agent.
.It Fl d .It Fl d
@ -228,6 +233,9 @@ internal USB HID support.
.It Fl s Ar pkcs11 .It Fl s Ar pkcs11
Add keys provided by the PKCS#11 shared library Add keys provided by the PKCS#11 shared library
.Ar pkcs11 . .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 ... .It Fl T Ar pubkey ...
Tests whether the private keys that correspond to the specified Tests whether the private keys that correspond to the specified
.Ar pubkey .Ar pubkey

View file

@ -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 <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -124,7 +124,7 @@ delete_one(int agent_fd, const struct sshkey *key, const char *comment,
} }
static int 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; char *line = NULL, *cp;
size_t linesize = 0; size_t linesize = 0;
@ -145,30 +145,40 @@ delete_stdin(int agent_fd, int qflag)
error_r(r, "(stdin):%d: invalid key", lnum); error_r(r, "(stdin):%d: invalid key", lnum);
continue; continue;
} }
if (delete_one(agent_fd, key, cp, "(stdin)", qflag) == 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; ret = 0;
} }
}
sshkey_free(key); sshkey_free(key);
free(line); free(line);
return ret; return ret;
} }
static int 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; struct sshkey *public, *cert = NULL;
char *certpath = NULL, *comment = NULL; char *certpath = NULL, *comment = NULL;
int r, ret = -1; int r, ret = -1;
if (strcmp(filename, "-") == 0) 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) { if ((r = sshkey_load_public(filename, &public, &comment)) != 0) {
printf("Bad key file %s: %s\n", filename, ssh_err(r)); printf("Bad key file %s: %s\n", filename, ssh_err(r));
return -1; return -1;
} }
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) if (delete_one(agent_fd, public, comment, filename, qflag) == 0)
ret = 0; ret = 0;
}
if (key_only) if (key_only)
goto out; goto out;
@ -224,8 +234,9 @@ delete_all(int agent_fd, int qflag)
} }
static int static int
add_file(int agent_fd, const char *filename, int key_only, int qflag, add_file(int agent_fd, const char *filename, int key_only, int cert_only,
const char *skprovider, struct dest_constraint **dest_constraints, int qflag, const char *skprovider,
struct dest_constraint **dest_constraints,
size_t ndest_constraints) size_t ndest_constraints)
{ {
struct sshkey *private, *cert; struct sshkey *private, *cert;
@ -354,7 +365,8 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag,
skprovider = NULL; 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, lifetime, confirm, maxsign, skprovider,
dest_constraints, ndest_constraints)) == 0) { dest_constraints, ndest_constraints)) == 0) {
ret = 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); xasprintf(&certpath, "%s-cert.pub", filename);
if ((r = sshkey_load_public(certpath, &cert, NULL)) != 0) { if ((r = sshkey_load_public(certpath, &cert, NULL)) != 0) {
if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT) 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; goto out;
} }
@ -438,11 +451,16 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag,
static int static int
update_card(int agent_fd, int add, const char *id, int qflag, 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; char *pin = NULL;
int r, ret = -1; int r, ret = -1;
if (key_only)
ncerts = 0;
if (add) { if (add) {
if ((pin = read_passphrase("Enter passphrase for PKCS#11: ", if ((pin = read_passphrase("Enter passphrase for PKCS#11: ",
RP_ALLOW_STDIN)) == NULL) 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, 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; ret = 0;
if (!qflag) { if (!qflag) {
fprintf(stderr, "Card %s: %s\n", fprintf(stderr, "Card %s: %s\n",
@ -626,16 +645,17 @@ load_resident_keys(int agent_fd, const char *skprovider, int qflag,
} }
static int static int
do_file(int agent_fd, int deleting, int key_only, char *file, int qflag, do_file(int agent_fd, int deleting, int key_only, int cert_only,
const char *skprovider, struct dest_constraint **dest_constraints, char *file, int qflag, const char *skprovider,
size_t ndest_constraints) struct dest_constraint **dest_constraints, size_t ndest_constraints)
{ {
if (deleting) { 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; return -1;
} else { } else {
if (add_file(agent_fd, file, key_only, qflag, skprovider, if (add_file(agent_fd, file, key_only, cert_only, qflag,
dest_constraints, ndest_constraints) == -1) skprovider, dest_constraints, ndest_constraints) == -1)
return -1; return -1;
} }
return 0; return 0;
@ -783,12 +803,14 @@ main(int argc, char **argv)
int agent_fd; int agent_fd;
char *pkcs11provider = NULL, *skprovider = NULL; char *pkcs11provider = NULL, *skprovider = NULL;
char **dest_constraint_strings = NULL, **hostkey_files = NULL; char **dest_constraint_strings = NULL, **hostkey_files = NULL;
int r, i, ch, deleting = 0, ret = 0, key_only = 0, do_download = 0; int r, i, ch, deleting = 0, ret = 0, key_only = 0, cert_only = 0;
int xflag = 0, lflag = 0, Dflag = 0, qflag = 0, Tflag = 0; int do_download = 0, xflag = 0, lflag = 0, Dflag = 0;
int qflag = 0, Tflag = 0;
SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
LogLevel log_level = SYSLOG_LEVEL_INFO; LogLevel log_level = SYSLOG_LEVEL_INFO;
struct sshkey *k, **certs = NULL;
struct dest_constraint **dest_constraints = 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 */ /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
sanitise_stdfd(); sanitise_stdfd();
@ -815,7 +837,7 @@ main(int argc, char **argv)
skprovider = getenv("SSH_SK_PROVIDER"); 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) { switch (ch) {
case 'v': case 'v':
if (log_level == SYSLOG_LEVEL_INFO) if (log_level == SYSLOG_LEVEL_INFO)
@ -837,6 +859,9 @@ main(int argc, char **argv)
case 'k': case 'k':
key_only = 1; key_only = 1;
break; break;
case 'C':
cert_only = 1;
break;
case 'K': case 'K':
do_download = 1; do_download = 1;
break; break;
@ -952,8 +977,19 @@ main(int argc, char **argv)
goto done; goto done;
} }
if (pkcs11provider != NULL) { 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, 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; ret = 1;
goto done; goto done;
} }
@ -983,8 +1019,8 @@ main(int argc, char **argv)
default_files[i]); default_files[i]);
if (stat(buf, &st) == -1) if (stat(buf, &st) == -1)
continue; continue;
if (do_file(agent_fd, deleting, key_only, buf, if (do_file(agent_fd, deleting, key_only, cert_only,
qflag, skprovider, buf, qflag, skprovider,
dest_constraints, ndest_constraints) == -1) dest_constraints, ndest_constraints) == -1)
ret = 1; ret = 1;
else else
@ -994,7 +1030,7 @@ main(int argc, char **argv)
ret = 1; ret = 1;
} else { } else {
for (i = 0; i < argc; i++) { 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, argv[i], qflag, skprovider,
dest_constraints, ndest_constraints) == -1) dest_constraints, ndest_constraints) == -1)
ret = 1; ret = 1;

View file

@ -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 <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -92,6 +92,8 @@
#define AGENT_MAX_SID_LEN 128 #define AGENT_MAX_SID_LEN 128
/* Maximum number of destination constraints to accept on a key */ /* Maximum number of destination constraints to accept on a key */
#define AGENT_MAX_DEST_CONSTRAINTS 1024 #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 */ /* XXX store hostkey_sid in a refcounted tree */
@ -115,6 +117,7 @@ typedef struct socket_entry {
struct sshbuf *request; struct sshbuf *request;
size_t nsession_ids; size_t nsession_ids;
struct hostkey_sid *session_ids; struct hostkey_sid *session_ids;
int session_bind_attempted;
} SocketEntry; } SocketEntry;
u_int sockets_alloc = 0; u_int sockets_alloc = 0;
@ -234,6 +237,91 @@ free_dest_constraints(struct dest_constraint *dcs, size_t ndcs)
free(dcs); 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 static void
free_identity(Identity *id) free_identity(Identity *id)
{ {
@ -377,6 +465,10 @@ identity_permitted(Identity *id, SocketEntry *e, char *user,
e->nsession_ids, id->ndest_constraints); e->nsession_ids, id->ndest_constraints);
if (id->ndest_constraints == 0) if (id->ndest_constraints == 0)
return 0; /* unconstrained */ 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) if (e->nsession_ids == 0)
return 0; /* local use */ return 0; /* local use */
/* /*
@ -456,6 +548,12 @@ identity_permitted(Identity *id, SocketEntry *e, char *user,
return 0; 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 */ /* return matching private key for given public key */
static Identity * static Identity *
lookup_identity(struct sshkey *key) lookup_identity(struct sshkey *key)
@ -505,13 +603,22 @@ process_request_identities(SocketEntry *e)
Identity *id; Identity *id;
struct sshbuf *msg, *keys; struct sshbuf *msg, *keys;
int r; int r;
u_int nentries = 0; u_int i = 0, nentries = 0;
char *fp;
debug2_f("entering"); debug2_f("entering");
if ((msg = sshbuf_new()) == NULL || (keys = sshbuf_new()) == NULL) if ((msg = sshbuf_new()) == NULL || (keys = sshbuf_new()) == NULL)
fatal_f("sshbuf_new failed"); fatal_f("sshbuf_new failed");
TAILQ_FOREACH(id, &idtab->idlist, next) { 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 */ /* identity not visible, don't include in response */
if (identity_permitted(id, e, NULL, NULL, NULL) != 0) if (identity_permitted(id, e, NULL, NULL, NULL) != 0)
continue; continue;
@ -1051,11 +1158,14 @@ parse_dest_constraint(struct sshbuf *m, struct dest_constraint *dc)
static int static int
parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp, 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; char *ext_name = NULL;
int r; int r;
struct sshbuf *b = NULL; struct sshbuf *b = NULL;
u_char v;
struct sshkey *k;
if ((r = sshbuf_get_cstring(m, &ext_name, NULL)) != 0) { if ((r = sshbuf_get_cstring(m, &ext_name, NULL)) != 0) {
error_fr(r, "parse constraint extension"); error_fr(r, "parse constraint extension");
@ -1098,6 +1208,36 @@ parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp,
*dcsp + (*ndcsp)++)) != 0) *dcsp + (*ndcsp)++)) != 0)
goto out; /* error already logged */ 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 { } else {
error_f("unsupported constraint \"%s\"", ext_name); error_f("unsupported constraint \"%s\"", ext_name);
r = SSH_ERR_FEATURE_UNSUPPORTED; r = SSH_ERR_FEATURE_UNSUPPORTED;
@ -1114,7 +1254,8 @@ parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp,
static int static int
parse_key_constraints(struct sshbuf *m, struct sshkey *k, time_t *deathp, parse_key_constraints(struct sshbuf *m, struct sshkey *k, time_t *deathp,
u_int *secondsp, int *confirmp, char **sk_providerp, 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; u_char ctype;
int r; int r;
@ -1169,7 +1310,8 @@ parse_key_constraints(struct sshbuf *m, struct sshkey *k, time_t *deathp,
break; break;
case SSH_AGENT_CONSTRAIN_EXTENSION: case SSH_AGENT_CONSTRAIN_EXTENSION:
if ((r = parse_key_constraint_extension(m, 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 */ goto out; /* error already logged */
break; break;
default: default:
@ -1206,11 +1348,13 @@ process_add_identity(SocketEntry *e)
goto out; goto out;
} }
if (parse_key_constraints(e->request, k, &death, &seconds, &confirm, 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"); error_f("failed to parse constraints");
sshbuf_reset(e->request); sshbuf_reset(e->request);
goto out; goto out;
} }
dump_dest_constraints(__func__, dest_constraints, ndest_constraints);
if (sk_provider != NULL) { if (sk_provider != NULL) {
if (!sshkey_is_sk(k)) { if (!sshkey_is_sk(k)) {
@ -1221,7 +1365,7 @@ process_add_identity(SocketEntry *e)
if (strcasecmp(sk_provider, "internal") == 0) { if (strcasecmp(sk_provider, "internal") == 0) {
debug_f("internal provider"); debug_f("internal provider");
} else { } else {
if (e->nsession_ids != 0 && !remote_add_provider) { if (socket_is_remote(e) && !remote_add_provider) {
verbose("failed add of SK provider \"%.100s\": " verbose("failed add of SK provider \"%.100s\": "
"remote addition of providers is disabled", "remote addition of providers is disabled",
sk_provider); sk_provider);
@ -1365,6 +1509,32 @@ no_identities(SocketEntry *e)
sshbuf_free(msg); 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 #ifdef ENABLE_PKCS11
static void static void
process_add_smartcard_key(SocketEntry *e) process_add_smartcard_key(SocketEntry *e)
@ -1375,9 +1545,10 @@ process_add_smartcard_key(SocketEntry *e)
u_int seconds = 0; u_int seconds = 0;
time_t death = 0; time_t death = 0;
struct sshkey **keys = NULL, *k; struct sshkey **keys = NULL, *k;
Identity *id;
struct dest_constraint *dest_constraints = NULL; 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"); debug2_f("entering");
if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 || if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 ||
@ -1386,11 +1557,13 @@ process_add_smartcard_key(SocketEntry *e)
goto send; goto send;
} }
if (parse_key_constraints(e->request, NULL, &death, &seconds, &confirm, 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"); error_f("failed to parse constraints");
goto send; 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 " verbose("failed PKCS#11 add of \"%.100s\": remote addition of "
"providers is disabled", provider); "providers is disabled", provider);
goto send; goto send;
@ -1411,26 +1584,28 @@ process_add_smartcard_key(SocketEntry *e)
count = pkcs11_add_provider(canonical_provider, pin, &keys, &comments); count = pkcs11_add_provider(canonical_provider, pin, &keys, &comments);
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
k = keys[i]; if (comments[i] == NULL || comments[i][0] == '\0') {
if (lookup_identity(k) == NULL) { free(comments[i]);
id = xcalloc(1, sizeof(Identity)); comments[i] = xstrdup(canonical_provider);
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; for (j = 0; j < ncerts; j++) {
id->confirm = confirm; if (!sshkey_is_cert(certs[j]))
id->dest_constraints = dest_constraints; continue;
id->ndest_constraints = ndest_constraints; if (!sshkey_equal_public(keys[i], certs[j]))
dest_constraints = NULL; /* transferred */ continue;
ndest_constraints = 0; if (pkcs11_make_cert(keys[i], certs[j], &k) != 0)
TAILQ_INSERT_TAIL(&idtab->idlist, id, next); continue;
idtab->nentries++; 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; success = 1;
} }
/* XXX update constraints for existing keys */ /* XXX update constraints for existing keys */
@ -1443,6 +1618,9 @@ send:
free(keys); free(keys);
free(comments); free(comments);
free_dest_constraints(dest_constraints, ndest_constraints); free_dest_constraints(dest_constraints, ndest_constraints);
for (j = 0; j < ncerts; j++)
sshkey_free(certs[j]);
free(certs);
send_status(e, success); send_status(e, success);
} }
@ -1500,6 +1678,7 @@ process_ext_session_bind(SocketEntry *e)
u_char fwd = 0; u_char fwd = 0;
debug2_f("entering"); debug2_f("entering");
e->session_bind_attempted = 1;
if ((r = sshkey_froms(e->request, &key)) != 0 || if ((r = sshkey_froms(e->request, &key)) != 0 ||
(r = sshbuf_froms(e->request, &sid)) != 0 || (r = sshbuf_froms(e->request, &sid)) != 0 ||
(r = sshbuf_froms(e->request, &sig)) != 0 || (r = sshbuf_froms(e->request, &sig)) != 0 ||

View file

@ -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) 2010 Markus Friedl. All rights reserved.
* Copyright (c) 2014 Pedro Martelletto. 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); 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 static int
pkcs11_start_helper_methods(struct helper *helper) pkcs11_start_helper_methods(struct helper *helper)
{ {

View file

@ -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. * Copyright (c) 2010 Markus Friedl. All rights reserved.
* *
@ -35,6 +35,9 @@ struct sshkey *
u_int32_t *); u_int32_t *);
#endif #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) #if !defined(WITH_OPENSSL) && defined(ENABLE_PKCS11)
#undef ENABLE_PKCS11 #undef ENABLE_PKCS11
#endif #endif

View file

@ -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 <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -610,6 +610,41 @@ ssh_conn_info_free(struct ssh_conn_info *cinfo)
free(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. * Main program for the ssh client.
*/ */
@ -1092,6 +1127,10 @@ main(int ac, char **av)
if (!host) if (!host)
usage(); 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); options.host_arg = xstrdup(host);
#ifdef WITH_OPENSSL #ifdef WITH_OPENSSL

View file

@ -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) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2008 Damien Miller. 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_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_success(int, u_int32_t, struct ssh *);
static int input_userauth_failure(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 *); 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; authctxt.mech_tried = 0;
#endif #endif
authctxt.agent_fd = -1; 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"); fatal_f("internal error: cannot send userauth none request");
}
if ((r = sshpkt_start(ssh, SSH2_MSG_SERVICE_REQUEST)) != 0 || if ((r = sshpkt_start(ssh, SSH2_MSG_SERVICE_REQUEST)) != 0 ||
(r = sshpkt_put_cstring(ssh, "ssh-userauth")) != 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->authctxt = &authctxt;
ssh_dispatch_init(ssh, &input_userauth_error); 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_set(ssh, SSH2_MSG_SERVICE_ACCEPT, &input_userauth_service_accept);
ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &authctxt.success); /* loop until success */ ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &authctxt.success); /* loop until success */
pubkey_cleanup(ssh); pubkey_cleanup(ssh);
@ -515,7 +512,9 @@ input_userauth_service_accept(int type, u_int32_t seq, struct ssh *ssh)
/* initial userauth request */ /* initial userauth request */
userauth_none(ssh); 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_SUCCESS, &input_userauth_success);
ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure); ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure);
ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner); 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; 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 void
userauth(struct ssh *ssh, char *authlist) 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); free(authctxt->methoddata);
authctxt->methoddata = NULL; authctxt->methoddata = NULL;
authctxt->success = 1; /* break out */ authctxt->success = 1; /* break out */
ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, dispatch_protocol_error);
return 0; return 0;
} }
@ -1677,10 +1671,10 @@ pubkey_prepare(struct ssh *ssh, Authctxt *authctxt)
struct identity *id, *id2, *tmp; struct identity *id, *id2, *tmp;
struct idlist agent, files, *preferred; struct idlist agent, files, *preferred;
struct sshkey *key; struct sshkey *key;
int agent_fd = -1, i, r, found; int disallowed, agent_fd = -1, i, r, found;
size_t j; size_t j;
struct ssh_identitylist *idlist; struct ssh_identitylist *idlist;
char *ident; char *cp, *ident;
TAILQ_INIT(&agent); /* keys from the agent */ TAILQ_INIT(&agent); /* keys from the agent */
TAILQ_INIT(&files); /* keys from the config file */ TAILQ_INIT(&files); /* keys from the config file */
@ -1798,16 +1792,30 @@ pubkey_prepare(struct ssh *ssh, Authctxt *authctxt)
TAILQ_CONCAT(preferred, &files, next); TAILQ_CONCAT(preferred, &files, next);
/* finally, filter by PubkeyAcceptedAlgorithms */ /* finally, filter by PubkeyAcceptedAlgorithms */
TAILQ_FOREACH_SAFE(id, preferred, next, id2) { TAILQ_FOREACH_SAFE(id, preferred, next, id2) {
if (id->key != NULL && !key_type_allowed_by_config(id->key)) { disallowed = 0;
debug("Skipping %s key %s - " cp = NULL;
"corresponding algo not in PubkeyAcceptedAlgorithms", 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); 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); TAILQ_REMOVE(preferred, id, next);
sshkey_free(id->key); sshkey_free(id->key);
free(id->filename); free(id->filename);
memset(id, 0, sizeof(*id)); memset(id, 0, sizeof(*id));
continue;
}
} }
/* List the keys we plan on using */ /* List the keys we plan on using */
TAILQ_FOREACH_SAFE(id, preferred, next, id2) { TAILQ_FOREACH_SAFE(id, preferred, next, id2) {
@ -1853,6 +1861,12 @@ userauth_pubkey(struct ssh *ssh)
Identity *id; Identity *id;
int sent = 0; int sent = 0;
char *ident; char *ident;
static int prepared;
if (!prepared) {
pubkey_prepare(ssh, authctxt);
prepared = 1;
}
while ((id = TAILQ_FIRST(&authctxt->keys))) { while ((id = TAILQ_FIRST(&authctxt->keys))) {
if (id->tried++) if (id->tried++)

View file

@ -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 <ylo@cs.hut.fi> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -2240,7 +2240,9 @@ do_ssh2_kex(struct ssh *ssh)
/* start key exchange */ /* start key exchange */
if ((r = kex_setup(ssh, myproposal)) != 0) if ((r = kex_setup(ssh, myproposal)) != 0)
fatal_r(r, "kex_setup"); fatal_r(r, "kex_setup");
kex_set_server_sig_algs(ssh, options.pubkey_accepted_algos);
kex = ssh->kex; kex = ssh->kex;
#ifdef WITH_OPENSSL #ifdef WITH_OPENSSL
kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_server; kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_server;
kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_server; kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_server;

View file

@ -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"

View file

@ -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"

View file

@ -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 <martijn@openbsd.org> * Copyright (c) 2023 Martijn van Duren <martijn@openbsd.org>
@ -130,8 +130,6 @@ appl_internal_init(void)
NULL); NULL);
appl_internal_object(&OID(MIB_snmpInReadOnlys), appl_internal_snmp, appl_internal_object(&OID(MIB_snmpInReadOnlys), appl_internal_snmp,
NULL); 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_snmpInGenErrs), appl_internal_snmp, NULL);
appl_internal_object(&OID(MIB_snmpInTotalReqVars), appl_internal_snmp, appl_internal_object(&OID(MIB_snmpInTotalReqVars), appl_internal_snmp,
NULL); NULL);
@ -253,6 +251,7 @@ appl_internal_object(struct ber_oid *oid,
struct ber_element *(*getnext)(int8_t, struct ber_oid *)) struct ber_element *(*getnext)(int8_t, struct ber_oid *))
{ {
struct appl_internal_object *obj; struct appl_internal_object *obj;
char buf[1024];
if ((obj = calloc(1, sizeof(*obj))) == NULL) if ((obj = calloc(1, sizeof(*obj))) == NULL)
fatal(NULL); fatal(NULL);
@ -261,7 +260,10 @@ appl_internal_object(struct ber_oid *oid,
obj->getnext = getnext; obj->getnext = getnext;
obj->stringval = NULL; 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 * const char *
@ -351,6 +353,8 @@ appl_internal_get(struct appl_backend *backend, __unused int32_t transactionid,
resp[i - 1].av_next = NULL; resp[i - 1].av_next = NULL;
appl_response(backend, requestid, APPL_ERROR_NOERROR, 0, resp); appl_response(backend, requestid, APPL_ERROR_NOERROR, 0, resp);
free(resp);
return; return;
fail: fail:
@ -434,6 +438,8 @@ appl_internal_getnext(struct appl_backend *backend,
resp[i - 1].av_next = NULL; resp[i - 1].av_next = NULL;
appl_response(backend, requestid, APPL_ERROR_NOERROR, 0, resp); appl_response(backend, requestid, APPL_ERROR_NOERROR, 0, resp);
free(resp);
return; return;
fail: fail:

View file

@ -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 <reyk@openbsd.org> * Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@openbsd.org>
@ -105,7 +105,7 @@ static uint8_t engineid[SNMPD_MAXENGINEIDLEN];
static int32_t enginepen; static int32_t enginepen;
static size_t engineidlen; static size_t engineidlen;
int host(const char *, const char *, int, int host(const char *, const char *, int, int,
struct sockaddr_storage *, int); struct sockaddr_storage *, int);
int listen_add(struct sockaddr_storage *, int, 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++) { for (i = 0; i < addresslen; i++) {
nhosts = host(address[i], port, $1, ss, nitems(ss)); if ((nhosts = host(address[i], port, AF_UNSPEC,
if (nhosts < 1) { $1, ss, nitems(ss))) < 1) {
yyerror("invalid address: %s", $2); yyerror("invalid address: %s", $2);
free($2); free($2);
free($3); free($3);
@ -1021,7 +1021,8 @@ hostdef : STRING hostoid hostauth srcaddr {
YYERROR; 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); yyerror("invalid host: %s", $1);
free($1); free($1);
free($2); free($2);
@ -1033,8 +1034,10 @@ hostdef : STRING hostoid hostauth srcaddr {
free($1); free($1);
memcpy(&(tr->ta_ss), &ss, sizeof(ss)); memcpy(&(tr->ta_ss), &ss, sizeof(ss));
if ($4 != NULL) { if ($4 != NULL) {
if (host($1, "0", SOCK_DGRAM, &ss, 1) <= 0) { if (host($4, "0", ss.ss_family, SOCK_DGRAM,
yyerror("invalid host: %s", $1); &ss, 1) <= 0) {
yyerror("invalid source-address: %s",
$4);
free($2); free($2);
free($3.data); free($3.data);
free($4); free($4);
@ -1702,11 +1705,12 @@ parse_config(const char *filename, u_int flags)
/* Setup default listen addresses */ /* Setup default listen addresses */
if (TAILQ_EMPTY(&conf->sc_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"); fatal("Unexpected resolving of 0.0.0.0");
if (listen_add(&ss, SOCK_DGRAM, 0) == -1) if (listen_add(&ss, SOCK_DGRAM, 0) == -1)
fatal("calloc"); 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 ::"); fatal("Unexpected resolving of ::");
if (listen_add(&ss, SOCK_DGRAM, 0) == -1) if (listen_add(&ss, SOCK_DGRAM, 0) == -1)
fatal("calloc"); fatal("calloc");
@ -1843,14 +1847,14 @@ symget(const char *nam)
} }
int int
host(const char *s, const char *port, int type, struct sockaddr_storage *ss, host(const char *s, const char *port, int family, int type,
int max) struct sockaddr_storage *ss, int max)
{ {
struct addrinfo hints, *res0, *res; struct addrinfo hints, *res0, *res;
int error, i; int error, i;
bzero(&hints, sizeof(hints)); bzero(&hints, sizeof(hints));
hints.ai_family = PF_UNSPEC; hints.ai_family = family;
hints.ai_socktype = type; hints.ai_socktype = type;
/* /*
* Without AI_NUMERICHOST getaddrinfo might not resolve ip addresses * Without AI_NUMERICHOST getaddrinfo might not resolve ip addresses