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

# 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 9101 * Copyright (c) 2015-2020 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 michael 9161 #if GNUTLS_VERSION_NUMBER < 0x030605
38     #error "GnuTLS 3.6.5 and above is required to build this module"
39     #endif
40 michael 7105
41 michael 8664 static bool TLS_initialized;
42 michael 8959 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 michael 7271
49 michael 8959
50 michael 8664 bool
51 michael 7271 tls_is_initialized(void)
52     {
53     return TLS_initialized;
54     }
55    
56 michael 7105 void
57     tls_init(void)
58     {
59     }
60    
61     static void
62 michael 9224 tls_free_credentials(tls_context_t cred)
63 michael 7105 {
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 michael 8664 bool
74 michael 9224 tls_new_credentials(void)
75 michael 7105 {
76     struct gnutls_context *context;
77    
78 michael 8664 TLS_initialized = false;
79 michael 7271
80 michael 9202 if (ConfigServerInfo.tls_certificate_file == NULL || ConfigServerInfo.rsa_private_key_file == NULL)
81 michael 8664 return true;
82 michael 7105
83     context = xcalloc(sizeof(*context));
84    
85 michael 9215 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 michael 7105
93 michael 9215 ret = gnutls_certificate_allocate_credentials(&context->x509_cred);
94 michael 7134 if (ret != GNUTLS_E_SUCCESS)
95 michael 7105 {
96 michael 7134 ilog(LOG_TYPE_IRCD, "ERROR: Could not initialize the TLS credentials -- %s", gnutls_strerror(ret));
97 michael 7105 xfree(context);
98 michael 8664 return false;
99 michael 7105 }
100    
101 michael 9145 /* TBD: set ciphers based on serverinfo::tls_cipher_list */
102 michael 7105
103 michael 8959 gnutls_priority_init(&context->priorities, tls_default_priority_string, NULL);
104 michael 7193
105 michael 9145 ret = gnutls_certificate_set_x509_key_file(context->x509_cred, ConfigServerInfo.tls_certificate_file, ConfigServerInfo.rsa_private_key_file, GNUTLS_X509_FMT_PEM);
106 michael 7134 if (ret != GNUTLS_E_SUCCESS)
107 michael 7105 {
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 michael 8664 return false;
114 michael 7105 }
115    
116 michael 9056 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 michael 7105
124 michael 9145 if (ConfigServerInfo.tls_dh_param_file)
125 michael 7105 {
126     gnutls_datum_t data;
127    
128 michael 9145 ret = gnutls_load_file(ConfigServerInfo.tls_dh_param_file, &data);
129 michael 7105 if (ret != GNUTLS_E_SUCCESS)
130 michael 9145 ilog(LOG_TYPE_IRCD, "Ignoring serverinfo::tls_dh_param_file -- unable to load file -- %s", gnutls_strerror(ret));
131 michael 7105 else
132     {
133     ret = gnutls_dh_params_import_pkcs3(context->dh_params, &data, GNUTLS_X509_FMT_PEM);
134     if (ret != GNUTLS_E_SUCCESS)
135 michael 9145 ilog(LOG_TYPE_IRCD, "Ignoring serverinfo::tls_dh_param_file -- unable to import dh params -- %s", gnutls_strerror(ret));
136 michael 9066 else
137     /* TBR once 3.6 is our minimum supported version */
138     gnutls_certificate_set_dh_params(context->x509_cred, context->dh_params);
139 michael 7105
140     gnutls_free(data.data);
141     }
142     }
143    
144 michael 9145 if (ConfigServerInfo.tls_message_digest_algorithm == NULL)
145 michael 7105 ConfigServerInfo.message_digest_algorithm = GNUTLS_DIG_SHA256;
146     else
147     {
148 michael 9145 ConfigServerInfo.message_digest_algorithm = gnutls_digest_get_id(ConfigServerInfo.tls_message_digest_algorithm);
149 michael 7105
150     if (ConfigServerInfo.message_digest_algorithm == GNUTLS_DIG_UNKNOWN)
151     {
152     ConfigServerInfo.message_digest_algorithm = GNUTLS_DIG_SHA256;
153 michael 9145 ilog(LOG_TYPE_IRCD, "Ignoring serverinfo::tls_message_digest_algorithm -- unknown message digest algorithm");
154 michael 7105 }
155     }
156    
157     if (ConfigServerInfo.tls_ctx && --ConfigServerInfo.tls_ctx->refs == 0)
158 michael 9224 tls_free_credentials(ConfigServerInfo.tls_ctx);
159 michael 7105
160     ConfigServerInfo.tls_ctx = context;
161     ++context->refs;
162    
163 michael 8664 TLS_initialized = true;
164     return true;
165 michael 7105 }
166    
167     const char *
168     tls_get_cipher(const tls_data_t *tls_data)
169     {
170 michael 9165 static char buf[128];
171 michael 7105
172 michael 9165 snprintf(buf, sizeof(buf), "%s-%s-%s-%s",
173 michael 7120 gnutls_protocol_get_name(gnutls_protocol_get_version(tls_data->session)),
174 michael 7105 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 michael 9165 return buf;
178 michael 7105 }
179    
180 michael 7708 const char *
181     tls_get_version(void)
182     {
183 michael 9165 static char buf[256];
184 michael 7708
185     snprintf(buf, sizeof(buf), "GnuTLS version: library: %s, header: %s",
186     gnutls_check_version(NULL), GNUTLS_VERSION);
187     return buf;
188     }
189    
190 michael 8664 bool
191 michael 7105 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 michael 8956 ssize_t
203 michael 8660 tls_read(tls_data_t *tls_data, char *buf, size_t bufsize, bool *want_write)
204 michael 7105 {
205 michael 9529 ssize_t ret = gnutls_record_recv(tls_data->session, buf, bufsize);
206 michael 7105
207 michael 9529 if (ret <= 0)
208 michael 7105 {
209 michael 9529 switch (ret)
210 michael 7105 {
211     case GNUTLS_E_AGAIN:
212     case GNUTLS_E_INTERRUPTED:
213     errno = EWOULDBLOCK;
214 michael 7146 return -1;
215 michael 7105 case 0: /* Closed */
216     default: /* Other error */
217 michael 9529 /* XXX can gnutls_strerror(ret) if <0 for gnutls's idea of the reason */
218 michael 7146 return 0;
219 michael 7105 }
220     }
221    
222 michael 9529 return ret;
223 michael 7105 }
224    
225 michael 8956 ssize_t
226 michael 8660 tls_write(tls_data_t *tls_data, const char *buf, size_t bufsize, bool *want_read)
227 michael 7105 {
228 michael 9529 ssize_t ret = gnutls_record_send(tls_data->session, buf, bufsize);
229 michael 7105
230 michael 9529 if (ret <= 0)
231 michael 7105 {
232 michael 9529 switch (ret)
233 michael 7105 {
234     case GNUTLS_E_AGAIN:
235     case GNUTLS_E_INTERRUPTED:
236     case 0:
237     errno = EWOULDBLOCK;
238 michael 7146 return -1;
239 michael 7105 default:
240 michael 7146 return 0;
241 michael 7105 }
242     }
243    
244 michael 9529 return ret;
245 michael 7105 }
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 michael 9224 tls_free_credentials(tls_data->context);
254 michael 7105 }
255    
256 michael 8664 bool
257 michael 7105 tls_new(tls_data_t *tls_data, int fd, tls_role_t role)
258     {
259 michael 8664 if (TLS_initialized == false)
260     return false;
261 michael 7274
262 michael 7105 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 michael 8664 return true;
277 michael 7105 }
278    
279 michael 8664 bool
280 michael 7105 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 michael 9202 int ret = gnutls_priority_init(&tls_data->context->priorities, cipher_list, &prioerror);
287 michael 7134 if (ret != GNUTLS_E_SUCCESS)
288 michael 7105 {
289     /* GnuTLS did not understand the user supplied string, log and fall back to the default priorities */
290 michael 9591 ilog(LOG_TYPE_IRCD, "Failed to set GnuTLS priorities to \"%s\": %s Syntax error at position %u, falling back to default %s",
291 michael 8959 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 michael 8664 return false;
294 michael 7105 }
295    
296 michael 8664 return true;
297 michael 7105 }
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 michael 8664 bool
330 michael 9224 tls_verify_certificate(tls_data_t *tls_data, tls_md_t digest, char **fingerprint)
331 michael 7105 {
332     gnutls_x509_crt_t cert;
333 michael 7134 unsigned char digestbuf[TLS_GNUTLS_MAX_HASH_SIZE];
334 michael 7105 size_t digest_size = sizeof(digestbuf);
335 michael 7134 char buf[TLS_GNUTLS_MAX_HASH_SIZE * 2 + 1];
336 michael 7105
337 michael 9202 const gnutls_datum_t *cert_list = gnutls_certificate_get_peers(tls_data->session, NULL);
338 michael 8961 if (cert_list == NULL)
339 michael 8664 return true; /* No certificate */
340 michael 7105
341 michael 9202 int ret = gnutls_x509_crt_init(&cert);
342 michael 7134 if (ret != GNUTLS_E_SUCCESS)
343 michael 8664 return true;
344 michael 7105
345     ret = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
346 michael 7134 if (ret != GNUTLS_E_SUCCESS)
347 michael 7105 goto info_done_dealloc;
348    
349     ret = gnutls_x509_crt_get_fingerprint(cert, digest, digestbuf, &digest_size);
350 michael 7134 if (ret != GNUTLS_E_SUCCESS)
351 michael 7105 goto info_done_dealloc;
352    
353     binary_to_hex(digestbuf, buf, digest_size);
354     *fingerprint = xstrdup(buf);
355    
356 michael 7145 gnutls_x509_crt_deinit(cert);
357 michael 8664 return true;
358 michael 7105
359     info_done_dealloc:
360     gnutls_x509_crt_deinit(cert);
361 michael 8664 return false;
362 michael 7105 }
363     #endif /* HAVE_TLS_GNUTLS */

Properties

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