1 /*
2  * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
7  *
8  * See the COPYRIGHT file distributed with this work for additional
9  * information regarding copyright ownership.
10  *
11  * Portions Copyright (C) Network Associates, Inc.
12  *
13  * Permission to use, copy, modify, and/or distribute this software for any
14  * purpose with or without fee is hereby granted, provided that the above
15  * copyright notice and this permission notice appear in all copies.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
18  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
20  * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
21  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
22  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
23  * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24  */
25 
26 #include <isc/mem.h>
27 #include <isc/mutex.h>
28 #include <isc/mutexblock.h>
29 #include <isc/result.h>
30 #include <isc/string.h>
31 #include <isc/thread.h>
32 #include <isc/tls.h>
33 #include <isc/util.h>
34 
35 #include <dns/log.h>
36 
37 #include "dst_internal.h"
38 #include "dst_openssl.h"
39 
40 #if !defined(OPENSSL_NO_ENGINE)
41 #include <openssl/engine.h>
42 #endif /* if !defined(OPENSSL_NO_ENGINE) */
43 
44 #if !defined(OPENSSL_NO_ENGINE)
45 static ENGINE *e = NULL;
46 #endif /* if !defined(OPENSSL_NO_ENGINE) */
47 
48 static void
enable_fips_mode(void)49 enable_fips_mode(void) {
50 #ifdef HAVE_FIPS_MODE
51 	if (FIPS_mode() != 0) {
52 		/*
53 		 * FIPS mode is already enabled.
54 		 */
55 		return;
56 	}
57 
58 	if (FIPS_mode_set(1) == 0) {
59 		dst__openssl_toresult2("FIPS_mode_set", DST_R_OPENSSLFAILURE);
60 		exit(1);
61 	}
62 #endif /* HAVE_FIPS_MODE */
63 }
64 
65 isc_result_t
dst__openssl_init(const char * engine)66 dst__openssl_init(const char *engine) {
67 	isc_result_t result = ISC_R_SUCCESS;
68 
69 	enable_fips_mode();
70 
71 #if !defined(OPENSSL_NO_ENGINE)
72 	if (engine != NULL && *engine == '\0') {
73 		engine = NULL;
74 	}
75 
76 	if (engine != NULL) {
77 		e = ENGINE_by_id(engine);
78 		if (e == NULL) {
79 			result = DST_R_NOENGINE;
80 			goto cleanup_rm;
81 		}
82 		/* This will init the engine. */
83 		if (!ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
84 			result = DST_R_NOENGINE;
85 			goto cleanup_rm;
86 		}
87 	}
88 
89 	return (ISC_R_SUCCESS);
90 cleanup_rm:
91 	if (e != NULL) {
92 		ENGINE_free(e);
93 	}
94 	e = NULL;
95 #else
96 	UNUSED(engine);
97 #endif /* if !defined(OPENSSL_NO_ENGINE) */
98 	return (result);
99 }
100 
101 void
dst__openssl_destroy(void)102 dst__openssl_destroy(void) {
103 #if !defined(OPENSSL_NO_ENGINE)
104 	if (e != NULL) {
105 		ENGINE_free(e);
106 	}
107 	e = NULL;
108 #endif /* if !defined(OPENSSL_NO_ENGINE) */
109 }
110 
111 static isc_result_t
toresult(isc_result_t fallback)112 toresult(isc_result_t fallback) {
113 	isc_result_t result = fallback;
114 	unsigned long err = ERR_peek_error();
115 #if defined(ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED)
116 	int lib = ERR_GET_LIB(err);
117 #endif /* if defined(ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED) */
118 	int reason = ERR_GET_REASON(err);
119 
120 	switch (reason) {
121 	/*
122 	 * ERR_* errors are globally unique; others
123 	 * are unique per sublibrary
124 	 */
125 	case ERR_R_MALLOC_FAILURE:
126 		result = ISC_R_NOMEMORY;
127 		break;
128 	default:
129 #if defined(ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED)
130 		if (lib == ERR_R_ECDSA_LIB &&
131 		    reason == ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED) {
132 			result = ISC_R_NOENTROPY;
133 			break;
134 		}
135 #endif /* if defined(ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED) */
136 		break;
137 	}
138 
139 	return (result);
140 }
141 
142 isc_result_t
dst__openssl_toresult(isc_result_t fallback)143 dst__openssl_toresult(isc_result_t fallback) {
144 	isc_result_t result;
145 
146 	result = toresult(fallback);
147 
148 	ERR_clear_error();
149 	return (result);
150 }
151 
152 isc_result_t
dst__openssl_toresult2(const char * funcname,isc_result_t fallback)153 dst__openssl_toresult2(const char *funcname, isc_result_t fallback) {
154 	return (dst__openssl_toresult3(DNS_LOGCATEGORY_GENERAL, funcname,
155 				       fallback));
156 }
157 
158 isc_result_t
dst__openssl_toresult3(isc_logcategory_t * category,const char * funcname,isc_result_t fallback)159 dst__openssl_toresult3(isc_logcategory_t *category, const char *funcname,
160 		       isc_result_t fallback) {
161 	isc_result_t result;
162 	unsigned long err;
163 	const char *file, *data;
164 	int line, flags;
165 	char buf[256];
166 
167 	result = toresult(fallback);
168 
169 	isc_log_write(dns_lctx, category, DNS_LOGMODULE_CRYPTO, ISC_LOG_WARNING,
170 		      "%s failed (%s)", funcname, isc_result_totext(result));
171 
172 	if (result == ISC_R_NOMEMORY) {
173 		goto done;
174 	}
175 
176 	for (;;) {
177 		err = ERR_get_error_line_data(&file, &line, &data, &flags);
178 		if (err == 0U) {
179 			goto done;
180 		}
181 		ERR_error_string_n(err, buf, sizeof(buf));
182 		isc_log_write(dns_lctx, category, DNS_LOGMODULE_CRYPTO,
183 			      ISC_LOG_INFO, "%s:%s:%d:%s", buf, file, line,
184 			      ((flags & ERR_TXT_STRING) != 0) ? data : "");
185 	}
186 
187 done:
188 	ERR_clear_error();
189 	return (result);
190 }
191 
192 #if !defined(OPENSSL_NO_ENGINE)
193 ENGINE *
dst__openssl_getengine(const char * engine)194 dst__openssl_getengine(const char *engine) {
195 	if (engine == NULL) {
196 		return (NULL);
197 	}
198 	if (e == NULL) {
199 		return (NULL);
200 	}
201 	if (strcmp(engine, ENGINE_get_id(e)) == 0) {
202 		return (e);
203 	}
204 	return (NULL);
205 }
206 #endif /* if !defined(OPENSSL_NO_ENGINE) */
207 
208 /*! \file */
209