mirror of git://anongit.mindrot.org/openssh.git
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:
parent
65cf8730de
commit
56cffcc09f
19
ssh-keygen.1
19
ssh-keygen.1
|
@ -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
|
||||
|
|
84
ssh-keygen.c
84
ssh-keygen.c
|
@ -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
117
sshsig.c
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue