[openssh-commits] [openssh] 03/03: upstream: When adding certificates to an agent, set the expiry to

git+noreply at mindrot.org git+noreply at mindrot.org
Thu Sep 11 12:58:28 AEST 2025


This is an automated email from the git hooks/post-receive script.

djm pushed a commit to branch master
in repository openssh.

commit 0c719c6aabc061f02a907fc96c390d0449b49f26
Author: djm at openbsd.org <djm at openbsd.org>
AuthorDate: Thu Sep 11 02:54:42 2025 +0000

    upstream: When adding certificates to an agent, set the expiry to
    
    the certificate expiry time plus a short (5 min) grace period.
    
    This will cause the agent to automtically remove certificates shortly
    after they expire.
    
    A new ssh-add -N option disables this behaviour.
    
    Feedback/ok deraadt@
    
    OpenBSD-Commit-ID: 92fed1bba1025069ad45deebb534be7530e181df
---
 ssh-add.1 | 13 +++++++++---
 ssh-add.c | 71 ++++++++++++++++++++++++++++++++++++++++++++-------------------
 2 files changed, 60 insertions(+), 24 deletions(-)

diff --git a/ssh-add.1 b/ssh-add.1
index c31de4dd9..babe78040 100644
--- a/ssh-add.1
+++ b/ssh-add.1
@@ -1,4 +1,4 @@
-.\"	$OpenBSD: ssh-add.1,v 1.87 2024/06/17 08:30:29 djm Exp $
+.\"	$OpenBSD: ssh-add.1,v 1.88 2025/09/11 02:54:42 djm Exp $
 .\"
 .\" Author: Tatu Ylonen <ylo at cs.hut.fi>
 .\" Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
@@ -35,7 +35,7 @@
 .\" (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: June 17 2024 $
+.Dd $Mdocdate: September 11 2025 $
 .Dt SSH-ADD 1
 .Os
 .Sh NAME
@@ -43,7 +43,7 @@
 .Nd adds private key identities to the OpenSSH authentication agent
 .Sh SYNOPSIS
 .Nm ssh-add
-.Op Fl CcDdKkLlqvXx
+.Op Fl CcDdKkLlNqvXx
 .Op Fl E Ar fingerprint_hash
 .Op Fl H Ar hostkey_file
 .Op Fl h Ar destination_constraint
@@ -223,6 +223,13 @@ Lists public key parameters of all identities currently represented
 by the agent.
 .It Fl l
 Lists fingerprints of all identities currently represented by the agent.
+.It Fl N
+When adding certificates, by default
+.Nm
+will request that the agent automatically delete the certificate shortly
+after the certificate's expiry date.
+This flag suppresses this behaviour and does not specify a lifetime for
+certificates added to an agent.
 .It Fl q
 Be quiet after a successful operation.
 .It Fl S Ar provider
diff --git a/ssh-add.c b/ssh-add.c
index 2e41bc26a..115400680 100644
--- a/ssh-add.c
+++ b/ssh-add.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-add.c,v 1.175 2025/08/29 03:50:38 djm Exp $ */
+/* $OpenBSD: ssh-add.c,v 1.176 2025/09/11 02:54:42 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo at cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo at cs.hut.fi>, Espoo, Finland
@@ -70,6 +70,8 @@
 #include "sk-api.h"
 #include "hostfile.h"
 
+#define CERT_EXPIRY_GRACE	(5*60)
+
 /* argv0 */
 extern char *__progname;
 
@@ -234,16 +236,36 @@ delete_all(int agent_fd, int qflag)
 	return ret;
 }
 
