This source file includes following definitions.
- get_gnutls_priorities
- tls_cred_str
- tls_load_x509_data
- verify_peer_cert
- _gnutls_log_func
- pcmk__free_tls
- pcmk__init_tls
- pcmk__init_tls_dh
- pcmk__new_tls_session
- pcmk__tls_get_client_sock
- pcmk__read_handshake_data
- pcmk__tls_add_psk_key
- pcmk__tls_add_psk_callback
- pcmk__tls_check_cert_expiration
- pcmk__tls_client_try_handshake
- pcmk__tls_client_handshake
- pcmk__x509_enabled
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <errno.h>
13 #include <gnutls/gnutls.h>
14 #include <gnutls/x509.h>
15 #include <stdlib.h>
16
17 #include <glib.h>
18
19 #include <crm/common/tls_internal.h>
20
21 static char *
22 get_gnutls_priorities(gnutls_credentials_type_t cred_type)
23 {
24 const char *prio_base = pcmk__env_option(PCMK__ENV_TLS_PRIORITIES);
25
26 if (prio_base == NULL) {
27 prio_base = PCMK__GNUTLS_PRIORITIES;
28 }
29
30 if (cred_type == GNUTLS_CRD_ANON) {
31 return crm_strdup_printf("%s:+ANON-DH", prio_base);
32 } else if (cred_type == GNUTLS_CRD_PSK) {
33 return crm_strdup_printf("%s:+DHE-PSK:+PSK", prio_base);
34 } else {
35 return strdup(prio_base);
36 }
37 }
38
39 static const char *
40 tls_cred_str(gnutls_credentials_type_t cred_type)
41 {
42 if (cred_type == GNUTLS_CRD_ANON) {
43 return "unauthenticated";
44 } else if (cred_type == GNUTLS_CRD_PSK) {
45 return "shared-key-authenticated";
46 } else if (cred_type == GNUTLS_CRD_CERTIFICATE) {
47 return "certificate-authenticated";
48 } else {
49 return "unknown";
50 }
51 }
52
53 static int
54 tls_load_x509_data(pcmk__tls_t *tls)
55 {
56 int rc;
57
58 CRM_CHECK(tls->cred_type == GNUTLS_CRD_CERTIFICATE, return EINVAL);
59
60
61
62
63
64
65
66 rc = gnutls_certificate_set_x509_trust_file(tls->credentials.cert,
67 tls->ca_file,
68 GNUTLS_X509_FMT_PEM);
69 if (rc <= 0) {
70 crm_err("Failed to set X509 CA file: %s", gnutls_strerror(rc));
71 return ENODATA;
72 }
73
74
75
76
77 if (tls->crl_file != NULL) {
78 rc = gnutls_certificate_set_x509_crl_file(tls->credentials.cert,
79 tls->crl_file,
80 GNUTLS_X509_FMT_PEM);
81 if (rc < 0) {
82 crm_err("Failed to set X509 CRL file: %s",
83 gnutls_strerror(rc));
84 return ENODATA;
85 }
86 }
87
88
89
90
91 rc = gnutls_certificate_set_x509_key_file2(tls->credentials.cert,
92 tls->cert_file, tls->key_file,
93 GNUTLS_X509_FMT_PEM, NULL,
94 GNUTLS_PKCS_PLAIN);
95 if (rc < 0) {
96 crm_err("Failed to set X509 cert/key pair: %s",
97 gnutls_strerror(rc));
98 return ENODATA;
99 }
100
101 return pcmk_rc_ok;
102 }
103
104
105
106
107
108
109
110
111 static int
112 verify_peer_cert(gnutls_session_t session)
113 {
114 int rc;
115 int type;
116 unsigned int status;
117 gnutls_datum_t out;
118
119
120 rc = gnutls_certificate_verify_peers3(session, NULL, &status);
121
122
123
124
125 if (rc != GNUTLS_E_SUCCESS) {
126 crm_err("Failed to verify peer certificate: %s", gnutls_strerror(rc));
127 return -1;
128 }
129
130 if (status == 0) {
131
132 return 0;
133 }
134
135 type = gnutls_certificate_type_get(session);
136 gnutls_certificate_verification_status_print(status, type, &out, 0);
137 crm_err("Peer certificate invalid: %s", out.data);
138 gnutls_free(out.data);
139 return GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR;
140 }
141
142 static void
143 _gnutls_log_func(int level, const char *msg)
144 {
145 crm_trace("%s", msg);
146 }
147
148 void
149 pcmk__free_tls(pcmk__tls_t *tls)
150 {
151 if (tls == NULL) {
152 return;
153 }
154
155
156 if (tls->server) {
157 gnutls_dh_params_deinit(tls->dh_params);
158 }
159
160 if (tls->cred_type == GNUTLS_CRD_ANON) {
161 if (tls->server) {
162 gnutls_anon_free_server_credentials(tls->credentials.anon_s);
163 } else {
164 gnutls_anon_free_client_credentials(tls->credentials.anon_c);
165 }
166 } else if (tls->cred_type == GNUTLS_CRD_CERTIFICATE) {
167 gnutls_certificate_free_credentials(tls->credentials.cert);
168 } else if (tls->cred_type == GNUTLS_CRD_PSK) {
169 if (tls->server) {
170 gnutls_psk_free_server_credentials(tls->credentials.psk_s);
171 } else {
172 gnutls_psk_free_client_credentials(tls->credentials.psk_c);
173 }
174 }
175
176 free(tls);
177 tls = NULL;
178
179 gnutls_global_deinit();
180 }
181
182 int
183 pcmk__init_tls(pcmk__tls_t **tls, bool server, gnutls_credentials_type_t cred_type)
184 {
185 int rc = pcmk_rc_ok;
186
187 if (*tls != NULL) {
188 return rc;
189 }
190
191 *tls = pcmk__assert_alloc(1, sizeof(pcmk__tls_t));
192
193 signal(SIGPIPE, SIG_IGN);
194
195
196
197
198
199
200
201
202 gnutls_global_init();
203 gnutls_global_set_log_level(8);
204 gnutls_global_set_log_function(_gnutls_log_func);
205
206 if (server) {
207 rc = pcmk__init_tls_dh(&(*tls)->dh_params);
208 if (rc != pcmk_rc_ok) {
209 pcmk__free_tls(*tls);
210 *tls = NULL;
211 return rc;
212 }
213 }
214
215 (*tls)->cred_type = cred_type;
216 (*tls)->server = server;
217
218 if (cred_type == GNUTLS_CRD_ANON) {
219 if (server) {
220 gnutls_anon_allocate_server_credentials(&(*tls)->credentials.anon_s);
221 gnutls_anon_set_server_dh_params((*tls)->credentials.anon_s,
222 (*tls)->dh_params);
223 } else {
224 gnutls_anon_allocate_client_credentials(&(*tls)->credentials.anon_c);
225 }
226 } else if (cred_type == GNUTLS_CRD_CERTIFICATE) {
227
228
229
230 (*tls)->ca_file = pcmk__env_option(PCMK__ENV_CA_FILE);
231 if (pcmk__str_empty((*tls)->ca_file)) {
232 (*tls)->ca_file = getenv("CIB_ca_file");
233 }
234
235 (*tls)->cert_file = pcmk__env_option(PCMK__ENV_CERT_FILE);
236 if (pcmk__str_empty((*tls)->cert_file)) {
237 (*tls)->cert_file = getenv("CIB_cert_file");
238 }
239
240 (*tls)->crl_file = pcmk__env_option(PCMK__ENV_CRL_FILE);
241 if (pcmk__str_empty((*tls)->crl_file)) {
242 (*tls)->crl_file = getenv("CIB_crl_file");
243 }
244
245 (*tls)->key_file = pcmk__env_option(PCMK__ENV_KEY_FILE);
246 if (pcmk__str_empty((*tls)->key_file)) {
247 (*tls)->key_file = getenv("CIB_key_file");
248 }
249
250 gnutls_certificate_allocate_credentials(&(*tls)->credentials.cert);
251
252 if (server) {
253 gnutls_certificate_set_dh_params((*tls)->credentials.cert,
254 (*tls)->dh_params);
255
256 }
257
258 rc = tls_load_x509_data(*tls);
259 if (rc != pcmk_rc_ok) {
260 pcmk__free_tls(*tls);
261 *tls = NULL;
262 return rc;
263 }
264 } else if (cred_type == GNUTLS_CRD_PSK) {
265 if (server) {
266 gnutls_psk_allocate_server_credentials(&(*tls)->credentials.psk_s);
267 gnutls_psk_set_server_dh_params((*tls)->credentials.psk_s,
268 (*tls)->dh_params);
269 } else {
270 gnutls_psk_allocate_client_credentials(&(*tls)->credentials.psk_c);
271 }
272 }
273
274 return rc;
275 }
276
277 int
278 pcmk__init_tls_dh(gnutls_dh_params_t *dh_params)
279 {
280 int rc = GNUTLS_E_SUCCESS;
281 unsigned int dh_bits = 0;
282 int dh_max_bits = 0;
283
284 rc = gnutls_dh_params_init(dh_params);
285 if (rc != GNUTLS_E_SUCCESS) {
286 goto error;
287 }
288
289 dh_bits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH,
290 GNUTLS_SEC_PARAM_NORMAL);
291 if (dh_bits == 0) {
292 rc = GNUTLS_E_DH_PRIME_UNACCEPTABLE;
293 goto error;
294 }
295
296 pcmk__scan_min_int(pcmk__env_option(PCMK__ENV_DH_MAX_BITS), &dh_max_bits, 0);
297 if ((dh_max_bits > 0) && (dh_bits > dh_max_bits)) {
298 dh_bits = dh_max_bits;
299 }
300
301 crm_info("Generating Diffie-Hellman parameters with %u-bit prime for TLS",
302 dh_bits);
303 rc = gnutls_dh_params_generate2(*dh_params, dh_bits);
304 if (rc != GNUTLS_E_SUCCESS) {
305 goto error;
306 }
307
308 return pcmk_rc_ok;
309
310 error:
311 crm_err("Could not initialize Diffie-Hellman parameters for TLS: %s "
312 QB_XS " rc=%d", gnutls_strerror(rc), rc);
313 return EPROTO;
314 }
315
316 gnutls_session_t
317 pcmk__new_tls_session(pcmk__tls_t *tls, int csock)
318 {
319 unsigned int conn_type = GNUTLS_CLIENT;
320 int rc = GNUTLS_E_SUCCESS;
321 char *prio = NULL;
322 gnutls_session_t session = NULL;
323
324 CRM_CHECK((tls != NULL) && (csock >= 0), return NULL);
325
326 if (tls->server) {
327 conn_type = GNUTLS_SERVER;
328 }
329
330 rc = gnutls_init(&session, conn_type);
331 if (rc != GNUTLS_E_SUCCESS) {
332 goto error;
333 }
334
335
336
337
338
339
340
341 prio = get_gnutls_priorities(tls->cred_type);
342
343
344
345
346
347 rc = gnutls_priority_set_direct(session, prio, NULL);
348 if (rc != GNUTLS_E_SUCCESS) {
349 goto error;
350 }
351
352 gnutls_transport_set_ptr(session,
353 (gnutls_transport_ptr_t) GINT_TO_POINTER(csock));
354
355
356 if (tls->cred_type == GNUTLS_CRD_ANON && tls->server) {
357 rc = gnutls_credentials_set(session, tls->cred_type, tls->credentials.anon_s);
358 } else if (tls->cred_type == GNUTLS_CRD_ANON) {
359 rc = gnutls_credentials_set(session, tls->cred_type, tls->credentials.anon_c);
360 } else if (tls->cred_type == GNUTLS_CRD_CERTIFICATE) {
361 rc = gnutls_credentials_set(session, tls->cred_type, tls->credentials.cert);
362 } else if (tls->cred_type == GNUTLS_CRD_PSK && tls->server) {
363 rc = gnutls_credentials_set(session, tls->cred_type, tls->credentials.psk_s);
364 } else if (tls->cred_type == GNUTLS_CRD_PSK) {
365 rc = gnutls_credentials_set(session, tls->cred_type, tls->credentials.psk_c);
366 } else {
367 crm_err("Unknown credential type: %d", tls->cred_type);
368 rc = EINVAL;
369 goto error;
370 }
371
372 if (rc != GNUTLS_E_SUCCESS) {
373 goto error;
374 }
375
376 free(prio);
377
378 if (tls->cred_type == GNUTLS_CRD_CERTIFICATE) {
379 if (conn_type == GNUTLS_SERVER) {
380
381 gnutls_certificate_server_set_request(session, GNUTLS_CERT_REQUIRE);
382 }
383
384
385
386
387
388
389 gnutls_certificate_set_verify_function(tls->credentials.cert, verify_peer_cert);
390 }
391
392 return session;
393
394 error:
395 crm_err("Could not initialize %s TLS %s session: %s " QB_XS " rc=%d priority='%s'",
396 tls_cred_str(tls->cred_type),
397 (conn_type == GNUTLS_SERVER)? "server" : "client",
398 gnutls_strerror(rc), rc, prio);
399 free(prio);
400 if (session != NULL) {
401 gnutls_deinit(session);
402 }
403 return NULL;
404 }
405
406
407
408
409
410
411
412
413
414
415
416
417 int
418 pcmk__tls_get_client_sock(const pcmk__remote_t *remote)
419 {
420 gpointer sock_ptr = NULL;
421
422 pcmk__assert((remote != NULL) && (remote->tls_session != NULL));
423
424 sock_ptr = (gpointer) gnutls_transport_get_ptr(remote->tls_session);
425 return GPOINTER_TO_INT(sock_ptr);
426 }
427
428 int
429 pcmk__read_handshake_data(const pcmk__client_t *client)
430 {
431 int rc = 0;
432
433 pcmk__assert((client != NULL) && (client->remote != NULL)
434 && (client->remote->tls_session != NULL));
435
436 do {
437 rc = gnutls_handshake(client->remote->tls_session);
438 } while (rc == GNUTLS_E_INTERRUPTED);
439
440 if (rc == GNUTLS_E_AGAIN) {
441
442
443
444 return EAGAIN;
445 } else if (rc != GNUTLS_E_SUCCESS) {
446 crm_err("TLS handshake with remote client failed: %s "
447 QB_XS " rc=%d", gnutls_strerror(rc), rc);
448 return EPROTO;
449 }
450 return pcmk_rc_ok;
451 }
452
453 void
454 pcmk__tls_add_psk_key(pcmk__tls_t *tls, gnutls_datum_t *key)
455 {
456 gnutls_psk_set_client_credentials(tls->credentials.psk_c,
457 DEFAULT_REMOTE_USERNAME, key,
458 GNUTLS_PSK_KEY_RAW);
459 }
460
461 void
462 pcmk__tls_add_psk_callback(pcmk__tls_t *tls,
463 gnutls_psk_server_credentials_function *cb)
464 {
465 gnutls_psk_set_server_credentials_function(tls->credentials.psk_s, cb);
466 }
467
468 void
469 pcmk__tls_check_cert_expiration(gnutls_session_t session)
470 {
471 gnutls_x509_crt_t cert;
472 const gnutls_datum_t *datum = NULL;
473 time_t expiry;
474
475 if (session == NULL) {
476 return;
477 }
478
479 if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509) {
480 return;
481 }
482
483 datum = gnutls_certificate_get_ours(session);
484 if (datum == NULL) {
485 return;
486 }
487
488 gnutls_x509_crt_init(&cert);
489 gnutls_x509_crt_import(cert, datum, GNUTLS_X509_FMT_DER);
490
491 expiry = gnutls_x509_crt_get_expiration_time(cert);
492
493 if (expiry != -1) {
494 time_t now = time(NULL);
495
496
497 if (expiry - now <= 60 * 60 * 24 * 30) {
498 crm_time_t *expiry_t = pcmk__copy_timet(expiry);
499
500 crm_time_log(LOG_WARNING, "TLS certificate will expire on",
501 expiry_t, crm_time_log_date | crm_time_log_timeofday);
502 crm_time_free(expiry_t);
503 }
504 }
505
506 gnutls_x509_crt_deinit(cert);
507 }
508
509 int
510 pcmk__tls_client_try_handshake(pcmk__remote_t *remote, int *gnutls_rc)
511 {
512 int rc = pcmk_rc_ok;
513
514 if (gnutls_rc != NULL) {
515 *gnutls_rc = GNUTLS_E_SUCCESS;
516 }
517
518 rc = gnutls_handshake(remote->tls_session);
519
520 switch (rc) {
521 case GNUTLS_E_SUCCESS:
522 rc = pcmk_rc_ok;
523 break;
524
525 case GNUTLS_E_INTERRUPTED:
526 case GNUTLS_E_AGAIN:
527 rc = EAGAIN;
528 break;
529
530 default:
531 if (gnutls_rc != NULL) {
532 *gnutls_rc = rc;
533 }
534
535 rc = EPROTO;
536 break;
537 }
538
539 return rc;
540 }
541
542 int
543 pcmk__tls_client_handshake(pcmk__remote_t *remote, int timeout_sec,
544 int *gnutls_rc)
545 {
546 const time_t time_limit = time(NULL) + timeout_sec;
547
548 do {
549 int rc = pcmk__tls_client_try_handshake(remote, gnutls_rc);
550
551 if (rc != EAGAIN) {
552 return rc;
553 }
554 } while (time(NULL) < time_limit);
555
556 return ETIME;
557 }
558
559 bool
560 pcmk__x509_enabled(void)
561 {
562
563
564
565
566
567 return (!pcmk__str_empty(pcmk__env_option(PCMK__ENV_CERT_FILE)) ||
568 !pcmk__str_empty(getenv("CIB_cert_file"))) &&
569 (!pcmk__str_empty(pcmk__env_option(PCMK__ENV_CA_FILE)) ||
570 !pcmk__str_empty(getenv("CIB_ca_file"))) &&
571 (!pcmk__str_empty(pcmk__env_option(PCMK__ENV_KEY_FILE)) ||
572 !pcmk__str_empty(getenv("CIB_key_file")));
573 }