upstream: add a new signature operations "find-principal" to look

up the principal associated with a signature from an allowed-signers file.
Work by Sebastian Kinne; ok dtucker@

OpenBSD-Commit-ID: 6f782cc7e18e38fcfafa62af53246a1dcfe74e5d
This commit is contained in:
djm@openbsd.org 2020-01-23 02:43:48 +00:00 committed by Damien Miller
parent 65cf8730de
commit 56cffcc09f
3 changed files with 209 additions and 11 deletions

View File

@ -1,4 +1,4 @@
.\" $OpenBSD: ssh-keygen.1,v 1.193 2020/01/18 21:16:43 naddy Exp $ .\" $OpenBSD: ssh-keygen.1,v 1.194 2020/01/23 02:43:48 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: January 18 2020 $ .Dd $Mdocdate: January 23 2020 $
.Dt SSH-KEYGEN 1 .Dt SSH-KEYGEN 1
.Os .Os
.Sh NAME .Sh NAME
@ -138,6 +138,10 @@
.Fl f Ar krl_file .Fl f Ar krl_file
.Ar .Ar
.Nm ssh-keygen .Nm ssh-keygen
.Fl Y Cm find-principal
.Fl s Ar signature_file
.Fl f Ar allowed_signers_file
.Nm ssh-keygen
.Fl Y Cm check-novalidate .Fl Y Cm check-novalidate
.Fl n Ar namespace .Fl n Ar namespace
.Fl s Ar signature_file .Fl s Ar signature_file
@ -614,6 +618,17 @@ The maximum is 3.
Specifies a path to a library that will be used when creating Specifies a path to a library that will be used when creating
FIDO authenticator-hosted keys, overriding the default of using FIDO authenticator-hosted keys, overriding the default of using
the internal USB HID support. the internal USB HID support.
.It Fl Y Cm find-principal
Find the principal associated with the public key of a signature,
provided using the
.Fl s
flag in an authorized signers file provided using the
.Fl f
flag.
The format of the allowed signers file is documented in the
.Sx ALLOWED SIGNERS
section below. If a matching principal is found, it is 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.385 2020/01/22 04:51:51 claudio Exp $ */ /* $OpenBSD: ssh-keygen.c,v 1.386 2020/01/23 02:43:48 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
@ -2599,7 +2599,7 @@ sign_one(struct sshkey *signkey, const char *filename, int fd,
} }
static int static int
sign(const char *keypath, const char *sig_namespace, int argc, char **argv) sig_sign(const char *keypath, const char *sig_namespace, int argc, char **argv)
{ {
int i, fd = -1, r, ret = -1; int i, fd = -1, r, ret = -1;
int agent_fd = -1; int agent_fd = -1;
@ -2670,8 +2670,8 @@ done:
} }
static int static int
verify(const char *signature, const char *sig_namespace, const char *principal, sig_verify(const char *signature, const char *sig_namespace,
const char *allowed_keys, const char *revoked_keys) const char *principal, const char *allowed_keys, const char *revoked_keys)
{ {
int r, ret = -1, sigfd = -1; int r, ret = -1, sigfd = -1;
struct sshbuf *sigbuf = NULL, *abuf = NULL; struct sshbuf *sigbuf = NULL, *abuf = NULL;
@ -2694,7 +2694,7 @@ verify(const char *signature, const char *sig_namespace, const char *principal,
} }
if ((r = sshsig_dearmor(abuf, &sigbuf)) != 0) { if ((r = sshsig_dearmor(abuf, &sigbuf)) != 0) {
error("%s: sshsig_armor: %s", __func__, ssh_err(r)); error("%s: sshsig_armor: %s", __func__, ssh_err(r));
return r; goto done;
} }
if ((r = sshsig_verify_fd(sigbuf, STDIN_FILENO, sig_namespace, if ((r = sshsig_verify_fd(sigbuf, STDIN_FILENO, sig_namespace,
&sign_key, &sig_details)) != 0) &sign_key, &sig_details)) != 0)
@ -2757,6 +2757,57 @@ done:
return ret; return ret;
} }
static int
sig_find_principal(const char *signature, const char *allowed_keys) {
int r, ret = -1, sigfd = -1;
struct sshbuf *sigbuf = NULL, *abuf = NULL;
struct sshkey *sign_key = NULL;
char *principal = NULL;
if ((abuf = sshbuf_new()) == NULL)
fatal("%s: sshbuf_new() failed", __func__);
if ((sigfd = open(signature, O_RDONLY)) < 0) {
error("Couldn't open signature file %s", signature);
goto done;
}
if ((r = sshkey_load_file(sigfd, abuf)) != 0) {
error("Couldn't read signature file: %s", ssh_err(r));
goto done;
}
if ((r = sshsig_dearmor(abuf, &sigbuf)) != 0) {
error("%s: sshsig_armor: %s", __func__, ssh_err(r));
goto done;
}
if ((r = sshsig_get_pubkey(sigbuf, &sign_key)) != 0) {
error("%s: sshsig_get_pubkey: %s",
__func__, ssh_err(r));
goto done;
}
if ((r = sshsig_find_principal(allowed_keys, sign_key,
&principal)) != 0) {
error("%s: sshsig_get_principal: %s",
__func__, ssh_err(r));
goto done;
}
ret = 0;
done:
if (ret == 0 ) {
printf("Found matching principal: %s\n", principal);
} else {
printf("Could not find matching principal.\n");
}
if (sigfd != -1)
close(sigfd);
sshbuf_free(sigbuf);
sshbuf_free(abuf);
sshkey_free(sign_key);
free(principal);
return ret;
}
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)
{ {
@ -3042,6 +3093,7 @@ usage(void)
" ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n" " ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n"
" file ...\n" " file ...\n"
" ssh-keygen -Q -f krl_file file ...\n" " ssh-keygen -Q -f krl_file file ...\n"
" ssh-keygen -Y find-principal -s signature_file -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"
@ -3305,6 +3357,19 @@ main(int argc, char **argv)
argc -= optind; argc -= optind;
if (sign_op != NULL) { if (sign_op != NULL) {
if (strncmp(sign_op, "find-principal", 14) == 0) {
if (ca_key_path == NULL) {
error("Too few arguments for find-principal:"
"missing signature file");
exit(1);
}
if (!have_identity) {
error("Too few arguments for find-principal:"
"missing allowed keys file");
exit(1);
}
return sig_find_principal(ca_key_path, identity_file);
}
if (cert_principals == NULL || *cert_principals == '\0') { if (cert_principals == NULL || *cert_principals == '\0') {
error("Too few arguments for sign/verify: " error("Too few arguments for sign/verify: "
"missing namespace"); "missing namespace");
@ -3316,15 +3381,16 @@ main(int argc, char **argv)
"missing key"); "missing key");
exit(1); exit(1);
} }
return sign(identity_file, cert_principals, argc, argv); return sig_sign(identity_file, cert_principals,
argc, argv);
} else if (strncmp(sign_op, "check-novalidate", 16) == 0) { } else if (strncmp(sign_op, "check-novalidate", 16) == 0) {
if (ca_key_path == NULL) { if (ca_key_path == NULL) {
error("Too few arguments for check-novalidate: " error("Too few arguments for check-novalidate: "
"missing signature file"); "missing signature file");
exit(1); exit(1);
} }
return verify(ca_key_path, cert_principals, return sig_verify(ca_key_path, cert_principals,
NULL, NULL, NULL); NULL, NULL, NULL);
} else if (strncmp(sign_op, "verify", 6) == 0) { } else if (strncmp(sign_op, "verify", 6) == 0) {
if (ca_key_path == NULL) { if (ca_key_path == NULL) {
error("Too few arguments for verify: " error("Too few arguments for verify: "
@ -3341,7 +3407,7 @@ main(int argc, char **argv)
"missing principal ID"); "missing principal ID");
exit(1); exit(1);
} }
return verify(ca_key_path, cert_principals, return sig_verify(ca_key_path, cert_principals,
cert_key_id, identity_file, rr_hostname); cert_key_id, identity_file, rr_hostname);
} }
usage(); usage();

117
sshsig.c
View File

@ -866,3 +866,120 @@ sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key,
free(line); free(line);
return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r; return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r;
} }
static int
get_matching_principal_from_line(const char *path, u_long linenum, char *line,
const struct sshkey *sign_key, char **principalsp)
{
struct sshkey *found_key = NULL;
char *principals = NULL;
int r, found = 0;
const char *reason = NULL;
struct sshsigopt *sigopts = NULL;
if (principalsp != NULL)
*principalsp = NULL;
/* Parse the line */
if ((r = parse_principals_key_and_options(path, linenum, line,
NULL, &principals, &found_key, &sigopts)) != 0) {
/* error already logged */
goto done;
}
if (!sigopts->ca && sshkey_equal(found_key, sign_key)) {
/* Exact match of key */
debug("%s:%lu: matched key", path, linenum);
/* success */
found = 1;
} else if (sigopts->ca && sshkey_is_cert(sign_key) &&
sshkey_equal_public(sign_key->cert->signature_key, found_key)) {
/* Match of certificate's CA key */
if ((r = sshkey_cert_check_authority(sign_key, 0, 1,
principals, &reason)) != 0) {
error("%s:%lu: certificate not authorized: %s",
path, linenum, reason);
goto done;
}
debug("%s:%lu: matched certificate CA key", path, linenum);
/* success */
found = 1;
} else {
/* Key didn't match */
goto done;
}
done:
if (found) {
*principalsp = principals;
principals = NULL; /* transferred */
}
free(principals);
sshkey_free(found_key);
sshsigopt_free(sigopts);
return found ? 0 : SSH_ERR_KEY_NOT_FOUND;
}
int
sshsig_find_principal(const char *path, const struct sshkey *sign_key,
char **principal)
{
FILE *f = NULL;
char *line = NULL;
size_t linesize = 0;
u_long linenum = 0;
int r, oerrno;
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++;
r = get_matching_principal_from_line(path, linenum, line,
sign_key, principal);
free(line);
line = NULL;
if (r == SSH_ERR_KEY_NOT_FOUND)
continue;
else if (r == 0) {
/* success */
fclose(f);
return 0;
} else
break;
}
free(line);
/* Either we hit an error parsing or we simply didn't find the key */
if (ferror(f) != 0) {
oerrno = errno;
fclose(f);
error("Unable to read allowed keys file \"%s\": %s",
path, strerror(errno));
errno = oerrno;
return SSH_ERR_SYSTEM_ERROR;
}
fclose(f);
return r == 0 ? SSH_ERR_KEY_NOT_FOUND : r;
}
int
sshsig_get_pubkey(struct sshbuf *signature, struct sshkey **pubkey)
{
struct sshkey *pk = NULL;
int r = SSH_ERR_SIGNATURE_INVALID;
if (pubkey != NULL)
*pubkey = NULL;
if ((r = sshsig_parse_preamble(signature)) != 0)
return r;
if ((r = sshkey_froms(signature, &pk)) != 0)
return r;
*pubkey = pk;
pk = NULL;
return 0;
}