ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/tls_gnutls.c
Revision: 8959
Committed: Sat May 11 08:22:46 2019 UTC (6 years, 3 months ago) by michael
Content type: text/x-csrc
File size: 9538 byte(s)
Log Message:
- tls_gnutls.c: avoid maintaining multiple priority strings

File Contents

# User Rev Content
1 michael 7105 /*
2     * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3     *
4 michael 7148 * Copyright (c) 2015 Attila Molnar <attilamolnar@hush.com>
5     * Copyright (c) 2015 Adam <Adam@anope.org>
6 michael 8752 * Copyright (c) 2015-2019 ircd-hybrid development team
7 michael 7105 *
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 michael 7152 #include "misc.h"
34 michael 7105 #include "memory.h"
35    
36     #ifdef HAVE_TLS_GNUTLS
37    
38 michael 8664 static bool TLS_initialized;
39 michael 8959 static const char tls_default_priority_string[] =
40     "NORMAL:"
41     "%SERVER_PRECEDENCE:"
42     "!VERS-TLS1.1:"
43     "!VERS-TLS1.0:"
44     "!VERS-SSL3.0";
45 michael 7271
46 michael 8959
47 michael 8664 bool
48 michael 7271 tls_is_initialized(void)
49     {
50     return TLS_initialized;
51     }
52    
53 michael 7105 void
54     tls_init(void)
55     {
56     }
57    
58     static void
59     tls_free_cred(tls_context_t cred)
60     {
61     gnutls_priority_deinit(cred->priorities);
62     gnutls_dh_params_deinit(cred->dh_params);
63     gnutls_certificate_free_credentials(cred->x509_cred);
64    
65     gnutls_global_deinit();
66    
67     xfree(cred);
68     }
69    
70 michael 8664 bool
71 michael 7105 tls_new_cred(void)
72     {
73     int ret;
74     struct gnutls_context *context;
75    
76 michael 8664 TLS_initialized = false;
77 michael 7271
78 michael 7105 if (!ConfigServerInfo.ssl_certificate_file || !ConfigServerInfo.rsa_private_key_file)
79 michael 8664 return true;
80 michael 7105
81     context = xcalloc(sizeof(*context));
82    
83     gnutls_global_init();
84    
85     ret = gnutls_certificate_allocate_credentials(&context->x509_cred);
86 michael 7134 if (ret != GNUTLS_E_SUCCESS)
87 michael 7105 {
88 michael 7134 ilog(LOG_TYPE_IRCD, "ERROR: Could not initialize the TLS credentials -- %s", gnutls_strerror(ret));
89 michael 7105 xfree(context);
90 michael 8664 return false;
91 michael 7105 }
92    
93 michael 7193 /* TBD: set ciphers based on serverinfo::ssl_cipher_list */
94 michael 7105
95 michael 8959 gnutls_priority_init(&context->priorities, tls_default_priority_string, NULL);
96 michael 7193
97 michael 7105 ret = gnutls_certificate_set_x509_key_file(context->x509_cred, ConfigServerInfo.ssl_certificate_file, ConfigServerInfo.rsa_private_key_file, GNUTLS_X509_FMT_PEM);
98 michael 7134 if (ret != GNUTLS_E_SUCCESS)
99 michael 7105 {
100     ilog(LOG_TYPE_IRCD, "Could not set TLS keys -- %s", gnutls_strerror(ret));
101    
102     gnutls_certificate_free_credentials(context->x509_cred);
103     gnutls_priority_deinit(context->priorities);
104     xfree(context);
105 michael 8664 return false;
106 michael 7105 }
107    
108     gnutls_dh_params_init(&context->dh_params);
109    
110     if (ConfigServerInfo.ssl_dh_param_file)
111     {
112     gnutls_datum_t data;
113    
114     ret = gnutls_load_file(ConfigServerInfo.ssl_dh_param_file, &data);
115    
116     if (ret != GNUTLS_E_SUCCESS)
117     ilog(LOG_TYPE_IRCD, "Ignoring serverinfo::ssl_dh_param_file -- unable to load file -- %s", gnutls_strerror(ret));
118     else
119     {
120     ret = gnutls_dh_params_import_pkcs3(context->dh_params, &data, GNUTLS_X509_FMT_PEM);
121    
122     if (ret != GNUTLS_E_SUCCESS)
123     ilog(LOG_TYPE_IRCD, "Ignoring serverinfo::ssl_dh_param_file -- unable to import dh params -- %s", gnutls_strerror(ret));
124    
125     gnutls_free(data.data);
126     }
127     }
128    
129     gnutls_certificate_set_dh_params(context->x509_cred, context->dh_params);
130    
131     if (ConfigServerInfo.ssl_message_digest_algorithm == NULL)
132     ConfigServerInfo.message_digest_algorithm = GNUTLS_DIG_SHA256;
133     else
134     {
135     ConfigServerInfo.message_digest_algorithm = gnutls_digest_get_id(ConfigServerInfo.ssl_message_digest_algorithm);
136    
137     if (ConfigServerInfo.message_digest_algorithm == GNUTLS_DIG_UNKNOWN)
138     {
139     ConfigServerInfo.message_digest_algorithm = GNUTLS_DIG_SHA256;
140     ilog(LOG_TYPE_IRCD, "Ignoring serverinfo::ssl_message_digest_algorithm -- unknown message digest algorithm");
141     }
142     }
143    
144     if (ConfigServerInfo.tls_ctx && --ConfigServerInfo.tls_ctx->refs == 0)
145     tls_free_cred(ConfigServerInfo.tls_ctx);
146    
147     ConfigServerInfo.tls_ctx = context;
148     ++context->refs;
149    
150 michael 8664 TLS_initialized = true;
151     return true;
152 michael 7105 }
153    
154     const char *
155     tls_get_cipher(const tls_data_t *tls_data)
156     {
157     static char buffer[IRCD_BUFSIZE];
158    
159 michael 7120 snprintf(buffer, sizeof(buffer), "%s-%s-%s-%s",
160     gnutls_protocol_get_name(gnutls_protocol_get_version(tls_data->session)),
161 michael 7105 gnutls_kx_get_name(gnutls_kx_get(tls_data->session)),
162     gnutls_cipher_get_name(gnutls_cipher_get(tls_data->session)),
163     gnutls_mac_get_name(gnutls_mac_get(tls_data->session)));
164    
165     return buffer;
166     }
167    
168 michael 7708 const char *
169     tls_get_version(void)
170     {
171     static char buf[IRCD_BUFSIZE];
172    
173     snprintf(buf, sizeof(buf), "GnuTLS version: library: %s, header: %s",
174     gnutls_check_version(NULL), GNUTLS_VERSION);
175     return buf;
176     }
177    
178 michael 8664 bool
179 michael 7105 tls_isusing(tls_data_t *tls_data)
180     {
181     return tls_data->session != NULL;
182     }
183    
184     void
185     tls_free(tls_data_t *tls_data)
186     {
187     gnutls_deinit(tls_data->session);
188     }
189    
190 michael 8956 ssize_t
191 michael 8660 tls_read(tls_data_t *tls_data, char *buf, size_t bufsize, bool *want_write)
192 michael 7105 {
193 michael 8956 ssize_t length = gnutls_record_recv(tls_data->session, buf, bufsize);
194 michael 7105
195     if (length <= 0)
196     {
197     switch (length)
198     {
199     case GNUTLS_E_AGAIN:
200     case GNUTLS_E_INTERRUPTED:
201     errno = EWOULDBLOCK;
202 michael 7146 return -1;
203 michael 7105 case 0: /* Closed */
204     default: /* Other error */
205 michael 7146 /* XXX can gnutls_strerror(length) if <0 for gnutls's idea of the reason */
206     return 0;
207 michael 7105 }
208     }
209    
210     return length;
211     }
212    
213 michael 8956 ssize_t
214 michael 8660 tls_write(tls_data_t *tls_data, const char *buf, size_t bufsize, bool *want_read)
215 michael 7105 {
216 michael 8956 ssize_t length = gnutls_record_send(tls_data->session, buf, bufsize);
217 michael 7105
218     if (length <= 0)
219     {
220     switch (length)
221     {
222     case GNUTLS_E_AGAIN:
223     case GNUTLS_E_INTERRUPTED:
224     case 0:
225     errno = EWOULDBLOCK;
226 michael 7146 return -1;
227 michael 7105 default:
228 michael 7146 return 0;
229 michael 7105 }
230     }
231    
232     return length;
233     }
234    
235     void
236     tls_shutdown(tls_data_t *tls_data)
237     {
238     gnutls_bye(tls_data->session, GNUTLS_SHUT_WR);
239    
240     if (--tls_data->context->refs == 0)
241     tls_free_cred(tls_data->context);
242     }
243    
244 michael 8664 bool
245 michael 7105 tls_new(tls_data_t *tls_data, int fd, tls_role_t role)
246     {
247 michael 8664 if (TLS_initialized == false)
248     return false;
249 michael 7274
250 michael 7105 gnutls_init(&tls_data->session, role == TLS_ROLE_SERVER ? GNUTLS_SERVER : GNUTLS_CLIENT);
251    
252     tls_data->context = ConfigServerInfo.tls_ctx;
253     ++tls_data->context->refs;
254    
255     gnutls_priority_set(tls_data->session, tls_data->context->priorities);
256     gnutls_credentials_set(tls_data->session, GNUTLS_CRD_CERTIFICATE, tls_data->context->x509_cred);
257     gnutls_dh_set_prime_bits(tls_data->session, 1024);
258     gnutls_transport_set_int(tls_data->session, fd);
259    
260     if (role == TLS_ROLE_SERVER)
261     /* Request client certificate if any. */
262     gnutls_certificate_server_set_request(tls_data->session, GNUTLS_CERT_REQUEST);
263    
264 michael 8664 return true;
265 michael 7105 }
266    
267 michael 8664 bool
268 michael 7105 tls_set_ciphers(tls_data_t *tls_data, const char *cipher_list)
269     {
270     int ret;
271     const char *prioerror;
272    
273     gnutls_priority_deinit(tls_data->context->priorities);
274    
275     ret = gnutls_priority_init(&tls_data->context->priorities, cipher_list, &prioerror);
276 michael 7134 if (ret != GNUTLS_E_SUCCESS)
277 michael 7105 {
278     /* GnuTLS did not understand the user supplied string, log and fall back to the default priorities */
279 michael 8959 ilog(LOG_TYPE_IRCD, "Failed to set GnuTLS priorities to \"%s\": %s Syntax error at position %u, falling back to default %",
280     cipher_list, gnutls_strerror(ret), (unsigned int)(prioerror - cipher_list), tls_default_priority_string);
281     gnutls_priority_init(&tls_data->context->priorities, tls_default_priority_string, NULL);
282 michael 8664 return false;
283 michael 7105 }
284    
285 michael 8664 return true;
286 michael 7105 }
287    
288     tls_handshake_status_t
289     tls_handshake(tls_data_t *tls_data, tls_role_t role, const char **errstr)
290     {
291     int ret = gnutls_handshake(tls_data->session);
292    
293     if (ret >= 0)
294     return TLS_HANDSHAKE_DONE;
295    
296     if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED)
297     {
298     /* Handshake needs resuming later, read() or write() would have blocked. */
299    
300     if (gnutls_record_get_direction(tls_data->session) == 0)
301     {
302     /* gnutls_handshake() wants to read() again. */
303     return TLS_HANDSHAKE_WANT_READ;
304     }
305     else
306     {
307     /* gnutls_handshake() wants to write() again. */
308     return TLS_HANDSHAKE_WANT_WRITE;
309     }
310     }
311     else
312     {
313     const char *error = gnutls_strerror(ret);
314    
315     if (errstr)
316     *errstr = error;
317    
318     return TLS_HANDSHAKE_ERROR;
319     }
320     }
321    
322 michael 8664 bool
323 michael 7142 tls_verify_cert(tls_data_t *tls_data, tls_md_t digest, char **fingerprint)
324 michael 7105 {
325     int ret;
326     gnutls_x509_crt_t cert;
327     const gnutls_datum_t *cert_list;
328     unsigned int cert_list_size = 0;
329 michael 7134 unsigned char digestbuf[TLS_GNUTLS_MAX_HASH_SIZE];
330 michael 7105 size_t digest_size = sizeof(digestbuf);
331 michael 7134 char buf[TLS_GNUTLS_MAX_HASH_SIZE * 2 + 1];
332 michael 7105
333     cert_list = gnutls_certificate_get_peers(tls_data->session, &cert_list_size);
334     if (!cert_list)
335 michael 8664 return true; /* No certificate */
336 michael 7105
337     ret = gnutls_x509_crt_init(&cert);
338 michael 7134 if (ret != GNUTLS_E_SUCCESS)
339 michael 8664 return true;
340 michael 7105
341     ret = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
342 michael 7134 if (ret != GNUTLS_E_SUCCESS)
343 michael 7105 goto info_done_dealloc;
344    
345     ret = gnutls_x509_crt_get_fingerprint(cert, digest, digestbuf, &digest_size);
346 michael 7134 if (ret != GNUTLS_E_SUCCESS)
347 michael 7105 goto info_done_dealloc;
348    
349     binary_to_hex(digestbuf, buf, digest_size);
350     *fingerprint = xstrdup(buf);
351    
352 michael 7145 gnutls_x509_crt_deinit(cert);
353 michael 8664 return true;
354 michael 7105
355     info_done_dealloc:
356     gnutls_x509_crt_deinit(cert);
357 michael 8664 return false;
358 michael 7105 }
359     #endif /* HAVE_TLS_GNUTLS */

Properties

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