1 /*
2  * Copyright 1999-2001 The OpenSSL Project Authors. All Rights Reserved.
3  * Written by Geoff Thorpe (geoff@geoffthorpe.net) for the OpenSSL
4  * project 2000.
5  * Portions Copyright (c) 2003 Kevin Stefanik (kstef@mtppi.org)
6  * Copied/modified by Kevin Stefanik (kstef@mtppi.org) for the OpenSC
7  * project 2003.
8  * Copyright (c) 2016-2018 Michał Trojnara <Michal.Trojnara@stunnel.org>
9  *
10  * Licensed under the OpenSSL license (the "License").  You may not use
11  * this file except in compliance with the License.  You can obtain a copy
12  * in the file LICENSE in the source distribution or at
13  * https://www.openssl.org/source/license.html
14  */
15 
16 #include "engine.h"
17 #include <stdio.h>
18 #include <string.h>
19 #include <openssl/opensslconf.h>
20 #include <openssl/opensslv.h>
21 #include <openssl/crypto.h>
22 #include <openssl/objects.h>
23 #include <openssl/engine.h>
24 #ifndef ENGINE_CMD_BASE
25 #error did not get engine.h
26 #endif
27 
28 #define PKCS11_ENGINE_ID "pkcs11"
29 #define PKCS11_ENGINE_NAME "pkcs11 engine"
30 
31 static int pkcs11_idx = -1;
32 
33 /* The definitions for control commands specific to this engine */
34 
35 /* need to add function to pass in reader id? or user reader:key as key id string? */
36 
37 static const ENGINE_CMD_DEFN engine_cmd_defns[] = {
38 	{CMD_SO_PATH,
39 		"SO_PATH",
40 		"Specifies the path to the 'pkcs11' engine shared library",
41 		ENGINE_CMD_FLAG_STRING},
42 	{CMD_MODULE_PATH,
43 		"MODULE_PATH",
44 		"Specifies the path to the PKCS#11 module shared library",
45 		ENGINE_CMD_FLAG_STRING},
46 	{CMD_PIN,
47 		"PIN",
48 		"Specifies the pin code",
49 		ENGINE_CMD_FLAG_STRING},
50 	{CMD_VERBOSE,
51 		"VERBOSE",
52 		"Print additional details",
53 		ENGINE_CMD_FLAG_NO_INPUT},
54 	{CMD_QUIET,
55 		"QUIET",
56 		"Remove additional details",
57 		ENGINE_CMD_FLAG_NO_INPUT},
58 	{CMD_LOAD_CERT_CTRL,
59 		"LOAD_CERT_CTRL",
60 		"Get the certificate from card",
61 		ENGINE_CMD_FLAG_INTERNAL},
62 	{CMD_INIT_ARGS,
63 		"INIT_ARGS",
64 		"Specifies additional initialization arguments to the PKCS#11 module",
65 		ENGINE_CMD_FLAG_STRING},
66 	{CMD_SET_USER_INTERFACE,
67 		"SET_USER_INTERFACE",
68 		"Set the global user interface (internal)",
69 		ENGINE_CMD_FLAG_INTERNAL},
70 	{CMD_SET_CALLBACK_DATA,
71 		"SET_CALLBACK_DATA",
72 		"Set the global user interface extra data (internal)",
73 		ENGINE_CMD_FLAG_INTERNAL},
74 	{CMD_FORCE_LOGIN,
75 		"FORCE_LOGIN",
76 		"Force login to the PKCS#11 module",
77 		ENGINE_CMD_FLAG_NO_INPUT},
78 	{0, NULL, NULL, 0}
79 };
80 
get_ctx(ENGINE * engine)81 static ENGINE_CTX *get_ctx(ENGINE *engine)
82 {
83 	ENGINE_CTX *ctx;
84 
85 	if (pkcs11_idx < 0) {
86 		pkcs11_idx = ENGINE_get_ex_new_index(0, "pkcs11", NULL, NULL, 0);
87 		if (pkcs11_idx < 0)
88 			return NULL;
89 		ctx = NULL;
90 	} else {
91 		ctx = ENGINE_get_ex_data(engine, pkcs11_idx);
92 	}
93 	if (!ctx) {
94 		ctx = ctx_new();
95 		ENGINE_set_ex_data(engine, pkcs11_idx, ctx);
96 	}
97 	return ctx;
98 }
99 
100 /* Destroy the context allocated with ctx_new() */
engine_destroy(ENGINE * engine)101 static int engine_destroy(ENGINE *engine)
102 {
103 	ENGINE_CTX *ctx;
104 	int rv = 1;
105 
106 	ctx = get_ctx(engine);
107 	if (!ctx)
108 		return 0;
109 
110 	/* ENGINE_remove() invokes our engine_destroy() function with
111 	 * CRYPTO_LOCK_ENGINE / global_engine_lock acquired.
112 	 * Any attempt to re-acquire the lock either by directly
113 	 * invoking OpenSSL functions, or indirectly via PKCS#11 modules
114 	 * that use OpenSSL engines, causes a deadlock. */
115 	/* Our workaround is to skip ctx_finish() entirely, as a memory
116 	 * leak is better than a deadlock. */
117 #if 0
118 	rv &= ctx_finish(ctx);
119 #endif
120 
121 	rv &= ctx_destroy(ctx);
122 	ENGINE_set_ex_data(engine, pkcs11_idx, NULL);
123 	ERR_unload_ENG_strings();
124 	return rv;
125 }
126 
engine_init(ENGINE * engine)127 static int engine_init(ENGINE *engine)
128 {
129 	ENGINE_CTX *ctx;
130 
131 	ctx = get_ctx(engine);
132 	if (!ctx)
133 		return 0;
134 	return ctx_init(ctx);
135 }
136 
137 /* Finish engine operations initialized with ctx_init() */
engine_finish(ENGINE * engine)138 static int engine_finish(ENGINE *engine)
139 {
140 	ENGINE_CTX *ctx;
141 	int rv = 1;
142 
143 	ctx = get_ctx(engine);
144 	if (!ctx)
145 		return 0;
146 
147 	/* ENGINE_cleanup() used by OpenSSL versions before 1.1.0 invokes
148 	 * our engine_finish() function with CRYPTO_LOCK_ENGINE acquired.
149 	 * Any attempt to re-acquire CRYPTO_LOCK_ENGINE either by directly
150 	 * invoking OpenSSL functions, or indirectly via PKCS#11 modules
151 	 * that use OpenSSL engines, causes a deadlock. */
152 	/* Our workaround is to skip ctx_finish() for the affected OpenSSL
153 	 * versions, as a memory leak is better than a deadlock. */
154 	/* We cannot simply temporarily release CRYPTO_LOCK_ENGINE here, as
155 	 * engine_finish() is also executed from ENGINE_finish() without
156 	 * acquired CRYPTO_LOCK_ENGINE, and there is no way with to check
157 	 * whether a lock is already acquired with OpenSSL < 1.1.0 API. */
158 #if OPENSSL_VERSION_NUMBER >= 0x10100005L && !defined(LIBRESSL_VERSION_NUMBER)
159 	rv &= ctx_finish(ctx);
160 #endif
161 
162 	return rv;
163 }
164 
load_pubkey(ENGINE * engine,const char * s_key_id,UI_METHOD * ui_method,void * callback_data)165 static EVP_PKEY *load_pubkey(ENGINE *engine, const char *s_key_id,
166 		UI_METHOD *ui_method, void *callback_data)
167 {
168 	ENGINE_CTX *ctx;
169 
170 	ctx = get_ctx(engine);
171 	if (!ctx)
172 		return 0;
173 	return ctx_load_pubkey(ctx, s_key_id, ui_method, callback_data);
174 }
175 
load_privkey(ENGINE * engine,const char * s_key_id,UI_METHOD * ui_method,void * callback_data)176 static EVP_PKEY *load_privkey(ENGINE *engine, const char *s_key_id,
177 		UI_METHOD *ui_method, void *callback_data)
178 {
179 	ENGINE_CTX *ctx;
180 	EVP_PKEY *pkey;
181 
182 	ctx = get_ctx(engine);
183 	if (!ctx)
184 		return 0;
185 	pkey = ctx_load_privkey(ctx, s_key_id, ui_method, callback_data);
186 #ifdef EVP_F_EVP_PKEY_SET1_ENGINE
187 	/* EVP_PKEY_set1_engine() is required for OpenSSL 1.1.x,
188 	 * but otherwise setting pkey->engine breaks OpenSSL 1.0.2 */
189 	if (pkey && !EVP_PKEY_set1_engine(pkey, engine)) {
190 		EVP_PKEY_free(pkey);
191 		pkey = NULL;
192 	}
193 #endif /* EVP_F_EVP_PKEY_SET1_ENGINE */
194 	return pkey;
195 }
196 
engine_ctrl(ENGINE * engine,int cmd,long i,void * p,void (* f)())197 static int engine_ctrl(ENGINE *engine, int cmd, long i, void *p, void (*f) ())
198 {
199 	ENGINE_CTX *ctx;
200 
201 	ctx = get_ctx(engine);
202 	if (!ctx)
203 		return 0;
204 	return ctx_engine_ctrl(ctx, cmd, i, p, f);
205 }
206 
207 /* This internal function is used by ENGINE_pkcs11() and possibly by the
208  * "dynamic" ENGINE support too */
bind_helper(ENGINE * e)209 static int bind_helper(ENGINE *e)
210 {
211 	if (!ENGINE_set_id(e, PKCS11_ENGINE_ID) ||
212 			!ENGINE_set_destroy_function(e, engine_destroy) ||
213 			!ENGINE_set_init_function(e, engine_init) ||
214 			!ENGINE_set_finish_function(e, engine_finish) ||
215 			!ENGINE_set_ctrl_function(e, engine_ctrl) ||
216 			!ENGINE_set_cmd_defns(e, engine_cmd_defns) ||
217 			!ENGINE_set_name(e, PKCS11_ENGINE_NAME) ||
218 #ifndef OPENSSL_NO_RSA
219 			!ENGINE_set_RSA(e, PKCS11_get_rsa_method()) ||
220 #endif
221 #if OPENSSL_VERSION_NUMBER  >= 0x10100002L
222 #ifndef OPENSSL_NO_EC
223 			/* PKCS11_get_ec_key_method combines ECDSA and ECDH */
224 			!ENGINE_set_EC(e, PKCS11_get_ec_key_method()) ||
225 #endif /* OPENSSL_NO_EC */
226 #else /* OPENSSL_VERSION_NUMBER */
227 #ifndef OPENSSL_NO_ECDSA
228 			!ENGINE_set_ECDSA(e, PKCS11_get_ecdsa_method()) ||
229 #endif
230 #ifndef OPENSSL_NO_ECDH
231 			!ENGINE_set_ECDH(e, PKCS11_get_ecdh_method()) ||
232 #endif
233 #endif /* OPENSSL_VERSION_NUMBER */
234 			!ENGINE_set_pkey_meths(e, PKCS11_pkey_meths) ||
235 			!ENGINE_set_load_pubkey_function(e, load_pubkey) ||
236 			!ENGINE_set_load_privkey_function(e, load_privkey)) {
237 		return 0;
238 	} else {
239 		ERR_load_ENG_strings();
240 		return 1;
241 	}
242 }
243 
bind_fn(ENGINE * e,const char * id)244 static int bind_fn(ENGINE *e, const char *id)
245 {
246 	if (id && (strcmp(id, PKCS11_ENGINE_ID) != 0)) {
247 		fprintf(stderr, "bad engine id\n");
248 		return 0;
249 	}
250 	if (!bind_helper(e)) {
251 		fprintf(stderr, "bind failed\n");
252 		return 0;
253 	}
254 	return 1;
255 }
256 
257 IMPLEMENT_DYNAMIC_CHECK_FN()
258 IMPLEMENT_DYNAMIC_BIND_FN(bind_fn)
259 
260 /* vim: set noexpandtab: */
261