-/* $OpenBSD: auth2-hostbased.c,v 1.55 2025/08/14 09:26:53 dtucker Exp $ */
+/* $OpenBSD: auth2-hostbased.c,v 1.56 2025/12/22 01:49:03 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
}
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");
-/* $OpenBSD: auth2-pubkey.c,v 1.124 2025/08/14 09:44:39 dtucker Exp $ */
+/* $OpenBSD: auth2-pubkey.c,v 1.125 2025/12/22 01:49:03 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2010 Damien Miller. All rights reserved.
}
if (use_authorized_principals && principals_opts == NULL)
fatal_f("internal error: missing principals_opts");
- if (sshkey_cert_check_authority_now(key, 0, 1, 0,
+ if (sshkey_cert_check_authority_now(key, 0, 0,
use_authorized_principals ? NULL : pw->pw_name, &reason) != 0)
goto fail_reason;
-/* $OpenBSD: auth2-pubkeyfile.c,v 1.6 2025/08/14 10:03:44 dtucker Exp $ */
+/* $OpenBSD: auth2-pubkeyfile.c,v 1.7 2025/12/22 01:49:03 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2010 Damien Miller. All rights reserved.
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, 0,
keyopts->cert_principals == NULL ? pw->pw_name : NULL,
&reason) != 0)
goto cert_fail_reason;
-/* $OpenBSD: ssh-agent.c,v 1.315 2025/11/13 10:35:14 dtucker Exp $ */
+/* $OpenBSD: ssh-agent.c,v 1.316 2025/12/22 01:49:03 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
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);
-.\" $OpenBSD: ssh-keygen.1,v 1.236 2025/10/04 21:41:35 naddy Exp $
+.\" $OpenBSD: ssh-keygen.1,v 1.237 2025/12/22 01:49:03 djm Exp $
.\"
.\" Author: Tatu Ylonen <ylo@cs.hut.fi>
.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd $Mdocdate: October 4 2025 $
+.Dd $Mdocdate: December 22 2025 $
.Dt SSH-KEYGEN 1
.Os
.Sh NAME
authenticate server hosts to users.
To generate a user certificate:
.Pp
-.Dl $ ssh-keygen -s /path/to/ca_key -I key_id /path/to/user_key.pub
+.Dl $ ssh-keygen -s /path/to/ca_key -I id -n user \e
+.Dl \ \ \ \ \ \ /path/to/user_key.pub
.Pp
The resultant certificate will be placed in
.Pa /path/to/user_key-cert.pub .
+The argument to
+.Fl I
+is a key identifier that will be used in logs and may be used to revoke
+keys.
+The argument to
+.Fl n
+is one or more (comma-separated) principals, typically usernames, that
+the certificate represents.
A host certificate requires the
.Fl h
option:
.Pp
-.Dl $ ssh-keygen -s /path/to/ca_key -I key_id -h /path/to/host_key.pub
+.Dl $ ssh-keygen -s /path/to/ca_key -I id -h -n foo.example.org \e
+.Dl \ \ \ \ \ \ /path/to/host_key.pub
+.Pp
+For host certificates, the principals specified using the
+.Fl n
+argument are hostnames and may contain wildcard characters.
.Pp
The host certificate will be output to
.Pa /path/to/host_key-cert.pub .
to
.Fl s :
.Pp
-.Dl $ ssh-keygen -s ca_key.pub -D libpkcs11.so -I key_id user_key.pub
+.Dl $ ssh-keygen -s ca_key.pub -D libpkcs11.so -I id -n user \e
+.Dl \ \ \ \ \ \ user_key.pub
.Pp
Similarly, it is possible for the CA key to be hosted in an
.Xr ssh-agent 1 .
.Fl U
flag and, again, the CA key must be identified by its public half.
.Pp
-.Dl $ ssh-keygen -Us ca_key.pub -I key_id user_key.pub
+.Dl $ ssh-keygen -Us ca_key.pub -I id -n user user_key.pub
.Pp
In all cases,
.Ar key_id
is a "key identifier" that is logged by the server when the certificate
is used for authentication.
.Pp
-Certificates may be limited to be valid for a set of principal (user/host)
+Certificates are limited to be valid for a set of principal (user/host)
names.
-By default, generated certificates are valid for all users or hosts.
To generate a certificate for a specified set of principals:
.Pp
-.Dl $ ssh-keygen -s ca_key -I key_id -n user1,user2 user_key.pub
-.Dl "$ ssh-keygen -s ca_key -I key_id -h -n host.domain host_key.pub"
+.Dl $ ssh-keygen -s ca_key -I id -n user1,user2 user_key.pub
+.Dl $ ssh-keygen -s ca_key -I id -h -n host.domain host_key.pub
.Pp
Additional limitations on the validity and use of user certificates may
be specified through certificate options.
-/* $OpenBSD: ssh-keygen.c,v 1.487 2025/11/13 10:35:14 dtucker Exp $ */
+/* $OpenBSD: ssh-keygen.c,v 1.488 2025/12/22 01:49:03 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
if (ca_key_path != NULL) {
if (cert_key_id == NULL)
fatal("Must specify key id (-I) when certifying");
+ if (cert_principals == NULL) {
+ /*
+ * Ideally this would be a fatal(), but we need to
+ * be able to generate such certificates for testing
+ * even though they will be rejected.
+ */
+ error("Warning: certificate will contain no "
+ "principals (-n)");
+ }
for (i = 0; i < nopts; i++)
add_cert_option(opts[i]);
do_ca_sign(pw, ca_key_path, prefer_agent,
-/* $OpenBSD: sshconnect.c,v 1.376 2025/09/25 06:23:19 jsg Exp $ */
+/* $OpenBSD: sshconnect.c,v 1.377 2025/12/22 01:49:03 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
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;
-/* $OpenBSD: sshkey.c,v 1.158 2025/11/25 01:08:35 djm Exp $ */
+/* $OpenBSD: sshkey.c,v 1.159 2025/12/22 01:49:03 djm Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
* Copyright (c) 2008 Alexander von Gernler. All rights reserved.
int
sshkey_cert_check_authority(const struct sshkey *k,
- int want_host, int require_principal, int wildcard_pattern,
- uint64_t verify_time, const char *name, const char **reason)
+ int want_host, int wildcard_pattern, uint64_t verify_time,
+ const char *name, const char **reason)
{
u_int i, principal_matches;
return SSH_ERR_KEY_CERT_INVALID;
}
if (k->cert->nprincipals == 0) {
- if (require_principal) {
- *reason = "Certificate lacks principal list";
- return SSH_ERR_KEY_CERT_INVALID;
- }
- } else if (name != NULL) {
- principal_matches = 0;
- for (i = 0; i < k->cert->nprincipals; i++) {
- if (wildcard_pattern) {
- if (match_pattern(k->cert->principals[i],
- name)) {
- principal_matches = 1;
- break;
- }
- } else if (strcmp(name, k->cert->principals[i]) == 0) {
+ *reason = "Certificate lacks principal list";
+ return SSH_ERR_KEY_CERT_INVALID;
+ }
+ if (name == NULL)
+ return 0; /* principal matching not requested */
+
+ principal_matches = 0;
+ for (i = 0; i < k->cert->nprincipals; i++) {
+ if (wildcard_pattern) {
+ if (match_pattern(name, k->cert->principals[i])) {
principal_matches = 1;
break;
}
+ } else if (strcmp(name, k->cert->principals[i]) == 0) {
+ principal_matches = 1;
+ break;
}
- if (!principal_matches) {
- *reason = "Certificate invalid: name is not a listed "
- "principal";
- return SSH_ERR_KEY_CERT_INVALID;
- }
+ }
+ if (!principal_matches) {
+ *reason = "Certificate invalid: name is not a listed "
+ "principal";
+ return SSH_ERR_KEY_CERT_INVALID;
}
return 0;
}
int
sshkey_cert_check_authority_now(const struct sshkey *k,
- int want_host, int require_principal, int wildcard_pattern,
- const char *name, const char **reason)
+ int want_host, int wildcard_pattern, const char *name,
+ const char **reason)
{
time_t now;
*reason = "Certificate invalid: not yet valid";
return SSH_ERR_KEY_CERT_INVALID;
}
- return sshkey_cert_check_authority(k, want_host, require_principal,
- wildcard_pattern, (uint64_t)now, name, reason);
+ return sshkey_cert_check_authority(k, want_host, wildcard_pattern,
+ (uint64_t)now, name, reason);
}
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,
- host, reason)) != 0)
+ if ((r = sshkey_cert_check_authority_now(key, 1, 1, host, reason)) != 0)
return r;
if (sshbuf_len(key->cert->critical) != 0) {
*reason = "Certificate contains unsupported critical options";
-/* $OpenBSD: sshkey.h,v 1.70 2025/08/29 03:50:38 djm Exp $ */
+/* $OpenBSD: sshkey.h,v 1.71 2025/12/22 01:49:03 djm Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
int sshkey_to_certified(struct sshkey *);
int sshkey_drop_cert(struct sshkey *);
int sshkey_cert_copy(const struct sshkey *, struct sshkey *);
-int sshkey_cert_check_authority(const struct sshkey *, int, int, int,
+int sshkey_cert_check_authority(const struct sshkey *, int, int,
uint64_t, const char *, const char **);
-int sshkey_cert_check_authority_now(const struct sshkey *, int, int, int,
+int sshkey_cert_check_authority_now(const struct sshkey *, 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 *);
-/* $OpenBSD: sshsig.c,v 1.40 2025/09/25 06:23:19 jsg Exp $ */
+/* $OpenBSD: sshsig.c,v 1.41 2025/12/22 01:49:03 djm Exp $ */
/*
* Copyright (c) 2019 Google LLC
*
while ((cp = strsep(&principals, ",")) != NULL && *cp != '\0') {
/* Check certificate validity */
- if ((r = sshkey_cert_check_authority(cert, 0, 1, 0,
- verify_time, NULL, &reason)) != 0) {
+ if ((r = sshkey_cert_check_authority(cert, 0, 0, verify_time,
+ NULL, &reason)) != 0) {
debug("%s:%lu: principal \"%s\" not authorized: %s",
path, linenum, cp, reason);
continue;
sshkey_equal_public(sign_key->cert->signature_key, found_key)) {
if (principal) {
/* Match certificate CA key with specified principal */
- if ((r = sshkey_cert_check_authority(sign_key, 0, 1, 0,
+ if ((r = sshkey_cert_check_authority(sign_key, 0, 0,
verify_time, principal, &reason)) != 0) {
error("%s:%lu: certificate not authorized: %s",
path, linenum, reason);