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