mirror of
https://github.com/PowerShell/openssh-portable.git
synced 2025-07-28 16:24:39 +02:00
upstream: Make it possible to load certs from PKCS#11 tokens
Adds a protocol extension to allow grafting certificates supplied by ssh-add to keys loaded from PKCS#11 tokens in the agent. feedback/ok markus@ OpenBSD-Commit-ID: bb5433cd28ede2bc910996eb3c0b53e20f86037f
This commit is contained in:
parent
881d9c6af9
commit
4448a2938a
@ -81,4 +81,35 @@ the constraint is:
|
|||||||
|
|
||||||
This option is only valid for XMSS keys.
|
This option is only valid for XMSS keys.
|
||||||
|
|
||||||
$OpenBSD: PROTOCOL.agent,v 1.20 2023/10/03 23:56:10 djm Exp $
|
3. associated-certs-v00@openssh.com key constraint extension
|
||||||
|
|
||||||
|
The key constraint extension allows certificates to be associated
|
||||||
|
with private keys as they are loaded from a PKCS#11 token.
|
||||||
|
|
||||||
|
byte SSH_AGENT_CONSTRAIN_EXTENSION (0xff)
|
||||||
|
string associated-certs-v00@openssh.com
|
||||||
|
bool certs_only
|
||||||
|
string certsblob
|
||||||
|
|
||||||
|
Where "certsblob" constists of one or more certificates encoded as public
|
||||||
|
key blobs:
|
||||||
|
|
||||||
|
string[] certificates
|
||||||
|
|
||||||
|
This extension is only valid for SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED
|
||||||
|
requests. When an agent receives this extension, it will attempt to match
|
||||||
|
each certificate in the request with a corresponding private key loaded
|
||||||
|
from the requested PKCS#11 token. When a matching key is found, the
|
||||||
|
agent will graft the certificate contents to the token-hosted private key
|
||||||
|
and store the result for subsequent use by regular agent operations.
|
||||||
|
|
||||||
|
If the "certs_only" flag is set, then this extension will cause ONLY
|
||||||
|
the resultant certificates to be loaded to the agent. The default
|
||||||
|
behaviour is to load the PKCS#11-hosted private key as well as the
|
||||||
|
resultant certificate.
|
||||||
|
|
||||||
|
A SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED will return SSH_AGENT_SUCCESS
|
||||||
|
if any key (plain private or certificate) was successfully loaded, or
|
||||||
|
SSH_AGENT_FAILURE if no key was loaded.
|
||||||
|
|
||||||
|
$OpenBSD: PROTOCOL.agent,v 1.21 2023/12/18 14:46:56 djm Exp $
|
||||||
|
40
authfd.c
40
authfd.c
@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: authfd.c,v 1.133 2023/03/09 21:06:24 jcs Exp $ */
|
/* $OpenBSD: authfd.c,v 1.134 2023/12/18 14:46:56 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
|
||||||
@ -504,9 +504,10 @@ encode_dest_constraint(struct sshbuf *m, const struct dest_constraint *dc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
encode_constraints(struct sshbuf *m, u_int life, u_int confirm, u_int maxsign,
|
encode_constraints(struct sshbuf *m, u_int life, u_int confirm,
|
||||||
const char *provider, struct dest_constraint **dest_constraints,
|
u_int maxsign, const char *provider,
|
||||||
size_t ndest_constraints)
|
struct dest_constraint **dest_constraints, size_t ndest_constraints,
|
||||||
|
int cert_only, struct sshkey **certs, size_t ncerts)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
struct sshbuf *b = NULL;
|
struct sshbuf *b = NULL;
|
||||||
@ -550,6 +551,27 @@ encode_constraints(struct sshbuf *m, u_int life, u_int confirm, u_int maxsign,
|
|||||||
"restrict-destination-v00@openssh.com")) != 0 ||
|
"restrict-destination-v00@openssh.com")) != 0 ||
|
||||||
(r = sshbuf_put_stringb(m, b)) != 0)
|
(r = sshbuf_put_stringb(m, b)) != 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
sshbuf_free(b);
|
||||||
|
b = NULL;
|
||||||
|
}
|
||||||
|
if (ncerts != 0) {
|
||||||
|
if ((b = sshbuf_new()) == NULL) {
|
||||||
|
r = SSH_ERR_ALLOC_FAIL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
for (i = 0; i < ncerts; i++) {
|
||||||
|
if ((r = sshkey_puts(certs[i], b)) != 0)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if ((r = sshbuf_put_u8(m,
|
||||||
|
SSH_AGENT_CONSTRAIN_EXTENSION)) != 0 ||
|
||||||
|
(r = sshbuf_put_cstring(m,
|
||||||
|
"associated-certs-v00@openssh.com")) != 0 ||
|
||||||
|
(r = sshbuf_put_u8(m, cert_only != 0)) != 0 ||
|
||||||
|
(r = sshbuf_put_stringb(m, b)) != 0)
|
||||||
|
goto out;
|
||||||
|
sshbuf_free(b);
|
||||||
|
b = NULL;
|
||||||
}
|
}
|
||||||
r = 0;
|
r = 0;
|
||||||
out:
|
out:
|
||||||
@ -607,7 +629,7 @@ ssh_add_identity_constrained(int sock, struct sshkey *key,
|
|||||||
}
|
}
|
||||||
if (constrained &&
|
if (constrained &&
|
||||||
(r = encode_constraints(msg, life, confirm, maxsign,
|
(r = encode_constraints(msg, life, confirm, maxsign,
|
||||||
provider, dest_constraints, ndest_constraints)) != 0)
|
provider, dest_constraints, ndest_constraints, 0, NULL, 0)) != 0)
|
||||||
goto out;
|
goto out;
|
||||||
if ((r = ssh_request_reply_decode(sock, msg)) != 0)
|
if ((r = ssh_request_reply_decode(sock, msg)) != 0)
|
||||||
goto out;
|
goto out;
|
||||||
@ -662,10 +684,11 @@ ssh_remove_identity(int sock, const struct sshkey *key)
|
|||||||
int
|
int
|
||||||
ssh_update_card(int sock, int add, const char *reader_id, const char *pin,
|
ssh_update_card(int sock, int add, const char *reader_id, const char *pin,
|
||||||
u_int life, u_int confirm,
|
u_int life, u_int confirm,
|
||||||
struct dest_constraint **dest_constraints, size_t ndest_constraints)
|
struct dest_constraint **dest_constraints, size_t ndest_constraints,
|
||||||
|
int cert_only, struct sshkey **certs, size_t ncerts)
|
||||||
{
|
{
|
||||||
struct sshbuf *msg;
|
struct sshbuf *msg;
|
||||||
int r, constrained = (life || confirm || dest_constraints);
|
int r, constrained = (life || confirm || dest_constraints || certs);
|
||||||
u_char type;
|
u_char type;
|
||||||
|
|
||||||
if (add) {
|
if (add) {
|
||||||
@ -683,7 +706,8 @@ ssh_update_card(int sock, int add, const char *reader_id, const char *pin,
|
|||||||
goto out;
|
goto out;
|
||||||
if (constrained &&
|
if (constrained &&
|
||||||
(r = encode_constraints(msg, life, confirm, 0, NULL,
|
(r = encode_constraints(msg, life, confirm, 0, NULL,
|
||||||
dest_constraints, ndest_constraints)) != 0)
|
dest_constraints, ndest_constraints,
|
||||||
|
cert_only, certs, ncerts)) != 0)
|
||||||
goto out;
|
goto out;
|
||||||
if ((r = ssh_request_reply_decode(sock, msg)) != 0)
|
if ((r = ssh_request_reply_decode(sock, msg)) != 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
5
authfd.h
5
authfd.h
@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: authfd.h,v 1.51 2021/12/19 22:10:24 djm Exp $ */
|
/* $OpenBSD: authfd.h,v 1.52 2023/12/18 14:46:56 djm Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||||
@ -56,7 +56,8 @@ int ssh_remove_identity(int sock, const struct sshkey *key);
|
|||||||
int ssh_update_card(int sock, int add, const char *reader_id,
|
int ssh_update_card(int sock, int add, const char *reader_id,
|
||||||
const char *pin, u_int life, u_int confirm,
|
const char *pin, u_int life, u_int confirm,
|
||||||
struct dest_constraint **dest_constraints,
|
struct dest_constraint **dest_constraints,
|
||||||
size_t ndest_constraints);
|
size_t ndest_constraints,
|
||||||
|
int cert_only, struct sshkey **certs, size_t ncerts);
|
||||||
int ssh_remove_all_identities(int sock, int version);
|
int ssh_remove_all_identities(int sock, int version);
|
||||||
|
|
||||||
int ssh_agent_sign(int sock, const struct sshkey *key,
|
int ssh_agent_sign(int sock, const struct sshkey *key,
|
||||||
|
14
ssh-add.1
14
ssh-add.1
@ -1,4 +1,4 @@
|
|||||||
.\" $OpenBSD: ssh-add.1,v 1.84 2022/02/04 02:49:17 dtucker Exp $
|
.\" $OpenBSD: ssh-add.1,v 1.85 2023/12/18 14:46:56 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: February 4 2022 $
|
.Dd $Mdocdate: December 18 2023 $
|
||||||
.Dt SSH-ADD 1
|
.Dt SSH-ADD 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -43,7 +43,7 @@
|
|||||||
.Nd adds private key identities to the OpenSSH authentication agent
|
.Nd adds private key identities to the OpenSSH authentication agent
|
||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm ssh-add
|
.Nm ssh-add
|
||||||
.Op Fl cDdKkLlqvXx
|
.Op Fl cCDdKkLlqvXx
|
||||||
.Op Fl E Ar fingerprint_hash
|
.Op Fl E Ar fingerprint_hash
|
||||||
.Op Fl H Ar hostkey_file
|
.Op Fl H Ar hostkey_file
|
||||||
.Op Fl h Ar destination_constraint
|
.Op Fl h Ar destination_constraint
|
||||||
@ -52,6 +52,8 @@
|
|||||||
.Op Ar
|
.Op Ar
|
||||||
.Nm ssh-add
|
.Nm ssh-add
|
||||||
.Fl s Ar pkcs11
|
.Fl s Ar pkcs11
|
||||||
|
.Op Fl vC
|
||||||
|
.Op Ar certificate ...
|
||||||
.Nm ssh-add
|
.Nm ssh-add
|
||||||
.Fl e Ar pkcs11
|
.Fl e Ar pkcs11
|
||||||
.Nm ssh-add
|
.Nm ssh-add
|
||||||
@ -100,6 +102,9 @@ Confirmation is performed by
|
|||||||
Successful confirmation is signaled by a zero exit status from
|
Successful confirmation is signaled by a zero exit status from
|
||||||
.Xr ssh-askpass 1 ,
|
.Xr ssh-askpass 1 ,
|
||||||
rather than text entered into the requester.
|
rather than text entered into the requester.
|
||||||
|
.It Fl C
|
||||||
|
When loading keys into or deleting keys from the agent, process
|
||||||
|
certificates only and skip plain keys.
|
||||||
.It Fl D
|
.It Fl D
|
||||||
Deletes all identities from the agent.
|
Deletes all identities from the agent.
|
||||||
.It Fl d
|
.It Fl d
|
||||||
@ -228,6 +233,9 @@ internal USB HID support.
|
|||||||
.It Fl s Ar pkcs11
|
.It Fl s Ar pkcs11
|
||||||
Add keys provided by the PKCS#11 shared library
|
Add keys provided by the PKCS#11 shared library
|
||||||
.Ar pkcs11 .
|
.Ar pkcs11 .
|
||||||
|
Certificate files may optionally be listed as command-line arguments.
|
||||||
|
If these are present, then they will be loaded into the agent using any
|
||||||
|
corresponding private keys loaded from the PKCS#11 token.
|
||||||
.It Fl T Ar pubkey ...
|
.It Fl T Ar pubkey ...
|
||||||
Tests whether the private keys that correspond to the specified
|
Tests whether the private keys that correspond to the specified
|
||||||
.Ar pubkey
|
.Ar pubkey
|
||||||
|
92
ssh-add.c
92
ssh-add.c
@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: ssh-add.c,v 1.168 2023/07/06 22:17:59 dtucker Exp $ */
|
/* $OpenBSD: ssh-add.c,v 1.169 2023/12/18 14:46:56 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
|
||||||
@ -131,7 +131,7 @@ delete_one(int agent_fd, const struct sshkey *key, const char *comment,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
delete_stdin(int agent_fd, int qflag)
|
delete_stdin(int agent_fd, int qflag, int key_only, int cert_only)
|
||||||
{
|
{
|
||||||
char *line = NULL, *cp;
|
char *line = NULL, *cp;
|
||||||
size_t linesize = 0;
|
size_t linesize = 0;
|
||||||
@ -152,8 +152,13 @@ delete_stdin(int agent_fd, int qflag)
|
|||||||
error_r(r, "(stdin):%d: invalid key", lnum);
|
error_r(r, "(stdin):%d: invalid key", lnum);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (delete_one(agent_fd, key, cp, "(stdin)", qflag) == 0)
|
if ((!key_only && !cert_only) ||
|
||||||
ret = 0;
|
(key_only && !sshkey_is_cert(key)) ||
|
||||||
|
(cert_only && sshkey_is_cert(key))) {
|
||||||
|
if (delete_one(agent_fd, key, cp,
|
||||||
|
"(stdin)", qflag) == 0)
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sshkey_free(key);
|
sshkey_free(key);
|
||||||
free(line);
|
free(line);
|
||||||
@ -161,21 +166,26 @@ delete_stdin(int agent_fd, int qflag)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
delete_file(int agent_fd, const char *filename, int key_only, int qflag)
|
delete_file(int agent_fd, const char *filename, int key_only,
|
||||||
|
int cert_only, int qflag)
|
||||||
{
|
{
|
||||||
struct sshkey *public, *cert = NULL;
|
struct sshkey *public, *cert = NULL;
|
||||||
char *certpath = NULL, *comment = NULL;
|
char *certpath = NULL, *comment = NULL;
|
||||||
int r, ret = -1;
|
int r, ret = -1;
|
||||||
|
|
||||||
if (strcmp(filename, "-") == 0)
|
if (strcmp(filename, "-") == 0)
|
||||||
return delete_stdin(agent_fd, qflag);
|
return delete_stdin(agent_fd, qflag, key_only, cert_only);
|
||||||
|
|
||||||
if ((r = sshkey_load_public(filename, &public, &comment)) != 0) {
|
if ((r = sshkey_load_public(filename, &public, &comment)) != 0) {
|
||||||
printf("Bad key file %s: %s\n", filename, ssh_err(r));
|
printf("Bad key file %s: %s\n", filename, ssh_err(r));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (delete_one(agent_fd, public, comment, filename, qflag) == 0)
|
if ((!key_only && !cert_only) ||
|
||||||
ret = 0;
|
(key_only && !sshkey_is_cert(public)) ||
|
||||||
|
(cert_only && sshkey_is_cert(public))) {
|
||||||
|
if (delete_one(agent_fd, public, comment, filename, qflag) == 0)
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (key_only)
|
if (key_only)
|
||||||
goto out;
|
goto out;
|
||||||
@ -231,8 +241,9 @@ delete_all(int agent_fd, int qflag)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
add_file(int agent_fd, const char *filename, int key_only, int qflag,
|
add_file(int agent_fd, const char *filename, int key_only, int cert_only,
|
||||||
const char *skprovider, struct dest_constraint **dest_constraints,
|
int qflag, const char *skprovider,
|
||||||
|
struct dest_constraint **dest_constraints,
|
||||||
size_t ndest_constraints)
|
size_t ndest_constraints)
|
||||||
{
|
{
|
||||||
struct sshkey *private, *cert;
|
struct sshkey *private, *cert;
|
||||||
@ -361,7 +372,8 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag,
|
|||||||
skprovider = NULL;
|
skprovider = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((r = ssh_add_identity_constrained(agent_fd, private, comment,
|
if (!cert_only &&
|
||||||
|
(r = ssh_add_identity_constrained(agent_fd, private, comment,
|
||||||
lifetime, confirm, maxsign, skprovider,
|
lifetime, confirm, maxsign, skprovider,
|
||||||
dest_constraints, ndest_constraints)) == 0) {
|
dest_constraints, ndest_constraints)) == 0) {
|
||||||
ret = 0;
|
ret = 0;
|
||||||
@ -390,7 +402,8 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag,
|
|||||||
xasprintf(&certpath, "%s-cert.pub", filename);
|
xasprintf(&certpath, "%s-cert.pub", filename);
|
||||||
if ((r = sshkey_load_public(certpath, &cert, NULL)) != 0) {
|
if ((r = sshkey_load_public(certpath, &cert, NULL)) != 0) {
|
||||||
if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT)
|
if (r != SSH_ERR_SYSTEM_ERROR || errno != ENOENT)
|
||||||
error_r(r, "Failed to load certificate \"%s\"", certpath);
|
error_r(r, "Failed to load certificate \"%s\"",
|
||||||
|
certpath);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -445,11 +458,16 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag,
|
|||||||
|
|
||||||
static int
|
static int
|
||||||
update_card(int agent_fd, int add, const char *id, int qflag,
|
update_card(int agent_fd, int add, const char *id, int qflag,
|
||||||
struct dest_constraint **dest_constraints, size_t ndest_constraints)
|
int key_only, int cert_only,
|
||||||
|
struct dest_constraint **dest_constraints, size_t ndest_constraints,
|
||||||
|
struct sshkey **certs, size_t ncerts)
|
||||||
{
|
{
|
||||||
char *pin = NULL;
|
char *pin = NULL;
|
||||||
int r, ret = -1;
|
int r, ret = -1;
|
||||||
|
|
||||||
|
if (key_only)
|
||||||
|
ncerts = 0;
|
||||||
|
|
||||||
if (add) {
|
if (add) {
|
||||||
if ((pin = read_passphrase("Enter passphrase for PKCS#11: ",
|
if ((pin = read_passphrase("Enter passphrase for PKCS#11: ",
|
||||||
RP_ALLOW_STDIN)) == NULL)
|
RP_ALLOW_STDIN)) == NULL)
|
||||||
@ -457,7 +475,8 @@ update_card(int agent_fd, int add, const char *id, int qflag,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((r = ssh_update_card(agent_fd, add, id, pin == NULL ? "" : pin,
|
if ((r = ssh_update_card(agent_fd, add, id, pin == NULL ? "" : pin,
|
||||||
lifetime, confirm, dest_constraints, ndest_constraints)) == 0) {
|
lifetime, confirm, dest_constraints, ndest_constraints,
|
||||||
|
cert_only, certs, ncerts)) == 0) {
|
||||||
ret = 0;
|
ret = 0;
|
||||||
if (!qflag) {
|
if (!qflag) {
|
||||||
fprintf(stderr, "Card %s: %s\n",
|
fprintf(stderr, "Card %s: %s\n",
|
||||||
@ -633,16 +652,17 @@ load_resident_keys(int agent_fd, const char *skprovider, int qflag,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
do_file(int agent_fd, int deleting, int key_only, char *file, int qflag,
|
do_file(int agent_fd, int deleting, int key_only, int cert_only,
|
||||||
const char *skprovider, struct dest_constraint **dest_constraints,
|
char *file, int qflag, const char *skprovider,
|
||||||
size_t ndest_constraints)
|
struct dest_constraint **dest_constraints, size_t ndest_constraints)
|
||||||
{
|
{
|
||||||
if (deleting) {
|
if (deleting) {
|
||||||
if (delete_file(agent_fd, file, key_only, qflag) == -1)
|
if (delete_file(agent_fd, file, key_only,
|
||||||
|
cert_only, qflag) == -1)
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
if (add_file(agent_fd, file, key_only, qflag, skprovider,
|
if (add_file(agent_fd, file, key_only, cert_only, qflag,
|
||||||
dest_constraints, ndest_constraints) == -1)
|
skprovider, dest_constraints, ndest_constraints) == -1)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -790,12 +810,14 @@ main(int argc, char **argv)
|
|||||||
int agent_fd;
|
int agent_fd;
|
||||||
char *pkcs11provider = NULL, *skprovider = NULL;
|
char *pkcs11provider = NULL, *skprovider = NULL;
|
||||||
char **dest_constraint_strings = NULL, **hostkey_files = NULL;
|
char **dest_constraint_strings = NULL, **hostkey_files = NULL;
|
||||||
int r, i, ch, deleting = 0, ret = 0, key_only = 0, do_download = 0;
|
int r, i, ch, deleting = 0, ret = 0, key_only = 0, cert_only = 0;
|
||||||
int xflag = 0, lflag = 0, Dflag = 0, qflag = 0, Tflag = 0;
|
int do_download = 0, xflag = 0, lflag = 0, Dflag = 0;
|
||||||
|
int qflag = 0, Tflag = 0;
|
||||||
SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
|
SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
|
||||||
LogLevel log_level = SYSLOG_LEVEL_INFO;
|
LogLevel log_level = SYSLOG_LEVEL_INFO;
|
||||||
|
struct sshkey *k, **certs = NULL;
|
||||||
struct dest_constraint **dest_constraints = NULL;
|
struct dest_constraint **dest_constraints = NULL;
|
||||||
size_t ndest_constraints = 0;
|
size_t ndest_constraints = 0i, ncerts = 0;
|
||||||
|
|
||||||
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
|
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
|
||||||
sanitise_stdfd();
|
sanitise_stdfd();
|
||||||
@ -822,7 +844,7 @@ main(int argc, char **argv)
|
|||||||
|
|
||||||
skprovider = getenv("SSH_SK_PROVIDER");
|
skprovider = getenv("SSH_SK_PROVIDER");
|
||||||
|
|
||||||
while ((ch = getopt(argc, argv, "vkKlLcdDTxXE:e:h:H:M:m:qs:S:t:")) != -1) {
|
while ((ch = getopt(argc, argv, "vkKlLCcdDTxXE:e:h:H:M:m:qs:S:t:")) != -1) {
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
case 'v':
|
case 'v':
|
||||||
if (log_level == SYSLOG_LEVEL_INFO)
|
if (log_level == SYSLOG_LEVEL_INFO)
|
||||||
@ -844,6 +866,9 @@ main(int argc, char **argv)
|
|||||||
case 'k':
|
case 'k':
|
||||||
key_only = 1;
|
key_only = 1;
|
||||||
break;
|
break;
|
||||||
|
case 'C':
|
||||||
|
cert_only = 1;
|
||||||
|
break;
|
||||||
case 'K':
|
case 'K':
|
||||||
do_download = 1;
|
do_download = 1;
|
||||||
break;
|
break;
|
||||||
@ -962,8 +987,19 @@ main(int argc, char **argv)
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (pkcs11provider != NULL) {
|
if (pkcs11provider != NULL) {
|
||||||
|
for (i = 0; i < argc; i++) {
|
||||||
|
if ((r = sshkey_load_public(argv[i], &k, NULL)) != 0)
|
||||||
|
fatal_fr(r, "load certificate %s", argv[i]);
|
||||||
|
certs = xrecallocarray(certs, ncerts, ncerts + 1,
|
||||||
|
sizeof(*certs));
|
||||||
|
debug2("%s: %s", argv[i], sshkey_ssh_name(k));
|
||||||
|
certs[ncerts++] = k;
|
||||||
|
}
|
||||||
|
debug2_f("loaded %zu certificates", ncerts);
|
||||||
if (update_card(agent_fd, !deleting, pkcs11provider,
|
if (update_card(agent_fd, !deleting, pkcs11provider,
|
||||||
qflag, dest_constraints, ndest_constraints) == -1)
|
qflag, key_only, cert_only,
|
||||||
|
dest_constraints, ndest_constraints,
|
||||||
|
certs, ncerts) == -1)
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
@ -993,8 +1029,8 @@ main(int argc, char **argv)
|
|||||||
default_files[i]);
|
default_files[i]);
|
||||||
if (stat(buf, &st) == -1)
|
if (stat(buf, &st) == -1)
|
||||||
continue;
|
continue;
|
||||||
if (do_file(agent_fd, deleting, key_only, buf,
|
if (do_file(agent_fd, deleting, key_only, cert_only,
|
||||||
qflag, skprovider,
|
buf, qflag, skprovider,
|
||||||
dest_constraints, ndest_constraints) == -1)
|
dest_constraints, ndest_constraints) == -1)
|
||||||
ret = 1;
|
ret = 1;
|
||||||
else
|
else
|
||||||
@ -1004,7 +1040,7 @@ main(int argc, char **argv)
|
|||||||
ret = 1;
|
ret = 1;
|
||||||
} else {
|
} else {
|
||||||
for (i = 0; i < argc; i++) {
|
for (i = 0; i < argc; i++) {
|
||||||
if (do_file(agent_fd, deleting, key_only,
|
if (do_file(agent_fd, deleting, key_only, cert_only,
|
||||||
argv[i], qflag, skprovider,
|
argv[i], qflag, skprovider,
|
||||||
dest_constraints, ndest_constraints) == -1)
|
dest_constraints, ndest_constraints) == -1)
|
||||||
ret = 1;
|
ret = 1;
|
||||||
|
124
ssh-agent.c
124
ssh-agent.c
@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: ssh-agent.c,v 1.301 2023/12/18 14:46:12 djm Exp $ */
|
/* $OpenBSD: ssh-agent.c,v 1.302 2023/12/18 14:46:56 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
|
||||||
@ -105,6 +105,8 @@
|
|||||||
#define AGENT_MAX_SID_LEN 128
|
#define AGENT_MAX_SID_LEN 128
|
||||||
/* Maximum number of destination constraints to accept on a key */
|
/* Maximum number of destination constraints to accept on a key */
|
||||||
#define AGENT_MAX_DEST_CONSTRAINTS 1024
|
#define AGENT_MAX_DEST_CONSTRAINTS 1024
|
||||||
|
/* Maximum number of associated certificate constraints to accept on a key */
|
||||||
|
#define AGENT_MAX_EXT_CERTS 1024
|
||||||
|
|
||||||
/* XXX store hostkey_sid in a refcounted tree */
|
/* XXX store hostkey_sid in a refcounted tree */
|
||||||
|
|
||||||
@ -1158,11 +1160,14 @@ parse_dest_constraint(struct sshbuf *m, struct dest_constraint *dc)
|
|||||||
|
|
||||||
static int
|
static int
|
||||||
parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp,
|
parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp,
|
||||||
struct dest_constraint **dcsp, size_t *ndcsp)
|
struct dest_constraint **dcsp, size_t *ndcsp, int *cert_onlyp,
|
||||||
|
struct sshkey ***certs, size_t *ncerts)
|
||||||
{
|
{
|
||||||
char *ext_name = NULL;
|
char *ext_name = NULL;
|
||||||
int r;
|
int r;
|
||||||
struct sshbuf *b = NULL;
|
struct sshbuf *b = NULL;
|
||||||
|
u_char v;
|
||||||
|
struct sshkey *k;
|
||||||
|
|
||||||
if ((r = sshbuf_get_cstring(m, &ext_name, NULL)) != 0) {
|
if ((r = sshbuf_get_cstring(m, &ext_name, NULL)) != 0) {
|
||||||
error_fr(r, "parse constraint extension");
|
error_fr(r, "parse constraint extension");
|
||||||
@ -1205,6 +1210,36 @@ parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp,
|
|||||||
*dcsp + (*ndcsp)++)) != 0)
|
*dcsp + (*ndcsp)++)) != 0)
|
||||||
goto out; /* error already logged */
|
goto out; /* error already logged */
|
||||||
}
|
}
|
||||||
|
} else if (strcmp(ext_name,
|
||||||
|
"associated-certs-v00@openssh.com") == 0) {
|
||||||
|
if (certs == NULL || ncerts == NULL || cert_onlyp == NULL) {
|
||||||
|
error_f("%s not valid here", ext_name);
|
||||||
|
r = SSH_ERR_INVALID_FORMAT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (*certs != NULL) {
|
||||||
|
error_f("%s already set", ext_name);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if ((r = sshbuf_get_u8(m, &v)) != 0 ||
|
||||||
|
(r = sshbuf_froms(m, &b)) != 0) {
|
||||||
|
error_fr(r, "parse %s", ext_name);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
*cert_onlyp = v != 0;
|
||||||
|
while (sshbuf_len(b) != 0) {
|
||||||
|
if (*ncerts >= AGENT_MAX_EXT_CERTS) {
|
||||||
|
error_f("too many %s constraints", ext_name);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
*certs = xrecallocarray(*certs, *ncerts, *ncerts + 1,
|
||||||
|
sizeof(**certs));
|
||||||
|
if ((r = sshkey_froms(b, &k)) != 0) {
|
||||||
|
error_fr(r, "parse key");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
(*certs)[(*ncerts)++] = k;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
error_f("unsupported constraint \"%s\"", ext_name);
|
error_f("unsupported constraint \"%s\"", ext_name);
|
||||||
r = SSH_ERR_FEATURE_UNSUPPORTED;
|
r = SSH_ERR_FEATURE_UNSUPPORTED;
|
||||||
@ -1221,7 +1256,8 @@ parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp,
|
|||||||
static int
|
static int
|
||||||
parse_key_constraints(struct sshbuf *m, struct sshkey *k, time_t *deathp,
|
parse_key_constraints(struct sshbuf *m, struct sshkey *k, time_t *deathp,
|
||||||
u_int *secondsp, int *confirmp, char **sk_providerp,
|
u_int *secondsp, int *confirmp, char **sk_providerp,
|
||||||
struct dest_constraint **dcsp, size_t *ndcsp)
|
struct dest_constraint **dcsp, size_t *ndcsp,
|
||||||
|
int *cert_onlyp, size_t *ncerts, struct sshkey ***certs)
|
||||||
{
|
{
|
||||||
u_char ctype;
|
u_char ctype;
|
||||||
int r;
|
int r;
|
||||||
@ -1276,7 +1312,8 @@ parse_key_constraints(struct sshbuf *m, struct sshkey *k, time_t *deathp,
|
|||||||
break;
|
break;
|
||||||
case SSH_AGENT_CONSTRAIN_EXTENSION:
|
case SSH_AGENT_CONSTRAIN_EXTENSION:
|
||||||
if ((r = parse_key_constraint_extension(m,
|
if ((r = parse_key_constraint_extension(m,
|
||||||
sk_providerp, dcsp, ndcsp)) != 0)
|
sk_providerp, dcsp, ndcsp,
|
||||||
|
cert_onlyp, certs, ncerts)) != 0)
|
||||||
goto out; /* error already logged */
|
goto out; /* error already logged */
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -1313,7 +1350,8 @@ process_add_identity(SocketEntry *e)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (parse_key_constraints(e->request, k, &death, &seconds, &confirm,
|
if (parse_key_constraints(e->request, k, &death, &seconds, &confirm,
|
||||||
&sk_provider, &dest_constraints, &ndest_constraints) != 0) {
|
&sk_provider, &dest_constraints, &ndest_constraints,
|
||||||
|
NULL, NULL, NULL) != 0) {
|
||||||
error_f("failed to parse constraints");
|
error_f("failed to parse constraints");
|
||||||
sshbuf_reset(e->request);
|
sshbuf_reset(e->request);
|
||||||
goto out;
|
goto out;
|
||||||
@ -1473,6 +1511,32 @@ no_identities(SocketEntry *e)
|
|||||||
sshbuf_free(msg);
|
sshbuf_free(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Add an identity to idlist; takes ownership of 'key' and 'comment' */
|
||||||
|
static void
|
||||||
|
add_p11_identity(struct sshkey *key, char *comment, const char *provider,
|
||||||
|
time_t death, int confirm, struct dest_constraint *dest_constraints,
|
||||||
|
size_t ndest_constraints)
|
||||||
|
{
|
||||||
|
Identity *id;
|
||||||
|
|
||||||
|
if (lookup_identity(key) != NULL) {
|
||||||
|
sshkey_free(key);
|
||||||
|
free(comment);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
id = xcalloc(1, sizeof(Identity));
|
||||||
|
id->key = key;
|
||||||
|
id->comment = comment;
|
||||||
|
id->provider = xstrdup(provider);
|
||||||
|
id->death = death;
|
||||||
|
id->confirm = confirm;
|
||||||
|
id->dest_constraints = dup_dest_constraints(dest_constraints,
|
||||||
|
ndest_constraints);
|
||||||
|
id->ndest_constraints = ndest_constraints;
|
||||||
|
TAILQ_INSERT_TAIL(&idtab->idlist, id, next);
|
||||||
|
idtab->nentries++;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_PKCS11
|
#ifdef ENABLE_PKCS11
|
||||||
static void
|
static void
|
||||||
process_add_smartcard_key(SocketEntry *e)
|
process_add_smartcard_key(SocketEntry *e)
|
||||||
@ -1483,9 +1547,10 @@ process_add_smartcard_key(SocketEntry *e)
|
|||||||
u_int seconds = 0;
|
u_int seconds = 0;
|
||||||
time_t death = 0;
|
time_t death = 0;
|
||||||
struct sshkey **keys = NULL, *k;
|
struct sshkey **keys = NULL, *k;
|
||||||
Identity *id;
|
|
||||||
struct dest_constraint *dest_constraints = NULL;
|
struct dest_constraint *dest_constraints = NULL;
|
||||||
size_t ndest_constraints = 0;
|
size_t j, ndest_constraints = 0, ncerts = 0;
|
||||||
|
struct sshkey **certs = NULL;
|
||||||
|
int cert_only = 0;
|
||||||
|
|
||||||
debug2_f("entering");
|
debug2_f("entering");
|
||||||
if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 ||
|
if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 ||
|
||||||
@ -1494,7 +1559,8 @@ process_add_smartcard_key(SocketEntry *e)
|
|||||||
goto send;
|
goto send;
|
||||||
}
|
}
|
||||||
if (parse_key_constraints(e->request, NULL, &death, &seconds, &confirm,
|
if (parse_key_constraints(e->request, NULL, &death, &seconds, &confirm,
|
||||||
NULL, &dest_constraints, &ndest_constraints) != 0) {
|
NULL, &dest_constraints, &ndest_constraints, &cert_only,
|
||||||
|
&ncerts, &certs) != 0) {
|
||||||
error_f("failed to parse constraints");
|
error_f("failed to parse constraints");
|
||||||
goto send;
|
goto send;
|
||||||
}
|
}
|
||||||
@ -1520,25 +1586,28 @@ process_add_smartcard_key(SocketEntry *e)
|
|||||||
|
|
||||||
count = pkcs11_add_provider(canonical_provider, pin, &keys, &comments);
|
count = pkcs11_add_provider(canonical_provider, pin, &keys, &comments);
|
||||||
for (i = 0; i < count; i++) {
|
for (i = 0; i < count; i++) {
|
||||||
k = keys[i];
|
if (comments[i] == NULL || comments[i][0] == '\0') {
|
||||||
if (lookup_identity(k) == NULL) {
|
free(comments[i]);
|
||||||
id = xcalloc(1, sizeof(Identity));
|
comments[i] = xstrdup(canonical_provider);
|
||||||
id->key = k;
|
}
|
||||||
keys[i] = NULL; /* transferred */
|
for (j = 0; j < ncerts; j++) {
|
||||||
id->provider = xstrdup(canonical_provider);
|
if (!sshkey_is_cert(certs[j]))
|
||||||
if (*comments[i] != '\0') {
|
continue;
|
||||||
id->comment = comments[i];
|
if (!sshkey_equal_public(keys[i], certs[j]))
|
||||||
comments[i] = NULL; /* transferred */
|
continue;
|
||||||
} else {
|
if (pkcs11_make_cert(keys[i], certs[j], &k) != 0)
|
||||||
id->comment = xstrdup(canonical_provider);
|
continue;
|
||||||
}
|
add_p11_identity(k, xstrdup(comments[i]),
|
||||||
id->death = death;
|
canonical_provider, death, confirm,
|
||||||
id->confirm = confirm;
|
|
||||||
id->dest_constraints = dup_dest_constraints(
|
|
||||||
dest_constraints, ndest_constraints);
|
dest_constraints, ndest_constraints);
|
||||||
id->ndest_constraints = ndest_constraints;
|
success = 1;
|
||||||
TAILQ_INSERT_TAIL(&idtab->idlist, id, next);
|
}
|
||||||
idtab->nentries++;
|
if (!cert_only && lookup_identity(keys[i]) == NULL) {
|
||||||
|
add_p11_identity(keys[i], comments[i],
|
||||||
|
canonical_provider, death, confirm,
|
||||||
|
dest_constraints, ndest_constraints);
|
||||||
|
keys[i] = NULL; /* transferred */
|
||||||
|
comments[i] = NULL; /* transferred */
|
||||||
success = 1;
|
success = 1;
|
||||||
}
|
}
|
||||||
/* XXX update constraints for existing keys */
|
/* XXX update constraints for existing keys */
|
||||||
@ -1551,6 +1620,9 @@ send:
|
|||||||
free(keys);
|
free(keys);
|
||||||
free(comments);
|
free(comments);
|
||||||
free_dest_constraints(dest_constraints, ndest_constraints);
|
free_dest_constraints(dest_constraints, ndest_constraints);
|
||||||
|
for (j = 0; j < ncerts; j++)
|
||||||
|
sshkey_free(certs[j]);
|
||||||
|
free(certs);
|
||||||
send_status(e, success);
|
send_status(e, success);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: ssh-pkcs11-client.c,v 1.18 2023/07/19 14:03:45 djm Exp $ */
|
/* $OpenBSD: ssh-pkcs11-client.c,v 1.19 2023/12/18 14:46:56 djm Exp $ */
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2010 Markus Friedl. All rights reserved.
|
* Copyright (c) 2010 Markus Friedl. All rights reserved.
|
||||||
* Copyright (c) 2014 Pedro Martelletto. All rights reserved.
|
* Copyright (c) 2014 Pedro Martelletto. All rights reserved.
|
||||||
@ -426,6 +426,60 @@ wrap_key(struct helper *helper, struct sshkey *k)
|
|||||||
helper->path, helper->nrsa, helper->nec);
|
helper->path, helper->nrsa, helper->nec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make a private PKCS#11-backed certificate by grafting a previously-loaded
|
||||||
|
* PKCS#11 private key and a public certificate key.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
pkcs11_make_cert(const struct sshkey *priv,
|
||||||
|
const struct sshkey *certpub, struct sshkey **certprivp)
|
||||||
|
{
|
||||||
|
struct helper *helper = NULL;
|
||||||
|
struct sshkey *ret;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
debug3_f("private key type %s cert type %s", sshkey_type(priv),
|
||||||
|
sshkey_type(certpub));
|
||||||
|
*certprivp = NULL;
|
||||||
|
if (!sshkey_is_cert(certpub) || sshkey_is_cert(priv) ||
|
||||||
|
!sshkey_equal_public(priv, certpub)) {
|
||||||
|
error_f("private key %s doesn't match cert %s",
|
||||||
|
sshkey_type(priv), sshkey_type(certpub));
|
||||||
|
return SSH_ERR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
*certprivp = NULL;
|
||||||
|
if (priv->type == KEY_RSA) {
|
||||||
|
if ((helper = helper_by_rsa(priv->rsa)) == NULL ||
|
||||||
|
helper->fd == -1)
|
||||||
|
fatal_f("no helper for PKCS11 RSA key");
|
||||||
|
if ((r = sshkey_from_private(priv, &ret)) != 0)
|
||||||
|
fatal_fr(r, "copy key");
|
||||||
|
RSA_set_method(ret->rsa, helper->rsa_meth);
|
||||||
|
if (helper->nrsa++ >= INT_MAX)
|
||||||
|
fatal_f("RSA refcount error");
|
||||||
|
} else if (priv->type == KEY_ECDSA) {
|
||||||
|
if ((helper = helper_by_ec(priv->ecdsa)) == NULL ||
|
||||||
|
helper->fd == -1)
|
||||||
|
fatal_f("no helper for PKCS11 EC key");
|
||||||
|
if ((r = sshkey_from_private(priv, &ret)) != 0)
|
||||||
|
fatal_fr(r, "copy key");
|
||||||
|
EC_KEY_set_method(ret->ecdsa, helper->ec_meth);
|
||||||
|
if (helper->nec++ >= INT_MAX)
|
||||||
|
fatal_f("EC refcount error");
|
||||||
|
} else
|
||||||
|
fatal_f("unknown key type %s", sshkey_type(priv));
|
||||||
|
|
||||||
|
ret->flags |= SSHKEY_FLAG_EXT;
|
||||||
|
if ((r = sshkey_to_certified(ret)) != 0 ||
|
||||||
|
(r = sshkey_cert_copy(certpub, ret)) != 0)
|
||||||
|
fatal_fr(r, "graft certificate");
|
||||||
|
debug3_f("provider %s remaining keys: %zu RSA %zu ECDSA",
|
||||||
|
helper->path, helper->nrsa, helper->nec);
|
||||||
|
/* success */
|
||||||
|
*certprivp = ret;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
pkcs11_start_helper_methods(struct helper *helper)
|
pkcs11_start_helper_methods(struct helper *helper)
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* $OpenBSD: ssh-pkcs11.h,v 1.6 2020/01/25 00:03:36 djm Exp $ */
|
/* $OpenBSD: ssh-pkcs11.h,v 1.7 2023/12/18 14:46:56 djm Exp $ */
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2010 Markus Friedl. All rights reserved.
|
* Copyright (c) 2010 Markus Friedl. All rights reserved.
|
||||||
*
|
*
|
||||||
@ -35,6 +35,9 @@ struct sshkey *
|
|||||||
u_int32_t *);
|
u_int32_t *);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Only available in ssh-pkcs11-client.c so far */
|
||||||
|
int pkcs11_make_cert(const struct sshkey *,
|
||||||
|
const struct sshkey *, struct sshkey **);
|
||||||
#if !defined(WITH_OPENSSL) && defined(ENABLE_PKCS11)
|
#if !defined(WITH_OPENSSL) && defined(ENABLE_PKCS11)
|
||||||
#undef ENABLE_PKCS11
|
#undef ENABLE_PKCS11
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user