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>
.\" 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: January 18 2020 $
.Dd $Mdocdate: January 23 2020 $
.Dt SSH-KEYGEN 1
.Os
.Sh NAME
@ -138,6 +138,10 @@
.Fl f Ar krl_file
.Ar
.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 n Ar namespace
.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
FIDO authenticator-hosted keys, overriding the default of using
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
Checks that a signature generated using
.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>
* 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
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 agent_fd = -1;
@ -2670,8 +2670,8 @@ done:
}
static int
verify(const char *signature, const char *sig_namespace, const char *principal,
const char *allowed_keys, const char *revoked_keys)
sig_verify(const char *signature, const char *sig_namespace,
const char *principal, const char *allowed_keys, const char *revoked_keys)
{
int r, ret = -1, sigfd = -1;
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) {
error("%s: sshsig_armor: %s", __func__, ssh_err(r));
return r;
goto done;
}
if ((r = sshsig_verify_fd(sigbuf, STDIN_FILENO, sig_namespace,
&sign_key, &sig_details)) != 0)
@ -2757,6 +2757,57 @@ done:
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
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"
" 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 sign -f key_file -n namespace file ...\n"
" ssh-keygen -Y verify -f allowed_signers_file -I signer_identity\n"
@ -3305,6 +3357,19 @@ main(int argc, char **argv)
argc -= optind;
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') {
error("Too few arguments for sign/verify: "
"missing namespace");
@ -3316,15 +3381,16 @@ main(int argc, char **argv)
"missing key");
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) {
if (ca_key_path == NULL) {
error("Too few arguments for check-novalidate: "
"missing signature file");
exit(1);
}
return verify(ca_key_path, cert_principals,
NULL, NULL, NULL);
return sig_verify(ca_key_path, cert_principals,
NULL, NULL, NULL);
} else if (strncmp(sign_op, "verify", 6) == 0) {
if (ca_key_path == NULL) {
error("Too few arguments for verify: "
@ -3341,7 +3407,7 @@ main(int argc, char **argv)
"missing principal ID");
exit(1);
}
return verify(ca_key_path, cert_principals,
return sig_verify(ca_key_path, cert_principals,
cert_key_id, identity_file, rr_hostname);
}
usage();

117
sshsig.c
View File

@ -866,3 +866,120 @@ sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key,
free(line);
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;
}