xref: /openssh-portable/ssh-pkcs11.c (revision 19af04e2)
1*19af04e2Sdjm@openbsd.org /* $OpenBSD: ssh-pkcs11.c,v 1.52 2020/11/22 22:38:26 djm Exp $ */
27ea845e4SDamien Miller /*
37ea845e4SDamien Miller  * Copyright (c) 2010 Markus Friedl.  All rights reserved.
493f02107Sdjm@openbsd.org  * Copyright (c) 2014 Pedro Martelletto. All rights reserved.
57ea845e4SDamien Miller  *
67ea845e4SDamien Miller  * Permission to use, copy, modify, and distribute this software for any
77ea845e4SDamien Miller  * purpose with or without fee is hereby granted, provided that the above
87ea845e4SDamien Miller  * copyright notice and this permission notice appear in all copies.
97ea845e4SDamien Miller  *
107ea845e4SDamien Miller  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
117ea845e4SDamien Miller  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
127ea845e4SDamien Miller  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
137ea845e4SDamien Miller  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
147ea845e4SDamien Miller  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
157ea845e4SDamien Miller  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
167ea845e4SDamien Miller  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
177ea845e4SDamien Miller  */
187ea845e4SDamien Miller 
198ad0fbd9SDamien Miller #include "includes.h"
208ad0fbd9SDamien Miller 
21dfa4156dSDamien Miller #ifdef ENABLE_PKCS11
22dfa4156dSDamien Miller 
238ad0fbd9SDamien Miller #ifdef HAVE_SYS_TIME_H
248ad0fbd9SDamien Miller # include <sys/time.h>
258ad0fbd9SDamien Miller #endif
2693f02107Sdjm@openbsd.org 
2793f02107Sdjm@openbsd.org #include <sys/types.h>
287ea845e4SDamien Miller #include <stdarg.h>
297ea845e4SDamien Miller #include <stdio.h>
307ea845e4SDamien Miller 
3193f02107Sdjm@openbsd.org #include <ctype.h>
327ea845e4SDamien Miller #include <string.h>
337ea845e4SDamien Miller #include <dlfcn.h>
347ea845e4SDamien Miller 
358ad0fbd9SDamien Miller #include "openbsd-compat/sys-queue.h"
3648f54b9dSDamien Miller #include "openbsd-compat/openssl-compat.h"
378ad0fbd9SDamien Miller 
3893f02107Sdjm@openbsd.org #include <openssl/ecdsa.h>
39d2252c79SDamien Miller #include <openssl/x509.h>
4093f02107Sdjm@openbsd.org #include <openssl/err.h>
41d2252c79SDamien Miller 
427ea845e4SDamien Miller #define CRYPTOKI_COMPAT
437ea845e4SDamien Miller #include "pkcs11.h"
447ea845e4SDamien Miller 
457ea845e4SDamien Miller #include "log.h"
467ea845e4SDamien Miller #include "misc.h"
471129dcfcSdjm@openbsd.org #include "sshkey.h"
487ea845e4SDamien Miller #include "ssh-pkcs11.h"
49*19af04e2Sdjm@openbsd.org #include "digest.h"
507ea845e4SDamien Miller #include "xmalloc.h"
517ea845e4SDamien Miller 
527ea845e4SDamien Miller struct pkcs11_slotinfo {
537ea845e4SDamien Miller 	CK_TOKEN_INFO		token;
547ea845e4SDamien Miller 	CK_SESSION_HANDLE	session;
557ea845e4SDamien Miller 	int			logged_in;
567ea845e4SDamien Miller };
577ea845e4SDamien Miller 
587ea845e4SDamien Miller struct pkcs11_provider {
597ea845e4SDamien Miller 	char			*name;
607ea845e4SDamien Miller 	void			*handle;
617ea845e4SDamien Miller 	CK_FUNCTION_LIST	*function_list;
627ea845e4SDamien Miller 	CK_INFO			info;
637ea845e4SDamien Miller 	CK_ULONG		nslots;
647ea845e4SDamien Miller 	CK_SLOT_ID		*slotlist;
657ea845e4SDamien Miller 	struct pkcs11_slotinfo	*slotinfo;
667ea845e4SDamien Miller 	int			valid;
677ea845e4SDamien Miller 	int			refcount;
687ea845e4SDamien Miller 	TAILQ_ENTRY(pkcs11_provider) next;
697ea845e4SDamien Miller };
707ea845e4SDamien Miller 
717ea845e4SDamien Miller TAILQ_HEAD(, pkcs11_provider) pkcs11_providers;
727ea845e4SDamien Miller 
737ea845e4SDamien Miller struct pkcs11_key {
747ea845e4SDamien Miller 	struct pkcs11_provider	*provider;
757ea845e4SDamien Miller 	CK_ULONG		slotidx;
767ea845e4SDamien Miller 	char			*keyid;
777ea845e4SDamien Miller 	int			keyid_len;
787ea845e4SDamien Miller };
797ea845e4SDamien Miller 
807ea845e4SDamien Miller int pkcs11_interactive = 0;
817ea845e4SDamien Miller 
822efcf812SDamien Miller #ifdef HAVE_EC_KEY_METHOD_NEW
8393f02107Sdjm@openbsd.org static void
ossl_error(const char * msg)8493f02107Sdjm@openbsd.org ossl_error(const char *msg)
8593f02107Sdjm@openbsd.org {
8693f02107Sdjm@openbsd.org 	unsigned long    e;
8793f02107Sdjm@openbsd.org 
88816036f1Sdjm@openbsd.org 	error_f("%s", msg);
8993f02107Sdjm@openbsd.org 	while ((e = ERR_get_error()) != 0)
90816036f1Sdjm@openbsd.org 		error_f("libcrypto error: %s", ERR_error_string(e, NULL));
9193f02107Sdjm@openbsd.org }
922efcf812SDamien Miller #endif /* HAVE_EC_KEY_METHOD_NEW */
9393f02107Sdjm@openbsd.org 
947ea845e4SDamien Miller int
pkcs11_init(int interactive)957ea845e4SDamien Miller pkcs11_init(int interactive)
967ea845e4SDamien Miller {
977ea845e4SDamien Miller 	pkcs11_interactive = interactive;
987ea845e4SDamien Miller 	TAILQ_INIT(&pkcs11_providers);
997ea845e4SDamien Miller 	return (0);
1007ea845e4SDamien Miller }
1017ea845e4SDamien Miller 
1027ea845e4SDamien Miller /*
10393f02107Sdjm@openbsd.org  * finalize a provider shared library, it's no longer usable.
1047ea845e4SDamien Miller  * however, there might still be keys referencing this provider,
10593f02107Sdjm@openbsd.org  * so the actual freeing of memory is handled by pkcs11_provider_unref().
1067ea845e4SDamien Miller  * this is called when a provider gets unregistered.
1077ea845e4SDamien Miller  */
1087ea845e4SDamien Miller static void
pkcs11_provider_finalize(struct pkcs11_provider * p)1097ea845e4SDamien Miller pkcs11_provider_finalize(struct pkcs11_provider *p)
1107ea845e4SDamien Miller {
1117ea845e4SDamien Miller 	CK_RV rv;
1127ea845e4SDamien Miller 	CK_ULONG i;
1137ea845e4SDamien Miller 
1147ea845e4SDamien Miller 	debug("pkcs11_provider_finalize: %p refcount %d valid %d",
1157ea845e4SDamien Miller 	    p, p->refcount, p->valid);
1167ea845e4SDamien Miller 	if (!p->valid)
1177ea845e4SDamien Miller 		return;
1187ea845e4SDamien Miller 	for (i = 0; i < p->nslots; i++) {
1197ea845e4SDamien Miller 		if (p->slotinfo[i].session &&
1207ea845e4SDamien Miller 		    (rv = p->function_list->C_CloseSession(
1217ea845e4SDamien Miller 		    p->slotinfo[i].session)) != CKR_OK)
1227ea845e4SDamien Miller 			error("C_CloseSession failed: %lu", rv);
1237ea845e4SDamien Miller 	}
1247ea845e4SDamien Miller 	if ((rv = p->function_list->C_Finalize(NULL)) != CKR_OK)
1257ea845e4SDamien Miller 		error("C_Finalize failed: %lu", rv);
1267ea845e4SDamien Miller 	p->valid = 0;
1277ea845e4SDamien Miller 	p->function_list = NULL;
1287ea845e4SDamien Miller 	dlclose(p->handle);
1297ea845e4SDamien Miller }
1307ea845e4SDamien Miller 
1317ea845e4SDamien Miller /*
1327ea845e4SDamien Miller  * remove a reference to the provider.
1337ea845e4SDamien Miller  * called when a key gets destroyed or when the provider is unregistered.
1347ea845e4SDamien Miller  */
1357ea845e4SDamien Miller static void
pkcs11_provider_unref(struct pkcs11_provider * p)1367ea845e4SDamien Miller pkcs11_provider_unref(struct pkcs11_provider *p)
1377ea845e4SDamien Miller {
1387ea845e4SDamien Miller 	debug("pkcs11_provider_unref: %p refcount %d", p, p->refcount);
1397ea845e4SDamien Miller 	if (--p->refcount <= 0) {
1407ea845e4SDamien Miller 		if (p->valid)
1417ea845e4SDamien Miller 			error("pkcs11_provider_unref: %p still valid", p);
14293f02107Sdjm@openbsd.org 		free(p->name);
143a627d42eSDarren Tucker 		free(p->slotlist);
144a627d42eSDarren Tucker 		free(p->slotinfo);
145a627d42eSDarren Tucker 		free(p);
1467ea845e4SDamien Miller 	}
1477ea845e4SDamien Miller }
1487ea845e4SDamien Miller 
1497ea845e4SDamien Miller /* unregister all providers, keys might still point to the providers */
1507ea845e4SDamien Miller void
pkcs11_terminate(void)1517ea845e4SDamien Miller pkcs11_terminate(void)
1527ea845e4SDamien Miller {
1537ea845e4SDamien Miller 	struct pkcs11_provider *p;
1547ea845e4SDamien Miller 
1557ea845e4SDamien Miller 	while ((p = TAILQ_FIRST(&pkcs11_providers)) != NULL) {
1567ea845e4SDamien Miller 		TAILQ_REMOVE(&pkcs11_providers, p, next);
1577ea845e4SDamien Miller 		pkcs11_provider_finalize(p);
1587ea845e4SDamien Miller 		pkcs11_provider_unref(p);
1597ea845e4SDamien Miller 	}
1607ea845e4SDamien Miller }
1617ea845e4SDamien Miller 
1627ea845e4SDamien Miller /* lookup provider by name */
1637ea845e4SDamien Miller static struct pkcs11_provider *
pkcs11_provider_lookup(char * provider_id)1647ea845e4SDamien Miller pkcs11_provider_lookup(char *provider_id)
1657ea845e4SDamien Miller {
1667ea845e4SDamien Miller 	struct pkcs11_provider *p;
1677ea845e4SDamien Miller 
1687ea845e4SDamien Miller 	TAILQ_FOREACH(p, &pkcs11_providers, next) {
1697ea845e4SDamien Miller 		debug("check %p %s", p, p->name);
1707ea845e4SDamien Miller 		if (!strcmp(provider_id, p->name))
1717ea845e4SDamien Miller 			return (p);
1727ea845e4SDamien Miller 	}
1737ea845e4SDamien Miller 	return (NULL);
1747ea845e4SDamien Miller }
1757ea845e4SDamien Miller 
1767ea845e4SDamien Miller /* unregister provider by name */
1777ea845e4SDamien Miller int
pkcs11_del_provider(char * provider_id)1787ea845e4SDamien Miller pkcs11_del_provider(char *provider_id)
1797ea845e4SDamien Miller {
1807ea845e4SDamien Miller 	struct pkcs11_provider *p;
1817ea845e4SDamien Miller 
1827ea845e4SDamien Miller 	if ((p = pkcs11_provider_lookup(provider_id)) != NULL) {
1837ea845e4SDamien Miller 		TAILQ_REMOVE(&pkcs11_providers, p, next);
1847ea845e4SDamien Miller 		pkcs11_provider_finalize(p);
1857ea845e4SDamien Miller 		pkcs11_provider_unref(p);
1867ea845e4SDamien Miller 		return (0);
1877ea845e4SDamien Miller 	}
1887ea845e4SDamien Miller 	return (-1);
1897ea845e4SDamien Miller }
1907ea845e4SDamien Miller 
191ce46c3a0Sdjm@openbsd.org static RSA_METHOD *rsa_method;
192ce46c3a0Sdjm@openbsd.org static int rsa_idx = 0;
19323490a6cSDamien Miller #ifdef HAVE_EC_KEY_METHOD_NEW
194ce46c3a0Sdjm@openbsd.org static EC_KEY_METHOD *ec_key_method;
195ce46c3a0Sdjm@openbsd.org static int ec_key_idx = 0;
196b6dd3277SDarren Tucker #endif
197ce46c3a0Sdjm@openbsd.org 
19858622a8cSdjm@openbsd.org /* release a wrapped object */
19958622a8cSdjm@openbsd.org static void
pkcs11_k11_free(void * parent,void * ptr,CRYPTO_EX_DATA * ad,int idx,long argl,void * argp)20058622a8cSdjm@openbsd.org pkcs11_k11_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
20158622a8cSdjm@openbsd.org     long argl, void *argp)
2027ea845e4SDamien Miller {
20358622a8cSdjm@openbsd.org 	struct pkcs11_key	*k11 = ptr;
2047ea845e4SDamien Miller 
205816036f1Sdjm@openbsd.org 	debug_f("parent %p ptr %p idx %d", parent, ptr, idx);
20658622a8cSdjm@openbsd.org 	if (k11 == NULL)
20758622a8cSdjm@openbsd.org 		return;
2087ea845e4SDamien Miller 	if (k11->provider)
2097ea845e4SDamien Miller 		pkcs11_provider_unref(k11->provider);
210a627d42eSDarren Tucker 	free(k11->keyid);
211a627d42eSDarren Tucker 	free(k11);
2127ea845e4SDamien Miller }
2137ea845e4SDamien Miller 
214031c9100SDamien Miller /* find a single 'obj' for given attributes */
215031c9100SDamien Miller static int
pkcs11_find(struct pkcs11_provider * p,CK_ULONG slotidx,CK_ATTRIBUTE * attr,CK_ULONG nattr,CK_OBJECT_HANDLE * obj)216031c9100SDamien Miller pkcs11_find(struct pkcs11_provider *p, CK_ULONG slotidx, CK_ATTRIBUTE *attr,
217031c9100SDamien Miller     CK_ULONG nattr, CK_OBJECT_HANDLE *obj)
218031c9100SDamien Miller {
219031c9100SDamien Miller 	CK_FUNCTION_LIST	*f;
220031c9100SDamien Miller 	CK_SESSION_HANDLE	session;
221031c9100SDamien Miller 	CK_ULONG		nfound = 0;
222031c9100SDamien Miller 	CK_RV			rv;
223031c9100SDamien Miller 	int			ret = -1;
224031c9100SDamien Miller 
225031c9100SDamien Miller 	f = p->function_list;
226031c9100SDamien Miller 	session = p->slotinfo[slotidx].session;
227031c9100SDamien Miller 	if ((rv = f->C_FindObjectsInit(session, attr, nattr)) != CKR_OK) {
228031c9100SDamien Miller 		error("C_FindObjectsInit failed (nattr %lu): %lu", nattr, rv);
229031c9100SDamien Miller 		return (-1);
230031c9100SDamien Miller 	}
231031c9100SDamien Miller 	if ((rv = f->C_FindObjects(session, obj, 1, &nfound)) != CKR_OK ||
232031c9100SDamien Miller 	    nfound != 1) {
233031c9100SDamien Miller 		debug("C_FindObjects failed (nfound %lu nattr %lu): %lu",
234031c9100SDamien Miller 		    nfound, nattr, rv);
235031c9100SDamien Miller 	} else
236031c9100SDamien Miller 		ret = 0;
237031c9100SDamien Miller 	if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK)
238031c9100SDamien Miller 		error("C_FindObjectsFinal failed: %lu", rv);
239031c9100SDamien Miller 	return (ret);
240031c9100SDamien Miller }
241031c9100SDamien Miller 
2427ea845e4SDamien Miller static int
pkcs11_login_slot(struct pkcs11_provider * provider,struct pkcs11_slotinfo * si,CK_USER_TYPE type)2437d6034bdSdjm@openbsd.org pkcs11_login_slot(struct pkcs11_provider *provider, struct pkcs11_slotinfo *si,
2447d6034bdSdjm@openbsd.org     CK_USER_TYPE type)
2452162171aSdjm@openbsd.org {
2462162171aSdjm@openbsd.org 	char			*pin = NULL, prompt[1024];
2472162171aSdjm@openbsd.org 	CK_RV			 rv;
2482162171aSdjm@openbsd.org 
2497d6034bdSdjm@openbsd.org 	if (provider == NULL || si == NULL || !provider->valid) {
2502162171aSdjm@openbsd.org 		error("no pkcs11 (valid) provider found");
2512162171aSdjm@openbsd.org 		return (-1);
2522162171aSdjm@openbsd.org 	}
2532162171aSdjm@openbsd.org 
2542162171aSdjm@openbsd.org 	if (!pkcs11_interactive) {
2552162171aSdjm@openbsd.org 		error("need pin entry%s",
2562162171aSdjm@openbsd.org 		    (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH) ?
2572162171aSdjm@openbsd.org 		    " on reader keypad" : "");
2582162171aSdjm@openbsd.org 		return (-1);
2592162171aSdjm@openbsd.org 	}
2602162171aSdjm@openbsd.org 	if (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH)
2612162171aSdjm@openbsd.org 		verbose("Deferring PIN entry to reader keypad.");
2622162171aSdjm@openbsd.org 	else {
2632162171aSdjm@openbsd.org 		snprintf(prompt, sizeof(prompt), "Enter PIN for '%s': ",
2642162171aSdjm@openbsd.org 		    si->token.label);
2652162171aSdjm@openbsd.org 		if ((pin = read_passphrase(prompt, RP_ALLOW_EOF)) == NULL) {
266816036f1Sdjm@openbsd.org 			debug_f("no pin specified");
2672162171aSdjm@openbsd.org 			return (-1);	/* bail out */
2682162171aSdjm@openbsd.org 		}
2692162171aSdjm@openbsd.org 	}
2707d6034bdSdjm@openbsd.org 	rv = provider->function_list->C_Login(si->session, type, (u_char *)pin,
2712162171aSdjm@openbsd.org 	    (pin != NULL) ? strlen(pin) : 0);
2722162171aSdjm@openbsd.org 	if (pin != NULL)
2732162171aSdjm@openbsd.org 		freezero(pin, strlen(pin));
2741d89232aSdjm@openbsd.org 
2751d89232aSdjm@openbsd.org 	switch (rv) {
2761d89232aSdjm@openbsd.org 	case CKR_OK:
2771d89232aSdjm@openbsd.org 	case CKR_USER_ALREADY_LOGGED_IN:
2781d89232aSdjm@openbsd.org 		/* success */
2791d89232aSdjm@openbsd.org 		break;
2801d89232aSdjm@openbsd.org 	case CKR_PIN_LEN_RANGE:
2811d89232aSdjm@openbsd.org 		error("PKCS#11 login failed: PIN length out of range");
2821d89232aSdjm@openbsd.org 		return -1;
2831d89232aSdjm@openbsd.org 	case CKR_PIN_INCORRECT:
2841d89232aSdjm@openbsd.org 		error("PKCS#11 login failed: PIN incorrect");
2851d89232aSdjm@openbsd.org 		return -1;
2861d89232aSdjm@openbsd.org 	case CKR_PIN_LOCKED:
2871d89232aSdjm@openbsd.org 		error("PKCS#11 login failed: PIN locked");
2881d89232aSdjm@openbsd.org 		return -1;
2891d89232aSdjm@openbsd.org 	default:
2901d89232aSdjm@openbsd.org 		error("PKCS#11 login failed: error %lu", rv);
2911d89232aSdjm@openbsd.org 		return -1;
2922162171aSdjm@openbsd.org 	}
2932162171aSdjm@openbsd.org 	si->logged_in = 1;
2942162171aSdjm@openbsd.org 	return (0);
2952162171aSdjm@openbsd.org }
2962162171aSdjm@openbsd.org 
2972162171aSdjm@openbsd.org static int
pkcs11_login(struct pkcs11_key * k11,CK_USER_TYPE type)2987d6034bdSdjm@openbsd.org pkcs11_login(struct pkcs11_key *k11, CK_USER_TYPE type)
2997d6034bdSdjm@openbsd.org {
3007d6034bdSdjm@openbsd.org 	if (k11 == NULL || k11->provider == NULL || !k11->provider->valid) {
3017d6034bdSdjm@openbsd.org 		error("no pkcs11 (valid) provider found");
3027d6034bdSdjm@openbsd.org 		return (-1);
3037d6034bdSdjm@openbsd.org 	}
3047d6034bdSdjm@openbsd.org 
3057d6034bdSdjm@openbsd.org 	return pkcs11_login_slot(k11->provider,
3067d6034bdSdjm@openbsd.org 	    &k11->provider->slotinfo[k11->slotidx], type);
3077d6034bdSdjm@openbsd.org }
3087d6034bdSdjm@openbsd.org 
3097d6034bdSdjm@openbsd.org 
3107d6034bdSdjm@openbsd.org static int
pkcs11_check_obj_bool_attrib(struct pkcs11_key * k11,CK_OBJECT_HANDLE obj,CK_ATTRIBUTE_TYPE type,int * val)3112162171aSdjm@openbsd.org pkcs11_check_obj_bool_attrib(struct pkcs11_key *k11, CK_OBJECT_HANDLE obj,
3122162171aSdjm@openbsd.org     CK_ATTRIBUTE_TYPE type, int *val)
3132162171aSdjm@openbsd.org {
3142162171aSdjm@openbsd.org 	struct pkcs11_slotinfo	*si;
3152162171aSdjm@openbsd.org 	CK_FUNCTION_LIST	*f;
3162162171aSdjm@openbsd.org 	CK_BBOOL		flag = 0;
3172162171aSdjm@openbsd.org 	CK_ATTRIBUTE		attr;
3182162171aSdjm@openbsd.org 	CK_RV			 rv;
3192162171aSdjm@openbsd.org 
3202162171aSdjm@openbsd.org 	*val = 0;
3212162171aSdjm@openbsd.org 
3222162171aSdjm@openbsd.org 	if (!k11->provider || !k11->provider->valid) {
3232162171aSdjm@openbsd.org 		error("no pkcs11 (valid) provider found");
3242162171aSdjm@openbsd.org 		return (-1);
3252162171aSdjm@openbsd.org 	}
3262162171aSdjm@openbsd.org 
3272162171aSdjm@openbsd.org 	f = k11->provider->function_list;
3282162171aSdjm@openbsd.org 	si = &k11->provider->slotinfo[k11->slotidx];
3292162171aSdjm@openbsd.org 
3302162171aSdjm@openbsd.org 	attr.type = type;
3312162171aSdjm@openbsd.org 	attr.pValue = &flag;
3322162171aSdjm@openbsd.org 	attr.ulValueLen = sizeof(flag);
3332162171aSdjm@openbsd.org 
3342162171aSdjm@openbsd.org 	rv = f->C_GetAttributeValue(si->session, obj, &attr, 1);
3352162171aSdjm@openbsd.org 	if (rv != CKR_OK) {
3362162171aSdjm@openbsd.org 		error("C_GetAttributeValue failed: %lu", rv);
3372162171aSdjm@openbsd.org 		return (-1);
3382162171aSdjm@openbsd.org 	}
3392162171aSdjm@openbsd.org 	*val = flag != 0;
340816036f1Sdjm@openbsd.org 	debug_f("provider %p slot %lu object %lu: attrib %lu = %d",
341816036f1Sdjm@openbsd.org 	    k11->provider, k11->slotidx, obj, type, *val);
3422162171aSdjm@openbsd.org 	return (0);
3432162171aSdjm@openbsd.org }
3442162171aSdjm@openbsd.org 
3452162171aSdjm@openbsd.org static int
pkcs11_get_key(struct pkcs11_key * k11,CK_MECHANISM_TYPE mech_type)34693f02107Sdjm@openbsd.org pkcs11_get_key(struct pkcs11_key *k11, CK_MECHANISM_TYPE mech_type)
3477ea845e4SDamien Miller {
3487ea845e4SDamien Miller 	struct pkcs11_slotinfo	*si;
3497ea845e4SDamien Miller 	CK_FUNCTION_LIST	*f;
3507ea845e4SDamien Miller 	CK_OBJECT_HANDLE	 obj;
3517ea845e4SDamien Miller 	CK_RV			 rv;
35293f02107Sdjm@openbsd.org 	CK_OBJECT_CLASS		 private_key_class;
35393f02107Sdjm@openbsd.org 	CK_BBOOL		 true_val;
35493f02107Sdjm@openbsd.org 	CK_MECHANISM		 mech;
35593f02107Sdjm@openbsd.org 	CK_ATTRIBUTE		 key_filter[3];
3562162171aSdjm@openbsd.org 	int			 always_auth = 0;
3572162171aSdjm@openbsd.org 	int			 did_login = 0;
3587ea845e4SDamien Miller 
3597ea845e4SDamien Miller 	if (!k11->provider || !k11->provider->valid) {
36093f02107Sdjm@openbsd.org 		error("no pkcs11 (valid) provider found");
3617ea845e4SDamien Miller 		return (-1);
3627ea845e4SDamien Miller 	}
36393f02107Sdjm@openbsd.org 
3647ea845e4SDamien Miller 	f = k11->provider->function_list;
3657ea845e4SDamien Miller 	si = &k11->provider->slotinfo[k11->slotidx];
36693f02107Sdjm@openbsd.org 
3677ea845e4SDamien Miller 	if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) {
3682162171aSdjm@openbsd.org 		if (pkcs11_login(k11, CKU_USER) < 0) {
3692162171aSdjm@openbsd.org 			error("login failed");
3707ea845e4SDamien Miller 			return (-1);
3717ea845e4SDamien Miller 		}
3722162171aSdjm@openbsd.org 		did_login = 1;
3737ea845e4SDamien Miller 	}
37493f02107Sdjm@openbsd.org 
37593f02107Sdjm@openbsd.org 	memset(&key_filter, 0, sizeof(key_filter));
37693f02107Sdjm@openbsd.org 	private_key_class = CKO_PRIVATE_KEY;
37793f02107Sdjm@openbsd.org 	key_filter[0].type = CKA_CLASS;
37893f02107Sdjm@openbsd.org 	key_filter[0].pValue = &private_key_class;
37993f02107Sdjm@openbsd.org 	key_filter[0].ulValueLen = sizeof(private_key_class);
38093f02107Sdjm@openbsd.org 
38193f02107Sdjm@openbsd.org 	key_filter[1].type = CKA_ID;
3827ea845e4SDamien Miller 	key_filter[1].pValue = k11->keyid;
3837ea845e4SDamien Miller 	key_filter[1].ulValueLen = k11->keyid_len;
38493f02107Sdjm@openbsd.org 
38593f02107Sdjm@openbsd.org 	true_val = CK_TRUE;
38693f02107Sdjm@openbsd.org 	key_filter[2].type = CKA_SIGN;
38793f02107Sdjm@openbsd.org 	key_filter[2].pValue = &true_val;
38893f02107Sdjm@openbsd.org 	key_filter[2].ulValueLen = sizeof(true_val);
38993f02107Sdjm@openbsd.org 
390031c9100SDamien Miller 	/* try to find object w/CKA_SIGN first, retry w/o */
391031c9100SDamien Miller 	if (pkcs11_find(k11->provider, k11->slotidx, key_filter, 3, &obj) < 0 &&
392031c9100SDamien Miller 	    pkcs11_find(k11->provider, k11->slotidx, key_filter, 2, &obj) < 0) {
393031c9100SDamien Miller 		error("cannot find private key");
39493f02107Sdjm@openbsd.org 		return (-1);
39593f02107Sdjm@openbsd.org 	}
39693f02107Sdjm@openbsd.org 
39793f02107Sdjm@openbsd.org 	memset(&mech, 0, sizeof(mech));
39893f02107Sdjm@openbsd.org 	mech.mechanism = mech_type;
39993f02107Sdjm@openbsd.org 	mech.pParameter = NULL_PTR;
40093f02107Sdjm@openbsd.org 	mech.ulParameterLen = 0;
40193f02107Sdjm@openbsd.org 
40293f02107Sdjm@openbsd.org 	if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) {
4037ea845e4SDamien Miller 		error("C_SignInit failed: %lu", rv);
40493f02107Sdjm@openbsd.org 		return (-1);
40593f02107Sdjm@openbsd.org 	}
40693f02107Sdjm@openbsd.org 
4072162171aSdjm@openbsd.org 	pkcs11_check_obj_bool_attrib(k11, obj, CKA_ALWAYS_AUTHENTICATE,
4082162171aSdjm@openbsd.org 	    &always_auth); /* ignore errors here */
4092162171aSdjm@openbsd.org 	if (always_auth && !did_login) {
410816036f1Sdjm@openbsd.org 		debug_f("always-auth key");
4112162171aSdjm@openbsd.org 		if (pkcs11_login(k11, CKU_CONTEXT_SPECIFIC) < 0) {
4122162171aSdjm@openbsd.org 			error("login failed for always-auth key");
4132162171aSdjm@openbsd.org 			return (-1);
4142162171aSdjm@openbsd.org 		}
4152162171aSdjm@openbsd.org 	}
4162162171aSdjm@openbsd.org 
41793f02107Sdjm@openbsd.org 	return (0);
41893f02107Sdjm@openbsd.org }
41993f02107Sdjm@openbsd.org 
42093f02107Sdjm@openbsd.org /* openssl callback doing the actual signing operation */
42193f02107Sdjm@openbsd.org static int
pkcs11_rsa_private_encrypt(int flen,const u_char * from,u_char * to,RSA * rsa,int padding)42293f02107Sdjm@openbsd.org pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
42393f02107Sdjm@openbsd.org     int padding)
42493f02107Sdjm@openbsd.org {
42593f02107Sdjm@openbsd.org 	struct pkcs11_key	*k11;
42693f02107Sdjm@openbsd.org 	struct pkcs11_slotinfo	*si;
42793f02107Sdjm@openbsd.org 	CK_FUNCTION_LIST	*f;
42893f02107Sdjm@openbsd.org 	CK_ULONG		tlen = 0;
42993f02107Sdjm@openbsd.org 	CK_RV			rv;
43093f02107Sdjm@openbsd.org 	int			rval = -1;
43193f02107Sdjm@openbsd.org 
432ce46c3a0Sdjm@openbsd.org 	if ((k11 = RSA_get_ex_data(rsa, rsa_idx)) == NULL) {
433f118542fSdjm@openbsd.org 		error("RSA_get_ex_data failed for rsa %p", rsa);
43493f02107Sdjm@openbsd.org 		return (-1);
43593f02107Sdjm@openbsd.org 	}
43693f02107Sdjm@openbsd.org 
43793f02107Sdjm@openbsd.org 	if (pkcs11_get_key(k11, CKM_RSA_PKCS) == -1) {
43893f02107Sdjm@openbsd.org 		error("pkcs11_get_key failed");
43993f02107Sdjm@openbsd.org 		return (-1);
44093f02107Sdjm@openbsd.org 	}
44193f02107Sdjm@openbsd.org 
44293f02107Sdjm@openbsd.org 	f = k11->provider->function_list;
44393f02107Sdjm@openbsd.org 	si = &k11->provider->slotinfo[k11->slotidx];
4447ea845e4SDamien Miller 	tlen = RSA_size(rsa);
44593f02107Sdjm@openbsd.org 
44693f02107Sdjm@openbsd.org 	/* XXX handle CKR_BUFFER_TOO_SMALL */
4477ea845e4SDamien Miller 	rv = f->C_Sign(si->session, (CK_BYTE *)from, flen, to, &tlen);
4487ea845e4SDamien Miller 	if (rv == CKR_OK)
4497ea845e4SDamien Miller 		rval = tlen;
4507ea845e4SDamien Miller 	else
4517ea845e4SDamien Miller 		error("C_Sign failed: %lu", rv);
45293f02107Sdjm@openbsd.org 
4537ea845e4SDamien Miller 	return (rval);
4547ea845e4SDamien Miller }
4557ea845e4SDamien Miller 
4567ea845e4SDamien Miller static int
pkcs11_rsa_private_decrypt(int flen,const u_char * from,u_char * to,RSA * rsa,int padding)4577ea845e4SDamien Miller pkcs11_rsa_private_decrypt(int flen, const u_char *from, u_char *to, RSA *rsa,
4587ea845e4SDamien Miller     int padding)
4597ea845e4SDamien Miller {
4607ea845e4SDamien Miller 	return (-1);
4617ea845e4SDamien Miller }
4627ea845e4SDamien Miller 
463f118542fSdjm@openbsd.org static int
pkcs11_rsa_start_wrapper(void)464f118542fSdjm@openbsd.org pkcs11_rsa_start_wrapper(void)
465f118542fSdjm@openbsd.org {
466f118542fSdjm@openbsd.org 	if (rsa_method != NULL)
467f118542fSdjm@openbsd.org 		return (0);
468f118542fSdjm@openbsd.org 	rsa_method = RSA_meth_dup(RSA_get_default_method());
469f118542fSdjm@openbsd.org 	if (rsa_method == NULL)
470f118542fSdjm@openbsd.org 		return (-1);
47158622a8cSdjm@openbsd.org 	rsa_idx = RSA_get_ex_new_index(0, "ssh-pkcs11-rsa",
47258622a8cSdjm@openbsd.org 	    NULL, NULL, pkcs11_k11_free);
47358622a8cSdjm@openbsd.org 	if (rsa_idx == -1)
47458622a8cSdjm@openbsd.org 		return (-1);
475f118542fSdjm@openbsd.org 	if (!RSA_meth_set1_name(rsa_method, "pkcs11") ||
476f118542fSdjm@openbsd.org 	    !RSA_meth_set_priv_enc(rsa_method, pkcs11_rsa_private_encrypt) ||
47758622a8cSdjm@openbsd.org 	    !RSA_meth_set_priv_dec(rsa_method, pkcs11_rsa_private_decrypt)) {
478816036f1Sdjm@openbsd.org 		error_f("setup pkcs11 method failed");
479f118542fSdjm@openbsd.org 		return (-1);
480f118542fSdjm@openbsd.org 	}
481f118542fSdjm@openbsd.org 	return (0);
482f118542fSdjm@openbsd.org }
483f118542fSdjm@openbsd.org 
4847ea845e4SDamien Miller /* redirect private key operations for rsa key to pkcs11 token */
4857ea845e4SDamien Miller static int
pkcs11_rsa_wrap(struct pkcs11_provider * provider,CK_ULONG slotidx,CK_ATTRIBUTE * keyid_attrib,RSA * rsa)4867ea845e4SDamien Miller pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
4877ea845e4SDamien Miller     CK_ATTRIBUTE *keyid_attrib, RSA *rsa)
4887ea845e4SDamien Miller {
4897ea845e4SDamien Miller 	struct pkcs11_key	*k11;
490f118542fSdjm@openbsd.org 
491f118542fSdjm@openbsd.org 	if (pkcs11_rsa_start_wrapper() == -1)
492f118542fSdjm@openbsd.org 		return (-1);
4937ea845e4SDamien Miller 
4947ea845e4SDamien Miller 	k11 = xcalloc(1, sizeof(*k11));
4957ea845e4SDamien Miller 	k11->provider = provider;
4967ea845e4SDamien Miller 	provider->refcount++;	/* provider referenced by RSA key */
4977ea845e4SDamien Miller 	k11->slotidx = slotidx;
4987ea845e4SDamien Miller 	/* identify key object on smartcard */
4997ea845e4SDamien Miller 	k11->keyid_len = keyid_attrib->ulValueLen;
500d2d772f5Sdjm@openbsd.org 	if (k11->keyid_len > 0) {
5017ea845e4SDamien Miller 		k11->keyid = xmalloc(k11->keyid_len);
5027ea845e4SDamien Miller 		memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
503d2d772f5Sdjm@openbsd.org 	}
504f118542fSdjm@openbsd.org 
505fcb1b093Sdjm@openbsd.org 	RSA_set_method(rsa, rsa_method);
50658622a8cSdjm@openbsd.org 	RSA_set_ex_data(rsa, rsa_idx, k11);
5077ea845e4SDamien Miller 	return (0);
5087ea845e4SDamien Miller }
5097ea845e4SDamien Miller 
510e2cb445dSDamien Miller #ifdef HAVE_EC_KEY_METHOD_NEW
51193f02107Sdjm@openbsd.org /* openssl callback doing the actual signing operation */
51293f02107Sdjm@openbsd.org static ECDSA_SIG *
ecdsa_do_sign(const unsigned char * dgst,int dgst_len,const BIGNUM * inv,const BIGNUM * rp,EC_KEY * ec)51393f02107Sdjm@openbsd.org ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv,
51493f02107Sdjm@openbsd.org     const BIGNUM *rp, EC_KEY *ec)
51593f02107Sdjm@openbsd.org {
51693f02107Sdjm@openbsd.org 	struct pkcs11_key	*k11;
51793f02107Sdjm@openbsd.org 	struct pkcs11_slotinfo	*si;
51893f02107Sdjm@openbsd.org 	CK_FUNCTION_LIST	*f;
51993f02107Sdjm@openbsd.org 	CK_ULONG		siglen = 0, bnlen;
52093f02107Sdjm@openbsd.org 	CK_RV			rv;
52193f02107Sdjm@openbsd.org 	ECDSA_SIG		*ret = NULL;
52293f02107Sdjm@openbsd.org 	u_char			*sig;
52363297641Sdjm@openbsd.org 	BIGNUM			*r = NULL, *s = NULL;
52493f02107Sdjm@openbsd.org 
525ce46c3a0Sdjm@openbsd.org 	if ((k11 = EC_KEY_get_ex_data(ec, ec_key_idx)) == NULL) {
52693f02107Sdjm@openbsd.org 		ossl_error("EC_KEY_get_key_method_data failed for ec");
52793f02107Sdjm@openbsd.org 		return (NULL);
52893f02107Sdjm@openbsd.org 	}
52993f02107Sdjm@openbsd.org 
53093f02107Sdjm@openbsd.org 	if (pkcs11_get_key(k11, CKM_ECDSA) == -1) {
53193f02107Sdjm@openbsd.org 		error("pkcs11_get_key failed");
53293f02107Sdjm@openbsd.org 		return (NULL);
53393f02107Sdjm@openbsd.org 	}
53493f02107Sdjm@openbsd.org 
53593f02107Sdjm@openbsd.org 	f = k11->provider->function_list;
53693f02107Sdjm@openbsd.org 	si = &k11->provider->slotinfo[k11->slotidx];
53793f02107Sdjm@openbsd.org 
53893f02107Sdjm@openbsd.org 	siglen = ECDSA_size(ec);
53993f02107Sdjm@openbsd.org 	sig = xmalloc(siglen);
54093f02107Sdjm@openbsd.org 
54193f02107Sdjm@openbsd.org 	/* XXX handle CKR_BUFFER_TOO_SMALL */
54293f02107Sdjm@openbsd.org 	rv = f->C_Sign(si->session, (CK_BYTE *)dgst, dgst_len, sig, &siglen);
54393f02107Sdjm@openbsd.org 	if (rv != CKR_OK) {
54493f02107Sdjm@openbsd.org 		error("C_Sign failed: %lu", rv);
54593f02107Sdjm@openbsd.org 		goto done;
54693f02107Sdjm@openbsd.org 	}
54793f02107Sdjm@openbsd.org 	if (siglen < 64 || siglen > 132 || siglen % 2) {
54893f02107Sdjm@openbsd.org 		ossl_error("d2i_ECDSA_SIG failed");
54993f02107Sdjm@openbsd.org 		goto done;
55093f02107Sdjm@openbsd.org 	}
55193f02107Sdjm@openbsd.org 	bnlen = siglen/2;
55293f02107Sdjm@openbsd.org 	if ((ret = ECDSA_SIG_new()) == NULL) {
55393f02107Sdjm@openbsd.org 		error("ECDSA_SIG_new failed");
55493f02107Sdjm@openbsd.org 		goto done;
55593f02107Sdjm@openbsd.org 	}
55663297641Sdjm@openbsd.org 	if ((r = BN_bin2bn(sig, bnlen, NULL)) == NULL ||
55763297641Sdjm@openbsd.org 	    (s = BN_bin2bn(sig+bnlen, bnlen, NULL)) == NULL) {
55893f02107Sdjm@openbsd.org 		ossl_error("d2i_ECDSA_SIG failed");
55993f02107Sdjm@openbsd.org 		ECDSA_SIG_free(ret);
56093f02107Sdjm@openbsd.org 		ret = NULL;
56193f02107Sdjm@openbsd.org 		goto done;
56293f02107Sdjm@openbsd.org 	}
56363297641Sdjm@openbsd.org 	if (!ECDSA_SIG_set0(ret, r, s)) {
564816036f1Sdjm@openbsd.org 		error_f("ECDSA_SIG_set0 failed");
56563297641Sdjm@openbsd.org 		ECDSA_SIG_free(ret);
56663297641Sdjm@openbsd.org 		ret = NULL;
56763297641Sdjm@openbsd.org 		goto done;
56863297641Sdjm@openbsd.org 	}
56963297641Sdjm@openbsd.org 	r = s = NULL; /* now owned by ret */
57063297641Sdjm@openbsd.org 	/* success */
57193f02107Sdjm@openbsd.org  done:
57263297641Sdjm@openbsd.org 	BN_free(r);
57363297641Sdjm@openbsd.org 	BN_free(s);
57493f02107Sdjm@openbsd.org 	free(sig);
57593f02107Sdjm@openbsd.org 
57693f02107Sdjm@openbsd.org 	return (ret);
57793f02107Sdjm@openbsd.org }
57893f02107Sdjm@openbsd.org 
57993f02107Sdjm@openbsd.org static int
pkcs11_ecdsa_start_wrapper(void)58093f02107Sdjm@openbsd.org pkcs11_ecdsa_start_wrapper(void)
58193f02107Sdjm@openbsd.org {
58293f02107Sdjm@openbsd.org 	int (*orig_sign)(int, const unsigned char *, int, unsigned char *,
58393f02107Sdjm@openbsd.org 	    unsigned int *, const BIGNUM *, const BIGNUM *, EC_KEY *) = NULL;
58493f02107Sdjm@openbsd.org 
58593f02107Sdjm@openbsd.org 	if (ec_key_method != NULL)
58693f02107Sdjm@openbsd.org 		return (0);
587445cfce4Sdjm@openbsd.org 	ec_key_idx = EC_KEY_get_ex_new_index(0, "ssh-pkcs11-ecdsa",
588445cfce4Sdjm@openbsd.org 	    NULL, NULL, pkcs11_k11_free);
589445cfce4Sdjm@openbsd.org 	if (ec_key_idx == -1)
590445cfce4Sdjm@openbsd.org 		return (-1);
59193f02107Sdjm@openbsd.org 	ec_key_method = EC_KEY_METHOD_new(EC_KEY_OpenSSL());
59293f02107Sdjm@openbsd.org 	if (ec_key_method == NULL)
59393f02107Sdjm@openbsd.org 		return (-1);
59493f02107Sdjm@openbsd.org 	EC_KEY_METHOD_get_sign(ec_key_method, &orig_sign, NULL, NULL);
59593f02107Sdjm@openbsd.org 	EC_KEY_METHOD_set_sign(ec_key_method, orig_sign, NULL, ecdsa_do_sign);
59693f02107Sdjm@openbsd.org 	return (0);
59793f02107Sdjm@openbsd.org }
59893f02107Sdjm@openbsd.org 
59993f02107Sdjm@openbsd.org static int
pkcs11_ecdsa_wrap(struct pkcs11_provider * provider,CK_ULONG slotidx,CK_ATTRIBUTE * keyid_attrib,EC_KEY * ec)60093f02107Sdjm@openbsd.org pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
60193f02107Sdjm@openbsd.org     CK_ATTRIBUTE *keyid_attrib, EC_KEY *ec)
60293f02107Sdjm@openbsd.org {
60393f02107Sdjm@openbsd.org 	struct pkcs11_key	*k11;
60493f02107Sdjm@openbsd.org 
60593f02107Sdjm@openbsd.org 	if (pkcs11_ecdsa_start_wrapper() == -1)
60693f02107Sdjm@openbsd.org 		return (-1);
60793f02107Sdjm@openbsd.org 
60893f02107Sdjm@openbsd.org 	k11 = xcalloc(1, sizeof(*k11));
60993f02107Sdjm@openbsd.org 	k11->provider = provider;
61093f02107Sdjm@openbsd.org 	provider->refcount++;	/* provider referenced by ECDSA key */
61193f02107Sdjm@openbsd.org 	k11->slotidx = slotidx;
61293f02107Sdjm@openbsd.org 	/* identify key object on smartcard */
61393f02107Sdjm@openbsd.org 	k11->keyid_len = keyid_attrib->ulValueLen;
61493f02107Sdjm@openbsd.org 	k11->keyid = xmalloc(k11->keyid_len);
61593f02107Sdjm@openbsd.org 	memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len);
61693f02107Sdjm@openbsd.org 
617fcb1b093Sdjm@openbsd.org 	EC_KEY_set_method(ec, ec_key_method);
618445cfce4Sdjm@openbsd.org 	EC_KEY_set_ex_data(ec, ec_key_idx, k11);
61993f02107Sdjm@openbsd.org 
62093f02107Sdjm@openbsd.org 	return (0);
62193f02107Sdjm@openbsd.org }
622e2cb445dSDamien Miller #endif /* HAVE_EC_KEY_METHOD_NEW */
62393f02107Sdjm@openbsd.org 
6247ea845e4SDamien Miller /* remove trailing spaces */
6257ea845e4SDamien Miller static void
rmspace(u_char * buf,size_t len)626746d1a6cSDamien Miller rmspace(u_char *buf, size_t len)
6277ea845e4SDamien Miller {
6287ea845e4SDamien Miller 	size_t i;
6297ea845e4SDamien Miller 
6307ea845e4SDamien Miller 	if (!len)
6317ea845e4SDamien Miller 		return;
6327ea845e4SDamien Miller 	for (i = len - 1;  i > 0; i--)
6337ea845e4SDamien Miller 		if (i == len - 1 || buf[i] == ' ')
6347ea845e4SDamien Miller 			buf[i] = '\0';
6357ea845e4SDamien Miller 		else
6367ea845e4SDamien Miller 			break;
6377ea845e4SDamien Miller }
6387ea845e4SDamien Miller 
6397ea845e4SDamien Miller /*
6407ea845e4SDamien Miller  * open a pkcs11 session and login if required.
6417ea845e4SDamien Miller  * if pin == NULL we delay login until key use
6427ea845e4SDamien Miller  */
6437ea845e4SDamien Miller static int
pkcs11_open_session(struct pkcs11_provider * p,CK_ULONG slotidx,char * pin,CK_ULONG user)64493f02107Sdjm@openbsd.org pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin,
64593f02107Sdjm@openbsd.org     CK_ULONG user)
6467ea845e4SDamien Miller {
64741923ce0Sdjm@openbsd.org 	struct pkcs11_slotinfo	*si;
6487ea845e4SDamien Miller 	CK_FUNCTION_LIST	*f;
64941923ce0Sdjm@openbsd.org 	CK_RV			rv;
6507ea845e4SDamien Miller 	CK_SESSION_HANDLE	session;
65108468278Sdjm@openbsd.org 	int			login_required, ret;
6527ea845e4SDamien Miller 
6537ea845e4SDamien Miller 	f = p->function_list;
65441923ce0Sdjm@openbsd.org 	si = &p->slotinfo[slotidx];
65541923ce0S<