1*479c151dSjsg /* $OpenBSD: ssh-pkcs11.c,v 1.64 2024/09/20 02:00:46 jsg Exp $ */
241503fafSmarkus /*
341503fafSmarkus * Copyright (c) 2010 Markus Friedl. All rights reserved.
421f43f82Sdjm * Copyright (c) 2014 Pedro Martelletto. All rights reserved.
541503fafSmarkus *
641503fafSmarkus * Permission to use, copy, modify, and distribute this software for any
741503fafSmarkus * purpose with or without fee is hereby granted, provided that the above
841503fafSmarkus * copyright notice and this permission notice appear in all copies.
941503fafSmarkus *
1041503fafSmarkus * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1141503fafSmarkus * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1241503fafSmarkus * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1341503fafSmarkus * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1441503fafSmarkus * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1541503fafSmarkus * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1641503fafSmarkus * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1741503fafSmarkus */
1841503fafSmarkus
1941503fafSmarkus #include <sys/types.h>
2041503fafSmarkus #include <sys/queue.h>
2141503fafSmarkus #include <stdarg.h>
2241503fafSmarkus #include <stdio.h>
2341503fafSmarkus
2421f43f82Sdjm #include <ctype.h>
2541503fafSmarkus #include <string.h>
2641503fafSmarkus #include <dlfcn.h>
2741503fafSmarkus
2821f43f82Sdjm #include <openssl/ecdsa.h>
2912770bd2Smarkus #include <openssl/x509.h>
3021f43f82Sdjm #include <openssl/err.h>
3112770bd2Smarkus
3241503fafSmarkus #define CRYPTOKI_COMPAT
3341503fafSmarkus #include "pkcs11.h"
3441503fafSmarkus
3541503fafSmarkus #include "log.h"
3641503fafSmarkus #include "misc.h"
372aa7d220Sdjm #include "sshkey.h"
3841503fafSmarkus #include "ssh-pkcs11.h"
39d176a5c8Sdjm #include "digest.h"
4041503fafSmarkus #include "xmalloc.h"
4141503fafSmarkus
4241503fafSmarkus struct pkcs11_slotinfo {
4341503fafSmarkus CK_TOKEN_INFO token;
4441503fafSmarkus CK_SESSION_HANDLE session;
4541503fafSmarkus int logged_in;
4641503fafSmarkus };
4741503fafSmarkus
4841503fafSmarkus struct pkcs11_provider {
4941503fafSmarkus char *name;
5041503fafSmarkus void *handle;
5141503fafSmarkus CK_FUNCTION_LIST *function_list;
5241503fafSmarkus CK_INFO info;
5341503fafSmarkus CK_ULONG nslots;
5441503fafSmarkus CK_SLOT_ID *slotlist;
5541503fafSmarkus struct pkcs11_slotinfo *slotinfo;
5641503fafSmarkus int valid;
5741503fafSmarkus int refcount;
5841503fafSmarkus TAILQ_ENTRY(pkcs11_provider) next;
5941503fafSmarkus };
6041503fafSmarkus
6141503fafSmarkus TAILQ_HEAD(, pkcs11_provider) pkcs11_providers;
6241503fafSmarkus
6341503fafSmarkus struct pkcs11_key {
6441503fafSmarkus struct pkcs11_provider *provider;
6541503fafSmarkus CK_ULONG slotidx;
6641503fafSmarkus char *keyid;
6741503fafSmarkus int keyid_len;
6841503fafSmarkus };
6941503fafSmarkus
7041503fafSmarkus int pkcs11_interactive = 0;
7141503fafSmarkus
7221f43f82Sdjm #ifdef HAVE_DLOPEN
7321f43f82Sdjm static void
ossl_error(const char * msg)7421f43f82Sdjm ossl_error(const char *msg)
7521f43f82Sdjm {
7621f43f82Sdjm unsigned long e;
7721f43f82Sdjm
7848e6b99dSdjm error_f("%s", msg);
7921f43f82Sdjm while ((e = ERR_get_error()) != 0)
8048e6b99dSdjm error_f("libcrypto error: %s", ERR_error_string(e, NULL));
8121f43f82Sdjm }
8221f43f82Sdjm #endif
8321f43f82Sdjm
8441503fafSmarkus int
pkcs11_init(int interactive)8541503fafSmarkus pkcs11_init(int interactive)
8641503fafSmarkus {
8741503fafSmarkus pkcs11_interactive = interactive;
8841503fafSmarkus TAILQ_INIT(&pkcs11_providers);
8941503fafSmarkus return (0);
9041503fafSmarkus }
9141503fafSmarkus
9241503fafSmarkus /*
9321f43f82Sdjm * finalize a provider shared library, it's no longer usable.
9441503fafSmarkus * however, there might still be keys referencing this provider,
9521f43f82Sdjm * so the actual freeing of memory is handled by pkcs11_provider_unref().
9641503fafSmarkus * this is called when a provider gets unregistered.
9741503fafSmarkus */
9841503fafSmarkus static void
pkcs11_provider_finalize(struct pkcs11_provider * p)9941503fafSmarkus pkcs11_provider_finalize(struct pkcs11_provider *p)
10041503fafSmarkus {
10141503fafSmarkus CK_RV rv;
10241503fafSmarkus CK_ULONG i;
10341503fafSmarkus
104efa52ff8Sdjm debug_f("provider \"%s\" refcount %d valid %d",
105efa52ff8Sdjm p->name, p->refcount, p->valid);
10641503fafSmarkus if (!p->valid)
10741503fafSmarkus return;
10841503fafSmarkus for (i = 0; i < p->nslots; i++) {
10941503fafSmarkus if (p->slotinfo[i].session &&
11041503fafSmarkus (rv = p->function_list->C_CloseSession(
11141503fafSmarkus p->slotinfo[i].session)) != CKR_OK)
11241503fafSmarkus error("C_CloseSession failed: %lu", rv);
11341503fafSmarkus }
11441503fafSmarkus if ((rv = p->function_list->C_Finalize(NULL)) != CKR_OK)
11541503fafSmarkus error("C_Finalize failed: %lu", rv);
11641503fafSmarkus p->valid = 0;
11741503fafSmarkus p->function_list = NULL;
118cd71c241Sderaadt #ifdef HAVE_DLOPEN
11941503fafSmarkus dlclose(p->handle);
120cd71c241Sderaadt #endif
12141503fafSmarkus }
12241503fafSmarkus
12341503fafSmarkus /*
12441503fafSmarkus * remove a reference to the provider.
12541503fafSmarkus * called when a key gets destroyed or when the provider is unregistered.
12641503fafSmarkus */
12741503fafSmarkus static void
pkcs11_provider_unref(struct pkcs11_provider * p)12841503fafSmarkus pkcs11_provider_unref(struct pkcs11_provider *p)
12941503fafSmarkus {
130efa52ff8Sdjm debug_f("provider \"%s\" refcount %d", p->name, p->refcount);
13141503fafSmarkus if (--p->refcount <= 0) {
13241503fafSmarkus if (p->valid)
133efa52ff8Sdjm error_f("provider \"%s\" still valid", p->name);
13421f43f82Sdjm free(p->name);
1350d40fefdSdjm free(p->slotlist);
1360d40fefdSdjm free(p->slotinfo);
1370d40fefdSdjm free(p);
13841503fafSmarkus }
13941503fafSmarkus }
14041503fafSmarkus
14141503fafSmarkus /* unregister all providers, keys might still point to the providers */
14241503fafSmarkus void
pkcs11_terminate(void)14341503fafSmarkus pkcs11_terminate(void)
14441503fafSmarkus {
14541503fafSmarkus struct pkcs11_provider *p;
14641503fafSmarkus
14741503fafSmarkus while ((p = TAILQ_FIRST(&pkcs11_providers)) != NULL) {
14841503fafSmarkus TAILQ_REMOVE(&pkcs11_providers, p, next);
14941503fafSmarkus pkcs11_provider_finalize(p);
15041503fafSmarkus pkcs11_provider_unref(p);
15141503fafSmarkus }
15241503fafSmarkus }
15341503fafSmarkus
15441503fafSmarkus /* lookup provider by name */
15541503fafSmarkus static struct pkcs11_provider *
pkcs11_provider_lookup(char * provider_id)15641503fafSmarkus pkcs11_provider_lookup(char *provider_id)
15741503fafSmarkus {
15841503fafSmarkus struct pkcs11_provider *p;
15941503fafSmarkus
16041503fafSmarkus TAILQ_FOREACH(p, &pkcs11_providers, next) {
161efa52ff8Sdjm debug("check provider \"%s\"", p->name);
16241503fafSmarkus if (!strcmp(provider_id, p->name))
16341503fafSmarkus return (p);
16441503fafSmarkus }
16541503fafSmarkus return (NULL);
16641503fafSmarkus }
16741503fafSmarkus
16841503fafSmarkus /* unregister provider by name */
16941503fafSmarkus int
pkcs11_del_provider(char * provider_id)17041503fafSmarkus pkcs11_del_provider(char *provider_id)
17141503fafSmarkus {
17241503fafSmarkus struct pkcs11_provider *p;
17341503fafSmarkus
17441503fafSmarkus if ((p = pkcs11_provider_lookup(provider_id)) != NULL) {
17541503fafSmarkus TAILQ_REMOVE(&pkcs11_providers, p, next);
17641503fafSmarkus pkcs11_provider_finalize(p);
17741503fafSmarkus pkcs11_provider_unref(p);
17841503fafSmarkus return (0);
17941503fafSmarkus }
18041503fafSmarkus return (-1);
18141503fafSmarkus }
18241503fafSmarkus
18321f43f82Sdjm #ifdef HAVE_DLOPEN
184dec222b5Sdjm static RSA_METHOD *rsa_method;
185dec222b5Sdjm static int rsa_idx = 0;
186dec222b5Sdjm static EC_KEY_METHOD *ec_key_method;
187dec222b5Sdjm static int ec_key_idx = 0;
188dec222b5Sdjm
1891ab0eecfSdjm /* release a wrapped object */
1901ab0eecfSdjm static void
pkcs11_k11_free(void * parent,void * ptr,CRYPTO_EX_DATA * ad,int idx,long argl,void * argp)1911ab0eecfSdjm pkcs11_k11_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
1921ab0eecfSdjm long argl, void *argp)
19341503fafSmarkus {
1941ab0eecfSdjm struct pkcs11_key *k11 = ptr;
19541503fafSmarkus
19648e6b99dSdjm debug_f("parent %p ptr %p idx %d", parent, ptr, idx);
1971ab0eecfSdjm if (k11 == NULL)
1981ab0eecfSdjm return;
19941503fafSmarkus if (k11->provider)
20041503fafSmarkus pkcs11_provider_unref(k11->provider);
2010d40fefdSdjm free(k11->keyid);
2020d40fefdSdjm free(k11);
20341503fafSmarkus }
20441503fafSmarkus
2052f7c3d34Smarkus /* find a single 'obj' for given attributes */
2062f7c3d34Smarkus static int
pkcs11_find(struct pkcs11_provider * p,CK_ULONG slotidx,CK_ATTRIBUTE * attr,CK_ULONG nattr,CK_OBJECT_HANDLE * obj)2072f7c3d34Smarkus pkcs11_find(struct pkcs11_provider *p, CK_ULONG slotidx, CK_ATTRIBUTE *attr,
2082f7c3d34Smarkus CK_ULONG nattr, CK_OBJECT_HANDLE *obj)
2092f7c3d34Smarkus {
2102f7c3d34Smarkus CK_FUNCTION_LIST *f;
2112f7c3d34Smarkus CK_SESSION_HANDLE session;
2122f7c3d34Smarkus CK_ULONG nfound = 0;
2132f7c3d34Smarkus CK_RV rv;
2142f7c3d34Smarkus int ret = -1;
2152f7c3d34Smarkus
2162f7c3d34Smarkus f = p->function_list;
2172f7c3d34Smarkus session = p->slotinfo[slotidx].session;
2182f7c3d34Smarkus if ((rv = f->C_FindObjectsInit(session, attr, nattr)) != CKR_OK) {
2192f7c3d34Smarkus error("C_FindObjectsInit failed (nattr %lu): %lu", nattr, rv);
2202f7c3d34Smarkus return (-1);
2212f7c3d34Smarkus }
2222f7c3d34Smarkus if ((rv = f->C_FindObjects(session, obj, 1, &nfound)) != CKR_OK ||
2232f7c3d34Smarkus nfound != 1) {
2242f7c3d34Smarkus debug("C_FindObjects failed (nfound %lu nattr %lu): %lu",
2252f7c3d34Smarkus nfound, nattr, rv);
2262f7c3d34Smarkus } else
2272f7c3d34Smarkus ret = 0;
2282f7c3d34Smarkus if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK)
2292f7c3d34Smarkus error("C_FindObjectsFinal failed: %lu", rv);
2302f7c3d34Smarkus return (ret);
2312f7c3d34Smarkus }
2322f7c3d34Smarkus
23341503fafSmarkus static int
pkcs11_login_slot(struct pkcs11_provider * provider,struct pkcs11_slotinfo * si,CK_USER_TYPE type)234f69acb9aSdjm pkcs11_login_slot(struct pkcs11_provider *provider, struct pkcs11_slotinfo *si,
235f69acb9aSdjm CK_USER_TYPE type)
2365a0bd427Sdjm {
2375a0bd427Sdjm char *pin = NULL, prompt[1024];
2385a0bd427Sdjm CK_RV rv;
2395a0bd427Sdjm
240f69acb9aSdjm if (provider == NULL || si == NULL || !provider->valid) {
2415a0bd427Sdjm error("no pkcs11 (valid) provider found");
2425a0bd427Sdjm return (-1);
2435a0bd427Sdjm }
2445a0bd427Sdjm
2455a0bd427Sdjm if (!pkcs11_interactive) {
2465a0bd427Sdjm error("need pin entry%s",
2475a0bd427Sdjm (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH) ?
2485a0bd427Sdjm " on reader keypad" : "");
2495a0bd427Sdjm return (-1);
2505a0bd427Sdjm }
2515a0bd427Sdjm if (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH)
2525a0bd427Sdjm verbose("Deferring PIN entry to reader keypad.");
2535a0bd427Sdjm else {
2545a0bd427Sdjm snprintf(prompt, sizeof(prompt), "Enter PIN for '%s': ",
2555a0bd427Sdjm si->token.label);
2565a0bd427Sdjm if ((pin = read_passphrase(prompt, RP_ALLOW_EOF)) == NULL) {
25748e6b99dSdjm debug_f("no pin specified");
2585a0bd427Sdjm return (-1); /* bail out */
2595a0bd427Sdjm }
2605a0bd427Sdjm }
261f69acb9aSdjm rv = provider->function_list->C_Login(si->session, type, (u_char *)pin,
2625a0bd427Sdjm (pin != NULL) ? strlen(pin) : 0);
2635a0bd427Sdjm if (pin != NULL)
2645a0bd427Sdjm freezero(pin, strlen(pin));
2651a9276bcSdjm
2661a9276bcSdjm switch (rv) {
2671a9276bcSdjm case CKR_OK:
2681a9276bcSdjm case CKR_USER_ALREADY_LOGGED_IN:
2691a9276bcSdjm /* success */
2701a9276bcSdjm break;
2711a9276bcSdjm case CKR_PIN_LEN_RANGE:
2721a9276bcSdjm error("PKCS#11 login failed: PIN length out of range");
2731a9276bcSdjm return -1;
2741a9276bcSdjm case CKR_PIN_INCORRECT:
2751a9276bcSdjm error("PKCS#11 login failed: PIN incorrect");
2761a9276bcSdjm return -1;
2771a9276bcSdjm case CKR_PIN_LOCKED:
2781a9276bcSdjm error("PKCS#11 login failed: PIN locked");
2791a9276bcSdjm return -1;
2801a9276bcSdjm default:
2811a9276bcSdjm error("PKCS#11 login failed: error %lu", rv);
2821a9276bcSdjm return -1;
2835a0bd427Sdjm }
2845a0bd427Sdjm si->logged_in = 1;
2855a0bd427Sdjm return (0);
2865a0bd427Sdjm }
2875a0bd427Sdjm
2885a0bd427Sdjm static int
pkcs11_login(struct pkcs11_key * k11,CK_USER_TYPE type)289f69acb9aSdjm pkcs11_login(struct pkcs11_key *k11, CK_USER_TYPE type)
290f69acb9aSdjm {
291f69acb9aSdjm if (k11 == NULL || k11->provider == NULL || !k11->provider->valid) {
292f69acb9aSdjm error("no pkcs11 (valid) provider found");
293f69acb9aSdjm return (-1);
294f69acb9aSdjm }
295f69acb9aSdjm
296f69acb9aSdjm return pkcs11_login_slot(k11->provider,
297f69acb9aSdjm &k11->provider->slotinfo[k11->slotidx], type);
298f69acb9aSdjm }
299f69acb9aSdjm
300f69acb9aSdjm
301f69acb9aSdjm static int
pkcs11_check_obj_bool_attrib(struct pkcs11_key * k11,CK_OBJECT_HANDLE obj,CK_ATTRIBUTE_TYPE type,int * val)3025a0bd427Sdjm pkcs11_check_obj_bool_attrib(struct pkcs11_key *k11, CK_OBJECT_HANDLE obj,
3035a0bd427Sdjm CK_ATTRIBUTE_TYPE type, int *val)
3045a0bd427Sdjm {
3055a0bd427Sdjm struct pkcs11_slotinfo *si;
3065a0bd427Sdjm CK_FUNCTION_LIST *f;
3075a0bd427Sdjm CK_BBOOL flag = 0;
3085a0bd427Sdjm CK_ATTRIBUTE attr;
3095a0bd427Sdjm CK_RV rv;
3105a0bd427Sdjm
3115a0bd427Sdjm *val = 0;
3125a0bd427Sdjm
3135a0bd427Sdjm if (!k11->provider || !k11->provider->valid) {
3145a0bd427Sdjm error("no pkcs11 (valid) provider found");
3155a0bd427Sdjm return (-1);
3165a0bd427Sdjm }
3175a0bd427Sdjm
3185a0bd427Sdjm f = k11->provider->function_list;
3195a0bd427Sdjm si = &k11->provider->slotinfo[k11->slotidx];
3205a0bd427Sdjm
3215a0bd427Sdjm attr.type = type;
3225a0bd427Sdjm attr.pValue = &flag;
3235a0bd427Sdjm attr.ulValueLen = sizeof(flag);
3245a0bd427Sdjm
3255a0bd427Sdjm rv = f->C_GetAttributeValue(si->session, obj, &attr, 1);
3265a0bd427Sdjm if (rv != CKR_OK) {
3275a0bd427Sdjm error("C_GetAttributeValue failed: %lu", rv);
3285a0bd427Sdjm return (-1);
3295a0bd427Sdjm }
3305a0bd427Sdjm *val = flag != 0;
331efa52ff8Sdjm debug_f("provider \"%s\" slot %lu object %lu: attrib %lu = %d",
332efa52ff8Sdjm k11->provider->name, k11->slotidx, obj, type, *val);
3335a0bd427Sdjm return (0);
3345a0bd427Sdjm }
3355a0bd427Sdjm
3365a0bd427Sdjm static int
pkcs11_get_key(struct pkcs11_key * k11,CK_MECHANISM_TYPE mech_type)33721f43f82Sdjm pkcs11_get_key(struct pkcs11_key *k11, CK_MECHANISM_TYPE mech_type)
33841503fafSmarkus {
33941503fafSmarkus struct pkcs11_slotinfo *si;
34041503fafSmarkus CK_FUNCTION_LIST *f;
34141503fafSmarkus CK_OBJECT_HANDLE obj;
34241503fafSmarkus CK_RV rv;
34321f43f82Sdjm CK_OBJECT_CLASS private_key_class;
34421f43f82Sdjm CK_BBOOL true_val;
34521f43f82Sdjm CK_MECHANISM mech;
34621f43f82Sdjm CK_ATTRIBUTE key_filter[3];
3475a0bd427Sdjm int always_auth = 0;
3485a0bd427Sdjm int did_login = 0;
34941503fafSmarkus
35041503fafSmarkus if (!k11->provider || !k11->provider->valid) {
35121f43f82Sdjm error("no pkcs11 (valid) provider found");
35241503fafSmarkus return (-1);
35341503fafSmarkus }
35421f43f82Sdjm
35541503fafSmarkus f = k11->provider->function_list;
35641503fafSmarkus si = &k11->provider->slotinfo[k11->slotidx];
35721f43f82Sdjm
35841503fafSmarkus if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) {
3595a0bd427Sdjm if (pkcs11_login(k11, CKU_USER) < 0) {
3605a0bd427Sdjm error("login failed");
36141503fafSmarkus return (-1);
36241503fafSmarkus }
3635a0bd427Sdjm did_login = 1;
36441503fafSmarkus }
36521f43f82Sdjm
36621f43f82Sdjm memset(&key_filter, 0, sizeof(key_filter));
36721f43f82Sdjm private_key_class = CKO_PRIVATE_KEY;
36821f43f82Sdjm key_filter[0].type = CKA_CLASS;
36921f43f82Sdjm key_filter[0].pValue = &private_key_class;
37021f43f82Sdjm key_filter[0].ulValueLen = sizeof(private_key_class);
37121f43f82Sdjm
37221f43f82Sdjm key_filter[1].type = CKA_ID;
37341503fafSmarkus key_filter[1].pValue = k11->keyid;
37441503fafSmarkus key_filter[1].ulValueLen = k11->keyid_len;
37521f43f82Sdjm
37621f43f82Sdjm true_val = CK_TRUE;
37721f43f82Sdjm key_filter[2].type = CKA_SIGN;
37821f43f82Sdjm key_filter[2].pValue = &true_val;
37921f43f82Sdjm key_filter[2].ulValueLen = sizeof(true_val);
38021f43f82Sdjm
3812f7c3d34Smarkus /* try to find object w/CKA_SIGN first, retry w/o */
3822f7c3d34Smarkus if (pkcs11_find(k11->provider, k11->slotidx, key_filter, 3, &obj) < 0 &&
3832f7c3d34Smarkus pkcs11_find(k11->provider, k11->slotidx, key_filter, 2, &obj) < 0) {
3842f7c3d34Smarkus error("cannot find private key");
38521f43f82Sdjm return (-1);
38621f43f82Sdjm }
38721f43f82Sdjm
38821f43f82Sdjm memset(&mech, 0, sizeof(mech));
38921f43f82Sdjm mech.mechanism = mech_type;
39021f43f82Sdjm mech.pParameter = NULL_PTR;
39121f43f82Sdjm mech.ulParameterLen = 0;
39221f43f82Sdjm
39321f43f82Sdjm if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) {
39441503fafSmarkus error("C_SignInit failed: %lu", rv);
39521f43f82Sdjm return (-1);
39621f43f82Sdjm }
39721f43f82Sdjm
3985a0bd427Sdjm pkcs11_check_obj_bool_attrib(k11, obj, CKA_ALWAYS_AUTHENTICATE,
3995a0bd427Sdjm &always_auth); /* ignore errors here */
4005a0bd427Sdjm if (always_auth && !did_login) {
40148e6b99dSdjm debug_f("always-auth key");
4025a0bd427Sdjm if (pkcs11_login(k11, CKU_CONTEXT_SPECIFIC) < 0) {
4035a0bd427Sdjm error("login failed for always-auth key");
4045a0bd427Sdjm return (-1);
4055a0bd427Sdjm }
4065a0bd427Sdjm }
4075a0bd427Sdjm
40821f43f82Sdjm return (0);
40921f43f82Sdjm }
41021f43f82Sdjm
41121f43f82Sdjm /* openssl callback doing the actual signing operation */
41221f43f82Sdjm static int
pkcs11_rsa_private_encrypt(int flen,const u_char * from,u_char * to,RSA * rsa,int padding)41321f43f82Sdjm pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
41421f43f82Sdjm int padding)
41521f43f82Sdjm {
41621f43f82Sdjm struct pkcs11_key *k11;
41721f43f82Sdjm struct pkcs11_slotinfo *si;
41821f43f82Sdjm CK_FUNCTION_LIST *f;
41921f43f82Sdjm CK_ULONG tlen = 0;
42021f43f82Sdjm CK_RV rv;
42121f43f82Sdjm int rval = -1;
42221f43f82Sdjm
423dec222b5Sdjm if ((k11 = RSA_get_ex_data(rsa, rsa_idx)) == NULL) {
424efa52ff8Sdjm error("RSA_get_ex_data failed");
42521f43f82Sdjm return (-1);
42621f43f82Sdjm }
42721f43f82Sdjm
42821f43f82Sdjm if (pkcs11_get_key(k11, CKM_RSA_PKCS) == -1) {
42921f43f82Sdjm error("pkcs11_get_key failed");
43021f43f82Sdjm return (-1);
43121f43f82Sdjm }
43221f43f82Sdjm
43321f43f82Sdjm f = k11->provider->function_list;
43421f43f82Sdjm si = &k11->provider->slotinfo[k11->slotidx];
43541503fafSmarkus tlen = RSA_size(rsa);
43621f43f82Sdjm
43721f43f82Sdjm /* XXX handle CKR_BUFFER_TOO_SMALL */
43841503fafSmarkus rv = f->C_Sign(si->session, (CK_BYTE *)from, flen, to, &tlen);
43941503fafSmarkus if (rv == CKR_OK)
44041503fafSmarkus rval = tlen;
44141503fafSmarkus else
44241503fafSmarkus error("C_Sign failed: %lu", rv);
44321f43f82Sdjm
44441503fafSmarkus return (rval);
44541503fafSmarkus }
44641503fafSmarkus
44741503fafSmarkus static int
pkcs11_rsa_private_decrypt(int flen,const u_char * from,u_char * to,RSA * rsa,int padding)44841503fafSmarkus pkcs11_rsa_private_decrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
44941503fafSmarkus int padding)
45041503fafSmarkus {
45141503fafSmarkus return (-1);
45241503fafSmarkus }
45341503fafSmarkus
454de64df3dSdjm static int
pkcs11_rsa_start_wrapper(void)455de64df3dSdjm pkcs11_rsa_start_wrapper(void)
456de64df3dSdjm {
457de64df3dSdjm if (rsa_method != NULL)
458de64df3dSdjm return (0);
459de64df3dSdjm rsa_method = RSA_meth_dup(RSA_get_default_method());
460de64df3dSdjm if (rsa_method == NULL)
461de64df3dSdjm return (-1);
4621ab0eecfSdjm rsa_idx = RSA_get_ex_new_index(0, "ssh-pkcs11-rsa",
4631ab0eecfSdjm NULL, NULL, pkcs11_k11_free);
4641ab0eecfSdjm if (rsa_idx == -1)
4651ab0eecfSdjm return (-1);
466de64df3dSdjm if (!RSA_meth_set1_name(rsa_method, "pkcs11") ||
467de64df3dSdjm !RSA_meth_set_priv_enc(rsa_method, pkcs11_rsa_private_encrypt) ||
4681ab0eecfSdjm !RSA_meth_set_priv_dec(rsa_method, pkcs11_rsa_private_decrypt)) {
46948e6b99dSdjm error_f("setup pkcs11 method failed");
470de64df3dSdjm return (-1);
471de64df3dSdjm }
472de64df3dSdjm return (0);
473de64df3dSdjm }
474de64df3dSdjm
47541503fafSmarkus /* redirect private key operations for rsa key to pkcs11 token */
47641503fafSmarkus static int
pkcs11_rsa_wrap(struct pkcs11_provider * provider,CK_ULONG slotidx,CK_ATTRIBUTE * keyid_attrib,RSA * rsa)47741503fafSmarkus pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
47841503fafSmarkus CK_ATTRIBUTE *keyid_attrib, RSA *rsa)
47941503fafSmarkus {
48041503fafSmarkus struct pkcs11_key *k11;
481de64df3dSdjm
482de64df3dSdjm if (pkcs11_rsa_start_wrapper() == -1)
483de64df3dSdjm return (-1);
48441503fafSmarkus
48541503fafSmarkus k11 = xcalloc(1, sizeof(*k11));
48641503fafSmarkus k11->provider = provider;
48741503fafSmarkus provider->refcount++; /* provider referenced by RSA key */
48841503fafSmarkus k11->slotidx = slotidx;
48941503fafSmarkus /* identify key object on smartcard */
49041503fafSmarkus k11->keyid_len = keyid_attrib->ulValueLen;
49114fcadb6Sdjm if (k11->keyid_len > 0) {
49241503fafSmarkus k11->keyid = xmalloc(k11->keyid_len);
49341503fafSmarkus memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
49414fcadb6Sdjm }
495de64df3dSdjm
4965411e769Sdjm if (RSA_set_method(rsa, rsa_method) != 1)
4975411e769Sdjm fatal_f("RSA_set_method failed");
4985411e769Sdjm if (RSA_set_ex_data(rsa, rsa_idx, k11) != 1)
4995411e769Sdjm fatal_f("RSA_set_ex_data failed");
50041503fafSmarkus return (0);
50141503fafSmarkus }
50241503fafSmarkus
50321f43f82Sdjm /* openssl callback doing the actual signing operation */
50421f43f82Sdjm static ECDSA_SIG *
ecdsa_do_sign(const unsigned char * dgst,int dgst_len,const BIGNUM * inv,const BIGNUM * rp,EC_KEY * ec)50521f43f82Sdjm ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv,
50621f43f82Sdjm const BIGNUM *rp, EC_KEY *ec)
50721f43f82Sdjm {
50821f43f82Sdjm struct pkcs11_key *k11;
50921f43f82Sdjm struct pkcs11_slotinfo *si;
51021f43f82Sdjm CK_FUNCTION_LIST *f;
51121f43f82Sdjm CK_ULONG siglen = 0, bnlen;
51221f43f82Sdjm CK_RV rv;
51321f43f82Sdjm ECDSA_SIG *ret = NULL;
51421f43f82Sdjm u_char *sig;
5152d94b486Sdjm BIGNUM *r = NULL, *s = NULL;
51621f43f82Sdjm
517dec222b5Sdjm if ((k11 = EC_KEY_get_ex_data(ec, ec_key_idx)) == NULL) {
51847f132e2Stb ossl_error("EC_KEY_get_ex_data failed for ec");
51921f43f82Sdjm return (NULL);
52021f43f82Sdjm }
52121f43f82Sdjm
52221f43f82Sdjm if (pkcs11_get_key(k11, CKM_ECDSA) == -1) {
52321f43f82Sdjm error("pkcs11_get_key failed");
52421f43f82Sdjm return (NULL);
52521f43f82Sdjm }
52621f43f82Sdjm
52721f43f82Sdjm f = k11->provider->function_list;
52821f43f82Sdjm si = &k11->provider->slotinfo[k11->slotidx];
52921f43f82Sdjm
53021f43f82Sdjm siglen = ECDSA_size(ec);
53121f43f82Sdjm sig = xmalloc(siglen);
53221f43f82Sdjm
53321f43f82Sdjm /* XXX handle CKR_BUFFER_TOO_SMALL */
53421f43f82Sdjm rv = f->C_Sign(si->session, (CK_BYTE *)dgst, dgst_len, sig, &siglen);
53521f43f82Sdjm if (rv != CKR_OK) {
53621f43f82Sdjm error("C_Sign failed: %lu", rv);
53721f43f82Sdjm goto done;
53821f43f82Sdjm }
53921f43f82Sdjm if (siglen < 64 || siglen > 132 || siglen % 2) {
54047f132e2Stb error_f("bad signature length: %lu", (u_long)siglen);
54121f43f82Sdjm goto done;
54221f43f82Sdjm }
54321f43f82Sdjm bnlen = siglen/2;
54421f43f82Sdjm if ((ret = ECDSA_SIG_new()) == NULL) {
54521f43f82Sdjm error("ECDSA_SIG_new failed");
54621f43f82Sdjm goto done;
54721f43f82Sdjm }
5482d94b486Sdjm if ((r = BN_bin2bn(sig, bnlen, NULL)) == NULL ||
5492d94b486Sdjm (s = BN_bin2bn(sig+bnlen, bnlen, NULL)) == NULL) {
55047f132e2Stb ossl_error("BN_bin2bn failed");
55121f43f82Sdjm ECDSA_SIG_free(ret);
55221f43f82Sdjm ret = NULL;
55321f43f82Sdjm goto done;
55421f43f82Sdjm }
5552d94b486Sdjm if (!ECDSA_SIG_set0(ret, r, s)) {
55648e6b99dSdjm error_f("ECDSA_SIG_set0 failed");
5572d94b486Sdjm ECDSA_SIG_free(ret);
5582d94b486Sdjm ret = NULL;
5592d94b486Sdjm goto done;
5602d94b486Sdjm }
5612d94b486Sdjm r = s = NULL; /* now owned by ret */
5622d94b486Sdjm /* success */
56321f43f82Sdjm done:
5642d94b486Sdjm BN_free(r);
5652d94b486Sdjm BN_free(s);
56621f43f82Sdjm free(sig);
56721f43f82Sdjm
56821f43f82Sdjm return (ret);
56921f43f82Sdjm }
57021f43f82Sdjm
57121f43f82Sdjm static int
pkcs11_ecdsa_start_wrapper(void)57221f43f82Sdjm pkcs11_ecdsa_start_wrapper(void)
57321f43f82Sdjm {
57421f43f82Sdjm int (*orig_sign)(int, const unsigned char *, int, unsigned char *,
57521f43f82Sdjm unsigned int *, const BIGNUM *, const BIGNUM *, EC_KEY *) = NULL;
57621f43f82Sdjm
57721f43f82Sdjm if (ec_key_method != NULL)
57821f43f82Sdjm return (0);
579d4d5784cSdjm ec_key_idx = EC_KEY_get_ex_new_index(0, "ssh-pkcs11-ecdsa",
580d4d5784cSdjm NULL, NULL, pkcs11_k11_free);
581d4d5784cSdjm if (ec_key_idx == -1)
582d4d5784cSdjm return (-1);
58321f43f82Sdjm ec_key_method = EC_KEY_METHOD_new(EC_KEY_OpenSSL());
58421f43f82Sdjm if (ec_key_method == NULL)
58521f43f82Sdjm return (-1);
58621f43f82Sdjm EC_KEY_METHOD_get_sign(ec_key_method, &orig_sign, NULL, NULL);
58721f43f82Sdjm EC_KEY_METHOD_set_sign(ec_key_method, orig_sign, NULL, ecdsa_do_sign);
58821f43f82Sdjm return (0);
58921f43f82Sdjm }
59021f43f82Sdjm
59121f43f82Sdjm static int
pkcs11_ecdsa_wrap(struct pkcs11_provider * provider,CK_ULONG slotidx,CK_ATTRIBUTE * keyid_attrib,EC_KEY * ec)59221f43f82Sdjm pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
59321f43f82Sdjm CK_ATTRIBUTE *keyid_attrib, EC_KEY *ec)
59421f43f82Sdjm {
59521f43f82Sdjm struct pkcs11_key *k11;
59621f43f82Sdjm
59721f43f82Sdjm if (pkcs11_ecdsa_start_wrapper() == -1)
59821f43f82Sdjm return (-1);
59921f43f82Sdjm
60021f43f82Sdjm k11 = xcalloc(1, sizeof(*k11));
60121f43f82Sdjm k11->provider = provider;
60221f43f82Sdjm provider->refcount++; /* provider referenced by ECDSA key */
60321f43f82Sdjm k11->slotidx = slotidx;
60421f43f82Sdjm /* identify key object on smartcard */
60521f43f82Sdjm k11->keyid_len = keyid_attrib->ulValueLen;
606a9f89e4dSdjm if (k11->keyid_len > 0) {
60721f43f82Sdjm k11->keyid = xmalloc(k11->keyid_len);
60821f43f82Sdjm memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
609a9f89e4dSdjm }
6105411e769Sdjm if (EC_KEY_set_method(ec, ec_key_method) != 1)
6115411e769Sdjm fatal_f("EC_KEY_set_method failed");
6125411e769Sdjm if (EC_KEY_set_ex_data(ec, ec_key_idx, k11) != 1)
6135411e769Sdjm fatal_f("EC_KEY_set_ex_data failed");
61421f43f82Sdjm
61521f43f82Sdjm return (0);
61621f43f82Sdjm }
61721f43f82Sdjm
61841503fafSmarkus /* remove trailing spaces */
6192c6bff6fSdjm static char *
rmspace(u_char * buf,size_t len)6202d917344Sdjm rmspace(u_char *buf, size_t len)
62141503fafSmarkus {
62241503fafSmarkus size_t i;
62341503fafSmarkus
6242c6bff6fSdjm if (len == 0)
6252c6bff6fSdjm return buf;
62641503fafSmarkus for (i = len - 1; i > 0; i--)
6272c6bff6fSdjm if (buf[i] == ' ')
62841503fafSmarkus buf[i] = '\0';
62941503fafSmarkus else
63041503fafSmarkus break;
6312c6bff6fSdjm return buf;
63241503fafSmarkus }
6332c6bff6fSdjm /* Used to printf fixed-width, space-padded, unterminated strings using %.*s */
6342c6bff6fSdjm #define RMSPACE(s) (int)sizeof(s), rmspace(s, sizeof(s))
63541503fafSmarkus
63641503fafSmarkus /*
63741503fafSmarkus * open a pkcs11 session and login if required.
63841503fafSmarkus * if pin == NULL we delay login until key use
63941503fafSmarkus */
64041503fafSmarkus static int
pkcs11_open_session(struct pkcs11_provider * p,CK_ULONG slotidx,char * pin,CK_ULONG user)64121f43f82Sdjm pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin,
64221f43f82Sdjm CK_ULONG user)
64341503fafSmarkus {
64407a271d3Sdjm struct pkcs11_slotinfo *si;
64541503fafSmarkus CK_FUNCTION_LIST *f;
64607a271d3Sdjm CK_RV rv;
64741503fafSmarkus CK_SESSION_HANDLE session;
648c386cd63Sdjm int login_required, ret;
64941503fafSmarkus
65041503fafSmarkus f = p->function_list;
65107a271d3Sdjm si = &p->slotinfo[slotidx];
65207a271d3Sdjm
65307a271d3Sdjm login_required = si->token.flags & CKF_LOGIN_REQUIRED;
65407a271d3Sdjm
65507a271d3Sdjm /* fail early before opening session */
656c386cd63Sdjm if (login_required && !pkcs11_interactive &&
65743b779d4Sdjm (pin == NULL || strlen(pin) == 0)) {
65841503fafSmarkus error("pin required");
65921f43f82Sdjm return (-SSH_PKCS11_ERR_PIN_REQUIRED);
66041503fafSmarkus }
66141503fafSmarkus if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION|
66207a271d3Sdjm CKF_SERIAL_SESSION, NULL, NULL, &session)) != CKR_OK) {
66341503fafSmarkus error("C_OpenSession failed: %lu", rv);
66441503fafSmarkus return (-1);
66541503fafSmarkus }
666c386cd63Sdjm if (login_required && pin != NULL && strlen(pin) != 0) {
667c386cd63Sdjm rv = f->C_Login(session, user, (u_char *)pin, strlen(pin));
6684cfc4a70Sdjm if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) {
66941503fafSmarkus error("C_Login failed: %lu", rv);
67021f43f82Sdjm ret = (rv == CKR_PIN_LOCKED) ?
67121f43f82Sdjm -SSH_PKCS11_ERR_PIN_LOCKED :
67221f43f82Sdjm -SSH_PKCS11_ERR_LOGIN_FAIL;
67341503fafSmarkus if ((rv = f->C_CloseSession(session)) != CKR_OK)
67441503fafSmarkus error("C_CloseSession failed: %lu", rv);
67521f43f82Sdjm return (ret);
67641503fafSmarkus }
67707a271d3Sdjm si->logged_in = 1;
67841503fafSmarkus }
67907a271d3Sdjm si->session = session;
68041503fafSmarkus return (0);
68141503fafSmarkus }
68241503fafSmarkus
68312770bd2Smarkus static int
pkcs11_key_included(struct sshkey *** keysp,int * nkeys,struct sshkey * key)6842aa7d220Sdjm pkcs11_key_included(struct sshkey ***keysp, int *nkeys, struct sshkey *key)
68512770bd2Smarkus {
68612770bd2Smarkus int i;
68712770bd2Smarkus
68812770bd2Smarkus for (i = 0; i < *nkeys; i++)
6892aa7d220Sdjm if (sshkey_equal(key, (*keysp)[i]))
69012770bd2Smarkus return (1);
69112770bd2Smarkus return (0);
69212770bd2Smarkus }
69312770bd2Smarkus
69421f43f82Sdjm static struct sshkey *
pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider * p,CK_ULONG slotidx,CK_OBJECT_HANDLE * obj)69521f43f82Sdjm pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
69621f43f82Sdjm CK_OBJECT_HANDLE *obj)
69721f43f82Sdjm {
69821f43f82Sdjm CK_ATTRIBUTE key_attr[3];
69921f43f82Sdjm CK_SESSION_HANDLE session;
70021f43f82Sdjm CK_FUNCTION_LIST *f = NULL;
70121f43f82Sdjm CK_RV rv;
70260996112Sdjm ASN1_OCTET_STRING *octet = NULL;
70321f43f82Sdjm EC_KEY *ec = NULL;
70421f43f82Sdjm EC_GROUP *group = NULL;
70521f43f82Sdjm struct sshkey *key = NULL;
70621f43f82Sdjm const unsigned char *attrp = NULL;
70721f43f82Sdjm int i;
70821f43f82Sdjm int nid;
70921f43f82Sdjm
71021f43f82Sdjm memset(&key_attr, 0, sizeof(key_attr));
71121f43f82Sdjm key_attr[0].type = CKA_ID;
71221f43f82Sdjm key_attr[1].type = CKA_EC_POINT;
71321f43f82Sdjm key_attr[2].type = CKA_EC_PARAMS;
71421f43f82Sdjm
71521f43f82Sdjm session = p->slotinfo[slotidx].session;
71621f43f82Sdjm f = p->function_list;
71721f43f82Sdjm
71821f43f82Sdjm /* figure out size of the attributes */
71921f43f82Sdjm rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
72021f43f82Sdjm if (rv != CKR_OK) {
72121f43f82Sdjm error("C_GetAttributeValue failed: %lu", rv);
72221f43f82Sdjm return (NULL);
72321f43f82Sdjm }
72421f43f82Sdjm
72521f43f82Sdjm /*
72621f43f82Sdjm * Allow CKA_ID (always first attribute) to be empty, but
72721f43f82Sdjm * ensure that none of the others are zero length.
72821f43f82Sdjm * XXX assumes CKA_ID is always first.
72921f43f82Sdjm */
73021f43f82Sdjm if (key_attr[1].ulValueLen == 0 ||
73121f43f82Sdjm key_attr[2].ulValueLen == 0) {
73221f43f82Sdjm error("invalid attribute length");
73321f43f82Sdjm return (NULL);
73421f43f82Sdjm }
73521f43f82Sdjm
73621f43f82Sdjm /* allocate buffers for attributes */
73721f43f82Sdjm for (i = 0; i < 3; i++)
73821f43f82Sdjm if (key_attr[i].ulValueLen > 0)
73921f43f82Sdjm key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen);
74021f43f82Sdjm
74121f43f82Sdjm /* retrieve ID, public point and curve parameters of EC key */
74221f43f82Sdjm rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
74321f43f82Sdjm if (rv != CKR_OK) {
74421f43f82Sdjm error("C_GetAttributeValue failed: %lu", rv);
74521f43f82Sdjm goto fail;
74621f43f82Sdjm }
74721f43f82Sdjm
74821f43f82Sdjm ec = EC_KEY_new();
74921f43f82Sdjm if (ec == NULL) {
75021f43f82Sdjm error("EC_KEY_new failed");
75121f43f82Sdjm goto fail;
75221f43f82Sdjm }
75321f43f82Sdjm
75421f43f82Sdjm attrp = key_attr[2].pValue;
75521f43f82Sdjm group = d2i_ECPKParameters(NULL, &attrp, key_attr[2].ulValueLen);
75621f43f82Sdjm if (group == NULL) {
75721f43f82Sdjm ossl_error("d2i_ECPKParameters failed");
75821f43f82Sdjm goto fail;
75921f43f82Sdjm }
76021f43f82Sdjm
76121f43f82Sdjm if (EC_KEY_set_group(ec, group) == 0) {
76221f43f82Sdjm ossl_error("EC_KEY_set_group failed");
76321f43f82Sdjm goto fail;
76421f43f82Sdjm }
76521f43f82Sdjm
76621f43f82Sdjm if (key_attr[1].ulValueLen <= 2) {
76721f43f82Sdjm error("CKA_EC_POINT too small");
76821f43f82Sdjm goto fail;
76921f43f82Sdjm }
77021f43f82Sdjm
77160996112Sdjm attrp = key_attr[1].pValue;
77260996112Sdjm octet = d2i_ASN1_OCTET_STRING(NULL, &attrp, key_attr[1].ulValueLen);
77360996112Sdjm if (octet == NULL) {
77460996112Sdjm ossl_error("d2i_ASN1_OCTET_STRING failed");
77521f43f82Sdjm goto fail;
77621f43f82Sdjm }
77760996112Sdjm attrp = octet->data;
77860996112Sdjm if (o2i_ECPublicKey(&ec, &attrp, octet->length) == NULL) {
77960996112Sdjm ossl_error("o2i_ECPublicKey failed");
78060996112Sdjm goto fail;
78121f43f82Sdjm }
78221f43f82Sdjm
78321f43f82Sdjm nid = sshkey_ecdsa_key_to_nid(ec);
78421f43f82Sdjm if (nid < 0) {
78521f43f82Sdjm error("couldn't get curve nid");
78621f43f82Sdjm goto fail;
78721f43f82Sdjm }
78821f43f82Sdjm
78921f43f82Sdjm if (pkcs11_ecdsa_wrap(p, slotidx, &key_attr[0], ec))
79021f43f82Sdjm goto fail;
79121f43f82Sdjm
79221f43f82Sdjm key = sshkey_new(KEY_UNSPEC);
79321f43f82Sdjm if (key == NULL) {
79421f43f82Sdjm error("sshkey_new failed");
79521f43f82Sdjm goto fail;
79621f43f82Sdjm }
79721f43f82Sdjm
7985411e769Sdjm EVP_PKEY_free(key->pkey);
7995411e769Sdjm if ((key->pkey = EVP_PKEY_new()) == NULL)
8005411e769Sdjm fatal("EVP_PKEY_new failed");
8015411e769Sdjm if (EVP_PKEY_set1_EC_KEY(key->pkey, ec) != 1)
8025411e769Sdjm fatal("EVP_PKEY_set1_EC_KEY failed");
80321f43f82Sdjm key->ecdsa_nid = nid;
80421f43f82Sdjm key->type = KEY_ECDSA;
80521f43f82Sdjm key->flags |= SSHKEY_FLAG_EXT;
80621f43f82Sdjm
80721f43f82Sdjm fail:
80821f43f82Sdjm for (i = 0; i < 3; i++)
80921f43f82Sdjm free(key_attr[i].pValue);
81021f43f82Sdjm if (ec)
81121f43f82Sdjm EC_KEY_free(ec);
81221f43f82Sdjm if (group)
81321f43f82Sdjm EC_GROUP_free(group);
81460996112Sdjm if (octet)
81560996112Sdjm ASN1_OCTET_STRING_free(octet);
81621f43f82Sdjm
81721f43f82Sdjm return (key);
81821f43f82Sdjm }
81921f43f82Sdjm
82021f43f82Sdjm static struct sshkey *
pkcs11_fetch_rsa_pubkey(struct pkcs11_provider * p,CK_ULONG slotidx,CK_OBJECT_HANDLE * obj)82121f43f82Sdjm pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
82221f43f82Sdjm CK_OBJECT_HANDLE *obj)
82321f43f82Sdjm {
82421f43f82Sdjm CK_ATTRIBUTE key_attr[3];
82521f43f82Sdjm CK_SESSION_HANDLE session;
82621f43f82Sdjm CK_FUNCTION_LIST *f = NULL;
82721f43f82Sdjm CK_RV rv;
82821f43f82Sdjm RSA *rsa = NULL;
82921f43f82Sdjm BIGNUM *rsa_n, *rsa_e;
83021f43f82Sdjm struct sshkey *key = NULL;
83121f43f82Sdjm int i;
83221f43f82Sdjm
83321f43f82Sdjm memset(&key_attr, 0, sizeof(key_attr));
83421f43f82Sdjm key_attr[0].type = CKA_ID;
83521f43f82Sdjm key_attr[1].type = CKA_MODULUS;
83621f43f82Sdjm key_attr[2].type = CKA_PUBLIC_EXPONENT;
83721f43f82Sdjm
83821f43f82Sdjm session = p->slotinfo[slotidx].session;
83921f43f82Sdjm f = p->function_list;
84021f43f82Sdjm
84121f43f82Sdjm /* figure out size of the attributes */
84221f43f82Sdjm rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
84321f43f82Sdjm if (rv != CKR_OK) {
84421f43f82Sdjm error("C_GetAttributeValue failed: %lu", rv);
84521f43f82Sdjm return (NULL);
84621f43f82Sdjm }
84721f43f82Sdjm
84821f43f82Sdjm /*
84921f43f82Sdjm * Allow CKA_ID (always first attribute) to be empty, but
85021f43f82Sdjm * ensure that none of the others are zero length.
85121f43f82Sdjm * XXX assumes CKA_ID is always first.
85221f43f82Sdjm */
85321f43f82Sdjm if (key_attr[1].ulValueLen == 0 ||
85421f43f82Sdjm key_attr[2].ulValueLen == 0) {
85521f43f82Sdjm error("invalid attribute length");
85621f43f82Sdjm return (NULL);
85721f43f82Sdjm }
85821f43f82Sdjm
85921f43f82Sdjm /* allocate buffers for attributes */
86021f43f82Sdjm for (i = 0; i < 3; i++)
86121f43f82Sdjm if (key_attr[i].ulValueLen > 0)
86221f43f82Sdjm key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen);
86321f43f82Sdjm
86421f43f82Sdjm /* retrieve ID, modulus and public exponent of RSA key */
86521f43f82Sdjm rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
86621f43f82Sdjm if (rv != CKR_OK) {
86721f43f82Sdjm error("C_GetAttributeValue failed: %lu", rv);
86821f43f82Sdjm goto fail;
86921f43f82Sdjm }
87021f43f82Sdjm
87121f43f82Sdjm rsa = RSA_new();
87221f43f82Sdjm if (rsa == NULL) {
87321f43f82Sdjm error("RSA_new failed");
87421f43f82Sdjm goto fail;
87521f43f82Sdjm }
87621f43f82Sdjm
87721f43f82Sdjm rsa_n = BN_bin2bn(key_attr[1].pValue, key_attr[1].ulValueLen, NULL);
87821f43f82Sdjm rsa_e = BN_bin2bn(key_attr[2].pValue, key_attr[2].ulValueLen, NULL);
87921f43f82Sdjm if (rsa_n == NULL || rsa_e == NULL) {
88021f43f82Sdjm error("BN_bin2bn failed");
88121f43f82Sdjm goto fail;
88221f43f82Sdjm }
88321f43f82Sdjm if (!RSA_set0_key(rsa, rsa_n, rsa_e, NULL))
88448e6b99dSdjm fatal_f("set key");
88521f43f82Sdjm rsa_n = rsa_e = NULL; /* transferred */
88621f43f82Sdjm
88721f43f82Sdjm if (pkcs11_rsa_wrap(p, slotidx, &key_attr[0], rsa))
88821f43f82Sdjm goto fail;
88921f43f82Sdjm
89021f43f82Sdjm key = sshkey_new(KEY_UNSPEC);
89121f43f82Sdjm if (key == NULL) {
89221f43f82Sdjm error("sshkey_new failed");
89321f43f82Sdjm goto fail;
89421f43f82Sdjm }
89521f43f82Sdjm
8965411e769Sdjm EVP_PKEY_free(key->pkey);
8975411e769Sdjm if ((key->pkey = EVP_PKEY_new()) == NULL)
8985411e769Sdjm fatal("EVP_PKEY_new failed");
8995411e769Sdjm if (EVP_PKEY_set1_RSA(key->pkey, rsa) != 1)
9005411e769Sdjm fatal("EVP_PKEY_set1_RSA failed");
90121f43f82Sdjm key->type = KEY_RSA;
90221f43f82Sdjm key->flags |= SSHKEY_FLAG_EXT;
90321f43f82Sdjm
90421f43f82Sdjm fail:
90521f43f82Sdjm for (i = 0; i < 3; i++)
90621f43f82Sdjm free(key_attr[i].pValue);
90721f43f82Sdjm RSA_free(rsa);
90821f43f82Sdjm
90921f43f82Sdjm return (key);
91021f43f82Sdjm }
91121f43f82Sdjm
91244e54ccbSdjm static int
pkcs11_fetch_x509_pubkey(struct pkcs11_provider * p,CK_ULONG slotidx,CK_OBJECT_HANDLE * obj,struct sshkey ** keyp,char ** labelp)91321f43f82Sdjm pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
91444e54ccbSdjm CK_OBJECT_HANDLE *obj, struct sshkey **keyp, char **labelp)
91521f43f82Sdjm {
91621f43f82Sdjm CK_ATTRIBUTE cert_attr[3];
91721f43f82Sdjm CK_SESSION_HANDLE session;
91821f43f82Sdjm CK_FUNCTION_LIST *f = NULL;
91921f43f82Sdjm CK_RV rv;
92021f43f82Sdjm X509 *x509 = NULL;
92144e54ccbSdjm X509_NAME *x509_name = NULL;
92221f43f82Sdjm EVP_PKEY *evp;
92321f43f82Sdjm RSA *rsa = NULL;
92421f43f82Sdjm EC_KEY *ec = NULL;
92521f43f82Sdjm struct sshkey *key = NULL;
92621f43f82Sdjm int i;
92721f43f82Sdjm int nid;
92821f43f82Sdjm const u_char *cp;
92944e54ccbSdjm char *subject = NULL;
93044e54ccbSdjm
93144e54ccbSdjm *keyp = NULL;
93244e54ccbSdjm *labelp = NULL;
93321f43f82Sdjm
93421f43f82Sdjm memset(&cert_attr, 0, sizeof(cert_attr));
93521f43f82Sdjm cert_attr[0].type = CKA_ID;
93621f43f82Sdjm cert_attr[1].type = CKA_SUBJECT;
93721f43f82Sdjm cert_attr[2].type = CKA_VALUE;
93821f43f82Sdjm
93921f43f82Sdjm session = p->slotinfo[slotidx].session;
94021f43f82Sdjm f = p->function_list;
94121f43f82Sdjm
94221f43f82Sdjm /* figure out size of the attributes */
94321f43f82Sdjm rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3);
94421f43f82Sdjm if (rv != CKR_OK) {
94521f43f82Sdjm error("C_GetAttributeValue failed: %lu", rv);
94644e54ccbSdjm return -1;
94721f43f82Sdjm }
94821f43f82Sdjm
94921f43f82Sdjm /*
95021f43f82Sdjm * Allow CKA_ID (always first attribute) to be empty, but
95121f43f82Sdjm * ensure that none of the others are zero length.
95221f43f82Sdjm * XXX assumes CKA_ID is always first.
95321f43f82Sdjm */
95421f43f82Sdjm if (cert_attr[1].ulValueLen == 0 ||
95521f43f82Sdjm cert_attr[2].ulValueLen == 0) {
95621f43f82Sdjm error("invalid attribute length");
95744e54ccbSdjm return -1;
95821f43f82Sdjm }
95921f43f82Sdjm
96021f43f82Sdjm /* allocate buffers for attributes */
96121f43f82Sdjm for (i = 0; i < 3; i++)
96221f43f82Sdjm if (cert_attr[i].ulValueLen > 0)
96321f43f82Sdjm cert_attr[i].pValue = xcalloc(1, cert_attr[i].ulValueLen);
96421f43f82Sdjm
96521f43f82Sdjm /* retrieve ID, subject and value of certificate */
96621f43f82Sdjm rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3);
96721f43f82Sdjm if (rv != CKR_OK) {
96821f43f82Sdjm error("C_GetAttributeValue failed: %lu", rv);
96944e54ccbSdjm goto out;
97021f43f82Sdjm }
97121f43f82Sdjm
97244e54ccbSdjm /* Decode DER-encoded cert subject */
973384b8b21Sdjm cp = cert_attr[1].pValue;
97444e54ccbSdjm if ((x509_name = d2i_X509_NAME(NULL, &cp,
97544e54ccbSdjm cert_attr[1].ulValueLen)) == NULL ||
97644e54ccbSdjm (subject = X509_NAME_oneline(x509_name, NULL, 0)) == NULL)
97744e54ccbSdjm subject = xstrdup("invalid subject");
97844e54ccbSdjm X509_NAME_free(x509_name);
97921f43f82Sdjm
98021f43f82Sdjm cp = cert_attr[2].pValue;
98144e54ccbSdjm if ((x509 = d2i_X509(NULL, &cp, cert_attr[2].ulValueLen)) == NULL) {
98221f43f82Sdjm error("d2i_x509 failed");
98344e54ccbSdjm goto out;
98421f43f82Sdjm }
98521f43f82Sdjm
98644e54ccbSdjm if ((evp = X509_get_pubkey(x509)) == NULL) {
98721f43f82Sdjm error("X509_get_pubkey failed");
98844e54ccbSdjm goto out;
98921f43f82Sdjm }
99021f43f82Sdjm
99121f43f82Sdjm if (EVP_PKEY_base_id(evp) == EVP_PKEY_RSA) {
99221f43f82Sdjm if (EVP_PKEY_get0_RSA(evp) == NULL) {
99321f43f82Sdjm error("invalid x509; no rsa key");
99444e54ccbSdjm goto out;
99521f43f82Sdjm }
99621f43f82Sdjm if ((rsa = RSAPublicKey_dup(EVP_PKEY_get0_RSA(evp))) == NULL) {
99721f43f82Sdjm error("RSAPublicKey_dup failed");
99844e54ccbSdjm goto out;
99921f43f82Sdjm }
100021f43f82Sdjm
100121f43f82Sdjm if (pkcs11_rsa_wrap(p, slotidx, &cert_attr[0], rsa))
100244e54ccbSdjm goto out;
100321f43f82Sdjm
100421f43f82Sdjm key = sshkey_new(KEY_UNSPEC);
100521f43f82Sdjm if (key == NULL) {
100621f43f82Sdjm error("sshkey_new failed");
100744e54ccbSdjm goto out;
100821f43f82Sdjm }
100921f43f82Sdjm
10105411e769Sdjm EVP_PKEY_free(key->pkey);
10115411e769Sdjm if ((key->pkey = EVP_PKEY_new()) == NULL)
10125411e769Sdjm fatal("EVP_PKEY_new failed");
10135411e769Sdjm if (EVP_PKEY_set1_RSA(key->pkey, rsa) != 1)
10145411e769Sdjm fatal("EVP_PKEY_set1_RSA failed");
101521f43f82Sdjm key->type = KEY_RSA;
101621f43f82Sdjm key->flags |= SSHKEY_FLAG_EXT;
101721f43f82Sdjm } else if (EVP_PKEY_base_id(evp) == EVP_PKEY_EC) {
1018ac22b3d0Sdjm if (EVP_PKEY_get0_EC_KEY(evp) == NULL) {
101921f43f82Sdjm error("invalid x509; no ec key");
102044e54ccbSdjm goto out;
102121f43f82Sdjm }
1022ac22b3d0Sdjm if ((ec = EC_KEY_dup(EVP_PKEY_get0_EC_KEY(evp))) == NULL) {
102321f43f82Sdjm error("EC_KEY_dup failed");
102444e54ccbSdjm goto out;
102521f43f82Sdjm }
102621f43f82Sdjm
102721f43f82Sdjm nid = sshkey_ecdsa_key_to_nid(ec);
102821f43f82Sdjm if (nid < 0) {
102921f43f82Sdjm error("couldn't get curve nid");
103044e54ccbSdjm goto out;
103121f43f82Sdjm }
103221f43f82Sdjm
103321f43f82Sdjm if (pkcs11_ecdsa_wrap(p, slotidx, &cert_attr[0], ec))
103444e54ccbSdjm goto out;
103521f43f82Sdjm
103621f43f82Sdjm key = sshkey_new(KEY_UNSPEC);
103721f43f82Sdjm if (key == NULL) {
103821f43f82Sdjm error("sshkey_new failed");
103944e54ccbSdjm goto out;
104021f43f82Sdjm }
104121f43f82Sdjm
10425411e769Sdjm EVP_PKEY_free(key->pkey);
10435411e769Sdjm if ((key->pkey = EVP_PKEY_new()) == NULL)
10445411e769Sdjm fatal("EVP_PKEY_new failed");
10455411e769Sdjm if (EVP_PKEY_set1_EC_KEY(key->pkey, ec) != 1)
10465411e769Sdjm fatal("EVP_PKEY_set1_EC_KEY failed");
104721f43f82Sdjm key->ecdsa_nid = nid;
104821f43f82Sdjm key->type = KEY_ECDSA;
104921f43f82Sdjm key->flags |= SSHKEY_FLAG_EXT;
105044e54ccbSdjm } else {
105121f43f82Sdjm error("unknown certificate key type");
105244e54ccbSdjm goto out;
105344e54ccbSdjm }
105444e54ccbSdjm out:
105521f43f82Sdjm for (i = 0; i < 3; i++)
105621f43f82Sdjm free(cert_attr[i].pValue);
105721f43f82Sdjm X509_free(x509);
105821f43f82Sdjm RSA_free(rsa);
105921f43f82Sdjm EC_KEY_free(ec);
106044e54ccbSdjm if (key == NULL) {
106144e54ccbSdjm free(subject);
106244e54ccbSdjm return -1;
106344e54ccbSdjm }
106444e54ccbSdjm /* success */
106544e54ccbSdjm *keyp = key;
106644e54ccbSdjm *labelp = subject;
106744e54ccbSdjm return 0;
106821f43f82Sdjm }
106921f43f82Sdjm
107021f43f82Sdjm #if 0
107112770bd2Smarkus static int
10727c94020aSdjm have_rsa_key(const RSA *rsa)
10737c94020aSdjm {
10747c94020aSdjm const BIGNUM *rsa_n, *rsa_e;
10757c94020aSdjm
10767c94020aSdjm RSA_get0_key(rsa, &rsa_n, &rsa_e, NULL);
10777c94020aSdjm return rsa_n != NULL && rsa_e != NULL;
10787c94020aSdjm }
107921f43f82Sdjm #endif
10807c94020aSdjm
1081d176a5c8Sdjm static void
note_key(struct pkcs11_provider * p,CK_ULONG slotidx,const char * context,struct sshkey * key)1082d176a5c8Sdjm note_key(struct pkcs11_provider *p, CK_ULONG slotidx, const char *context,
1083d176a5c8Sdjm struct sshkey *key)
1084d176a5c8Sdjm {
1085d176a5c8Sdjm char *fp;
1086d176a5c8Sdjm
1087d176a5c8Sdjm if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT,
1088d176a5c8Sdjm SSH_FP_DEFAULT)) == NULL) {
1089d176a5c8Sdjm error_f("sshkey_fingerprint failed");
1090d176a5c8Sdjm return;
1091d176a5c8Sdjm }
1092d176a5c8Sdjm debug2("%s: provider %s slot %lu: %s %s", context, p->name,
1093d176a5c8Sdjm (u_long)slotidx, sshkey_type(key), fp);
1094d176a5c8Sdjm free(fp);
1095d176a5c8Sdjm }
1096d176a5c8Sdjm
109721f43f82Sdjm /*
109821f43f82Sdjm * lookup certificates for token in slot identified by slotidx,
109921f43f82Sdjm * add 'wrapped' public keys to the 'keysp' array and increment nkeys.
110021f43f82Sdjm * keysp points to an (possibly empty) array with *nkeys keys.
110121f43f82Sdjm */
11027c94020aSdjm static int
pkcs11_fetch_certs(struct pkcs11_provider * p,CK_ULONG slotidx,struct sshkey *** keysp,char *** labelsp,int * nkeys)110321f43f82Sdjm pkcs11_fetch_certs(struct pkcs11_provider *p, CK_ULONG slotidx,
110444e54ccbSdjm struct sshkey ***keysp, char ***labelsp, int *nkeys)
110541503fafSmarkus {
110621f43f82Sdjm struct sshkey *key = NULL;
110721f43f82Sdjm CK_OBJECT_CLASS key_class;
110821f43f82Sdjm CK_ATTRIBUTE key_attr[1];
110921f43f82Sdjm CK_SESSION_HANDLE session;
111021f43f82Sdjm CK_FUNCTION_LIST *f = NULL;
111141503fafSmarkus CK_RV rv;
111241503fafSmarkus CK_OBJECT_HANDLE obj;
111321f43f82Sdjm CK_ULONG n = 0;
111421f43f82Sdjm int ret = -1;
111544e54ccbSdjm char *label;
111641503fafSmarkus
111721f43f82Sdjm memset(&key_attr, 0, sizeof(key_attr));
111821f43f82Sdjm memset(&obj, 0, sizeof(obj));
111921f43f82Sdjm
112021f43f82Sdjm key_class = CKO_CERTIFICATE;
112121f43f82Sdjm key_attr[0].type = CKA_CLASS;
112221f43f82Sdjm key_attr[0].pValue = &key_class;
112321f43f82Sdjm key_attr[0].ulValueLen = sizeof(key_class);
112421f43f82Sdjm
112541503fafSmarkus session = p->slotinfo[slotidx].session;
112621f43f82Sdjm f = p->function_list;
112721f43f82Sdjm
112821f43f82Sdjm rv = f->C_FindObjectsInit(session, key_attr, 1);
112921f43f82Sdjm if (rv != CKR_OK) {
113041503fafSmarkus error("C_FindObjectsInit failed: %lu", rv);
113121f43f82Sdjm goto fail;
113241503fafSmarkus }
113321f43f82Sdjm
113441503fafSmarkus while (1) {
113521f43f82Sdjm CK_CERTIFICATE_TYPE ck_cert_type;
113621f43f82Sdjm
113721f43f82Sdjm rv = f->C_FindObjects(session, &obj, 1, &n);
113821f43f82Sdjm if (rv != CKR_OK) {
113921f43f82Sdjm error("C_FindObjects failed: %lu", rv);
114021f43f82Sdjm goto fail;
114141503fafSmarkus }
114221f43f82Sdjm if (n == 0)
114341503fafSmarkus break;
114421f43f82Sdjm
114521f43f82Sdjm memset(&ck_cert_type, 0, sizeof(ck_cert_type));
114621f43f82Sdjm memset(&key_attr, 0, sizeof(key_attr));
114721f43f82Sdjm key_attr[0].type = CKA_CERTIFICATE_TYPE;
114821f43f82Sdjm key_attr[0].pValue = &ck_cert_type;
114921f43f82Sdjm key_attr[0].ulValueLen = sizeof(ck_cert_type);
115021f43f82Sdjm
115121f43f82Sdjm rv = f->C_GetAttributeValue(session, obj, key_attr, 1);
115221f43f82Sdjm if (rv != CKR_OK) {
115341503fafSmarkus error("C_GetAttributeValue failed: %lu", rv);
115421f43f82Sdjm goto fail;
115571eb7f6dSdjm }
115671eb7f6dSdjm
115744e54ccbSdjm key = NULL;
115844e54ccbSdjm label = NULL;
115921f43f82Sdjm switch (ck_cert_type) {
116021f43f82Sdjm case CKC_X_509:
116144e54ccbSdjm if (pkcs11_fetch_x509_pubkey(p, slotidx, &obj,
116244e54ccbSdjm &key, &label) != 0) {
116344e54ccbSdjm error("failed to fetch key");
116444e54ccbSdjm continue;
116544e54ccbSdjm }
116621f43f82Sdjm break;
116721f43f82Sdjm default:
116844e54ccbSdjm error("skipping unsupported certificate type %lu",
116944e54ccbSdjm ck_cert_type);
117021f43f82Sdjm continue;
11717c94020aSdjm }
1172d176a5c8Sdjm note_key(p, slotidx, __func__, key);
117312770bd2Smarkus if (pkcs11_key_included(keysp, nkeys, key)) {
1174*479c151dSjsg debug2_f("key already included");
11752aa7d220Sdjm sshkey_free(key);
117612770bd2Smarkus } else {
117741503fafSmarkus /* expand key array and add key */
1178eaf8e3f6Sderaadt *keysp = xrecallocarray(*keysp, *nkeys,
1179eaf8e3f6Sderaadt *nkeys + 1, sizeof(struct sshkey *));
118041503fafSmarkus (*keysp)[*nkeys] = key;
118144e54ccbSdjm if (labelsp != NULL) {
118244e54ccbSdjm *labelsp = xrecallocarray(*labelsp, *nkeys,
118344e54ccbSdjm *nkeys + 1, sizeof(char *));
118444e54ccbSdjm (*labelsp)[*nkeys] = xstrdup((char *)label);
118544e54ccbSdjm }
118641503fafSmarkus *nkeys = *nkeys + 1;
118741503fafSmarkus debug("have %d keys", *nkeys);
118841503fafSmarkus }
118941503fafSmarkus }
119041503fafSmarkus
119121f43f82Sdjm ret = 0;
119221f43f82Sdjm fail:
119321f43f82Sdjm rv = f->C_FindObjectsFinal(session);
119421f43f82Sdjm if (rv != CKR_OK) {
119521f43f82Sdjm error("C_FindObjectsFinal failed: %lu", rv);
119621f43f82Sdjm ret = -1;
119721f43f82Sdjm }
119821f43f82Sdjm
119921f43f82Sdjm return (ret);
120021f43f82Sdjm }
120121f43f82Sdjm
120221f43f82Sdjm /*
120321f43f82Sdjm * lookup public keys for token in slot identified by slotidx,
120421f43f82Sdjm * add 'wrapped' public keys to the 'keysp' array and increment nkeys.
120521f43f82Sdjm * keysp points to an (possibly empty) array with *nkeys keys.
120621f43f82Sdjm */
120721f43f82Sdjm static int
pkcs11_fetch_keys(struct pkcs11_provider * p,CK_ULONG slotidx,struct sshkey *** keysp,char *** labelsp,int * nkeys)120821f43f82Sdjm pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx,
120944e54ccbSdjm struct sshkey ***keysp, char ***labelsp, int *nkeys)
121021f43f82Sdjm {
121121f43f82Sdjm struct sshkey *key = NULL;
121221f43f82Sdjm CK_OBJECT_CLASS key_class;
121344e54ccbSdjm CK_ATTRIBUTE key_attr[2];
121421f43f82Sdjm CK_SESSION_HANDLE session;
121521f43f82Sdjm CK_FUNCTION_LIST *f = NULL;
121621f43f82Sdjm CK_RV rv;
121721f43f82Sdjm CK_OBJECT_HANDLE obj;
121821f43f82Sdjm CK_ULONG n = 0;
121921f43f82Sdjm int ret = -1;
122021f43f82Sdjm
122121f43f82Sdjm memset(&key_attr, 0, sizeof(key_attr));
122221f43f82Sdjm memset(&obj, 0, sizeof(obj));
122321f43f82Sdjm
122421f43f82Sdjm key_class = CKO_PUBLIC_KEY;
122521f43f82Sdjm key_attr[0].type = CKA_CLASS;
122621f43f82Sdjm key_attr[0].pValue = &key_class;
122721f43f82Sdjm key_attr[0].ulValueLen = sizeof(key_class);
122821f43f82Sdjm
122921f43f82Sdjm session = p->slotinfo[slotidx].session;
123021f43f82Sdjm f = p->function_list;
123121f43f82Sdjm
123221f43f82Sdjm rv = f->C_FindObjectsInit(session, key_attr, 1);
123321f43f82Sdjm if (rv != CKR_OK) {
123421f43f82Sdjm error("C_FindObjectsInit failed: %lu", rv);
123521f43f82Sdjm goto fail;
123621f43f82Sdjm }
123721f43f82Sdjm
123821f43f82Sdjm while (1) {
123921f43f82Sdjm CK_KEY_TYPE ck_key_type;
124044e54ccbSdjm CK_UTF8CHAR label[256];
124121f43f82Sdjm
124221f43f82Sdjm rv = f->C_FindObjects(session, &obj, 1, &n);
124321f43f82Sdjm if (rv != CKR_OK) {
124421f43f82Sdjm error("C_FindObjects failed: %lu", rv);
124521f43f82Sdjm goto fail;
124621f43f82Sdjm }
124721f43f82Sdjm if (n == 0)
124821f43f82Sdjm break;
124921f43f82Sdjm
125021f43f82Sdjm memset(&ck_key_type, 0, sizeof(ck_key_type));
125121f43f82Sdjm memset(&key_attr, 0, sizeof(key_attr));
125221f43f82Sdjm key_attr[0].type = CKA_KEY_TYPE;
125321f43f82Sdjm key_attr[0].pValue = &ck_key_type;
125421f43f82Sdjm key_attr[0].ulValueLen = sizeof(ck_key_type);
125544e54ccbSdjm key_attr[1].type = CKA_LABEL;
125644e54ccbSdjm key_attr[1].pValue = &label;
125744e54ccbSdjm key_attr[1].ulValueLen = sizeof(label) - 1;
125821f43f82Sdjm
125944e54ccbSdjm rv = f->C_GetAttributeValue(session, obj, key_attr, 2);
126021f43f82Sdjm if (rv != CKR_OK) {
126121f43f82Sdjm error("C_GetAttributeValue failed: %lu", rv);
126221f43f82Sdjm goto fail;
126321f43f82Sdjm }
126421f43f82Sdjm
126544e54ccbSdjm label[key_attr[1].ulValueLen] = '\0';
126644e54ccbSdjm
126721f43f82Sdjm switch (ck_key_type) {
126821f43f82Sdjm case CKK_RSA:
126921f43f82Sdjm key = pkcs11_fetch_rsa_pubkey(p, slotidx, &obj);
127021f43f82Sdjm break;
127121f43f82Sdjm case CKK_ECDSA:
127221f43f82Sdjm key = pkcs11_fetch_ecdsa_pubkey(p, slotidx, &obj);
127321f43f82Sdjm break;
127421f43f82Sdjm default:
127521f43f82Sdjm /* XXX print key type? */
12767b1c2372Smarkus key = NULL;
127721f43f82Sdjm error("skipping unsupported key type");
127821f43f82Sdjm }
127921f43f82Sdjm
128021f43f82Sdjm if (key == NULL) {
128121f43f82Sdjm error("failed to fetch key");
128221f43f82Sdjm continue;
128321f43f82Sdjm }
1284d176a5c8Sdjm note_key(p, slotidx, __func__, key);
128521f43f82Sdjm if (pkcs11_key_included(keysp, nkeys, key)) {
1286*479c151dSjsg debug2_f("key already included");
128721f43f82Sdjm sshkey_free(key);
128821f43f82Sdjm } else {
128921f43f82Sdjm /* expand key array and add key */
129021f43f82Sdjm *keysp = xrecallocarray(*keysp, *nkeys,
129121f43f82Sdjm *nkeys + 1, sizeof(struct sshkey *));
129221f43f82Sdjm (*keysp)[*nkeys] = key;
129344e54ccbSdjm if (labelsp != NULL) {
129444e54ccbSdjm *labelsp = xrecallocarray(*labelsp, *nkeys,
129544e54ccbSdjm *nkeys + 1, sizeof(char *));
129644e54ccbSdjm (*labelsp)[*nkeys] = xstrdup((char *)label);
129744e54ccbSdjm }
129821f43f82Sdjm *nkeys = *nkeys + 1;
129921f43f82Sdjm debug("have %d keys", *nkeys);
130021f43f82Sdjm }
130121f43f82Sdjm }
130221f43f82Sdjm
130321f43f82Sdjm ret = 0;
130421f43f82Sdjm fail:
130521f43f82Sdjm rv = f->C_FindObjectsFinal(session);
130621f43f82Sdjm if (rv != CKR_OK) {
130721f43f82Sdjm error("C_FindObjectsFinal failed: %lu", rv);
130821f43f82Sdjm ret = -1;
130921f43f82Sdjm }
131021f43f82Sdjm
131121f43f82Sdjm return (ret);
131221f43f82Sdjm }
131321f43f82Sdjm
131421f43f82Sdjm #ifdef WITH_PKCS11_KEYGEN
131521f43f82Sdjm #define FILL_ATTR(attr, idx, typ, val, len) \
131621f43f82Sdjm { (attr[idx]).type=(typ); (attr[idx]).pValue=(val); (attr[idx]).ulValueLen=len; idx++; }
131721f43f82Sdjm
131821f43f82Sdjm static struct sshkey *
pkcs11_rsa_generate_private_key(struct pkcs11_provider * p,CK_ULONG slotidx,char * label,CK_ULONG bits,CK_BYTE keyid,u_int32_t * err)131921f43f82Sdjm pkcs11_rsa_generate_private_key(struct pkcs11_provider *p, CK_ULONG slotidx,
132021f43f82Sdjm char *label, CK_ULONG bits, CK_BYTE keyid, u_int32_t *err)
132121f43f82Sdjm {
132221f43f82Sdjm struct pkcs11_slotinfo *si;
132321f43f82Sdjm char *plabel = label ? label : "";
132421f43f82Sdjm int npub = 0, npriv = 0;
132521f43f82Sdjm CK_RV rv;
132621f43f82Sdjm CK_FUNCTION_LIST *f;
132721f43f82Sdjm CK_SESSION_HANDLE session;
132821f43f82Sdjm CK_BBOOL true_val = CK_TRUE, false_val = CK_FALSE;
132921f43f82Sdjm CK_OBJECT_HANDLE pubKey, privKey;
133021f43f82Sdjm CK_ATTRIBUTE tpub[16], tpriv[16];
133121f43f82Sdjm CK_MECHANISM mech = {
133221f43f82Sdjm CKM_RSA_PKCS_KEY_PAIR_GEN, NULL_PTR, 0
133321f43f82Sdjm };
133421f43f82Sdjm CK_BYTE pubExponent[] = {
133521f43f82Sdjm 0x01, 0x00, 0x01 /* RSA_F4 in bytes */
133621f43f82Sdjm };
133721f43f82Sdjm
133821f43f82Sdjm *err = 0;
133921f43f82Sdjm
134021f43f82Sdjm FILL_ATTR(tpub, npub, CKA_TOKEN, &true_val, sizeof(true_val));
134121f43f82Sdjm FILL_ATTR(tpub, npub, CKA_LABEL, plabel, strlen(plabel));
134221f43f82Sdjm FILL_ATTR(tpub, npub, CKA_ENCRYPT, &false_val, sizeof(false_val));
134321f43f82Sdjm FILL_ATTR(tpub, npub, CKA_VERIFY, &true_val, sizeof(true_val));
134421f43f82Sdjm FILL_ATTR(tpub, npub, CKA_VERIFY_RECOVER, &false_val,
134521f43f82Sdjm sizeof(false_val));
134621f43f82Sdjm FILL_ATTR(tpub, npub, CKA_WRAP, &false_val, sizeof(false_val));
134721f43f82Sdjm FILL_ATTR(tpub, npub, CKA_DERIVE, &false_val, sizeof(false_val));
134821f43f82Sdjm FILL_ATTR(tpub, npub, CKA_MODULUS_BITS, &bits, sizeof(bits));
134921f43f82Sdjm FILL_ATTR(tpub, npub, CKA_PUBLIC_EXPONENT, pubExponent,
135021f43f82Sdjm sizeof(pubExponent));
135121f43f82Sdjm FILL_ATTR(tpub, npub, CKA_ID, &keyid, sizeof(keyid));
135221f43f82Sdjm
135321f43f82Sdjm FILL_ATTR(tpriv, npriv, CKA_TOKEN, &true_val, sizeof(true_val));
135421f43f82Sdjm FILL_ATTR(tpriv, npriv, CKA_LABEL, plabel, strlen(plabel));
135521f43f82Sdjm FILL_ATTR(tpriv, npriv, CKA_PRIVATE, &true_val, sizeof(true_val));
135621f43f82Sdjm FILL_ATTR(tpriv, npriv, CKA_SENSITIVE, &true_val, sizeof(true_val));
135721f43f82Sdjm FILL_ATTR(tpriv, npriv, CKA_DECRYPT, &false_val, sizeof(false_val));
135821f43f82Sdjm FILL_ATTR(tpriv, npriv, CKA_SIGN, &true_val, sizeof(true_val));
135921f43f82Sdjm FILL_ATTR(tpriv, npriv, CKA_SIGN_RECOVER, &false_val,
136021f43f82Sdjm sizeof(false_val));
136121f43f82Sdjm FILL_ATTR(tpriv, npriv, CKA_UNWRAP, &false_val, sizeof(false_val));
136221f43f82Sdjm FILL_ATTR(tpriv, npriv, CKA_DERIVE, &false_val, sizeof(false_val));
136321f43f82Sdjm FILL_ATTR(tpriv, npriv, CKA_ID, &keyid, sizeof(keyid));
136421f43f82Sdjm
136521f43f82Sdjm f = p->function_list;
136621f43f82Sdjm si = &p->slotinfo[slotidx];
136721f43f82Sdjm session = si->session;
136821f43f82Sdjm
136921f43f82Sdjm if ((rv = f->C_GenerateKeyPair(session, &mech, tpub, npub, tpriv, npriv,
137021f43f82Sdjm &pubKey, &privKey)) != CKR_OK) {
137148e6b99dSdjm error_f("key generation failed: error 0x%lx", rv);
137221f43f82Sdjm *err = rv;
137321f43f82Sdjm return NULL;
137421f43f82Sdjm }
137521f43f82Sdjm
137621f43f82Sdjm return pkcs11_fetch_rsa_pubkey(p, slotidx, &pubKey);
137721f43f82Sdjm }
137821f43f82Sdjm
137921f43f82Sdjm static int
h2i(char c)1380da716d80Sderaadt h2i(char c)
1381da716d80Sderaadt {
1382da716d80Sderaadt if (c >= '0' && c <= '9')
1383354c843bSderaadt return c - '0';
1384da716d80Sderaadt else if (c >= 'a' && c <= 'f')
1385354c843bSderaadt return c - 'a' + 10;
1386da716d80Sderaadt else if (c >= 'A' && c <= 'F')
1387354c843bSderaadt return c - 'A' + 10;
1388da716d80Sderaadt else
1389da716d80Sderaadt return -1;
1390da716d80Sderaadt }
1391da716d80Sderaadt
1392da716d80Sderaadt static int
pkcs11_decode_hex(const char * hex,unsigned char ** dest,size_t * rlen)139321f43f82Sdjm pkcs11_decode_hex(const char *hex, unsigned char **dest, size_t *rlen)
139421f43f82Sdjm {
139521f43f82Sdjm size_t i, len;
139621f43f82Sdjm
139721f43f82Sdjm if (dest)
139821f43f82Sdjm *dest = NULL;
139921f43f82Sdjm if (rlen)
140021f43f82Sdjm *rlen = 0;
140121f43f82Sdjm
140221f43f82Sdjm if ((len = strlen(hex)) % 2)
140321f43f82Sdjm return -1;
140421f43f82Sdjm len /= 2;
140521f43f82Sdjm
140621f43f82Sdjm *dest = xmalloc(len);
140721f43f82Sdjm
140821f43f82Sdjm for (i = 0; i < len; i++) {
1409da716d80Sderaadt int hi, low;
1410da716d80Sderaadt
1411da716d80Sderaadt hi = h2i(hex[2 * i]);
1412da716d80Sderaadt lo = h2i(hex[(2 * i) + 1]);
1413da716d80Sderaadt if (hi == -1 || lo == -1)
141421f43f82Sdjm return -1;
1415da716d80Sderaadt (*dest)[i] = (hi << 4) | lo;
141621f43f82Sdjm }
141721f43f82Sdjm
141821f43f82Sdjm if (rlen)
141921f43f82Sdjm *rlen = len;
142021f43f82Sdjm
142121f43f82Sdjm return 0;
142221f43f82Sdjm }
142321f43f82Sdjm
142421f43f82Sdjm static struct ec_curve_info {
142521f43f82Sdjm const char *name;
142621f43f82Sdjm const char *oid;
142721f43f82Sdjm const char *oid_encoded;
142821f43f82Sdjm size_t size;
142921f43f82Sdjm } ec_curve_infos[] = {
143021f43f82Sdjm {"prime256v1", "1.2.840.10045.3.1.7", "06082A8648CE3D030107", 256},
143121f43f82Sdjm {"secp384r1", "1.3.132.0.34", "06052B81040022", 384},
143221f43f82Sdjm {"secp521r1", "1.3.132.0.35", "06052B81040023", 521},
143321f43f82Sdjm {NULL, NULL, NULL, 0},
143421f43f82Sdjm };
143521f43f82Sdjm
143621f43f82Sdjm static struct sshkey *
pkcs11_ecdsa_generate_private_key(struct pkcs11_provider * p,CK_ULONG slotidx,char * label,CK_ULONG bits,CK_BYTE keyid,u_int32_t * err)143721f43f82Sdjm pkcs11_ecdsa_generate_private_key(struct pkcs11_provider *p, CK_ULONG slotidx,
143821f43f82Sdjm char *label, CK_ULONG bits, CK_BYTE keyid, u_int32_t *err)
143921f43f82Sdjm {
144021f43f82Sdjm struct pkcs11_slotinfo *si;
144121f43f82Sdjm char *plabel = label ? label : "";
144221f43f82Sdjm int i;
144321f43f82Sdjm size_t ecparams_size;
144421f43f82Sdjm unsigned char *ecparams = NULL;
144521f43f82Sdjm int npub = 0, npriv = 0;
144621f43f82Sdjm CK_RV rv;
144721f43f82Sdjm CK_FUNCTION_LIST *f;
144821f43f82Sdjm CK_SESSION_HANDLE session;
144921f43f82Sdjm CK_BBOOL true_val = CK_TRUE, false_val = CK_FALSE;
145021f43f82Sdjm CK_OBJECT_HANDLE pubKey, privKey;
145121f43f82Sdjm CK_MECHANISM mech = {
145221f43f82Sdjm CKM_EC_KEY_PAIR_GEN, NULL_PTR, 0
145321f43f82Sdjm };
145421f43f82Sdjm CK_ATTRIBUTE tpub[16], tpriv[16];
145521f43f82Sdjm
145621f43f82Sdjm *err = 0;
145721f43f82Sdjm
145821f43f82Sdjm for (i = 0; ec_curve_infos[i].name; i++) {
145921f43f82Sdjm if (ec_curve_infos[i].size == bits)
146021f43f82Sdjm break;
146121f43f82Sdjm }
146221f43f82Sdjm if (!ec_curve_infos[i].name) {
146348e6b99dSdjm error_f("invalid key size %lu", bits);
146421f43f82Sdjm return NULL;
146521f43f82Sdjm }
146621f43f82Sdjm if (pkcs11_decode_hex(ec_curve_infos[i].oid_encoded, &ecparams,
146721f43f82Sdjm &ecparams_size) == -1) {
146848e6b99dSdjm error_f("invalid oid");
146921f43f82Sdjm return NULL;
147021f43f82Sdjm }
147121f43f82Sdjm
147221f43f82Sdjm FILL_ATTR(tpub, npub, CKA_TOKEN, &true_val, sizeof(true_val));
147321f43f82Sdjm FILL_ATTR(tpub, npub, CKA_LABEL, plabel, strlen(plabel));
147421f43f82Sdjm FILL_ATTR(tpub, npub, CKA_ENCRYPT, &false_val, sizeof(false_val));
147521f43f82Sdjm FILL_ATTR(tpub, npub, CKA_VERIFY, &true_val, sizeof(true_val));
147621f43f82Sdjm FILL_ATTR(tpub, npub, CKA_VERIFY_RECOVER, &false_val,
147721f43f82Sdjm sizeof(false_val));
147821f43f82Sdjm FILL_ATTR(tpub, npub, CKA_WRAP, &false_val, sizeof(false_val));
147921f43f82Sdjm FILL_ATTR(tpub, npub, CKA_DERIVE, &false_val, sizeof(false_val));
148021f43f82Sdjm FILL_ATTR(tpub, npub, CKA_EC_PARAMS, ecparams, ecparams_size);
148121f43f82Sdjm FILL_ATTR(tpub, npub, CKA_ID, &keyid, sizeof(keyid));
148221f43f82Sdjm
148321f43f82Sdjm FILL_ATTR(tpriv, npriv, CKA_TOKEN, &true_val, sizeof(true_val));
148421f43f82Sdjm FILL_ATTR(tpriv, npriv, CKA_LABEL, plabel, strlen(plabel));
148521f43f82Sdjm FILL_ATTR(tpriv, npriv, CKA_PRIVATE, &true_val, sizeof(true_val));
148621f43f82Sdjm FILL_ATTR(tpriv, npriv, CKA_SENSITIVE, &true_val, sizeof(true_val));
148721f43f82Sdjm FILL_ATTR(tpriv, npriv, CKA_DECRYPT, &false_val, sizeof(false_val));
148821f43f82Sdjm FILL_ATTR(tpriv, npriv, CKA_SIGN, &true_val, sizeof(true_val));
148921f43f82Sdjm FILL_ATTR(tpriv, npriv, CKA_SIGN_RECOVER, &false_val,
149021f43f82Sdjm sizeof(false_val));
149121f43f82Sdjm FILL_ATTR(tpriv, npriv, CKA_UNWRAP, &false_val, sizeof(false_val));
149221f43f82Sdjm FILL_ATTR(tpriv, npriv, CKA_DERIVE, &false_val, sizeof(false_val));
149321f43f82Sdjm FILL_ATTR(tpriv, npriv, CKA_ID, &keyid, sizeof(keyid));
149421f43f82Sdjm
149521f43f82Sdjm f = p->function_list;
149621f43f82Sdjm si = &p->slotinfo[slotidx];
149721f43f82Sdjm session = si->session;
149821f43f82Sdjm
149921f43f82Sdjm if ((rv = f->C_GenerateKeyPair(session, &mech, tpub, npub, tpriv, npriv,
150021f43f82Sdjm &pubKey, &privKey)) != CKR_OK) {
150148e6b99dSdjm error_f("key generation failed: error 0x%lx", rv);
150221f43f82Sdjm *err = rv;
150321f43f82Sdjm return NULL;
150421f43f82Sdjm }
150521f43f82Sdjm
150621f43f82Sdjm return pkcs11_fetch_ecdsa_pubkey(p, slotidx, &pubKey);
150721f43f82Sdjm }
150821f43f82Sdjm #endif /* WITH_PKCS11_KEYGEN */
150921f43f82Sdjm
151021f43f82Sdjm /*
151121f43f82Sdjm * register a new provider, fails if provider already exists. if
151221f43f82Sdjm * keyp is provided, fetch keys.
151321f43f82Sdjm */
151421f43f82Sdjm static int
pkcs11_register_provider(char * provider_id,char * pin,struct sshkey *** keyp,char *** labelsp,struct pkcs11_provider ** providerp,CK_ULONG user)151544e54ccbSdjm pkcs11_register_provider(char *provider_id, char *pin,
151644e54ccbSdjm struct sshkey ***keyp, char ***labelsp,
151721f43f82Sdjm struct pkcs11_provider **providerp, CK_ULONG user)
151841503fafSmarkus {
151941503fafSmarkus int nkeys, need_finalize = 0;
152021f43f82Sdjm int ret = -1;
152141503fafSmarkus struct pkcs11_provider *p = NULL;
152241503fafSmarkus void *handle = NULL;
152341503fafSmarkus CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **);
152441503fafSmarkus CK_RV rv;
152541503fafSmarkus CK_FUNCTION_LIST *f = NULL;
152641503fafSmarkus CK_TOKEN_INFO *token;
152741503fafSmarkus CK_ULONG i;
152841503fafSmarkus
152921f43f82Sdjm if (providerp == NULL)
153021f43f82Sdjm goto fail;
153121f43f82Sdjm *providerp = NULL;
153221f43f82Sdjm
153321f43f82Sdjm if (keyp != NULL)
153441503fafSmarkus *keyp = NULL;
153544e54ccbSdjm if (labelsp != NULL)
153644e54ccbSdjm *labelsp = NULL;
153721f43f82Sdjm
153841503fafSmarkus if (pkcs11_provider_lookup(provider_id) != NULL) {
153948e6b99dSdjm debug_f("provider already registered: %s", provider_id);
154041503fafSmarkus goto fail;
154141503fafSmarkus }
1542f8f5a6b0Sdjm if (lib_contains_symbol(provider_id, "C_GetFunctionList") != 0) {
1543f8f5a6b0Sdjm error("provider %s is not a PKCS11 library", provider_id);
1544f8f5a6b0Sdjm goto fail;
1545f8f5a6b0Sdjm }
154621f43f82Sdjm /* open shared pkcs11-library */
154741503fafSmarkus if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) {
154841503fafSmarkus error("dlopen %s failed: %s", provider_id, dlerror());
154941503fafSmarkus goto fail;
155041503fafSmarkus }
1551f03a4faaSdjm if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL)
1552f03a4faaSdjm fatal("dlsym(C_GetFunctionList) failed: %s", dlerror());
155341503fafSmarkus p = xcalloc(1, sizeof(*p));
155441503fafSmarkus p->name = xstrdup(provider_id);
155541503fafSmarkus p->handle = handle;
155641503fafSmarkus /* setup the pkcs11 callbacks */
155741503fafSmarkus if ((rv = (*getfunctionlist)(&f)) != CKR_OK) {
15583c54a32fSdjm error("C_GetFunctionList for provider %s failed: %lu",
15593c54a32fSdjm provider_id, rv);
156041503fafSmarkus goto fail;
156141503fafSmarkus }
156241503fafSmarkus p->function_list = f;
156341503fafSmarkus if ((rv = f->C_Initialize(NULL)) != CKR_OK) {
15643c54a32fSdjm error("C_Initialize for provider %s failed: %lu",
15653c54a32fSdjm provider_id, rv);
156641503fafSmarkus goto fail;
156741503fafSmarkus }
156841503fafSmarkus need_finalize = 1;
156941503fafSmarkus if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) {
15703c54a32fSdjm error("C_GetInfo for provider %s failed: %lu",
15713c54a32fSdjm provider_id, rv);
157241503fafSmarkus goto fail;
157341503fafSmarkus }
15742c6bff6fSdjm debug("provider %s: manufacturerID <%.*s> cryptokiVersion %d.%d"
15752c6bff6fSdjm " libraryDescription <%.*s> libraryVersion %d.%d",
15763c54a32fSdjm provider_id,
15772c6bff6fSdjm RMSPACE(p->info.manufacturerID),
157841503fafSmarkus p->info.cryptokiVersion.major,
157941503fafSmarkus p->info.cryptokiVersion.minor,
15802c6bff6fSdjm RMSPACE(p->info.libraryDescription),
158141503fafSmarkus p->info.libraryVersion.major,
158241503fafSmarkus p->info.libraryVersion.minor);
158341503fafSmarkus if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &p->nslots)) != CKR_OK) {
158441503fafSmarkus error("C_GetSlotList failed: %lu", rv);
158541503fafSmarkus goto fail;
158641503fafSmarkus }
158741503fafSmarkus if (p->nslots == 0) {
158848e6b99dSdjm debug_f("provider %s returned no slots", provider_id);
158921f43f82Sdjm ret = -SSH_PKCS11_ERR_NO_SLOTS;
159041503fafSmarkus goto fail;
159141503fafSmarkus }
159241503fafSmarkus p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID));
159341503fafSmarkus if ((rv = f->C_GetSlotList(CK_TRUE, p->slotlist, &p->nslots))
159441503fafSmarkus != CKR_OK) {
15953c54a32fSdjm error("C_GetSlotList for provider %s failed: %lu",
15963c54a32fSdjm provider_id, rv);
159741503fafSmarkus goto fail;
159841503fafSmarkus }
159941503fafSmarkus p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo));
160041503fafSmarkus p->valid = 1;
160141503fafSmarkus nkeys = 0;
160241503fafSmarkus for (i = 0; i < p->nslots; i++) {
160341503fafSmarkus token = &p->slotinfo[i].token;
160441503fafSmarkus if ((rv = f->C_GetTokenInfo(p->slotlist[i], token))
160541503fafSmarkus != CKR_OK) {
16063c54a32fSdjm error("C_GetTokenInfo for provider %s slot %lu "
160748e6b99dSdjm "failed: %lu", provider_id, (u_long)i, rv);
160841503fafSmarkus continue;
160941503fafSmarkus }
1610eea3c97aSdjm if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) {
161148e6b99dSdjm debug2_f("ignoring uninitialised token in "
161248e6b99dSdjm "provider %s slot %lu", provider_id, (u_long)i);
1613eea3c97aSdjm continue;
1614eea3c97aSdjm }
16152c6bff6fSdjm debug("provider %s slot %lu: label <%.*s> "
16162c6bff6fSdjm "manufacturerID <%.*s> model <%.*s> serial <%.*s> "
16172c6bff6fSdjm "flags 0x%lx",
16183c54a32fSdjm provider_id, (unsigned long)i,
16192c6bff6fSdjm RMSPACE(token->label), RMSPACE(token->manufacturerID),
16202c6bff6fSdjm RMSPACE(token->model), RMSPACE(token->serialNumber),
16212c6bff6fSdjm token->flags);
162221f43f82Sdjm /*
162321f43f82Sdjm * open session, login with pin and retrieve public
162421f43f82Sdjm * keys (if keyp is provided)
162521f43f82Sdjm */
1626f69acb9aSdjm if ((ret = pkcs11_open_session(p, i, pin, user)) != 0 ||
1627f69acb9aSdjm keyp == NULL)
162821f43f82Sdjm continue;
162944e54ccbSdjm pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys);
163044e54ccbSdjm pkcs11_fetch_certs(p, i, keyp, labelsp, &nkeys);
1631f69acb9aSdjm if (nkeys == 0 && !p->slotinfo[i].logged_in &&
1632f69acb9aSdjm pkcs11_interactive) {
1633f69acb9aSdjm /*
1634f69acb9aSdjm * Some tokens require login before they will
1635f69acb9aSdjm * expose keys.
1636f69acb9aSdjm */
1637f69acb9aSdjm if (pkcs11_login_slot(p, &p->slotinfo[i],
1638f69acb9aSdjm CKU_USER) < 0) {
1639f69acb9aSdjm error("login failed");
1640f69acb9aSdjm continue;
1641f69acb9aSdjm }
164244e54ccbSdjm pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys);
164344e54ccbSdjm pkcs11_fetch_certs(p, i, keyp, labelsp, &nkeys);
164441503fafSmarkus }
164521f43f82Sdjm }
164621f43f82Sdjm
164721f43f82Sdjm /* now owned by caller */
164821f43f82Sdjm *providerp = p;
164921f43f82Sdjm
165041503fafSmarkus TAILQ_INSERT_TAIL(&pkcs11_providers, p, next);
165141503fafSmarkus p->refcount++; /* add to provider list */
165221f43f82Sdjm
165341503fafSmarkus return (nkeys);
165441503fafSmarkus fail:
165541503fafSmarkus if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK)
16563c54a32fSdjm error("C_Finalize for provider %s failed: %lu",
16573c54a32fSdjm provider_id, rv);
165841503fafSmarkus if (p) {
165921f43f82Sdjm free(p->name);
16600d40fefdSdjm free(p->slotlist);
16610d40fefdSdjm free(p->slotinfo);
16620d40fefdSdjm free(p);
166341503fafSmarkus }
166441503fafSmarkus if (handle)
166541503fafSmarkus dlclose(handle);
1666816fc154Smarkus if (ret > 0)
1667816fc154Smarkus ret = -1;
166821f43f82Sdjm return (ret);
166941503fafSmarkus }
167021f43f82Sdjm
167121f43f82Sdjm /*
167221f43f82Sdjm * register a new provider and get number of keys hold by the token,
167321f43f82Sdjm * fails if provider already exists
167421f43f82Sdjm */
167521f43f82Sdjm int
pkcs11_add_provider(char * provider_id,char * pin,struct sshkey *** keyp,char *** labelsp)167644e54ccbSdjm pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp,
167744e54ccbSdjm char ***labelsp)
167821f43f82Sdjm {
167921f43f82Sdjm struct pkcs11_provider *p = NULL;
168021f43f82Sdjm int nkeys;
168121f43f82Sdjm
168244e54ccbSdjm nkeys = pkcs11_register_provider(provider_id, pin, keyp, labelsp,
168344e54ccbSdjm &p, CKU_USER);
168421f43f82Sdjm
168521f43f82Sdjm /* no keys found or some other error, de-register provider */
168621f43f82Sdjm if (nkeys <= 0 && p != NULL) {
168721f43f82Sdjm TAILQ_REMOVE(&pkcs11_providers, p, next);
168821f43f82Sdjm pkcs11_provider_finalize(p);
168921f43f82Sdjm pkcs11_provider_unref(p);
169021f43f82Sdjm }
169121f43f82Sdjm if (nkeys == 0)
169248e6b99dSdjm debug_f("provider %s returned no keys", provider_id);
169321f43f82Sdjm
169421f43f82Sdjm return (nkeys);
169521f43f82Sdjm }
169621f43f82Sdjm
169721f43f82Sdjm #ifdef WITH_PKCS11_KEYGEN
169821f43f82Sdjm struct sshkey *
pkcs11_gakp(char * provider_id,char * pin,unsigned int slotidx,char * label,unsigned int type,unsigned int bits,unsigned char keyid,u_int32_t * err)169921f43f82Sdjm pkcs11_gakp(char *provider_id, char *pin, unsigned int slotidx, char *label,
170021f43f82Sdjm unsigned int type, unsigned int bits, unsigned char keyid, u_int32_t *err)
170121f43f82Sdjm {
170221f43f82Sdjm struct pkcs11_provider *p = NULL;
170321f43f82Sdjm struct pkcs11_slotinfo *si;
170421f43f82Sdjm CK_FUNCTION_LIST *f;
170521f43f82Sdjm CK_SESSION_HANDLE session;
170621f43f82Sdjm struct sshkey *k = NULL;
170721f43f82Sdjm int ret = -1, reset_pin = 0, reset_provider = 0;
170821f43f82Sdjm CK_RV rv;
170921f43f82Sdjm
171021f43f82Sdjm *err = 0;
171121f43f82Sdjm
171221f43f82Sdjm if ((p = pkcs11_provider_lookup(provider_id)) != NULL)
171348e6b99dSdjm debug_f("provider \"%s\" available", provider_id);
171444e54ccbSdjm else if ((ret = pkcs11_register_provider(provider_id, pin, NULL, NULL,
171544e54ccbSdjm &p, CKU_SO)) < 0) {
171648e6b99dSdjm debug_f("could not register provider %s", provider_id);
171721f43f82Sdjm goto out;
171821f43f82Sdjm } else
171921f43f82Sdjm reset_provider = 1;
172021f43f82Sdjm
172121f43f82Sdjm f = p->function_list;
172221f43f82Sdjm si = &p->slotinfo[slotidx];
172321f43f82Sdjm session = si->session;
172421f43f82Sdjm
172521f43f82Sdjm if ((rv = f->C_SetOperationState(session , pin, strlen(pin),
172621f43f82Sdjm CK_INVALID_HANDLE, CK_INVALID_HANDLE)) != CKR_OK) {
172748e6b99dSdjm debug_f("could not supply SO pin: %lu", rv);
172821f43f82Sdjm reset_pin = 0;
172921f43f82Sdjm } else
173021f43f82Sdjm reset_pin = 1;
173121f43f82Sdjm
173221f43f82Sdjm switch (type) {
173321f43f82Sdjm case KEY_RSA:
173421f43f82Sdjm if ((k = pkcs11_rsa_generate_private_key(p, slotidx, label,
173521f43f82Sdjm bits, keyid, err)) == NULL) {
173648e6b99dSdjm debug_f("failed to generate RSA key");
173721f43f82Sdjm goto out;
173821f43f82Sdjm }
173921f43f82Sdjm break;
174021f43f82Sdjm case KEY_ECDSA:
174121f43f82Sdjm if ((k = pkcs11_ecdsa_generate_private_key(p, slotidx, label,
174221f43f82Sdjm bits, keyid, err)) == NULL) {
174348e6b99dSdjm debug_f("failed to generate ECDSA key");
174421f43f82Sdjm goto out;
174521f43f82Sdjm }
174621f43f82Sdjm break;
174721f43f82Sdjm default:
174821f43f82Sdjm *err = SSH_PKCS11_ERR_GENERIC;
174948e6b99dSdjm debug_f("unknown type %d", type);
175021f43f82Sdjm goto out;
175121f43f82Sdjm }
175221f43f82Sdjm
175321f43f82Sdjm out:
175421f43f82Sdjm if (reset_pin)
175521f43f82Sdjm f->C_SetOperationState(session , NULL, 0, CK_INVALID_HANDLE,
175621f43f82Sdjm CK_INVALID_HANDLE);
175721f43f82Sdjm
175821f43f82Sdjm if (reset_provider)
175921f43f82Sdjm pkcs11_del_provider(provider_id);
176021f43f82Sdjm
176121f43f82Sdjm return (k);
176221f43f82Sdjm }
176321f43f82Sdjm
176421f43f82Sdjm struct sshkey *
pkcs11_destroy_keypair(char * provider_id,char * pin,unsigned long slotidx,unsigned char keyid,u_int32_t * err)176521f43f82Sdjm pkcs11_destroy_keypair(char *provider_id, char *pin, unsigned long slotidx,
176621f43f82Sdjm unsigned char keyid, u_int32_t *err)
176721f43f82Sdjm {
176821f43f82Sdjm struct pkcs11_provider *p = NULL;
176921f43f82Sdjm struct pkcs11_slotinfo *si;
177021f43f82Sdjm struct sshkey *k = NULL;
177121f43f82Sdjm int reset_pin = 0, reset_provider = 0;
177221f43f82Sdjm CK_ULONG nattrs;
177321f43f82Sdjm CK_FUNCTION_LIST *f;
177421f43f82Sdjm CK_SESSION_HANDLE session;
177521f43f82Sdjm CK_ATTRIBUTE attrs[16];
177621f43f82Sdjm CK_OBJECT_CLASS key_class;
177721f43f82Sdjm CK_KEY_TYPE key_type;
177821f43f82Sdjm CK_OBJECT_HANDLE obj = CK_INVALID_HANDLE;
177921f43f82Sdjm CK_RV rv;
178021f43f82Sdjm
178121f43f82Sdjm *err = 0;
178221f43f82Sdjm
178321f43f82Sdjm if ((p = pkcs11_provider_lookup(provider_id)) != NULL) {
178448e6b99dSdjm debug_f("using provider \"%s\"", provider_id);
178544e54ccbSdjm } else if (pkcs11_register_provider(provider_id, pin, NULL, NULL, &p,
178621f43f82Sdjm CKU_SO) < 0) {
178748e6b99dSdjm debug_f("could not register provider %s",
178821f43f82Sdjm provider_id);
178921f43f82Sdjm goto out;
179021f43f82Sdjm } else
179121f43f82Sdjm reset_provider = 1;
179221f43f82Sdjm
179321f43f82Sdjm f = p->function_list;
179421f43f82Sdjm si = &p->slotinfo[slotidx];
179521f43f82Sdjm session = si->session;
179621f43f82Sdjm
179721f43f82Sdjm if ((rv = f->C_SetOperationState(session , pin, strlen(pin),
179821f43f82Sdjm CK_INVALID_HANDLE, CK_INVALID_HANDLE)) != CKR_OK) {
179948e6b99dSdjm debug_f("could not supply SO pin: %lu", rv);
180021f43f82Sdjm reset_pin = 0;
180121f43f82Sdjm } else
180221f43f82Sdjm reset_pin = 1;
180321f43f82Sdjm
180421f43f82Sdjm /* private key */
180521f43f82Sdjm nattrs = 0;
180621f43f82Sdjm key_class = CKO_PRIVATE_KEY;
180721f43f82Sdjm FILL_ATTR(attrs, nattrs, CKA_CLASS, &key_class, sizeof(key_class));
180821f43f82Sdjm FILL_ATTR(attrs, nattrs, CKA_ID, &keyid, sizeof(keyid));
180921f43f82Sdjm
181021f43f82Sdjm if (pkcs11_find(p, slotidx, attrs, nattrs, &obj) == 0 &&
181121f43f82Sdjm obj != CK_INVALID_HANDLE) {
181221f43f82Sdjm if ((rv = f->C_DestroyObject(session, obj)) != CKR_OK) {
181348e6b99dSdjm debug_f("could not destroy private key 0x%hhx",
181448e6b99dSdjm keyid);
181521f43f82Sdjm *err = rv;
181621f43f82Sdjm goto out;
181721f43f82Sdjm }
181821f43f82Sdjm }
181921f43f82Sdjm
182021f43f82Sdjm /* public key */
182121f43f82Sdjm nattrs = 0;
182221f43f82Sdjm key_class = CKO_PUBLIC_KEY;
182321f43f82Sdjm FILL_ATTR(attrs, nattrs, CKA_CLASS, &key_class, sizeof(key_class));
182421f43f82Sdjm FILL_ATTR(attrs, nattrs, CKA_ID, &keyid, sizeof(keyid));
182521f43f82Sdjm
182621f43f82Sdjm if (pkcs11_find(p, slotidx, attrs, nattrs, &obj) == 0 &&
182721f43f82Sdjm obj != CK_INVALID_HANDLE) {
182821f43f82Sdjm
182921f43f82Sdjm /* get key type */
183021f43f82Sdjm nattrs = 0;
183121f43f82Sdjm FILL_ATTR(attrs, nattrs, CKA_KEY_TYPE, &key_type,
183221f43f82Sdjm sizeof(key_type));
183321f43f82Sdjm rv = f->C_GetAttributeValue(session, obj, attrs, nattrs);
183421f43f82Sdjm if (rv != CKR_OK) {
183548e6b99dSdjm debug_f("could not get key type of public key 0x%hhx",
183648e6b99dSdjm keyid);
183721f43f82Sdjm *err = rv;
183821f43f82Sdjm key_type = -1;
183921f43f82Sdjm }
184021f43f82Sdjm if (key_type == CKK_RSA)
184121f43f82Sdjm k = pkcs11_fetch_rsa_pubkey(p, slotidx, &obj);
184221f43f82Sdjm else if (key_type == CKK_ECDSA)
184321f43f82Sdjm k = pkcs11_fetch_ecdsa_pubkey(p, slotidx, &obj);
184421f43f82Sdjm
184521f43f82Sdjm if ((rv = f->C_DestroyObject(session, obj)) != CKR_OK) {
184648e6b99dSdjm debug_f("could not destroy public key 0x%hhx", keyid);
184721f43f82Sdjm *err = rv;
184821f43f82Sdjm goto out;
184921f43f82Sdjm }
185021f43f82Sdjm }
185121f43f82Sdjm
185221f43f82Sdjm out:
185321f43f82Sdjm if (reset_pin)
185421f43f82Sdjm f->C_SetOperationState(session , NULL, 0, CK_INVALID_HANDLE,
185521f43f82Sdjm CK_INVALID_HANDLE);
185621f43f82Sdjm
185721f43f82Sdjm if (reset_provider)
185821f43f82Sdjm pkcs11_del_provider(provider_id);
185921f43f82Sdjm
186021f43f82Sdjm return (k);
186121f43f82Sdjm }
186221f43f82Sdjm #endif /* WITH_PKCS11_KEYGEN */
18634ad56734Smarkus #else
18644ad56734Smarkus int
pkcs11_add_provider(char * provider_id,char * pin,struct sshkey *** keyp,char *** labelsp)1865860701bfSdjm pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp,
1866860701bfSdjm char ***labelsp)
18674ad56734Smarkus {
18684ad56734Smarkus error("dlopen() not supported");
18694ad56734Smarkus return (-1);
18704ad56734Smarkus }
187121f43f82Sdjm #endif /* HAVE_DLOPEN */
1872