From 78230b3ec8cbabc1e7de68732dc5cbd4837c6675 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Sat, 27 Nov 2021 07:14:46 +0000 Subject: [PATCH] upstream: Add ssh-keygen -Y match-principals operation to perform matching of principals names against an allowed signers file. Requested by and mostly written by Fabian Stelzer, towards a TOFU model for SSH signatures in git. Some tweaks by me. "doesn't bother me" deraadt@ OpenBSD-Commit-ID: 8d1b71f5a4127bc5e10a880c8ea6053394465247 --- ssh-keygen.1 | 17 +++++++++++-- ssh-keygen.c | 42 +++++++++++++++++++++++++++++- sshsig.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++- sshsig.h | 6 ++++- 4 files changed, 132 insertions(+), 5 deletions(-) diff --git a/ssh-keygen.1 b/ssh-keygen.1 index f83f515f6..57c106d10 100644 --- a/ssh-keygen.1 +++ b/ssh-keygen.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ssh-keygen.1,v 1.216 2021/08/11 08:54:17 djm Exp $ +.\" $OpenBSD: ssh-keygen.1,v 1.217 2021/11/27 07:14:46 djm Exp $ .\" .\" Author: Tatu Ylonen .\" Copyright (c) 1995 Tatu Ylonen , 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: August 11 2021 $ +.Dd $Mdocdate: November 27 2021 $ .Dt SSH-KEYGEN 1 .Os .Sh NAME @@ -151,6 +151,11 @@ .Fl s Ar signature_file .Fl f Ar allowed_signers_file .Nm ssh-keygen +.Fl Y Cm match-principals +.Op Fl O Ar option +.Fl I Ar signer_identity +.Fl f Ar allowed_signers_file +.Nm ssh-keygen .Fl Y Cm check-novalidate .Op Fl O Ar option .Fl n Ar namespace @@ -683,6 +688,14 @@ The format of the allowed signers file is documented in the section below. If one or more matching principals are found, they are returned on standard output. +.It Fl Y Cm match-principals +Find principal matching the principal name provided using the +.Fl I +flag in the authorized signers file specified using the +.Fl f +flag. +If one or more matching principals are found, they are returned on +standard output. .It Fl Y Cm check-novalidate Checks that a signature generated using .Nm diff --git a/ssh-keygen.c b/ssh-keygen.c index 248a0ae76..f3f15cd07 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-keygen.c,v 1.440 2021/10/29 03:20:46 djm Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.441 2021/11/27 07:14:46 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland @@ -2849,6 +2849,32 @@ done: return ret; } +static int +sig_match_principals(const char *allowed_keys, char *principal, + char * const *opts, size_t nopts) +{ + int r; + char **principals = NULL; + size_t i, nprincipals = 0; + + if ((r = sig_process_opts(opts, nopts, NULL, NULL)) != 0) + return r; /* error already logged */ + + if ((r = sshsig_match_principals(allowed_keys, principal, + &principals, &nprincipals)) != 0) { + debug_f("match: %s", ssh_err(r)); + fprintf(stderr, "No principal matched.\n"); + return r; + } + for (i = 0; i < nprincipals; i++) { + printf("%s\n", principals[i]); + free(principals[i]); + } + free(principals); + + return 0; +} + static void do_moduli_gen(const char *out_file, char **opts, size_t nopts) { @@ -3187,6 +3213,7 @@ usage(void) " file ...\n" " ssh-keygen -Q [-l] -f krl_file [file ...]\n" " ssh-keygen -Y find-principals -s signature_file -f allowed_signers_file\n" + " ssh-keygen -Y match-principals -I signer_identity -f allowed_signers_file\n" " ssh-keygen -Y check-novalidate -n namespace -s signature_file\n" " ssh-keygen -Y sign -f key_file -n namespace file ...\n" " ssh-keygen -Y verify -f allowed_signers_file -I signer_identity\n" @@ -3468,6 +3495,19 @@ main(int argc, char **argv) } return sig_find_principals(ca_key_path, identity_file, opts, nopts); + } else if (strncmp(sign_op, "match-principals", 16) == 0) { + if (!have_identity) { + error("Too few arguments for match-principals:" + "missing allowed keys file"); + exit(1); + } + if (cert_key_id == NULL) { + error("Too few arguments for match-principals: " + "missing principal ID"); + exit(1); + } + return sig_match_principals(identity_file, cert_key_id, + opts, nopts); } else if (strncmp(sign_op, "sign", 4) == 0) { if (cert_principals == NULL || *cert_principals == '\0') { diff --git a/sshsig.c b/sshsig.c index 5039a0fb9..bd17d035f 100644 --- a/sshsig.c +++ b/sshsig.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshsig.c,v 1.23 2021/11/18 03:50:41 djm Exp $ */ +/* $OpenBSD: sshsig.c,v 1.24 2021/11/27 07:14:46 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -1050,6 +1050,76 @@ sshsig_find_principals(const char *path, const struct sshkey *sign_key, return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r; } +int +sshsig_match_principals(const char *path, const char *principal, + char ***principalsp, size_t *nprincipalsp) +{ + FILE *f = NULL; + char *found, *line = NULL, **principals = NULL, **tmp; + size_t i, nprincipals = 0, linesize = 0; + u_long linenum = 0; + int oerrno, r, ret = 0; + + if (principalsp != NULL) + *principalsp = NULL; + if (nprincipalsp != NULL) + *nprincipalsp = 0; + + /* Check key and principal against file */ + if ((f = fopen(path, "r")) == NULL) { + oerrno = errno; + error("Unable to open allowed keys file \"%s\": %s", + path, strerror(errno)); + errno = oerrno; + return SSH_ERR_SYSTEM_ERROR; + } + + while (getline(&line, &linesize, f) != -1) { + linenum++; + /* Parse the line */ + if ((r = parse_principals_key_and_options(path, linenum, line, + principal, &found, NULL, NULL)) != 0) { + if (r == SSH_ERR_KEY_NOT_FOUND) + continue; + ret = r; + oerrno = errno; + break; /* unexpected error */ + } + if ((tmp = recallocarray(principals, nprincipals, + nprincipals + 1, sizeof(*principals))) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + free(found); + break; + } + principals = tmp; + principals[nprincipals++] = found; /* transferred */ + free(line); + line = NULL; + linesize = 0; + } + fclose(f); + + if (ret == 0) { + if (nprincipals == 0) + ret = SSH_ERR_KEY_NOT_FOUND; + if (principalsp != NULL) { + *principalsp = principals; + principals = NULL; /* transferred */ + } + if (nprincipalsp != 0) { + *nprincipalsp = nprincipals; + nprincipals = 0; + } + } + + for (i = 0; i < nprincipals; i++) + free(principals[i]); + free(principals); + + errno = oerrno; + return ret; +} + int sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey) { diff --git a/sshsig.h b/sshsig.h index b725c7d7a..ac5577962 100644 --- a/sshsig.h +++ b/sshsig.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sshsig.h,v 1.10 2021/07/23 03:37:52 djm Exp $ */ +/* $OpenBSD: sshsig.h,v 1.11 2021/11/27 07:14:46 djm Exp $ */ /* * Copyright (c) 2019 Google LLC * @@ -104,4 +104,8 @@ int sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey); int sshsig_find_principals(const char *path, const struct sshkey *sign_key, uint64_t verify_time, char **principal); +/* Find all principals in allowed_keys file matching *principal */ +int sshsig_match_principals(const char *path, + const char *principal, char ***principalsp, size_t *nprincipalsp); + #endif /* SSHSIG_H */