+static int
+check_cert_lifetime(const struct sshkey *cert, int cert_lifetime)
+{
+	time_t now;
+	uint64_t n;
+
+	if (cert == NULL || cert->cert == NULL || !sshkey_is_cert(cert) ||
+	    cert->cert->valid_before == 0xFFFFFFFFFFFFFFFFULL)
+		return cert_lifetime;
+	if ((now = time(NULL)) <= 0)
+		fatal_f("system time is at/before epoch");
+	if ((uint64_t)now > (cert->cert->valid_before + CERT_EXPIRY_GRACE))
+		return -1; /* certificate already expired */
+	n = (CERT_EXPIRY_GRACE + cert->cert->valid_before) - (uint64_t)now;
+	n = MINIMUM(n, INT_MAX);
+	if (cert_lifetime <= 0)
+		return (int)n;
+	return MINIMUM(cert_lifetime, (int)n);
+}
+
 static int
 add_file(int agent_fd, const char *filename, int key_only, int cert_only,
-    int qflag, const char *skprovider,
+    int qflag, int Nflag, const char *skprovider,
     struct dest_constraint **dest_constraints,
     size_t ndest_constraints)
 {
-	struct sshkey *private, *cert;
+	struct sshkey *private = NULL, *cert = NULL;
 	char *comment = NULL;
 	char msg[1024], *certpath = NULL;
-	int r, fd, ret = -1;
+	int cert_lifetime, r, fd, ret = -1;
 	struct sshbuf *keyblob;
 
 	if (strcmp(filename, "-") == 0) {
@@ -340,8 +362,8 @@ add_file(int agent_fd, const char *filename, int key_only, int cert_only,
 			fprintf(stderr, "Identity added: %s (%s)\n",
 			    filename, comment);
 			if (lifetime != 0) {
-				fprintf(stderr,
-				    "Lifetime set to %d seconds\n", lifetime);
+				fprintf(stderr, "Lifetime set to %s\n",
+				    fmt_timeframe((time_t)lifetime));
 			}
 			if (confirm != 0) {
 				fprintf(stderr, "The user must confirm "
@@ -369,25 +391,28 @@ add_file(int agent_fd, const char *filename, int key_only, int cert_only,
 	if (!sshkey_equal_public(cert, private)) {
 		error("Certificate %s does not match private key %s",
 		    certpath, filename);
-		sshkey_free(cert);
+		goto out;
+	}
+
+	cert_lifetime = lifetime;
+	if (!Nflag &&
+	    (cert_lifetime = check_cert_lifetime(cert, cert_lifetime)) == -1) {
+		logit("Certificate %s has already expired; ignored", certpath);
 		goto out;
 	}
 
 	/* Graft with private bits */
 	if ((r = sshkey_to_certified(private)) != 0) {
 		error_fr(r, "sshkey_to_certified");
-		sshkey_free(cert);
 		goto out;
 	}
 	if ((r = sshkey_cert_copy(cert, private)) != 0) {
 		error_fr(r, "sshkey_cert_copy");
-		sshkey_free(cert);
 		goto out;
 	}
-	sshkey_free(cert);
-
+	/* send to agent */
 	if ((r = ssh_add_identity_constrained(agent_fd, private, comment,
-	    lifetime, confirm, skprovider,
+	    cert_lifetime, confirm, skprovider,
 	    dest_constraints, ndest_constraints)) != 0) {
 		error_r(r, "Certificate %s (%s) add failed", certpath,
 		    private->cert->key_id);
@@ -397,9 +422,9 @@ add_file(int agent_fd, const char *filename, int key_only, int cert_only,
 	if (!qflag) {
 		fprintf(stderr, "Certificate added: %s (%s)\n", certpath,
 		    private->cert->key_id);
-		if (lifetime != 0) {
-			fprintf(stderr, "Lifetime set to %d seconds\n",
-			    lifetime);
+		if (cert_lifetime != 0) {
+			fprintf(stderr, "Lifetime set to %s\n",
+			    fmt_timeframe((time_t)cert_lifetime));
 		}
 		if (confirm != 0) {
 			fprintf(stderr, "The user must confirm each use "
@@ -410,6 +435,7 @@ add_file(int agent_fd, const char *filename, int key_only, int cert_only,
  out:
 	free(certpath);
 	free(comment);
+	sshkey_free(cert);
 	sshkey_free(private);
 
 	return ret;
@@ -606,7 +632,7 @@ load_resident_keys(int agent_fd, const char *skprovider, int qflag,
 
 static int
 do_file(int agent_fd, int deleting, int key_only, int cert_only,
-    char *file, int qflag, const char *skprovider,
+    char *file, int qflag, int Nflag, const char *skprovider,
     struct dest_constraint **dest_constraints, size_t ndest_constraints)
 {
 	if (deleting) {
@@ -614,7 +640,7 @@ do_file(int agent_fd, int deleting, int key_only, int cert_only,
 		    cert_only, qflag) == -1)
 			return -1;
 	} else {
-		if (add_file(agent_fd, file, key_only, cert_only, qflag,
+		if (add_file(agent_fd, file, key_only, cert_only, qflag, Nflag,
 		    skprovider, dest_constraints, ndest_constraints) == -1)
 			return -1;
 	}
@@ -762,7 +788,7 @@ main(int argc, char **argv)
 	char **dest_constraint_strings = NULL, **hostkey_files = NULL;
 	int r, i, ch, deleting = 0, ret = 0, key_only = 0, cert_only = 0;
 	int do_download = 0, xflag = 0, lflag = 0, Dflag = 0;
-	int qflag = 0, Tflag = 0;
+	int qflag = 0, Tflag = 0, Nflag = 0;
 	SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
 	LogLevel log_level = SYSLOG_LEVEL_INFO;
 	struct sshkey *k, **certs = NULL;
@@ -794,7 +820,7 @@ main(int argc, char **argv)
 
 	skprovider = getenv("SSH_SK_PROVIDER");
 
-	while ((ch = getopt(argc, argv, "vkKlLCcdDTxXE:e:h:H:M:m:qs:S:t:")) != -1) {
+	while ((ch = getopt(argc, argv, "vVkKlLCcdDTxXE:e:h:H:M:m:qs:S:t:")) != -1) {
 		switch (ch) {
 		case 'v':
 			if (log_level == SYSLOG_LEVEL_INFO)
@@ -802,6 +828,9 @@ main(int argc, char **argv)
 			else if (log_level < SYSLOG_LEVEL_DEBUG3)
 				log_level++;
 			break;
+		case 'V':
+			Nflag = 1;
+			break;
 		case 'E':
 			fingerprint_hash = ssh_digest_alg_by_name(optarg);
 			if (fingerprint_hash == -1)
@@ -968,7 +997,7 @@ main(int argc, char **argv)
 			if (stat(buf, &st) == -1)
 				continue;
 			if (do_file(agent_fd, deleting, key_only, cert_only,
-			    buf, qflag, skprovider,
+			    buf, qflag, Nflag, skprovider,
 			    dest_constraints, ndest_constraints) == -1)
 				ret = 1;
 			else
@@ -979,7 +1008,7 @@ main(int argc, char **argv)
 	} else {
 		for (i = 0; i < argc; i++) {
 			if (do_file(agent_fd, deleting, key_only, cert_only,
-			    argv[i], qflag, skprovider,
+			    argv[i], qflag, Nflag, skprovider,
 			    dest_constraints, ndest_constraints) == -1)
 				ret = 1;
 		}

-- 
To stop receiving notification emails like this one, please contact
djm at mindrot.org.


More information about the openssh-commits mailing list