ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/branches/8.2.x/src/tls_gnutls.c
Revision: 9102
Committed: Wed Jan 1 09:58:57 2020 UTC (6 years, 6 months ago) by michael
Content type: text/x-csrc
File size: 9760 byte(s)
Log Message:
- Bump copyright years everywhere

File Contents

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

Properties

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