anyone using certificates with an empty principals section?
Ashutosh Ghosh
ashutosh at g4es.org
Sat Aug 8 17:52:01 AEST 2026
Hi,
I'm very late to this party, sorry! I have been using host certificates with empty principals and today I got the first user with an OpenSSH 10.3 client report an issue (I think macOS 26.6 released a few days ago is bundling OpenSSH 10.3p1).
I think the way this patch is implemented makes it impossible to have a backwards compatible host certificate. If I set the principals to "*", then a pre-10.3 client will not accept it; if I don't set it to "*" for a wildcard certificate then a post-10.3 client will not accept it.
For my use case, the hostname and IP address are not stable so I don't have a way to express the set of valid hostnames in advance.
Am I missing something? Is there a way to issue a certificate that is effectively a wildcard for both old and new clients?
A last resort that I don't want to do is issue every host 2 certificates - one with an empty principals and one with "*" principals.
Cheers,
Ashutosh
On 15/12/2025, 16:24, "Damien Miller" <djm mindrot ! org> wrote:
On Wed, 26 Nov 2025, Damien Miller wrote:
> Hi,
>
> Is anyone on here using certificates with an empty principals section?
> If so then I'd like to hear from you.
>
> These were originally documented as being wildcard, i.e. matching any
> principal, though this was supported only inconsistently and AFAIK
> only for host certificates and not user certificates.
>
> IMO this behaviour is a footgun and I'd like to remove it.
> Specifically this would mean making certificates without a principals
> section never usable for anything.
>
> To make it possible to do wildcard host certificates, I'd like to
> add the ability to do explicit wildcards using '*' characters in
> principals, e.g. "*.example.com".
>
> This would be a breaking change if you're depending on the existing
> behaviour.
Here's a diff to implement this:
commit 02bd6acbbf146b89e1d5f0a1ffb427d56df99024
Author: Damien Miller <djm at mindrot.org>
Date: Thu Nov 20 10:52:29 2025 +1100
ban empty principals, allow wildcard hostnames
diff --git a/auth2-hostbased.c b/auth2-hostbased.c
index d264913..a26bfaa 100644
--- a/auth2-hostbased.c
+++ b/auth2-hostbased.c
@@ -210,8 +210,8 @@ hostbased_key_allowed(struct ssh *ssh, struct passwd *pw,
}
debug2_f("access allowed by auth_rhosts2");
- if (sshkey_is_cert(key) &&
- sshkey_cert_check_authority_now(key, 1, 0, 0, lookup, &reason)) {
+ if (sshkey_is_cert(key) && sshkey_cert_check_host(key, lookup,
+ options.ca_sign_algorithms, &reason) != 0) {
if ((fp = sshkey_fingerprint(key->cert->signature_key,
options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
fatal_f("sshkey_fingerprint fail");
diff --git a/auth2-pubkeyfile.c b/auth2-pubkeyfile.c
index b580b89..4a109e6 100644
--- a/auth2-pubkeyfile.c
+++ b/auth2-pubkeyfile.c
@@ -363,7 +363,7 @@ auth_check_authkey_line(struct passwd *pw, struct sshkey *key,
reason = "Certificate does not contain an authorized principal";
goto cert_fail_reason;
}
- if (sshkey_cert_check_authority_now(key, 0, 0, 0,
+ if (sshkey_cert_check_authority_now(key, 0, 1, 0,
keyopts->cert_principals == NULL ? pw->pw_name : NULL,
&reason) != 0)
goto cert_fail_reason;
diff --git a/ssh-agent.c b/ssh-agent.c
index ebb573b..500acd1 100644
--- a/ssh-agent.c
+++ b/ssh-agent.c
@@ -389,7 +389,7 @@ match_key_hop(const char *tag, const struct sshkey *key,
return -1; /* shouldn't happen */
if (!sshkey_equal(key->cert->signature_key, dch->keys[i]))
continue;
- if (sshkey_cert_check_host(key, hostname, 1,
+ if (sshkey_cert_check_host(key, hostname,
SSH_ALLOWED_CA_SIGALGS, &reason) != 0) {
debug_f("cert %s / hostname %s rejected: %s",
key->cert->key_id, hostname, reason);
diff --git a/sshconnect.c b/sshconnect.c
index bd1359a..3d133c7 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -1054,7 +1054,7 @@ check_host_key(char *hostname, const struct ssh_conn_info *cinfo,
if (want_cert) {
if (sshkey_cert_check_host(host_key,
options.host_key_alias == NULL ?
- hostname : options.host_key_alias, 0,
+ hostname : options.host_key_alias,
options.ca_sign_algorithms, &fail_reason) != 0) {
error("%s", fail_reason);
goto fail;
diff --git a/sshkey.c b/sshkey.c
index 08425ad..91ce2f4 100644
--- a/sshkey.c
+++ b/sshkey.c
@@ -2418,12 +2418,11 @@ sshkey_cert_check_authority_now(const struct sshkey *k,
int
sshkey_cert_check_host(const struct sshkey *key, const char *host,
- int wildcard_principals, const char *ca_sign_algorithms,
- const char **reason)
+ const char *ca_sign_algorithms, const char **reason)
{
int r;
- if ((r = sshkey_cert_check_authority_now(key, 1, 0, wildcard_principals,
+ if ((r = sshkey_cert_check_authority_now(key, 1, 1, 1,
host, reason)) != 0)
return r;
if (sshbuf_len(key->cert->critical) != 0) {
diff --git a/sshkey.h b/sshkey.h
index a83ddfe..3a0a32b 100644
--- a/sshkey.h
+++ b/sshkey.h
@@ -217,7 +217,7 @@ int sshkey_cert_check_authority(const struct sshkey *, int, int, int,
int sshkey_cert_check_authority_now(const struct sshkey *, int, int, int,
const char *, const char **);
int sshkey_cert_check_host(const struct sshkey *, const char *,
- int , const char *, const char **);
+ const char *, const char **);
size_t sshkey_format_cert_validity(const struct sshkey_cert *,
char *, size_t) __attribute__((__bounded__(__string__, 2, 3)));
int sshkey_check_cert_sigtype(const struct sshkey *, const char *);
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev at mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
More information about the openssh-unix-dev
mailing list