ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/tls_gnutls.c
Revision: 9591
Committed: Sat Aug 29 14:56:06 2020 UTC (4 years, 11 months ago) by michael
Content type: text/x-csrc
File size: 9983 byte(s)
Log Message:
- tls_gnutls.c:tls_set_ciphers(): fixed broken string conversion specifier

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 *
4 * Copyright (c) 2015 Attila Molnar <attilamolnar@hush.com>
5 * Copyright (c) 2015 Adam <Adam@anope.org>
6 * Copyright (c) 2015-2020 ircd-hybrid development team
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
21 * USA
22 */
23
24 /*! \file tls_gnutls.c
25 * \brief Includes all GnuTLS-specific TLS functions
26 * \version $Id$
27 */
28
29 #include "stdinc.h"
30 #include "tls.h"
31 #include "conf.h"
32 #include "log.h"
33 #include "misc.h"
34 #include "memory.h"
35
36 #ifdef HAVE_TLS_GNUTLS
37 #if GNUTLS_VERSION_NUMBER < 0x030605
38 #error "GnuTLS 3.6.5 and above is required to build this module"
39 #endif
40
41 static bool TLS_initialized;
42 static const char tls_default_priority_string[] =
43 "NORMAL:"
44 "%SERVER_PRECEDENCE:"
45 "!VERS-TLS1.1:"
46 "!VERS-TLS1.0:"
47 "!VERS-SSL3.0";
48
49
50 bool
51 tls_is_initialized(void)
52 {
53 return TLS_initialized;
54 }
55
56 void
57 tls_init(void)
58 {
59 }
60
61 static void
62 tls_free_credentials(tls_context_t cred)
63 {
64 gnutls_priority_deinit(cred->priorities);
65 gnutls_dh_params_deinit(cred->dh_params);
66 gnutls_certificate_free_credentials(cred->x509_cred);
67
68 gnutls_global_deinit();
69
70 xfree(cred);
71 }
72
73 bool
74 tls_new_credentials(void)
75 {
76 struct gnutls_context *context;
77
78 TLS_initialized = false;
79
80 if (ConfigServerInfo.tls_certificate_file == NULL || ConfigServerInfo.rsa_private_key_file == NULL)
81 return true;
82
83 context = xcalloc(sizeof(*context));
84
85 int ret = gnutls_global_init();
86 if (ret != GNUTLS_E_SUCCESS)
87 {
88 ilog(LOG_TYPE_IRCD, "ERROR: Could not initialize GnuTLS library -- %s", gnutls_strerror(ret));
89 xfree(context);
90 return false;
91 }
92
93 ret = gnutls_certificate_allocate_credentials(&context->x509_cred);
94 if (ret != GNUTLS_E_SUCCESS)
95 {
96 ilog(LOG_TYPE_IRCD, "ERROR: Could not initialize the TLS credentials -- %s", gnutls_strerror(ret));
97 xfree(context);
98 return false;
99 }
100
101 /* TBD: set ciphers based on serverinfo::tls_cipher_list */
102
103 gnutls_priority_init(&context->priorities, tls_default_priority_string, NULL);
104
105 ret = gnutls_certificate_set_x509_key_file(context->x509_cred, ConfigServerInfo.tls_certificate_file, ConfigServerInfo.rsa_private_key_file, GNUTLS_X509_FMT_PEM);
106 if (ret != GNUTLS_E_SUCCESS)
107 {
108 ilog(LOG_TYPE_IRCD, "Could not set TLS keys -- %s", gnutls_strerror(ret));
109
110 gnutls_certificate_free_credentials(context->x509_cred);
111 gnutls_priority_deinit(context->priorities);
112 xfree(context);
113 return false;
114 }
115
116 ret = gnutls_dh_params_init(&context->dh_params);
117 if (ret != GNUTLS_E_SUCCESS)
118 {
119 ilog(LOG_TYPE_IRCD, "ERROR: Could not initialize the DH parameters -- %s", gnutls_strerror(ret));
120 xfree(context);
121 return false;
122 }
123
124 if (ConfigServerInfo.tls_dh_param_file)
125 {
126 gnutls_datum_t data;
127
128 ret = gnutls_load_file(ConfigServerInfo.tls_dh_param_file, &data);
129 if (ret != GNUTLS_E_SUCCESS)
130 ilog(LOG_TYPE_IRCD, "Ignoring serverinfo::tls_dh_param_file -- unable to load file -- %s", gnutls_strerror(ret));
131 else
132 {
133 ret = gnutls_dh_params_import_pkcs3(context->dh_params, &data, GNUTLS_X509_FMT_PEM);
134 if (ret != GNUTLS_E_SUCCESS)
135 ilog(LOG_TYPE_IRCD, "Ignoring serverinfo::tls_dh_param_file -- unable to import dh params -- %s", gnutls_strerror(ret));
136 else
137 /* TBR once 3.6 is our minimum supported version */
138 gnutls_certificate_set_dh_params(context->x509_cred, context->dh_params);
139
140 gnutls_free(data.data);
141 }
142 }
143
144 if (ConfigServerInfo.tls_message_digest_algorithm == NULL)
145 ConfigServerInfo.message_digest_algorithm = GNUTLS_DIG_SHA256;
146 else
147 {
148 ConfigServerInfo.message_digest_algorithm = gnutls_digest_get_id(ConfigServerInfo.tls_message_digest_algorithm);
149
150 if (ConfigServerInfo.message_digest_algorithm == GNUTLS_DIG_UNKNOWN)
151 {
152 ConfigServerInfo.message_digest_algorithm = GNUTLS_DIG_SHA256;
153 ilog(LOG_TYPE_IRCD, "Ignoring serverinfo::tls_message_digest_algorithm -- unknown message digest algorithm");
154 }
155 }
156
157 if (ConfigServerInfo.tls_ctx && --ConfigServerInfo.tls_ctx->refs == 0)
158 tls_free_credentials(ConfigServerInfo.tls_ctx);
159
160 ConfigServerInfo.tls_ctx = context;
161 ++context->refs;
162
163 TLS_initialized = true;
164 return true;
165 }
166
167 const char *
168 tls_get_cipher(const tls_data_t *tls_data)
169 {
170 static char buf[128];
171
172 snprintf(buf, sizeof(buf), "%s-%s-%s-%s",
173 gnutls_protocol_get_name(gnutls_protocol_get_version(tls_data->session)),
174 gnutls_kx_get_name(gnutls_kx_get(tls_data->session)),
175 gnutls_cipher_get_name(gnutls_cipher_get(tls_data->session)),
176 gnutls_mac_get_name(gnutls_mac_get(tls_data->session)));
177 return buf;
178 }
179
180 const char *
181 tls_get_version(void)
182 {
183 static char buf[256];
184
185 snprintf(buf, sizeof(buf), "GnuTLS version: library: %s, header: %s",
186 gnutls_check_version(NULL), GNUTLS_VERSION);
187 return buf;
188 }
189
190 bool
191 tls_isusing(tls_data_t *tls_data)
192 {
193 return tls_data->session != NULL;
194 }
195
196 void
197 tls_free(tls_data_t *tls_data)
198 {
199 gnutls_deinit(tls_data->session);
200 }
201
202 ssize_t
203 tls_read(tls_data_t *tls_data, char *buf, size_t bufsize, bool *want_write)
204 {
205 ssize_t ret = gnutls_record_recv(tls_data->session, buf, bufsize);
206
207 if (ret <= 0)
208 {
209 switch (ret)
210 {
211 case GNUTLS_E_AGAIN:
212 case GNUTLS_E_INTERRUPTED:
213 errno = EWOULDBLOCK;
214 return -1;
215 case 0: /* Closed */
216 default: /* Other error */
217 /* XXX can gnutls_strerror(ret) if <0 for gnutls's idea of the reason */
218 return 0;
219 }
220 }
221
222 return ret;
223 }
224
225 ssize_t
226 tls_write(tls_data_t *tls_data, const char *buf, size_t bufsize, bool *want_read)
227 {
228 ssize_t ret = gnutls_record_send(tls_data->session, buf, bufsize);
229
230 if (ret <= 0)
231 {
232 switch (ret)
233 {
234 case GNUTLS_E_AGAIN:
235 case GNUTLS_E_INTERRUPTED:
236 case 0:
237 errno = EWOULDBLOCK;
238 return -1;
239 default:
240 return 0;
241 }
242 }
243
244 return ret;
245 }
246
247 void
248 tls_shutdown(tls_data_t *tls_data)
249 {
250 gnutls_bye(tls_data->session, GNUTLS_SHUT_WR);
251
252 if (--tls_data->context->refs == 0)
253 tls_free_credentials(tls_data->context);
254 }
255
256 bool
257 tls_new(tls_data_t *tls_data, int fd, tls_role_t role)
258 {
259 if (TLS_initialized == false)
260 return false;
261
262 gnutls_init(&tls_data->session, role == TLS_ROLE_SERVER ? GNUTLS_SERVER : GNUTLS_CLIENT);
263
264 tls_data->context = ConfigServerInfo.tls_ctx;
265 ++tls_data->context->refs;
266
267 gnutls_priority_set(tls_data->session, tls_data->context->priorities);
268 gnutls_credentials_set(tls_data->session, GNUTLS_CRD_CERTIFICATE, tls_data->context->x509_cred);
269 gnutls_dh_set_prime_bits(tls_data->session, 1024);
270 gnutls_transport_set_int(tls_data->session, fd);
271
272 if (role == TLS_ROLE_SERVER)
273 /* Request client certificate if any. */
274 gnutls_certificate_server_set_request(tls_data->session, GNUTLS_CERT_REQUEST);
275
276 return true;
277 }
278
279 bool
280 tls_set_ciphers(tls_data_t *tls_data, const char *cipher_list)
281 {
282 const char *prioerror;
283
284 gnutls_priority_deinit(tls_data->context->priorities);
285
286 int ret = gnutls_priority_init(&tls_data->context->priorities, cipher_list, &prioerror);
287 if (ret != GNUTLS_E_SUCCESS)
288 {
289 /* GnuTLS did not understand the user supplied string, log and fall back to the default priorities */
290 ilog(LOG_TYPE_IRCD, "Failed to set GnuTLS priorities to \"%s\": %s Syntax error at position %u, falling back to default %s",
291 cipher_list, gnutls_strerror(ret), (unsigned int)(prioerror - cipher_list), tls_default_priority_string);
292 gnutls_priority_init(&tls_data->context->priorities, tls_default_priority_string, NULL);
293 return false;
294 }
295
296 return true;
297 }
298
299 tls_handshake_status_t
300 tls_handshake(tls_data_t *tls_data, tls_role_t role, const char **errstr)
301 {
302 int ret = gnutls_handshake(tls_data->session);
303
304 if (ret >= 0)
305 return TLS_HANDSHAKE_DONE;
306
307 if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
308 {
309 /* Handshake needs resuming later, read() or write() would have blocked. */
310
311 if (gnutls_record_get_direction(tls_data->session) == 0)
312 /* gnutls_handshake() wants to read() again. */
313 return TLS_HANDSHAKE_WANT_READ;
314 else
315 /* gnutls_handshake() wants to write() again. */
316 return TLS_HANDSHAKE_WANT_WRITE;
317 }
318 else
319 {
320 const char *error = gnutls_strerror(ret);
321
322 if (errstr)
323 *errstr = error;
324
325 return TLS_HANDSHAKE_ERROR;
326 }
327 }
328
329 bool
330 tls_verify_certificate(tls_data_t *tls_data, tls_md_t digest, char **fingerprint)
331 {
332 gnutls_x509_crt_t cert;
333 unsigned char digestbuf[TLS_GNUTLS_MAX_HASH_SIZE];
334 size_t digest_size = sizeof(digestbuf);
335 char buf[TLS_GNUTLS_MAX_HASH_SIZE * 2 + 1];
336
337 const gnutls_datum_t *cert_list = gnutls_certificate_get_peers(tls_data->session, NULL);
338 if (cert_list == NULL)
339 return true; /* No certificate */
340
341 int ret = gnutls_x509_crt_init(&cert);
342 if (ret != GNUTLS_E_SUCCESS)
343 return true;
344
345 ret = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
346 if (ret != GNUTLS_E_SUCCESS)
347 goto info_done_dealloc;
348
349 ret = gnutls_x509_crt_get_fingerprint(cert, digest, digestbuf, &digest_size);
350 if (ret != GNUTLS_E_SUCCESS)
351 goto info_done_dealloc;
352
353 binary_to_hex(digestbuf, buf, digest_size);
354 *fingerprint = xstrdup(buf);
355
356 gnutls_x509_crt_deinit(cert);
357 return true;
358
359 info_done_dealloc:
360 gnutls_x509_crt_deinit(cert);
361 return false;
362 }
363 #endif /* HAVE_TLS_GNUTLS */

Properties

Name Value
svn:eol-style native
svn:keywords Id