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
This commit is contained in:
djm@openbsd.org 2021-11-27 07:14:46 +00:00 committed by Damien Miller
parent 15db86611b
commit 78230b3ec8
4 changed files with 132 additions and 5 deletions

View File

@ -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> .\" Author: Tatu Ylonen <ylo@cs.hut.fi>
.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland .\" 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 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" 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 .Dt SSH-KEYGEN 1
.Os .Os
.Sh NAME .Sh NAME
@ -151,6 +151,11 @@
.Fl s Ar signature_file .Fl s Ar signature_file
.Fl f Ar allowed_signers_file .Fl f Ar allowed_signers_file
.Nm ssh-keygen .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 .Fl Y Cm check-novalidate
.Op Fl O Ar option .Op Fl O Ar option
.Fl n Ar namespace .Fl n Ar namespace
@ -683,6 +688,14 @@ The format of the allowed signers file is documented in the
section below. section below.
If one or more matching principals are found, they are returned on If one or more matching principals are found, they are returned on
standard output. 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 .It Fl Y Cm check-novalidate
Checks that a signature generated using Checks that a signature generated using
.Nm .Nm

View File

@ -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> * Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -2849,6 +2849,32 @@ done:
return ret; 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 static void
do_moduli_gen(const char *out_file, char **opts, size_t nopts) do_moduli_gen(const char *out_file, char **opts, size_t nopts)
{ {
@ -3187,6 +3213,7 @@ usage(void)
" file ...\n" " file ...\n"
" ssh-keygen -Q [-l] -f krl_file [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 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 check-novalidate -n namespace -s signature_file\n"
" ssh-keygen -Y sign -f key_file -n namespace 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" " 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, return sig_find_principals(ca_key_path, identity_file,
opts, nopts); 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) { } else if (strncmp(sign_op, "sign", 4) == 0) {
if (cert_principals == NULL || if (cert_principals == NULL ||
*cert_principals == '\0') { *cert_principals == '\0') {

View File

@ -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 * 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; 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 int
sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey) sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey)
{ {

View File

@ -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 * 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, int sshsig_find_principals(const char *path, const struct sshkey *sign_key,
uint64_t verify_time, char **principal); 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 */ #endif /* SSHSIG_H */