1/*
2 * Copyright 2018-2022 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License 2.0 (the "License").  You may not use
5 * this file except in compliance with the License.  You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
8 */
9
10/*
11 * Here is an STORE loader for ENGINE backed keys.  It relies on deprecated
12 * functions, and therefore need to have deprecation warnings suppressed.
13 * This file is not compiled at all in a '--api=3 no-deprecated' configuration.
14 */
15#define OPENSSL_SUPPRESS_DEPRECATED
16
17#include "apps.h"
18
19#ifndef OPENSSL_NO_ENGINE
20
21# include <stdarg.h>
22# include <string.h>
23# include <openssl/engine.h>
24# include <openssl/store.h>
25
26/*
27 * Support for legacy private engine keys via the 'org.openssl.engine:' scheme
28 *
29 * org.openssl.engine:{engineid}:{keyid}
30 *
31 * Note: we ONLY support ENGINE_load_private_key() and ENGINE_load_public_key()
32 * Note 2: This scheme has a precedent in code in PKIX-SSH. for exactly
33 * this sort of purpose.
34 */
35
36/* Local definition of OSSL_STORE_LOADER_CTX */
37struct ossl_store_loader_ctx_st {
38    ENGINE *e;                   /* Structural reference */
39    char *keyid;
40    int expected;
41    int loaded;                  /* 0 = key not loaded yet, 1 = key loaded */
42};
43
44static OSSL_STORE_LOADER_CTX *OSSL_STORE_LOADER_CTX_new(ENGINE *e, char *keyid)
45{
46    OSSL_STORE_LOADER_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx));
47
48    if (ctx != NULL) {
49        ctx->e = e;
50        ctx->keyid = keyid;
51    }
52    return ctx;
53}
54
55static void OSSL_STORE_LOADER_CTX_free(OSSL_STORE_LOADER_CTX *ctx)
56{
57    if (ctx != NULL) {
58        ENGINE_free(ctx->e);
59        OPENSSL_free(ctx->keyid);
60        OPENSSL_free(ctx);
61    }
62}
63
64static OSSL_STORE_LOADER_CTX *engine_open(const OSSL_STORE_LOADER *loader,
65                                          const char *uri,
66                                          const UI_METHOD *ui_method,
67                                          void *ui_data)
68{
69    const char *p = uri, *q;
70    ENGINE *e = NULL;
71    char *keyid = NULL;
72    OSSL_STORE_LOADER_CTX *ctx = NULL;
73
74    if (OPENSSL_strncasecmp(p, ENGINE_SCHEME_COLON, sizeof(ENGINE_SCHEME_COLON) - 1)
75        != 0)
76        return NULL;
77    p += sizeof(ENGINE_SCHEME_COLON) - 1;
78
79    /* Look for engine ID */
80    q = strchr(p, ':');
81    if (q != NULL                /* There is both an engine ID and a key ID */
82        && p[0] != ':'           /* The engine ID is at least one character */
83        && q[1] != '\0') {       /* The key ID is at least one character */
84        char engineid[256];
85        size_t engineid_l = q - p;
86
87        strncpy(engineid, p, engineid_l);
88        engineid[engineid_l] = '\0';
89        e = ENGINE_by_id(engineid);
90
91        keyid = OPENSSL_strdup(q + 1);
92    }
93
94    if (e != NULL && keyid != NULL)
95        ctx = OSSL_STORE_LOADER_CTX_new(e, keyid);
96
97    if (ctx == NULL) {
98        OPENSSL_free(keyid);
99        ENGINE_free(e);
100    }
101
102    return ctx;
103}
104
105static int engine_expect(OSSL_STORE_LOADER_CTX *ctx, int expected)
106{
107    if (expected == 0
108        || expected == OSSL_STORE_INFO_PUBKEY
109        || expected == OSSL_STORE_INFO_PKEY) {
110        ctx->expected = expected;
111        return 1;
112    }
113    return 0;
114}
115
116static OSSL_STORE_INFO *engine_load(OSSL_STORE_LOADER_CTX *ctx,
117                                    const UI_METHOD *ui_method, void *ui_data)
118{
119    EVP_PKEY *pkey = NULL, *pubkey = NULL;
120    OSSL_STORE_INFO *info = NULL;
121
122    if (ctx->loaded == 0) {
123        if (ENGINE_init(ctx->e)) {
124            if (ctx->expected == 0
125                || ctx->expected == OSSL_STORE_INFO_PKEY)
126                pkey =
127                    ENGINE_load_private_key(ctx->e, ctx->keyid,
128                                            (UI_METHOD *)ui_method, ui_data);
129            if ((pkey == NULL && ctx->expected == 0)
130                || ctx->expected == OSSL_STORE_INFO_PUBKEY)
131                pubkey =
132                    ENGINE_load_public_key(ctx->e, ctx->keyid,
133                                           (UI_METHOD *)ui_method, ui_data);
134            ENGINE_finish(ctx->e);
135        }
136    }
137
138    ctx->loaded = 1;
139
140    if (pubkey != NULL)
141        info = OSSL_STORE_INFO_new_PUBKEY(pubkey);
142    else if (pkey != NULL)
143        info = OSSL_STORE_INFO_new_PKEY(pkey);
144    if (info == NULL) {
145        EVP_PKEY_free(pkey);
146        EVP_PKEY_free(pubkey);
147    }
148    return info;
149}
150
151static int engine_eof(OSSL_STORE_LOADER_CTX *ctx)
152{
153    return ctx->loaded != 0;
154}
155
156static int engine_error(OSSL_STORE_LOADER_CTX *ctx)
157{
158    return 0;
159}
160
161static int engine_close(OSSL_STORE_LOADER_CTX *ctx)
162{
163    OSSL_STORE_LOADER_CTX_free(ctx);
164    return 1;
165}
166
167int setup_engine_loader(void)
168{
169    OSSL_STORE_LOADER *loader = NULL;
170
171    if ((loader = OSSL_STORE_LOADER_new(NULL, ENGINE_SCHEME)) == NULL
172        || !OSSL_STORE_LOADER_set_open(loader, engine_open)
173        || !OSSL_STORE_LOADER_set_expect(loader, engine_expect)
174        || !OSSL_STORE_LOADER_set_load(loader, engine_load)
175        || !OSSL_STORE_LOADER_set_eof(loader, engine_eof)
176        || !OSSL_STORE_LOADER_set_error(loader, engine_error)
177        || !OSSL_STORE_LOADER_set_close(loader, engine_close)
178        || !OSSL_STORE_register_loader(loader)) {
179        OSSL_STORE_LOADER_free(loader);
180        loader = NULL;
181    }
182
183    return loader != NULL;
184}
185
186void destroy_engine_loader(void)
187{
188    OSSL_STORE_LOADER *loader = OSSL_STORE_unregister_loader(ENGINE_SCHEME);
189    OSSL_STORE_LOADER_free(loader);
190}
191
192#else  /* !OPENSSL_NO_ENGINE */
193
194int setup_engine_loader(void)
195{
196    return 0;
197}
198
199void destroy_engine_loader(void)
200{
201}
202
203#endif
204