summaryrefslogtreecommitdiff
path: root/usr.bin/ssh
diff options
context:
space:
mode:
authorDamien Miller <djm@cvs.openbsd.org>2021-11-27 07:14:47 +0000
committerDamien Miller <djm@cvs.openbsd.org>2021-11-27 07:14:47 +0000
commit2065266dfdb682a659a7c1b4ab5922f7eaeb35ba (patch)
treeea43f19fd4ea165014a698dfdf52965afd9c1d01 /usr.bin/ssh
parent2e890860cc2b14ae8ade37e87520fd00b3f375b4 (diff)
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@
Diffstat (limited to 'usr.bin/ssh')
-rw-r--r--usr.bin/ssh/ssh-keygen.117
-rw-r--r--usr.bin/ssh/ssh-keygen.c42
-rw-r--r--usr.bin/ssh/sshsig.c72
-rw-r--r--usr.bin/ssh/sshsig.h6
4 files changed, 132 insertions, 5 deletions
diff --git a/usr.bin/ssh/ssh-keygen.1 b/usr.bin/ssh/ssh-keygen.1
index f83f515f600..57c106d10a7 100644
--- a/usr.bin/ssh/ssh-keygen.1
+++ b/usr.bin/ssh/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 <ylo@cs.hut.fi>
.\" Copyright (c) 1995 Tatu Ylonen <ylo@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: 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/usr.bin/ssh/ssh-keygen.c b/usr.bin/ssh/ssh-keygen.c
index 0e4d00013b2..ce10e61c442 100644
--- a/usr.bin/ssh/ssh-keygen.c
+++ b/usr.bin/ssh/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 <ylo@cs.hut.fi>
* Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -2826,6 +2826,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)
{
@@ -3164,6 +3190,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"
@@ -3442,6 +3469,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/usr.bin/ssh/sshsig.c b/usr.bin/ssh/sshsig.c
index a3a0ba17561..304c4282173 100644
--- a/usr.bin/ssh/sshsig.c
+++ b/usr.bin/ssh/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
*
@@ -1049,6 +1049,76 @@ sshsig_find_principals(const char *path, const struct sshkey *sign_key,
}
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)
{
struct sshkey *pk = NULL;
diff --git a/usr.bin/ssh/sshsig.h b/usr.bin/ssh/sshsig.h
index b725c7d7acd..ac5577962fb 100644
--- a/usr.bin/ssh/sshsig.h
+++ b/usr.bin/ssh/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 */