From 12f970ba7f7f2b6ebb5a9dd59f2c1c10542457c2 Mon Sep 17 00:00:00 2001 From: Alex Wilson Date: Tue, 27 Jun 2023 13:52:34 +1000 Subject: [PATCH] libpivy: add useful support functions --- Makefile | 13 +- bunyan.c | 56 ++++--- bunyan.h | 2 + ebox-cmd.c | 110 +----------- libpivy.version | 20 +++ piv-ca.c | 34 ++++ piv.c | 436 +++++++++++++++++++++++++++++++++--------------- piv.h | 9 + utils.c | 100 +++++++++++ utils.h | 9 + 10 files changed, 524 insertions(+), 265 deletions(-) diff --git a/Makefile b/Makefile index 1446a3c..ba6a9bc 100644 --- a/Makefile +++ b/Makefile @@ -358,10 +358,15 @@ pivy-tool: $(PIVTOOL_OBJS) $(LIBSSH) $(LIBCRYPTO) LIBPIVY_SOURCES= \ $(PIV_COMMON_SOURCES) \ $(PIV_CERT_SOURCES) \ + $(EBOX_COMMON_SOURCES) \ + $(SSS_SOURCES) \ + piv-ca.c \ cleanup-exit.c LIBPIVY_HEADERS= \ $(PIV_COMMON_HEADERS) \ - $(PIV_CERT_HEADERS) + $(PIV_CERT_HEADERS) \ + $(EBOX_COMMON_HEADERS) \ + $(PIV_CA_HEADERS) ifeq (yes, $(HAVE_JSONC)) LIBPIVY_SOURCES+= piv-ca.c LIBPIVY_HEADERS+= $(PIV_CA_HEADERS) @@ -370,6 +375,7 @@ LIBPIVY_OBJS= $(LIBPIVY_SOURCES:%.c=%.o) LIBPIVY_CFLAGS= $(PCSC_CFLAGS) \ $(CRYPTO_CFLAGS) \ $(ZLIB_CFLAGS) \ + $(JSONC_CFLAGS) \ $(SYSTEM_CFLAGS) \ $(SECURITY_CFLAGS) \ $(CONFIG_CFLAGS) \ @@ -383,11 +389,8 @@ LIBPIVY_LDFLAGS= $(SYSTEM_LDFLAGS) \ LIBPIVY_LIBS= $(CRYPTO_LIBS) \ $(PCSC_LIBS) \ $(ZLIB_LIBS) \ + $(JSONC_LIBS) \ $(SYSTEM_LIBS) -ifeq (yes, $(HAVE_JSONC)) - LIBPIVY_CFLAGS+= $(JSONC_CFLAGS) - LIBPIVY_LIBS+= $(JSONC_LIBS) -endif libpivy.so.1 : CFLAGS= $(LIBPIVY_CFLAGS) libpivy.so.1 : LIBS+= $(LIBPIVY_LIBS) diff --git a/bunyan.c b/bunyan.c index 3b21fa6..ab2697d 100644 --- a/bunyan.c +++ b/bunyan.c @@ -49,6 +49,13 @@ static const char *bunyan_name = NULL; * portable to lots of other operating systems. */ +static void +bunyan_default_printer(enum bunyan_log_level lvl, const char *msg) +{ + fprintf(stderr, "%s", msg); +} + +static bunyan_printer_t bunyan_printer = bunyan_default_printer; static char *bunyan_buf = NULL; static size_t bunyan_buf_sz = 0; @@ -92,6 +99,13 @@ bunyan_set_level(enum bunyan_log_level level) bunyan_min_level = level; } +void +bunyan_set_printer(bunyan_printer_t printer, boolean_t omit_timestamp) +{ + bunyan_printer = printer; + bunyan_omit_timestamp = omit_timestamp; +} + enum bunyan_log_level bunyan_get_level(void) { @@ -414,25 +428,27 @@ bunyan_log(enum bunyan_log_level level, const char *msg, ...) printf_buf("[%s] ", time); } - switch (level) { - case BNY_TRACE: - printf_buf("TRACE: "); - break; - case BNY_DEBUG: - printf_buf("DEBUG: "); - break; - case BNY_INFO: - printf_buf("INFO: "); - break; - case BNY_WARN: - printf_buf("WARN: "); - break; - case BNY_ERROR: - printf_buf("ERROR: "); - break; - case BNY_FATAL: - printf_buf("FATAL: "); - break; + if (bunyan_printer == bunyan_default_printer) { + switch (level) { + case BNY_TRACE: + printf_buf("TRACE: "); + break; + case BNY_DEBUG: + printf_buf("DEBUG: "); + break; + case BNY_INFO: + printf_buf("INFO: "); + break; + case BNY_WARN: + printf_buf("WARN: "); + break; + case BNY_ERROR: + printf_buf("ERROR: "); + break; + case BNY_FATAL: + printf_buf("FATAL: "); + break; + } } printf_buf("%s", msg); @@ -531,5 +547,5 @@ bunyan_log(enum bunyan_log_level level, const char *msg, ...) if (level < bunyan_min_level) { return; } - fprintf(stderr, "%s", bunyan_buf); + (*bunyan_printer)(level, bunyan_buf); } diff --git a/bunyan.h b/bunyan.h index dd2bb49..91ec912 100644 --- a/bunyan.h +++ b/bunyan.h @@ -35,6 +35,8 @@ enum bunyan_arg_type { void bunyan_init(void); void bunyan_unshare(void); void bunyan_set_name(const char *name); +typedef void (*bunyan_printer_t)(enum bunyan_log_level, const char *); +void bunyan_set_printer(bunyan_printer_t printer, boolean_t omit_timestamp); void bunyan_set_level(enum bunyan_log_level level); enum bunyan_log_level bunyan_get_level(void); void bunyan_log(enum bunyan_log_level level, const char *msg, ...); diff --git a/ebox-cmd.c b/ebox-cmd.c index 438139f..9d5611d 100644 --- a/ebox-cmd.c +++ b/ebox-cmd.c @@ -248,15 +248,11 @@ assert_pin(struct piv_token *pk, struct piv_slot *slot, const char *partname, errf_t * local_unlock_agent(struct piv_ecdh_box *box) { - struct piv_ecdh_box *rebox = NULL; - struct sshkey *pubkey, *temp = NULL, *temppub = NULL; + struct sshkey *pubkey; errf_t *err; int rc; uint i; - uint8_t code; struct ssh_identitylist *idl = NULL; - struct sshbuf *req = NULL, *buf = NULL, *boxbuf = NULL, *reply = NULL; - struct sshbuf *datab = NULL; boolean_t found = B_FALSE; if (ebox_authfd == -1 && @@ -285,115 +281,17 @@ local_unlock_agent(struct piv_ecdh_box *box) goto out; } - rc = sshkey_generate(KEY_ECDSA, sshkey_size(pubkey), &temp); - if (rc) { - err = ssherrf("sshkey_generate", rc); - goto out; - } - if ((rc = sshkey_demote(temp, &temppub))) { - err = ssherrf("sshkey_demote", rc); - goto out; - } - - req = sshbuf_new(); - reply = sshbuf_new(); - buf = sshbuf_new(); - boxbuf = sshbuf_new(); - if (req == NULL || reply == NULL || buf == NULL || boxbuf == NULL) { - err = ERRF_NOMEM; - goto out; - } - - if ((rc = sshbuf_put_u8(req, SSH_AGENTC_EXTENSION))) { - err = ssherrf("sshbuf_put_u8", rc); - goto out; - } - if ((rc = sshbuf_put_cstring(req, "ecdh-rebox@joyent.com"))) { - err = ssherrf("sshbuf_put_cstring", rc); - goto out; - } - - if ((err = sshbuf_put_piv_box(boxbuf, box))) - goto out; - if ((rc = sshbuf_put_stringb(buf, boxbuf))) { - err = ssherrf("sshbuf_put_stringb", rc); - goto out; - } - if ((rc = sshbuf_put_u32(buf, 0)) || - (rc = sshbuf_put_u8(buf, 0))) { - err = ssherrf("sshbuf_put_u32", rc); - goto out; - } - sshbuf_reset(boxbuf); - if ((rc = sshkey_putb(temppub, boxbuf))) { - err = ssherrf("sshkey_putb", rc); - goto out; - } - if ((rc = sshbuf_put_stringb(buf, boxbuf))) { - err = ssherrf("sshbuf_put_stringb", rc); - goto out; - } - if ((rc = sshbuf_put_u32(buf, 0))) { - err = ssherrf("sshbuf_put_u32", rc); - goto out; - } - - if ((rc = sshbuf_put_stringb(req, buf))) { - err = ssherrf("sshbuf_put_stringb", rc); - goto out; - } - if (!ebox_batch) { fprintf(stderr, "Using key '%s' in ssh-agent...\n", idl->comments[i]); } - rc = ssh_request_reply(ebox_authfd, req, reply); - if (rc) { - err = ssherrf("ssh_request_reply", rc); - goto out; - } - - if ((rc = sshbuf_get_u8(reply, &code))) { - err = ssherrf("sshbuf_get_u8", rc); - goto out; - } - if (code != SSH_AGENT_SUCCESS) { - err = errf("SSHAgentError", NULL, "SSH agent returned " - "message code %d to rebox request", (int)code); - goto out; - } - sshbuf_reset(boxbuf); - if ((rc = sshbuf_get_stringb(reply, boxbuf))) { - err = ssherrf("sshbuf_get_stringb", rc); - goto out; - } - if ((err = sshbuf_get_piv_box(boxbuf, &rebox))) - goto out; - - if ((err = piv_box_open_offline(temp, rebox))) - goto out; - - if ((err = piv_box_take_datab(rebox, &datab))) - goto out; - - if ((err = piv_box_set_datab(box, datab))) - goto out; - - err = ERRF_OK; + err = piv_box_open_agent(ebox_authfd, box); + if (err) + warnfx(err, "Failed to use key from ssh-agent"); out: - sshbuf_free(req); - sshbuf_free(reply); - sshbuf_free(buf); - sshbuf_free(boxbuf); - sshbuf_free(datab); - - sshkey_free(temp); - sshkey_free(temppub); - ssh_free_identitylist(idl); - piv_box_free(rebox); return (err); } diff --git a/libpivy.version b/libpivy.version index 4d5861d..e2674c6 100644 --- a/libpivy.version +++ b/libpivy.version @@ -46,6 +46,7 @@ CODEABI_1.0 { piv_box_new; piv_box_nonce_size; piv_box_open; + piv_box_open_agent; piv_box_open_offline; piv_box_pubkey; piv_box_seal; @@ -471,6 +472,7 @@ CODEABI_1.0 { bunyan_pop; bunyan_set_level; bunyan_set_name; + bunyan_set_printer; bunyan_unshare; /* @@ -689,5 +691,23 @@ CODEABI_1.0 { tlv_write_byte; tlv_write_u16; tlv_write_u8to32; + + /* + * selected LibreSSL bits + */ + X509_CRL_free; + X509_CRL_from_der; + X509_CRL_new; + X509_CRL_to_der; + X509_free; + X509_from_der; + X509_new; + X509_REQ_free; + X509_REQ_from_der; + X509_REQ_new; + X509_REQ_to_der; + X509_to_der; + + local: *; }; diff --git a/piv-ca.c b/piv-ca.c index eb75eec..c3fca3d 100644 --- a/piv-ca.c +++ b/piv-ca.c @@ -1519,6 +1519,40 @@ write_uri_array(json_object *array, struct ca_uri *head) return (ERRF_OK); } +const char * +ca_get_ebox_tpl(struct ca *ca, enum ca_ebox_type type) +{ + struct ca_ebox_tpl **ptr; + switch (type) { + case CA_EBOX_PIN: + case CA_EBOX_OLD_PIN: + ptr = &ca->ca_pin_tpl; + break; + case CA_EBOX_PUK: + ptr = &ca->ca_puk_tpl; + break; + case CA_EBOX_KEY_BACKUP: + ptr = &ca->ca_backup_tpl; + break; + case CA_EBOX_ADMIN_KEY: + ptr = &ca->ca_admin_tpl; + break; + } + if (*ptr == NULL) + return (NULL); + return ((*ptr)->cet_name); +} + +struct ebox_tpl * +ca_get_ebox_tpl_name(struct ca *ca, const char *name) +{ + struct ca_ebox_tpl *cet; + cet = get_ebox_tpl(&ca->ca_ebox_tpls, name, 0); + if (cet == NULL) + return (NULL); + return (cet->cet_tpl); +} + errf_t * ca_set_ebox_tpl(struct ca *ca, enum ca_ebox_type type, const char *tplname) { diff --git a/piv.c b/piv.c index 60bd11f..0ae8d7d 100644 --- a/piv.c +++ b/piv.c @@ -48,6 +48,7 @@ #include "openssh/sshbuf.h" #include "openssh/digest.h" #include "openssh/cipher.h" +#include "openssh/authfd.h" #include #include @@ -4992,16 +4993,16 @@ piv_box_take_datab(struct piv_ecdh_box *box, struct sshbuf **pbuf) return (ERRF_OK); } -errf_t * -piv_box_open_offline(struct sshkey *privkey, struct piv_ecdh_box *box) +static errf_t * +piv_box_open_common(uint8_t *sec, size_t seclen, struct piv_ecdh_box *box) { const struct sshcipher *cipher; int dgalg; struct sshcipher_ctx *cctx; struct ssh_digest_ctx *dgctx; - uint8_t *iv, *key, *sec, *enc, *plain; - size_t ivlen, authlen, blocksz, keylen, dglen, seclen; - size_t fieldsz, plainlen, enclen; + uint8_t *iv, *key, *enc, *plain; + size_t ivlen, authlen, blocksz, keylen, dglen; + size_t plainlen, enclen; size_t reallen, padding, i; errf_t *err; int rv; @@ -5009,6 +5010,9 @@ piv_box_open_offline(struct sshkey *privkey, struct piv_ecdh_box *box) VERIFY3P(box->pdb_cipher, !=, NULL); VERIFY3P(box->pdb_kdf, !=, NULL); + VERIFY3P(sec, !=, NULL); + VERIFY3U(seclen, >=, 0); + cipher = cipher_by_name(box->pdb_cipher); if (cipher == NULL) { err = boxverrf(errf("BadAlgorithmError", NULL, @@ -5036,21 +5040,6 @@ piv_box_open_offline(struct sshkey *privkey, struct piv_ecdh_box *box) return (err); } - fieldsz = EC_GROUP_get_degree(EC_KEY_get0_group(privkey->ecdsa)); - seclen = (fieldsz + 7) / 8; - sec = calloc_conceal(1, seclen); - VERIFY(sec != NULL); - rv = ECDH_compute_key(sec, seclen, - EC_KEY_get0_public_key(box->pdb_ephem_pub->ecdsa), privkey->ecdsa, - NULL); - if (rv <= 0) { - free(sec); - make_sslerrf(err, "ECDH_compute_key", "performing ECDH"); - err = boxderrf(err); - return (err); - } - seclen = (size_t)rv; - dgctx = ssh_digest_start(dgalg); VERIFY3P(dgctx, !=, NULL); VERIFY0(ssh_digest_update(dgctx, sec, seclen)); @@ -5138,51 +5127,40 @@ piv_box_open_offline(struct sshkey *privkey, struct piv_ecdh_box *box) } errf_t * -piv_box_open(struct piv_token *tk, struct piv_slot *slot, - struct piv_ecdh_box *box) +piv_box_open_offline(struct sshkey *privkey, struct piv_ecdh_box *box) { - const struct sshcipher *cipher; - int rv; + uint8_t *sec; + size_t seclen; + size_t fieldsz; errf_t *err; - int dgalg; - struct sshcipher_ctx *cctx; - struct ssh_digest_ctx *dgctx; - uint8_t *iv, *key, *sec, *enc, *plain; - size_t ivlen, authlen, blocksz, keylen, dglen, seclen; - size_t plainlen, enclen; - size_t reallen, padding, i; - - VERIFY3P(box->pdb_cipher, !=, NULL); - VERIFY3P(box->pdb_kdf, !=, NULL); + int rv; - cipher = cipher_by_name(box->pdb_cipher); - if (cipher == NULL) { - err = boxverrf(errf("BadAlgorithmError", NULL, - "Cipher '%s' is not supported", box->pdb_cipher)); + fieldsz = EC_GROUP_get_degree(EC_KEY_get0_group(privkey->ecdsa)); + seclen = (fieldsz + 7) / 8; + sec = calloc_conceal(1, seclen); + VERIFY(sec != NULL); + rv = ECDH_compute_key(sec, seclen, + EC_KEY_get0_public_key(box->pdb_ephem_pub->ecdsa), privkey->ecdsa, + NULL); + if (rv <= 0) { + free(sec); + make_sslerrf(err, "ECDH_compute_key", "performing ECDH"); + err = boxderrf(err); return (err); } - ivlen = cipher_ivlen(cipher); - authlen = cipher_authlen(cipher); - blocksz = cipher_blocksize(cipher); - keylen = cipher_keylen(cipher); - /* TODO: support non-authenticated ciphers by adding an HMAC */ - VERIFY3U(authlen, >, 0); + seclen = (size_t)rv; - dgalg = ssh_digest_alg_by_name(box->pdb_kdf); - if (dgalg == -1) { - err = boxverrf(errf("BadAlgorithmError", NULL, - "KDF digest '%s' is not supported", box->pdb_kdf)); - return (err); - } - dglen = ssh_digest_bytes(dgalg); - if (dglen < keylen) { - err = boxderrf(errf("BadAlgorithmError", NULL, - "KDF digest '%s' produces output too short for use as " - "key with cipher '%s'", box->pdb_kdf, box->pdb_cipher)); - return (err); - } + return (piv_box_open_common(sec, seclen, box)); +} + +errf_t * +piv_box_open(struct piv_token *tk, struct piv_slot *slot, + struct piv_ecdh_box *box) +{ + errf_t *err; + uint8_t *sec = NULL; + size_t seclen; - sec = NULL; VERIFY3P(box->pdb_ephem_pub, !=, NULL); err = piv_ecdh(tk, slot, box->pdb_ephem_pub, &sec, &seclen); if (err) { @@ -5190,83 +5168,8 @@ piv_box_open(struct piv_token *tk, struct piv_slot *slot, "operation needed to decrypt PIVBox"); return (err); } - VERIFY3P(sec, !=, NULL); - VERIFY3U(seclen, >=, 0); - - dgctx = ssh_digest_start(dgalg); - VERIFY3P(dgctx, !=, NULL); - VERIFY0(ssh_digest_update(dgctx, sec, seclen)); - if (box->pdb_nonce.b_len > 0) { - /* See comment in piv_box_open_offline */ - VERIFY0(ssh_digest_update(dgctx, box->pdb_nonce.b_data + - box->pdb_nonce.b_offset, box->pdb_nonce.b_len)); - } - key = calloc_conceal(1, dglen); - VERIFY3P(key, !=, NULL); - VERIFY0(ssh_digest_final(dgctx, key, dglen)); - ssh_digest_free(dgctx); - - freezero(sec, seclen); - - VERIFYB(box->pdb_iv); - iv = box->pdb_iv.b_data + box->pdb_iv.b_offset; - if (box->pdb_iv.b_len != ivlen) { - err = boxderrf(errf("LengthError", NULL, "IV length (%d) is not " - "appropriate for cipher '%s'", ivlen, box->pdb_cipher)); - return (err); - } - - VERIFYB(box->pdb_enc); - enc = box->pdb_enc.b_data + box->pdb_enc.b_offset; - enclen = box->pdb_enc.b_len; - if (enclen < authlen + blocksz) { - err = boxderrf(errf("LengthError", NULL, "Ciphertext length (%d) " - "is smaller than minimum length (auth tag + 1 block = %d)", - enclen, authlen + blocksz)); - return (err); - } - - plainlen = enclen - authlen; - plain = calloc_conceal(1, plainlen); - VERIFY3P(plain, !=, NULL); - - VERIFY0(cipher_init(&cctx, cipher, key, keylen, iv, ivlen, 0)); - rv = cipher_crypt(cctx, 0, plain, enc, enclen - authlen, 0, - authlen); - cipher_free(cctx); - - freezero(key, dglen); - - if (rv != 0) { - err = boxderrf(ssherrf("cipher_crypt", rv)); - return (err); - } - - /* Strip off the pkcs#7 padding and verify it. */ - padding = plain[plainlen - 1]; - if (padding < 1 || padding > blocksz) - goto paderr; - reallen = plainlen - padding; - for (i = reallen; i < plainlen; ++i) { - if (plain[i] != padding) { - goto paderr; - } - } - - if (box->pdb_plain.b_data != NULL) { - freezero(box->pdb_plain.b_data, box->pdb_plain.b_size); - } - box->pdb_plain.b_data = plain; - box->pdb_plain.b_offset = 0; - box->pdb_plain.b_size = plainlen; - box->pdb_plain.b_len = reallen; - - return (ERRF_OK); -paderr: - err = boxderrf(errf("PaddingError", NULL, "Padding failed validation")); - freezero(plain, plainlen); - return (err); + return (piv_box_open_common(sec, seclen, box)); } errf_t * @@ -8196,3 +8099,268 @@ piv_pinfo_get_kv(const struct piv_pinfo *pp, const char *key, size_t *len) *len = kv->ppk_len; return (kv->ppk_data); } + +errf_t * +piv_box_open_agent(int fd, struct piv_ecdh_box *box) +{ + struct piv_ecdh_box *rebox = NULL; + struct sshkey *pubkey, *temp = NULL, *temppub = NULL; + errf_t *err; + int rc; + uint i; + size_t len; + uint8_t code; + uint32_t nexts; + char *extname; + int has_rebox = 0, has_ecdh = 0; + struct ssh_identitylist *idl = NULL; + struct sshbuf *req = NULL, *buf = NULL, *boxbuf = NULL, *reply = NULL; + struct sshbuf *datab = NULL; + uint8_t *sec = NULL; + size_t seclen; + boolean_t found = B_FALSE; + + pubkey = piv_box_pubkey(box); + + rc = ssh_fetch_identitylist(fd, &idl); + if (rc) { + err = ssherrf("ssh_fetch_identitylist", rc); + goto out; + } + + for (i = 0; i < idl->nkeys; ++i) { + if (sshkey_equal_public(idl->keys[i], pubkey)) { + found = B_TRUE; + break; + } + } + if (!found) { + err = errf("KeyNotFound", NULL, "No matching key found in " + "SSH agent"); + goto out; + } + + req = sshbuf_new(); + reply = sshbuf_new(); + if (req == NULL || reply == NULL) { + err = ERRF_NOMEM; + goto out; + } + + if ((rc = sshbuf_put_u8(req, SSH_AGENTC_EXTENSION))) { + err = ssherrf("sshbuf_put_u8", rc); + goto out; + } + if ((rc = sshbuf_put_cstring(req, "query"))) { + err = ssherrf("sshbuf_put_cstring", rc); + goto out; + } + if ((rc = sshbuf_put_u32(req, 0))) { + err = ssherrf("sshbuf_put_u32", rc); + goto out; + } + rc = ssh_request_reply(fd, req, reply); + if (rc) { + err = ssherrf("ssh_request_reply", rc); + goto out; + } + + if ((rc = sshbuf_get_u8(reply, &code))) { + err = ssherrf("sshbuf_get_u8", rc); + goto out; + } + if (code != SSH_AGENT_SUCCESS) { + err = errf("NotSupportedError", NULL, "SSH agent does not " + "support 'query' extension (returned code %d)", (int)code); + goto out; + } + if ((rc = sshbuf_get_u32(reply, &nexts))) { + err = ssherrf("sshbuf_get_u32", rc); + goto out; + } + for (i = 0; i < nexts; ++i) { + if ((rc = sshbuf_get_cstring(reply, &extname, &len))) { + err = ssherrf("sshbuf_get_cstring", rc); + goto out; + } + if (strcmp("ecdh-rebox@joyent.com", extname) == 0) + has_rebox = 1; + else if (strcmp("ecdh@joyent.com", extname) == 0) + has_ecdh = 1; + free(extname); + extname = NULL; + } + + sshbuf_reset(req); + sshbuf_reset(reply); + buf = sshbuf_new(); + boxbuf = sshbuf_new(); + if (buf == NULL || boxbuf == NULL) { + err = ERRF_NOMEM; + goto out; + } + + if (has_rebox) { + rc = sshkey_generate(KEY_ECDSA, sshkey_size(pubkey), &temp); + if (rc) { + err = ssherrf("sshkey_generate", rc); + goto out; + } + if ((rc = sshkey_demote(temp, &temppub))) { + err = ssherrf("sshkey_demote", rc); + goto out; + } + + if ((rc = sshbuf_put_u8(req, SSH_AGENTC_EXTENSION))) { + err = ssherrf("sshbuf_put_u8", rc); + goto out; + } + if ((rc = sshbuf_put_cstring(req, "ecdh-rebox@joyent.com"))) { + err = ssherrf("sshbuf_put_cstring", rc); + goto out; + } + + if ((err = sshbuf_put_piv_box(boxbuf, box))) + goto out; + if ((rc = sshbuf_put_stringb(buf, boxbuf))) { + err = ssherrf("sshbuf_put_stringb", rc); + goto out; + } + if ((rc = sshbuf_put_u32(buf, 0)) || + (rc = sshbuf_put_u8(buf, 0))) { + err = ssherrf("sshbuf_put_u32", rc); + goto out; + } + sshbuf_reset(boxbuf); + if ((rc = sshkey_putb(temppub, boxbuf))) { + err = ssherrf("sshkey_putb", rc); + goto out; + } + if ((rc = sshbuf_put_stringb(buf, boxbuf))) { + err = ssherrf("sshbuf_put_stringb", rc); + goto out; + } + if ((rc = sshbuf_put_u32(buf, 0))) { + err = ssherrf("sshbuf_put_u32", rc); + goto out; + } + + if ((rc = sshbuf_put_stringb(req, buf))) { + err = ssherrf("sshbuf_put_stringb", rc); + goto out; + } + + rc = ssh_request_reply(fd, req, reply); + if (rc) { + err = ssherrf("ssh_request_reply", rc); + goto out; + } + + if ((rc = sshbuf_get_u8(reply, &code))) { + err = ssherrf("sshbuf_get_u8", rc); + goto out; + } + if (code != SSH_AGENT_SUCCESS) { + err = errf("SSHAgentError", NULL, "SSH agent returned " + "message code %d to rebox request", (int)code); + goto out; + } + sshbuf_reset(boxbuf); + if ((rc = sshbuf_get_stringb(reply, boxbuf))) { + err = ssherrf("sshbuf_get_stringb", rc); + goto out; + } + + if ((err = sshbuf_get_piv_box(boxbuf, &rebox))) + goto out; + + if ((err = piv_box_open_offline(temp, rebox))) + goto out; + + if ((err = piv_box_take_datab(rebox, &datab))) + goto out; + + if ((err = piv_box_set_datab(box, datab))) + goto out; + + err = ERRF_OK; + goto out; + } + + if (has_ecdh) { + if ((rc = sshkey_putb(pubkey, boxbuf))) { + err = ssherrf("sshkey_putb", rc); + goto out; + } + if ((rc = sshbuf_put_stringb(buf, boxbuf))) { + err = ssherrf("sshbuf_put_stringb", rc); + goto out; + } + sshbuf_reset(boxbuf); + if ((rc = sshkey_putb(box->pdb_ephem_pub, boxbuf))) { + err = ssherrf("sshkey_putb", rc); + goto out; + } + if ((rc = sshbuf_put_stringb(buf, boxbuf))) { + err = ssherrf("sshbuf_put_stringb", rc); + goto out; + } + if ((rc = sshbuf_put_u32(buf, 0))) { + err = ssherrf("sshbuf_put_u32", rc); + goto out; + } + + if ((rc = sshbuf_put_u8(req, SSH_AGENTC_EXTENSION))) { + err = ssherrf("sshbuf_put_u8", rc); + goto out; + } + if ((rc = sshbuf_put_cstring(req, "ecdh@joyent.com"))) { + err = ssherrf("sshbuf_put_cstring", rc); + goto out; + } + if ((rc = sshbuf_put_stringb(req, buf))) { + err = ssherrf("sshbuf_put_stringb", rc); + goto out; + } + + rc = ssh_request_reply(fd, req, reply); + if (rc) { + err = ssherrf("ssh_request_reply", rc); + goto out; + } + + if ((rc = sshbuf_get_u8(reply, &code))) { + err = ssherrf("sshbuf_get_u8", rc); + goto out; + } + if (code != SSH_AGENT_SUCCESS) { + err = errf("SSHAgentError", NULL, "SSH agent returned " + "message code %d to ECDH request", (int)code); + goto out; + } + if ((rc = sshbuf_get_string(reply, &sec, &seclen))) { + err = ssherrf("sshbuf_get_string", rc); + goto out; + } + + err = piv_box_open_common(sec, seclen, box); + goto out; + } + + err = errf("NotSupportedError", NULL, "SSH agent does not support " + "ECDH extensions"); + +out: + sshbuf_free(req); + sshbuf_free(reply); + sshbuf_free(buf); + sshbuf_free(boxbuf); + sshbuf_free(datab); + + sshkey_free(temp); + sshkey_free(temppub); + + ssh_free_identitylist(idl); + piv_box_free(rebox); + return (err); +} diff --git a/piv.h b/piv.h index 1675a32..385ec76 100644 --- a/piv.h +++ b/piv.h @@ -1010,6 +1010,15 @@ errf_t *piv_box_take_data(struct piv_ecdh_box *box, uint8_t **data, size_t *len) MUST_CHECK errf_t *piv_box_take_datab(struct piv_ecdh_box *box, struct sshbuf **buf); +/* + * Errors: + * - KeyNotFound + * - NotSupported + * - SSHAgentError + */ +MUST_CHECK +errf_t *piv_box_open_agent(int fd, struct piv_ecdh_box *box); + MUST_CHECK errf_t *sshbuf_put_piv_box(struct sshbuf *buf, struct piv_ecdh_box *box); MUST_CHECK diff --git a/utils.c b/utils.c index f16d777..5486cdc 100644 --- a/utils.c +++ b/utils.c @@ -17,6 +17,10 @@ #include "debug.h" #include "errf.h" +#include +#include +#include + #include "openssh/config.h" #include "openssh/sshbuf.h" #include "openssh/ssherr.h" @@ -391,3 +395,99 @@ unparse_lifetime(unsigned long secs) return (ret); } + +struct errf * +X509_to_der(X509 *cert, uint8_t **pbuf, size_t *plen) +{ + int rc; + uint8_t *cbuf = NULL; + errf_t *err; + rc = i2d_X509(cert, &cbuf); + if (rc < 0) { + make_sslerrf(err, "i2d_X509", "converting X509 cert to DER"); + return (err); + } + *plen = rc; + *pbuf = cbuf; + return (ERRF_OK); +} + +struct errf * +X509_REQ_to_der(X509_REQ *req, uint8_t **pbuf, size_t *plen) +{ + int rc; + uint8_t *cbuf = NULL; + errf_t *err; + rc = i2d_X509_REQ(req, &cbuf); + if (rc < 0) { + make_sslerrf(err, "i2d_X509_REQ", "converting X509 req to DER"); + return (err); + } + *plen = rc; + *pbuf = cbuf; + return (ERRF_OK); +} + +struct errf * +X509_CRL_to_der(X509_CRL *crl, uint8_t **pbuf, size_t *plen) +{ + int rc; + uint8_t *cbuf = NULL; + errf_t *err; + rc = i2d_X509_CRL(crl, &cbuf); + if (rc < 0) { + make_sslerrf(err, "i2d_X509_CRL", "converting X509 CRL to DER"); + return (err); + } + *plen = rc; + *pbuf = cbuf; + return (ERRF_OK); +} + +struct errf * +X509_from_der(const uint8_t *buf, size_t len, X509 **pcert) +{ + const unsigned char *p; + X509 *x; + errf_t *err; + p = buf; + x = d2i_X509(NULL, &p, len); + if (x == NULL) { + make_sslerrf(err, "d2i_X509", "parsing X509 certificate"); + return (err); + } + *pcert = x; + return (ERRF_OK); +} + +struct errf * +X509_REQ_from_der(const uint8_t *buf, size_t len, X509_REQ **preq) +{ + const unsigned char *p; + X509_REQ *x; + errf_t *err; + p = buf; + x = d2i_X509_REQ(NULL, &p, len); + if (x == NULL) { + make_sslerrf(err, "d2i_X509_REQ", "parsing X509 cert req"); + return (err); + } + *preq = x; + return (ERRF_OK); +} + +struct errf * +X509_CRL_from_der(const uint8_t *buf, size_t len, X509_CRL **pcrl) +{ + const unsigned char *p; + X509_CRL *x; + errf_t *err; + p = buf; + x = d2i_X509_CRL(NULL, &p, len); + if (x == NULL) { + make_sslerrf(err, "d2i_X509_CRL", "parsing X509 CRL"); + return (err); + } + *pcrl = x; + return (ERRF_OK); +} diff --git a/utils.h b/utils.h index 8382e33..24b2708 100644 --- a/utils.h +++ b/utils.h @@ -14,9 +14,18 @@ #include #include +#include + /* Can't use errf_t here (errf.h #includes this first) */ struct errf; +struct errf *X509_to_der(X509 *, uint8_t **, size_t *); +struct errf *X509_REQ_to_der(X509_REQ *, uint8_t **, size_t *); +struct errf *X509_CRL_to_der(X509_CRL *, uint8_t **, size_t *); +struct errf *X509_from_der(const uint8_t *, size_t, X509 **); +struct errf *X509_REQ_from_der(const uint8_t *, size_t, X509_REQ **); +struct errf *X509_CRL_from_der(const uint8_t *, size_t, X509_CRL **); + #if !defined(__APPLE__) && !defined(__sun) typedef uint64_t uintmax_t; #endif