diff --git a/regress/usr.bin/ssh/unittests/Makefile b/regress/usr.bin/ssh/unittests/Makefile index e370900e4de..8d7a6b1e3d8 100644 --- a/regress/usr.bin/ssh/unittests/Makefile +++ b/regress/usr.bin/ssh/unittests/Makefile @@ -1,6 +1,6 @@ -# $OpenBSD: Makefile,v 1.13 2023/09/24 08:14:13 claudio Exp $ +# $OpenBSD: Makefile,v 1.14 2026/05/31 11:39:44 djm Exp $ SUBDIR= test_helper sshbuf sshkey bitmap kex hostkeys utf8 match conversion -SUBDIR+=authopt misc sshsig +SUBDIR+=authopt misc sshsig servconf .include diff --git a/regress/usr.bin/ssh/unittests/servconf/Makefile b/regress/usr.bin/ssh/unittests/servconf/Makefile new file mode 100644 index 00000000000..277c08e0244 --- /dev/null +++ b/regress/usr.bin/ssh/unittests/servconf/Makefile @@ -0,0 +1,25 @@ +# $OpenBSD: Makefile,v 1.1 2026/05/31 11:39:44 djm Exp $ + +PROG=test_servconf +SRCS=tests.c + +SRCS+=servconf.c groupaccess.c + +# From usr.bin/ssh +SRCS+=sshbuf-getput-basic.c sshbuf-getput-crypto.c sshbuf-misc.c sshbuf.c +SRCS+=sshbuf-io.c atomicio.c sshkey.c authfile.c cipher.c log.c ssh-rsa.c +SRCS+=ssh-ecdsa.c ssh-ed25519.c mac.c umac.c umac128.c hmac.c misc.c +SRCS+=ssherr.c uidswap.c cleanup.c xmalloc.c match.c krl.c fatal.c +SRCS+=addr.c addrmatch.c bitmap.c +SRCS+=cipher-chachapoly.c chacha.c poly1305.c ssh-ecdsa-sk.c ssh-sk.c +SRCS+=ssh-ed25519-sk.c sk-usbhid.c ssh-pkcs11-client.c +SRCS+=utf8.c ssherr-libcrypto.c +SRCS+=kex.c kex-names.c packet.c dispatch.c compat.c canohost.c +SRCS+=digest-openssl.c ed25519-openssl.c + +REGRESS_TARGETS=run-regress-${PROG} + +run-regress-${PROG}: ${PROG} + env ${TEST_ENV} ./${PROG} ${UNITTEST_ARGS} + +.include diff --git a/regress/usr.bin/ssh/unittests/servconf/tests.c b/regress/usr.bin/ssh/unittests/servconf/tests.c new file mode 100644 index 00000000000..3272d668089 --- /dev/null +++ b/regress/usr.bin/ssh/unittests/servconf/tests.c @@ -0,0 +1,362 @@ +/* $OpenBSD: tests.c,v 1.1 2026/05/31 11:39:44 djm Exp $ */ +/* + * Regress tests for servconf.h server configuration API + * + * Placed in the public domain + */ + +#include +#include +#include +#include +#include +#include + +#include "../test_helper/test_helper.h" + +#include "log.h" +#include "misc.h" +#include "servconf.h" +#include "sshbuf.h" +#include "ssherr.h" +#include "xmalloc.h" + +struct sshbuf *cfg; + +/* stub */ +int auth2_methods_valid(const char *methods, int need_enable); +int +auth2_methods_valid(const char *methods, int need_enable) +{ + return 1; +} + +static void +onerror(void *fuzz) +{ + fprintf(stderr, "Failed during fuzz:\n"); + fuzz_dump((struct fuzz *)fuzz); +} + +static void +set_string(char **dst, const char *s) +{ + *dst = s == NULL ? NULL : xstrdup(s); +} + +static char ** +new_string_array(u_int n) +{ + return xcalloc(n, sizeof(char *)); +} + +static void +set_test_options(ServerOptions *o) +{ + initialize_server_options(o); + + o->num_ports = 2; + o->ports[0] = 22; + o->ports[1] = 2022; + o->ip_qos_interactive = 0x10; + o->ip_qos_bulk = 0x08; + o->log_facility = SYSLOG_FACILITY_AUTH; + o->log_level = SYSLOG_LEVEL_DEBUG3; + o->fwd_opts.gateway_ports = 1; + o->fwd_opts.streamlocal_bind_mask = 0177; + o->fwd_opts.streamlocal_bind_unlink = 1; + o->permit_user_env = 1; + set_string(&o->permit_user_env_allowlist, ""); + o->rekey_limit = 123456789; + o->rekey_interval = 600; + o->timing_secret = 0x0102030405060708ULL; + + set_string(&o->routing_domain, ""); + set_string(&o->banner, NULL); + set_string(&o->adm_forced_command, "internal-sftp"); + set_string(&o->chroot_directory, ""); + set_string(&o->host_key_agent, NULL); + set_string(&o->authorized_keys_command, "/bin/echo"); + set_string(&o->authorized_keys_command_user, ""); + set_string(&o->authorized_principals_file, NULL); + + o->num_authkeys_files = 3; + o->authorized_keys_files = new_string_array(o->num_authkeys_files); + set_string(&o->authorized_keys_files[0], NULL); + set_string(&o->authorized_keys_files[1], ""); + set_string(&o->authorized_keys_files[2], ".ssh/authorized_keys"); + + o->num_log_verbose = 2; + o->log_verbose = new_string_array(o->num_log_verbose); + set_string(&o->log_verbose[0], NULL); + set_string(&o->log_verbose[1], "kex.c:*"); + + o->num_host_key_files = 3; + o->host_key_file_userprovided = + xcalloc(o->num_host_key_files, + sizeof(*o->host_key_file_userprovided)); + o->host_key_files = new_string_array(o->num_host_key_files); + o->host_key_file_userprovided[0] = 0; + o->host_key_file_userprovided[1] = 1; + o->host_key_file_userprovided[2] = -1; + set_string(&o->host_key_files[0], NULL); + set_string(&o->host_key_files[1], ""); + set_string(&o->host_key_files[2], "/tmp/ssh_host_ed25519_key"); + + o->num_queued_listens = 2; + o->queued_listen_addrs = xcalloc(o->num_queued_listens, + sizeof(*o->queued_listen_addrs)); + set_string(&o->queued_listen_addrs[0].addr, "127.0.0.1"); + o->queued_listen_addrs[0].port = 2222; + set_string(&o->queued_listen_addrs[0].rdomain, NULL); + set_string(&o->queued_listen_addrs[1].addr, "::1"); + o->queued_listen_addrs[1].port = -1; + set_string(&o->queued_listen_addrs[1].rdomain, ""); + + o->num_subsystems = 2; + o->subsystem_name = new_string_array(o->num_subsystems); + o->subsystem_command = new_string_array(o->num_subsystems); + o->subsystem_args = new_string_array(o->num_subsystems); + set_string(&o->subsystem_name[0], "sftp"); + set_string(&o->subsystem_command[0], "internal-sftp"); + set_string(&o->subsystem_args[0], "internal-sftp -f AUTH"); + set_string(&o->subsystem_name[1], "echo"); + set_string(&o->subsystem_command[1], "/bin/echo"); + set_string(&o->subsystem_args[1], "/bin/echo hello"); +} + +static void +check_roundtrip(const ServerOptions *o) +{ + struct sshbuf *buf = NULL; + ServerOptions out; + + initialize_server_options(&out); + ASSERT_INT_EQ(serialise_server_options(o, &buf), 0); + ASSERT_PTR_NE(buf, NULL); + ASSERT_INT_EQ(deserialise_server_options(buf, &out), 0); + ASSERT_SIZE_T_EQ(sshbuf_len(buf), 0); + + ASSERT_U_INT_EQ(out.num_ports, 2); + ASSERT_INT_EQ(out.ports[0], 22); + ASSERT_INT_EQ(out.ports[1], 2022); + ASSERT_INT_EQ(out.ip_qos_interactive, 0x10); + ASSERT_INT_EQ(out.ip_qos_bulk, 0x08); + ASSERT_INT_EQ(out.log_facility, SYSLOG_FACILITY_AUTH); + ASSERT_INT_EQ(out.log_level, SYSLOG_LEVEL_DEBUG3); + ASSERT_INT_EQ(out.fwd_opts.gateway_ports, 1); + ASSERT_INT_EQ(out.fwd_opts.streamlocal_bind_mask, 0177); + ASSERT_INT_EQ(out.fwd_opts.streamlocal_bind_unlink, 1); + ASSERT_INT_EQ(out.permit_user_env, 1); + ASSERT_STRING_EQ(out.permit_user_env_allowlist, ""); + ASSERT_LONG_LONG_EQ(out.rekey_limit, 123456789); + ASSERT_INT_EQ(out.rekey_interval, 600); + ASSERT_U64_EQ(out.timing_secret, 0x0102030405060708ULL); + + ASSERT_STRING_EQ(out.routing_domain, ""); + ASSERT_PTR_EQ(out.banner, NULL); + ASSERT_STRING_EQ(out.adm_forced_command, "internal-sftp"); + ASSERT_STRING_EQ(out.chroot_directory, ""); + ASSERT_PTR_EQ(out.host_key_agent, NULL); + ASSERT_STRING_EQ(out.authorized_keys_command, "/bin/echo"); + ASSERT_STRING_EQ(out.authorized_keys_command_user, ""); + ASSERT_PTR_EQ(out.authorized_principals_file, NULL); + + ASSERT_U_INT_EQ(out.num_authkeys_files, 3); + ASSERT_PTR_EQ(out.authorized_keys_files[0], NULL); + ASSERT_STRING_EQ(out.authorized_keys_files[1], ""); + ASSERT_STRING_EQ(out.authorized_keys_files[2], ".ssh/authorized_keys"); + + ASSERT_U_INT_EQ(out.num_log_verbose, 2); + ASSERT_PTR_EQ(out.log_verbose[0], NULL); + ASSERT_STRING_EQ(out.log_verbose[1], "kex.c:*"); + + ASSERT_U_INT_EQ(out.num_host_key_files, 3); + ASSERT_INT_EQ(out.host_key_file_userprovided[0], 0); + ASSERT_INT_EQ(out.host_key_file_userprovided[1], 1); + ASSERT_INT_EQ(out.host_key_file_userprovided[2], -1); + ASSERT_PTR_EQ(out.host_key_files[0], NULL); + ASSERT_STRING_EQ(out.host_key_files[1], ""); + ASSERT_STRING_EQ(out.host_key_files[2], "/tmp/ssh_host_ed25519_key"); + + ASSERT_U_INT_EQ(out.num_queued_listens, 2); + ASSERT_STRING_EQ(out.queued_listen_addrs[0].addr, "127.0.0.1"); + ASSERT_INT_EQ(out.queued_listen_addrs[0].port, 2222); + ASSERT_PTR_EQ(out.queued_listen_addrs[0].rdomain, NULL); + ASSERT_STRING_EQ(out.queued_listen_addrs[1].addr, "::1"); + ASSERT_INT_EQ(out.queued_listen_addrs[1].port, -1); + ASSERT_STRING_EQ(out.queued_listen_addrs[1].rdomain, ""); + + ASSERT_U_INT_EQ(out.num_subsystems, 2); + ASSERT_STRING_EQ(out.subsystem_name[0], "sftp"); + ASSERT_STRING_EQ(out.subsystem_command[0], "internal-sftp"); + ASSERT_STRING_EQ(out.subsystem_args[0], "internal-sftp -f AUTH"); + ASSERT_STRING_EQ(out.subsystem_name[1], "echo"); + ASSERT_STRING_EQ(out.subsystem_command[1], "/bin/echo"); + ASSERT_STRING_EQ(out.subsystem_args[1], "/bin/echo hello"); + + free_server_options(&out); + sshbuf_free(buf); +} + +static void +check_rejects_trailing_data(const ServerOptions *o) +{ + struct sshbuf *buf = NULL; + ServerOptions out; + int r; + + initialize_server_options(&out); + ASSERT_INT_EQ(serialise_server_options(o, &buf), 0); + ASSERT_PTR_NE(buf, NULL); + ASSERT_INT_EQ(sshbuf_put_u8(buf, 0), 0); + r = deserialise_server_options(buf, &out); + ASSERT_INT_EQ(r, SSH_ERR_INVALID_FORMAT); + free_server_options(&out); + sshbuf_free(buf); +} + +static void +check_rejects_bad_version(const ServerOptions *o) +{ + struct sshbuf *buf = NULL; + ServerOptions out; + int r; + + initialize_server_options(&out); + ASSERT_INT_EQ(serialise_server_options(o, &buf), 0); + ASSERT_PTR_NE(buf, NULL); + ASSERT_PTR_NE(sshbuf_mutable_ptr(buf), NULL); + POKE_U32(sshbuf_mutable_ptr(buf), 2); + r = deserialise_server_options(buf, &out); + ASSERT_INT_EQ(r, SSH_ERR_INVALID_FORMAT); + free_server_options(&out); + sshbuf_free(buf); +} + +static void +check_rejects_bad_port_count(const ServerOptions *o) +{ + struct sshbuf *buf = NULL; + ServerOptions out; + int r; + + initialize_server_options(&out); + ASSERT_INT_EQ(serialise_server_options(o, &buf), 0); + ASSERT_PTR_NE(buf, NULL); + ASSERT_SIZE_T_GT(sshbuf_len(buf), 8); + ASSERT_PTR_NE(sshbuf_mutable_ptr(buf), NULL); + POKE_U32(sshbuf_mutable_ptr(buf) + 4, MAX_PORTS + 1); + r = deserialise_server_options(buf, &out); + ASSERT_INT_EQ(r, SSH_ERR_INVALID_FORMAT); + free_server_options(&out); + sshbuf_free(buf); +} + +static void +check_roundtrip_signed_limits(const ServerOptions *o) +{ + struct sshbuf *buf = NULL; + ServerOptions out, in; + + initialize_server_options(&out); + in = *o; + in.fwd_opts.streamlocal_bind_mask = (mode_t)-1; + in.ip_qos_bulk = INT_MIN; + in.rekey_limit = INT64_MIN; + ASSERT_INT_EQ(serialise_server_options(&in, &buf), 0); + ASSERT_PTR_NE(buf, NULL); + ASSERT_INT_EQ(deserialise_server_options(buf, &out), 0); + ASSERT_INT_EQ(out.fwd_opts.streamlocal_bind_mask == (mode_t)-1, 1); + ASSERT_INT_EQ(out.ip_qos_bulk, INT_MIN); + ASSERT_LONG_LONG_EQ(out.rekey_limit, INT64_MIN); + free_server_options(&out); + sshbuf_free(buf); +} + +static void +attempt_deserialise_blob(const u_char *p, size_t len) +{ + struct sshbuf *buf; + ServerOptions out; + + initialize_server_options(&out); + ASSERT_PTR_NE(buf = sshbuf_from(p, len), NULL); + (void)deserialise_server_options(buf, &out); + free_server_options(&out); + sshbuf_free(buf); +} + +static void +check_fuzz_deserialise(const ServerOptions *o) +{ + struct sshbuf *buf = NULL; + struct fuzz *fuzz; + u_int fuzzers = FUZZ_1_BIT_FLIP | FUZZ_1_BYTE_FLIP | + FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END; + + if (test_is_fast()) + fuzzers &= ~FUZZ_1_BIT_FLIP; + if (test_is_slow()) + fuzzers |= FUZZ_2_BYTE_FLIP; + + ASSERT_INT_EQ(serialise_server_options(o, &buf), 0); + ASSERT_PTR_NE(buf, NULL); + fuzz = fuzz_begin(fuzzers, sshbuf_ptr(buf), sshbuf_len(buf)); + TEST_ONERROR(onerror, fuzz); + for (; !fuzz_done(fuzz); fuzz_next(fuzz)) + attempt_deserialise_blob(fuzz_ptr(fuzz), fuzz_len(fuzz)); + TEST_ONERROR(NULL, NULL); + fuzz_cleanup(fuzz); + sshbuf_free(buf); +} + +void +tests(void) +{ + ServerOptions o; + + log_init("test_servconf", SYSLOG_LEVEL_QUIET, SYSLOG_FACILITY_AUTH, 1); + + TEST_START("server options round trip preserves nullable state"); + set_test_options(&o); + check_roundtrip(&o); + free_server_options(&o); + TEST_DONE(); + + TEST_START("server options reject trailing data"); + set_test_options(&o); + check_rejects_trailing_data(&o); + free_server_options(&o); + TEST_DONE(); + + TEST_START("server options reject unsupported version"); + set_test_options(&o); + check_rejects_bad_version(&o); + free_server_options(&o); + TEST_DONE(); + + TEST_START("server options reject bad port count"); + set_test_options(&o); + check_rejects_bad_port_count(&o); + free_server_options(&o); + TEST_DONE(); + + TEST_START("server options round trip signed limits"); + set_test_options(&o); + check_roundtrip_signed_limits(&o); + free_server_options(&o); + TEST_DONE(); + + TEST_START("server options deserialise fuzz"); + set_test_options(&o); + check_fuzz_deserialise(&o); + free_server_options(&o); + TEST_DONE(); +} + +void +benchmarks(void) +{ + printf("no benchmarks\n"); +}