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:
parent
15db86611b
commit
78230b3ec8
17
ssh-keygen.1
17
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>
|
.\" 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
|
||||||
|
|
42
ssh-keygen.c
42
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>
|
* 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') {
|
||||||
|
|
72
sshsig.c
72
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
|
* 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)
|
||||||
{
|
{
|
||||||
|
|
6
sshsig.h
6
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
|
* 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 */
|
||||||
|
|
Loading…
Reference in New Issue