]> CyberLeo.Net >> Repos - FreeBSD/releng/9.3.git/blob - contrib/bind9/lib/dns/opensslgost_link.c
Fix BIND remote denial of service vulnerability. [SA-15:27]
[FreeBSD/releng/9.3.git] / contrib / bind9 / lib / dns / opensslgost_link.c
1 /*
2  * Copyright (C) 2010-2014  Internet Systems Consortium, Inc. ("ISC")
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14  * PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 /* $Id: opensslgost_link.c,v 1.5 2011/01/19 23:47:12 tbox Exp $ */
18
19 #include <config.h>
20
21 #ifdef HAVE_OPENSSL_GOST
22
23 #include <isc/entropy.h>
24 #include <isc/mem.h>
25 #include <isc/string.h>
26 #include <isc/util.h>
27
28 #include <dst/result.h>
29
30 #include "dst_internal.h"
31 #include "dst_openssl.h"
32 #include "dst_parse.h"
33
34 #include <openssl/err.h>
35 #include <openssl/objects.h>
36 #include <openssl/rsa.h>
37 #include <openssl/engine.h>
38
39 static ENGINE *e = NULL;
40 static const EVP_MD *opensslgost_digest;
41 extern const EVP_MD *EVP_gost(void);
42
43 const EVP_MD *EVP_gost(void) {
44         return (opensslgost_digest);
45 }
46
47 #define DST_RET(a) {ret = a; goto err;}
48
49 static isc_result_t opensslgost_todns(const dst_key_t *key,
50                                       isc_buffer_t *data);
51
52 static isc_result_t
53 opensslgost_createctx(dst_key_t *key, dst_context_t *dctx) {
54         EVP_MD_CTX *evp_md_ctx;
55         const EVP_MD *md = EVP_gost();
56
57         UNUSED(key);
58
59         if (md == NULL)
60                 return (DST_R_OPENSSLFAILURE);
61
62         evp_md_ctx = EVP_MD_CTX_create();
63         if (evp_md_ctx == NULL)
64                 return (ISC_R_NOMEMORY);
65
66         if (!EVP_DigestInit_ex(evp_md_ctx, md, NULL)) {
67                 EVP_MD_CTX_destroy(evp_md_ctx);
68                 return (ISC_R_FAILURE);
69         }
70         dctx->ctxdata.evp_md_ctx = evp_md_ctx;
71
72         return (ISC_R_SUCCESS);
73 }
74
75 static void
76 opensslgost_destroyctx(dst_context_t *dctx) {
77         EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
78
79         if (evp_md_ctx != NULL) {
80                 EVP_MD_CTX_destroy(evp_md_ctx);
81                 dctx->ctxdata.evp_md_ctx = NULL;
82         }
83 }
84
85 static isc_result_t
86 opensslgost_adddata(dst_context_t *dctx, const isc_region_t *data) {
87         EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
88
89         if (!EVP_DigestUpdate(evp_md_ctx, data->base, data->length))
90                 return (ISC_R_FAILURE);
91
92         return (ISC_R_SUCCESS);
93 }
94
95 static isc_result_t
96 opensslgost_sign(dst_context_t *dctx, isc_buffer_t *sig) {
97         dst_key_t *key = dctx->key;
98         isc_region_t r;
99         unsigned int siglen = 0;
100         EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
101         EVP_PKEY *pkey = key->keydata.pkey;
102
103         isc_buffer_availableregion(sig, &r);
104
105         if (r.length < (unsigned int) EVP_PKEY_size(pkey))
106                 return (ISC_R_NOSPACE);
107
108         if (!EVP_SignFinal(evp_md_ctx, r.base, &siglen, pkey))
109                 return (ISC_R_FAILURE);
110
111         isc_buffer_add(sig, siglen);
112
113         return (ISC_R_SUCCESS);
114 }
115
116 static isc_result_t
117 opensslgost_verify(dst_context_t *dctx, const isc_region_t *sig) {
118         dst_key_t *key = dctx->key;
119         int status = 0;
120         EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
121         EVP_PKEY *pkey = key->keydata.pkey;
122
123         status = EVP_VerifyFinal(evp_md_ctx, sig->base, sig->length, pkey);
124         switch (status) {
125         case 1:
126                 return (ISC_R_SUCCESS);
127         case 0:
128                 return (dst__openssl_toresult(DST_R_VERIFYFAILURE));
129         default:
130                 return (dst__openssl_toresult3(dctx->category,
131                                                "EVP_VerifyFinal",
132                                                DST_R_VERIFYFAILURE));
133         }
134 }
135
136 static isc_boolean_t
137 opensslgost_compare(const dst_key_t *key1, const dst_key_t *key2) {
138         EVP_PKEY *pkey1, *pkey2;
139
140         pkey1 = key1->keydata.pkey;
141         pkey2 = key2->keydata.pkey;
142
143         if (pkey1 == NULL && pkey2 == NULL)
144                 return (ISC_TRUE);
145         else if (pkey1 == NULL || pkey2 == NULL)
146                 return (ISC_FALSE);
147
148         if (EVP_PKEY_cmp(pkey1, pkey2) != 1)
149                 return (ISC_FALSE);
150         return (ISC_TRUE);
151 }
152
153 static int
154 progress_cb(EVP_PKEY_CTX *ctx)
155 {
156         union {
157                 void *dptr;
158                 void (*fptr)(int);
159         } u;
160         int p;
161
162         u.dptr = EVP_PKEY_CTX_get_app_data(ctx);
163         p = EVP_PKEY_CTX_get_keygen_info(ctx, 0);
164         if (u.fptr != NULL)
165                 u.fptr(p);
166         return (1);
167 }
168
169 static isc_result_t
170 opensslgost_generate(dst_key_t *key, int unused, void (*callback)(int)) {
171         EVP_PKEY_CTX *ctx;
172         union {
173                 void *dptr;
174                 void (*fptr)(int);
175         } u;
176         EVP_PKEY *pkey = NULL;
177         isc_result_t ret;
178
179         UNUSED(unused);
180         ctx = EVP_PKEY_CTX_new_id(NID_id_GostR3410_2001, NULL);
181         if (ctx == NULL)
182                 DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_new_id",
183                                                DST_R_OPENSSLFAILURE));
184         if (callback != NULL) {
185                 u.fptr = callback;
186                 EVP_PKEY_CTX_set_app_data(ctx, u.dptr);
187                 EVP_PKEY_CTX_set_cb(ctx, &progress_cb);
188         }
189         if (EVP_PKEY_keygen_init(ctx) <= 0)
190                 DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen_init",
191                                                DST_R_OPENSSLFAILURE));
192         if (EVP_PKEY_CTX_ctrl_str(ctx, "paramset", "A") <= 0)
193                 DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_ctrl_str",
194                                                DST_R_OPENSSLFAILURE));
195         if (EVP_PKEY_keygen(ctx, &pkey) <= 0)
196                 DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen",
197                                                DST_R_OPENSSLFAILURE));
198         key->keydata.pkey = pkey;
199         EVP_PKEY_CTX_free(ctx);
200         return (ISC_R_SUCCESS);
201
202 err:
203         if (pkey != NULL)
204                 EVP_PKEY_free(pkey);
205         if (ctx != NULL)
206                 EVP_PKEY_CTX_free(ctx);
207         return (ret);
208 }
209
210 static isc_boolean_t
211 opensslgost_isprivate(const dst_key_t *key) {
212         EVP_PKEY *pkey = key->keydata.pkey;
213         EC_KEY *ec;
214
215         INSIST(pkey != NULL);
216
217         ec = EVP_PKEY_get0(pkey);
218         return (ISC_TF(ec != NULL && EC_KEY_get0_private_key(ec) != NULL));
219 }
220
221 static void
222 opensslgost_destroy(dst_key_t *key) {
223         EVP_PKEY *pkey = key->keydata.pkey;
224
225         EVP_PKEY_free(pkey);
226         key->keydata.pkey = NULL;
227 }
228
229 unsigned char gost_prefix[37] = {
230         0x30, 0x63, 0x30, 0x1c, 0x06, 0x06, 0x2a, 0x85,
231         0x03, 0x02, 0x02, 0x13, 0x30, 0x12, 0x06, 0x07,
232         0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01, 0x06,
233         0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01,
234         0x03, 0x43, 0x00, 0x04, 0x40
235 };
236
237 static isc_result_t
238 opensslgost_todns(const dst_key_t *key, isc_buffer_t *data) {
239         EVP_PKEY *pkey;
240         isc_region_t r;
241         unsigned char der[37 + 64], *p;
242         int len;
243
244         REQUIRE(key->keydata.pkey != NULL);
245
246         pkey = key->keydata.pkey;
247
248         isc_buffer_availableregion(data, &r);
249         if (r.length < 64)
250                 return (ISC_R_NOSPACE);
251
252         p = der;
253         len = i2d_PUBKEY(pkey, &p);
254         INSIST(len == sizeof(der));
255         INSIST(memcmp(gost_prefix, der, 37) == 0);
256         memmove(r.base, der + 37, 64);
257         isc_buffer_add(data, 64);
258
259         return (ISC_R_SUCCESS);
260 }
261
262 static isc_result_t
263 opensslgost_fromdns(dst_key_t *key, isc_buffer_t *data) {
264         isc_region_t r;
265         EVP_PKEY *pkey = NULL;
266         unsigned char der[37 + 64];
267         const unsigned char *p;
268
269         isc_buffer_remainingregion(data, &r);
270         if (r.length == 0)
271                 return (ISC_R_SUCCESS);
272
273         if (r.length != 64)
274                 return (DST_R_INVALIDPUBLICKEY);
275         memmove(der, gost_prefix, 37);
276         memmove(der + 37, r.base, 64);
277         isc_buffer_forward(data, 64);
278
279         p = der;
280         if (d2i_PUBKEY(&pkey, &p, (long) sizeof(der)) == NULL)
281                 return (dst__openssl_toresult2("d2i_PUBKEY",
282                                                DST_R_OPENSSLFAILURE));
283         key->keydata.pkey = pkey;
284
285         return (ISC_R_SUCCESS);
286 }
287
288 static isc_result_t
289 opensslgost_tofile(const dst_key_t *key, const char *directory) {
290         EVP_PKEY *pkey;
291         dst_private_t priv;
292         isc_result_t result;
293         unsigned char *der, *p;
294         int len;
295
296         if (key->keydata.pkey == NULL)
297                 return (DST_R_NULLKEY);
298
299         if (key->external) {
300                 priv.nelements = 0;
301                 return (dst__privstruct_writefile(key, &priv, directory));
302         }
303
304         pkey = key->keydata.pkey;
305
306         len = i2d_PrivateKey(pkey, NULL);
307         der = isc_mem_get(key->mctx, (size_t) len);
308         if (der == NULL)
309                 return (ISC_R_NOMEMORY);
310
311         p = der;
312         if (i2d_PrivateKey(pkey, &p) != len) {
313                 result = dst__openssl_toresult2("i2d_PrivateKey",
314                                                 DST_R_OPENSSLFAILURE);
315                 goto fail;
316         }
317
318         priv.elements[0].tag = TAG_GOST_PRIVASN1;
319         priv.elements[0].length = len;
320         priv.elements[0].data = der;
321         priv.nelements = GOST_NTAGS;
322
323         result = dst__privstruct_writefile(key, &priv, directory);
324  fail:
325         if (der != NULL)
326                 isc_mem_put(key->mctx, der, (size_t) len);
327         return (result);
328 }
329
330 static isc_result_t
331 opensslgost_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
332         dst_private_t priv;
333         isc_result_t ret;
334         isc_mem_t *mctx = key->mctx;
335         EVP_PKEY *pkey = NULL;
336         const unsigned char *p;
337
338         UNUSED(pub);
339
340         /* read private key file */
341         ret = dst__privstruct_parse(key, DST_ALG_ECCGOST, lexer, mctx, &priv);
342         if (ret != ISC_R_SUCCESS)
343                 return (ret);
344
345         if (key->external) {
346                 INSIST(priv.nelements == 0);
347                 if (pub == NULL)
348                         DST_RET(DST_R_INVALIDPRIVATEKEY);
349                 key->keydata.pkey = pub->keydata.pkey;
350                 pub->keydata.pkey = NULL;
351         } else {
352                 INSIST(priv.elements[0].tag == TAG_GOST_PRIVASN1);
353                 p = priv.elements[0].data;
354                 if (d2i_PrivateKey(NID_id_GostR3410_2001, &pkey, &p,
355                                    (long) priv.elements[0].length) == NULL)
356                         DST_RET(dst__openssl_toresult2("d2i_PrivateKey",
357                                                      DST_R_INVALIDPRIVATEKEY));
358                 key->keydata.pkey = pkey;
359         }
360         key->key_size = EVP_PKEY_bits(pkey);
361         dst__privstruct_free(&priv, mctx);
362         memset(&priv, 0, sizeof(priv));
363         return (ISC_R_SUCCESS);
364
365  err:
366         if (pkey != NULL)
367                 EVP_PKEY_free(pkey);
368         opensslgost_destroy(key);
369         dst__privstruct_free(&priv, mctx);
370         memset(&priv, 0, sizeof(priv));
371         return (ret);
372 }
373
374 static void
375 opensslgost_cleanup(void) {
376         if (e != NULL) {
377                 ENGINE_finish(e);
378                 ENGINE_free(e);
379                 e = NULL;
380         }
381 }
382
383 static dst_func_t opensslgost_functions = {
384         opensslgost_createctx,
385         opensslgost_destroyctx,
386         opensslgost_adddata,
387         opensslgost_sign,
388         opensslgost_verify,
389         NULL, /*%< verify2 */
390         NULL, /*%< computesecret */
391         opensslgost_compare,
392         NULL, /*%< paramcompare */
393         opensslgost_generate,
394         opensslgost_isprivate,
395         opensslgost_destroy,
396         opensslgost_todns,
397         opensslgost_fromdns,
398         opensslgost_tofile,
399         opensslgost_parse,
400         opensslgost_cleanup,
401         NULL, /*%< fromlabel */
402         NULL, /*%< dump */
403         NULL  /*%< restore */
404 };
405
406 isc_result_t
407 dst__opensslgost_init(dst_func_t **funcp) {
408         isc_result_t ret;
409
410         REQUIRE(funcp != NULL);
411
412         /* check if the gost engine works properly */
413         e = ENGINE_by_id("gost");
414         if (e == NULL)
415                 return (dst__openssl_toresult2("ENGINE_by_id",
416                                                DST_R_OPENSSLFAILURE));
417         if (ENGINE_init(e) <= 0) {
418                 ENGINE_free(e);
419                 e = NULL;
420                 return (dst__openssl_toresult2("ENGINE_init",
421                                                DST_R_OPENSSLFAILURE));
422         }
423         /* better than to rely on digest_gost symbol */
424         opensslgost_digest = ENGINE_get_digest(e, NID_id_GostR3411_94);
425         if (opensslgost_digest == NULL)
426                 DST_RET(dst__openssl_toresult2("ENGINE_get_digest",
427                                                DST_R_OPENSSLFAILURE));
428         /* from openssl.cnf */
429         if (ENGINE_register_pkey_asn1_meths(e) <= 0)
430                 DST_RET(dst__openssl_toresult2(
431                                 "ENGINE_register_pkey_asn1_meths",
432                                 DST_R_OPENSSLFAILURE));
433         if (ENGINE_ctrl_cmd_string(e,
434                                    "CRYPT_PARAMS",
435                                    "id-Gost28147-89-CryptoPro-A-ParamSet",
436                                    0) <= 0)
437                 DST_RET(dst__openssl_toresult2("ENGINE_ctrl_cmd_string",
438                                                DST_R_OPENSSLFAILURE));
439
440         if (*funcp == NULL)
441                 *funcp = &opensslgost_functions;
442         return (ISC_R_SUCCESS);
443
444  err:
445         ENGINE_finish(e);
446         ENGINE_free(e);
447         e = NULL;
448         return (ret);
449 }
450
451 #else /* HAVE_OPENSSL_GOST */
452
453 #include <isc/util.h>
454
455 EMPTY_TRANSLATION_UNIT
456
457 #endif /* HAVE_OPENSSL_GOST */
458 /*! \file */