diff --git a/.github/workflows/windows-cert-store-test.yml b/.github/workflows/windows-cert-store-test.yml index c5ca83e2..d9e9aa91 100644 --- a/.github/workflows/windows-cert-store-test.yml +++ b/.github/workflows/windows-cert-store-test.yml @@ -480,7 +480,7 @@ jobs: Set-Acl $keyFile $acl Write-Host "Granted SYSTEM FullControl on private key: $keyFile" - # Export the CN (without "CN=") for HostKeyStoreSubject + # Export the CN (without "CN=") for wolfSSH_HostKeyStoreSubject $subject = $serverCert.Subject if ($subject -match "^CN=(.+)$") { $subject = $matches[1] } Add-Content -Path $env:GITHUB_ENV -Value "SERVER_CERT_SUBJECT=$subject" @@ -654,12 +654,12 @@ jobs: if ("${{ matrix.server_key_source }}" -eq "store") { # The certificate is part of the store entry. HostKey and - # HostCertificate alongside HostKeyStore are rejected at startup. + # HostCertificate alongside wolfSSH_HostKeyStore are rejected at startup. $configContent += @" - HostKeyStore My - HostKeyStoreSubject $env:SERVER_CERT_SUBJECT - HostKeyStoreFlags LOCAL_MACHINE + wolfSSH_HostKeyStore My + wolfSSH_HostKeyStoreSubject $env:SERVER_CERT_SUBJECT + wolfSSH_HostKeyStoreFlags LOCAL_MACHINE "@ } else { $keyPath = (Resolve-Path "keys\server-key.pem").Path @@ -1171,34 +1171,34 @@ jobs: "wolfSSH_WinUserPvPara wolfSSHLeafOnlyCA", "wolfSSH_WinUserDwFlags LOCAL_MACHINE") ` "No usable CA certificates found in store" - Test-SshdRejects "HostKeyStore without HostKeyStoreSubject" ` - ($base[0..0] + @("TrustedUserCAKeys $caPem", "HostKeyStore My", - "HostKeyStoreFlags LOCAL_MACHINE")) ` - "HostKeyStoreSubject is missing" - Test-SshdRejects "HostKeyStore without HostKeyStoreFlags" ` - ($base[0..0] + @("TrustedUserCAKeys $caPem", "HostKeyStore My", - "HostKeyStoreSubject wolfSSH-Test-Server")) ` - "HostKeyStoreFlags is missing" - Test-SshdRejects "HostKeyStoreSubject/Flags without HostKeyStore" ` + Test-SshdRejects "wolfSSH_HostKeyStore without wolfSSH_HostKeyStoreSubject" ` + ($base[0..0] + @("TrustedUserCAKeys $caPem", "wolfSSH_HostKeyStore My", + "wolfSSH_HostKeyStoreFlags LOCAL_MACHINE")) ` + "wolfSSH_HostKeyStoreSubject is missing" + Test-SshdRejects "wolfSSH_HostKeyStore without wolfSSH_HostKeyStoreFlags" ` + ($base[0..0] + @("TrustedUserCAKeys $caPem", "wolfSSH_HostKeyStore My", + "wolfSSH_HostKeyStoreSubject wolfSSH-Test-Server")) ` + "wolfSSH_HostKeyStoreFlags is missing" + Test-SshdRejects "wolfSSH_HostKeyStoreSubject/Flags without wolfSSH_HostKeyStore" ` ($base[0..0] + @("TrustedUserCAKeys $caPem", - "HostKeyStoreSubject wolfSSH-Test-Server", - "HostKeyStoreFlags LOCAL_MACHINE")) ` - "HostKeyStore is missing" - Test-SshdRejects "HostKeyStore conflicts with HostKey" ` - ($base + @("HostKeyStore My", - "HostKeyStoreSubject wolfSSH-Test-Server", - "HostKeyStoreFlags LOCAL_MACHINE")) ` + "wolfSSH_HostKeyStoreSubject wolfSSH-Test-Server", + "wolfSSH_HostKeyStoreFlags LOCAL_MACHINE")) ` + "wolfSSH_HostKeyStore is missing" + Test-SshdRejects "wolfSSH_HostKeyStore conflicts with HostKey" ` + ($base + @("wolfSSH_HostKeyStore My", + "wolfSSH_HostKeyStoreSubject wolfSSH-Test-Server", + "wolfSSH_HostKeyStoreFlags LOCAL_MACHINE")) ` "HostKey conflicts" - Test-SshdRejects "HostKeyStore conflicts with HostCertificate" ` + Test-SshdRejects "wolfSSH_HostKeyStore conflicts with HostCertificate" ` ($base[0..0] + @("TrustedUserCAKeys $caPem", - "HostCertificate $hostCert", "HostKeyStore My", - "HostKeyStoreSubject wolfSSH-Test-Server", - "HostKeyStoreFlags LOCAL_MACHINE")) ` + "HostCertificate $hostCert", "wolfSSH_HostKeyStore My", + "wolfSSH_HostKeyStoreSubject wolfSSH-Test-Server", + "wolfSSH_HostKeyStoreFlags LOCAL_MACHINE")) ` "HostCertificate conflicts" - Test-SshdRejects "-h conflicts with HostKeyStore" ` - ($base[0..0] + @("TrustedUserCAKeys $caPem", "HostKeyStore My", - "HostKeyStoreSubject wolfSSH-Test-Server", - "HostKeyStoreFlags LOCAL_MACHINE")) ` + Test-SshdRejects "-h conflicts with wolfSSH_HostKeyStore" ` + ($base[0..0] + @("TrustedUserCAKeys $caPem", "wolfSSH_HostKeyStore My", + "wolfSSH_HostKeyStoreSubject wolfSSH-Test-Server", + "wolfSSH_HostKeyStoreFlags LOCAL_MACHINE")) ` "-h host key file conflicts" @("-h", $hostKey) # This build has no WOLFSSL_SYS_CA_CERTS, so the system CA directive # must fail closed rather than run without the configured anchors. diff --git a/README.md b/README.md index 639ba4ac..0003d235 100644 --- a/README.md +++ b/README.md @@ -569,8 +569,8 @@ search so file arguments resolve against the current directory. wolfSSHd gains these configuration directives, all global only (they are rejected inside a `Match` block): -* `HostKeyStore `, `HostKeyStoreSubject `, and - `HostKeyStoreFlags ` select the host key from a certificate +* `wolfSSH_HostKeyStore `, `wolfSSH_HostKeyStoreSubject `, and + `wolfSSH_HostKeyStoreFlags ` select the host key from a certificate store. All three must be set together, and they conflict with `HostKey`, `HostCertificate`, and the `-h` command line option. * `wolfSSH_TrustedUserCAStore yes|no` loads the client-certificate trust diff --git a/apps/wolfsshd/configuration.c b/apps/wolfsshd/configuration.c index 8cb737ff..771bd1a2 100644 --- a/apps/wolfsshd/configuration.c +++ b/apps/wolfsshd/configuration.c @@ -534,16 +534,6 @@ static const CONFIG_OPTION options[] = { {OPT_ACCEPT_ENV, "AcceptEnv"}, {OPT_PROTOCOL, "Protocol"}, {OPT_LOGIN_GRACE_TIME, "LoginGraceTime"}, - /* The parser requires a whitespace delimiter after the option name, which - * is the primary defence against a shorter name prefix-matching a longer - * one. As belt-and-braces, longer option names that share a common prefix - * MUST still appear before the shorter one: - * HostKeyStoreSubject/HostKeyStoreFlags before HostKeyStore, and all - * HostKeyStore* before HostKey. Kept unconditional so "HostKeyStore" - * never matches "HostKey" on non-store builds. */ - {OPT_HOST_KEY_STORE_SUBJECT, "HostKeyStoreSubject"}, - {OPT_HOST_KEY_STORE_FLAGS, "HostKeyStoreFlags"}, - {OPT_HOST_KEY_STORE, "HostKeyStore"}, {OPT_HOST_KEY, "HostKey"}, {OPT_PASSWORD_AUTH, "PasswordAuthentication"}, {OPT_PUBKEY_AUTH, "PubkeyAuthentication"}, @@ -564,6 +554,9 @@ static const CONFIG_OPTION options[] = { {OPT_WIN_USER_STORES, "wolfSSH_WinUserStores"}, {OPT_WIN_USER_DW_FLAGS, "wolfSSH_WinUserDwFlags"}, {OPT_WIN_USER_PV_PARA, "wolfSSH_WinUserPvPara"}, + {OPT_HOST_KEY_STORE_SUBJECT, "wolfSSH_HostKeyStoreSubject"}, + {OPT_HOST_KEY_STORE_FLAGS, "wolfSSH_HostKeyStoreFlags"}, + {OPT_HOST_KEY_STORE, "wolfSSH_HostKeyStore"}, {OPT_AUTHORIZED_UPN_DOMAINS, "AuthorizedUPNDomains"}, }; #define NUM_OPTIONS ((int)(sizeof(options) / sizeof(*options))) @@ -1570,13 +1563,13 @@ static int HandleConfigOption(WOLFSSHD_CONFIG** conf, int opt, break; #ifdef WOLFSSHD_WIN_STORE_CONFIG case OPT_HOST_KEY_STORE: - ret = CheckNotInMatch(*conf, "HostKeyStore"); + ret = CheckNotInMatch(*conf, "wolfSSH_HostKeyStore"); if (ret == WS_SUCCESS) ret = SetFileString(&(*conf)->hostKeyStore, value, (*conf)->heap); break; case OPT_HOST_KEY_STORE_SUBJECT: - ret = CheckNotInMatch(*conf, "HostKeyStoreSubject"); + ret = CheckNotInMatch(*conf, "wolfSSH_HostKeyStoreSubject"); /* use the full line remainder so a CN containing spaces is * kept instead of being cut at the first token */ if (ret == WS_SUCCESS) @@ -1584,7 +1577,7 @@ static int HandleConfigOption(WOLFSSHD_CONFIG** conf, int opt, fullSz, (*conf)->heap); break; case OPT_HOST_KEY_STORE_FLAGS: - ret = CheckNotInMatch(*conf, "HostKeyStoreFlags"); + ret = CheckNotInMatch(*conf, "wolfSSH_HostKeyStoreFlags"); if (ret == WS_SUCCESS) ret = SetFileString(&(*conf)->hostKeyStoreFlags, value, (*conf)->heap); @@ -1595,13 +1588,13 @@ static int HandleConfigOption(WOLFSSHD_CONFIG** conf, int opt, case OPT_HOST_KEY_STORE_FLAGS: #ifdef WOLFSSH_IGNORE_UNKNOWN_CONFIG wolfSSH_Log(WS_LOG_WARN, - "[SSHD] Ignoring HostKeyStore* option: requires a " + "[SSHD] Ignoring wolfSSH_HostKeyStore* option: requires a " "WOLFSSH_WINDOWS_CERT_STORE build"); ignored = 1; ret = WS_SUCCESS; #else wolfSSH_Log(WS_LOG_ERROR, - "[SSHD] HostKeyStore* options require a " + "[SSHD] wolfSSH_HostKeyStore* options require a " "WOLFSSH_WINDOWS_CERT_STORE build"); ret = WS_NOT_COMPILED; #endif diff --git a/apps/wolfsshd/test/test_configuration.c b/apps/wolfsshd/test/test_configuration.c index 1af49b45..28af9170 100644 --- a/apps/wolfsshd/test/test_configuration.c +++ b/apps/wolfsshd/test/test_configuration.c @@ -300,7 +300,8 @@ static int test_ParseConfigLine(void) #ifndef WOLFSSH_IGNORE_UNKNOWN_CONFIG {"Unknown extension of Port", "PortFoo 22", 1}, {"Unknown extension of HostKey", "HostKeyFoo /tmp/x", 1}, - {"Unknown extension of HostKeyStore", "HostKeyStoreX MY", 1}, + {"Unknown extension of wolfSSH_HostKeyStore", + "wolfSSH_HostKeyStoreX MY", 1}, {"Unknown extension of TrustedUserCAStore", "wolfSSH_TrustedUserCAStoreX yes", 1}, #endif @@ -454,9 +455,10 @@ static int test_ConfigCopy(void) if (ret == WS_SUCCESS) ret = PCL("wolfSSH_TrustedUserCAStore yes"); #ifdef WOLFSSHD_WIN_STORE_CONFIG - if (ret == WS_SUCCESS) ret = PCL("HostKeyStore MY"); - if (ret == WS_SUCCESS) ret = PCL("HostKeyStoreSubject wolfSSH Host"); - if (ret == WS_SUCCESS) ret = PCL("HostKeyStoreFlags 0x1000"); + if (ret == WS_SUCCESS) ret = PCL("wolfSSH_HostKeyStore MY"); + if (ret == WS_SUCCESS) + ret = PCL("wolfSSH_HostKeyStoreSubject wolfSSH Host"); + if (ret == WS_SUCCESS) ret = PCL("wolfSSH_HostKeyStoreFlags 0x1000"); if (ret == WS_SUCCESS) ret = PCL("wolfSSH_WinUserStores MY,Root"); if (ret == WS_SUCCESS) ret = PCL("wolfSSH_WinUserDwFlags 0x1"); if (ret == WS_SUCCESS) ret = PCL("wolfSSH_WinUserPvPara subjectName"); @@ -5142,8 +5144,7 @@ static int test_PermitRootLoginModes(void) } /* The config parser matches option names with WSTRNCMP over the options table - * in order, so no entry may be a strict prefix of a later one (e.g. "HostKey" - * must come after the "HostKeyStore*" names). */ + * in order, so no entry may be a strict prefix of a later one. */ static int test_ConfigOptionPrefixOrder(void) { int ret = WS_SUCCESS; @@ -5193,9 +5194,9 @@ static int test_ConfigGlobalOnlyOptionsInMatch(void) { "HostCertificate /etc/ssh/host_cert.pem", WS_BAD_ARGUMENT }, #endif #ifdef WOLFSSHD_WIN_STORE_CONFIG - { "HostKeyStore MY", WS_BAD_ARGUMENT }, - { "HostKeyStoreSubject wolfSSH Host", WS_BAD_ARGUMENT }, - { "HostKeyStoreFlags 0x1000", WS_BAD_ARGUMENT }, + { "wolfSSH_HostKeyStore MY", WS_BAD_ARGUMENT }, + { "wolfSSH_HostKeyStoreSubject wolfSSH Host", WS_BAD_ARGUMENT }, + { "wolfSSH_HostKeyStoreFlags 0x1000", WS_BAD_ARGUMENT }, { "wolfSSH_WinUserStores CERT_STORE_PROV_SYSTEM", WS_BAD_ARGUMENT }, { "wolfSSH_WinUserDwFlags LOCAL_MACHINE", WS_BAD_ARGUMENT }, { "wolfSSH_WinUserPvPara SSH_UserCA", WS_BAD_ARGUMENT }, diff --git a/apps/wolfsshd/wolfsshd.c b/apps/wolfsshd/wolfsshd.c index 9c8268a0..c0881855 100644 --- a/apps/wolfsshd/wolfsshd.c +++ b/apps/wolfsshd/wolfsshd.c @@ -902,7 +902,8 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx, if (hostKeyStore != NULL && hostKeyStoreSubject == NULL) { wolfSSH_Log(WS_LOG_ERROR, - "[SSHD] HostKeyStore set but HostKeyStoreSubject is missing"); + "[SSHD] wolfSSH_HostKeyStore set but " + "wolfSSH_HostKeyStoreSubject is missing"); ret = WS_BAD_ARGUMENT; } /* The location is mandatory for the same reason wolfSSH_WinUserDwFlags @@ -911,7 +912,8 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx, * anything running as that account install a host key. */ else if (hostKeyStore != NULL && hostKeyStoreFlags == NULL) { wolfSSH_Log(WS_LOG_ERROR, - "[SSHD] HostKeyStore set but HostKeyStoreFlags is missing."); + "[SSHD] wolfSSH_HostKeyStore set but " + "wolfSSH_HostKeyStoreFlags is missing."); wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Set the store location explicitly, normally " "LOCAL_MACHINE."); @@ -920,8 +922,8 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx, else if (hostKeyStore == NULL && (hostKeyStoreSubject != NULL || hostKeyStoreFlags != NULL)) { wolfSSH_Log(WS_LOG_ERROR, - "[SSHD] HostKeyStoreSubject/HostKeyStoreFlags set but " - "HostKeyStore is missing"); + "[SSHD] wolfSSH_HostKeyStoreSubject/wolfSSH_HostKeyStoreFlags " + "set but wolfSSH_HostKeyStore is missing"); ret = WS_BAD_ARGUMENT; } /* The store branch below wins over the file path, so a HostKey line @@ -930,8 +932,8 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx, else if (hostKeyStore != NULL && wolfSSHD_ConfigGetHostKeyFile(conf) != NULL) { wolfSSH_Log(WS_LOG_ERROR, - "[SSHD] HostKey conflicts with the configured HostKeyStore. " - "Use one or the other."); + "[SSHD] HostKey conflicts with the configured " + "wolfSSH_HostKeyStore. Use one or the other."); ret = WS_BAD_ARGUMENT; } /* A store host key carries its own certificate. A HostCertificate @@ -941,7 +943,7 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx, wolfSSHD_ConfigGetHostCertFile(conf) != NULL) { wolfSSH_Log(WS_LOG_ERROR, "[SSHD] HostCertificate conflicts with the configured " - "HostKeyStore, which supplies its own certificate."); + "wolfSSH_HostKeyStore, which supplies its own certificate."); wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Use one or the other."); ret = WS_BAD_ARGUMENT; } @@ -964,8 +966,8 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx, } if (ret == WS_SUCCESS && !IsElevationProtectedHive(dwFlags)) { wolfSSH_Log(WS_LOG_WARN, - "[SSHD] HostKeyStoreFlags selects a store hive that its " - "own account can write to without elevation."); + "[SSHD] wolfSSH_HostKeyStoreFlags selects a store hive " + "that its own account can write to without elevation."); wolfSSH_Log(WS_LOG_WARN, "[SSHD] LOCAL_MACHINE is the safer location for the host " "key."); @@ -1269,8 +1271,8 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx, /* Load user CA certs (trust anchors used to verify client X.509 certs) * directly from a Windows certificate store into the cert manager. */ #if defined(WOLFSSH_CERTS) && defined(WOLFSSH_WINDOWS_CERT_STORE) - /* Mirror the HostKeyStore* validation: the wolfSSH_WinUser* group only - * has an effect through LoadUserCACertsFromStore(), so accepting it + /* Mirror the wolfSSH_HostKeyStore* validation: the wolfSSH_WinUser* group + * only has an effect through LoadUserCACertsFromStore(), so accepting it * without the store enabled would start the daemon with no client CA * trust anchors and no indication why logins fail. */ if (ret == WS_SUCCESS && !wolfSSHD_ConfigGetUserCAStore(conf) && @@ -1490,7 +1492,7 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx, * also needs certificate support. Fail startup rather than silently * ignore a configured realm policy, unless the build opts into * WOLFSSH_IGNORE_UNKNOWN_CONFIG, which downgrades not-compiled-in - * directives to a warning the same way the HostKeyStore* and + * directives to a warning the same way the wolfSSH_HostKeyStore* and * wolfSSH_WinUser* handlers do. Check every config node since the * directive may sit in a Match block. */ #if !defined(WOLFSSL_FPKI) || !defined(WOLFSSH_CERTS) @@ -4226,7 +4228,7 @@ static int StartSSHD(int argc, char** argv) if (wolfSSHD_ConfigGetHostKeyStore(conf) != NULL) { wolfSSH_Log(WS_LOG_ERROR, "[SSHD] -h host key file conflicts with the configured " - "HostKeyStore. Use one or the other."); + "wolfSSH_HostKeyStore. Use one or the other."); ret = WS_BAD_ARGUMENT; } #endif diff --git a/src/certman.c b/src/certman.c index fddd4ee7..51a8f45a 100644 --- a/src/certman.c +++ b/src/certman.c @@ -757,7 +757,7 @@ int wolfSSH_CertStoreLocationValid(word32 dwFlags) /* The one name-to-value table for CERT_SYSTEM_STORE_* locations, shared by - * wolfSSH_ParseCertStoreSpec() and wolfsshd's HostKeyStoreFlags and + * wolfSSH_ParseCertStoreSpec() and wolfsshd's wolfSSH_HostKeyStoreFlags and * wolfSSH_WinUserDwFlags parsing so the accepted spellings cannot drift. */ static const struct { const char* shortName; @@ -905,10 +905,10 @@ int wolfSSH_ParseCertStoreSpec(const char* spec, WFREE(specCopy, heap, DYNTYPE_TEMP); return WS_BAD_ARGUMENT; } - /* Accept the same spellings as wolfsshd's HostKeyStoreFlags and - * wolfSSH_WinUserDwFlags so one name works everywhere; the - * shared parser also handles the numeric location forms and - * rejects control flags. */ + /* Accept the same spellings as wolfsshd's + * wolfSSH_HostKeyStoreFlags and wolfSSH_WinUserDwFlags so one + * name works everywhere; the shared parser also handles the + * numeric location forms and rejects control flags. */ if (wolfSSH_CertStoreLocationFromName(flagsStr, &flags) != WS_SUCCESS) { WLOG(WS_LOG_CERTMAN, "Malformed cert store flags value "