1    	/*
2    	 * Copyright 2024-2026 the Pacemaker project contributors
3    	 *
4    	 * The version control history for this file may have further details.
5    	 *
6    	 * This source code is licensed under the GNU Lesser General Public License
7    	 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8    	 */
9    	
10   	#include <crm_internal.h>
11   	
12   	#include <ctype.h>                  // isxdigit
13   	#include <errno.h>                  // EAGAIN, ENODATA, EPROTO, EINVAL, ETIME
14   	#include <pwd.h>                    // getpwuid
15   	#include <signal.h>                 // signal, SIGPIPE, SIG_IGN
16   	#include <stdbool.h>                // bool
17   	#include <stdlib.h>                 // NULL, getenv, free
18   	#include <string.h>                 // memcpy, strdup, strlen
19   	#include <sys/stat.h>               // S_ISREG, stat
20   	#include <syslog.h>                 // LOG_WARNING
21   	#include <time.h>                   // time, time_t
22   	#include <unistd.h>                 // geteuid
23   	
24   	#include <glib.h>                   // g_clear_pointer, g_file_get_contents
25   	#include <gnutls/gnutls.h>          // gnutls_*, GNUTLS_E_SUCCESS
26   	#include <gnutls/x509.h>            // gnutls_x509_*
27   	#include <qb/qblog.h>               // QB_XS
28   	
29   	#include <crm/common/internal.h>
30   	#include <crm/common/iso8601.h>     // crm_time_free, crm_time_log_date
31   	#include <crm/common/logging.h>     // CRM_CHECK
32   	#include <crm/common/results.h>     // pcmk_rc_*
33   	
34   	static char *
35   	get_gnutls_priorities(gnutls_credentials_type_t cred_type)
36   	{
37   	    const char *prio_base = pcmk__env_option(PCMK__ENV_TLS_PRIORITIES);
38   	
39   	    if (prio_base == NULL) {
40   	        prio_base = PCMK__GNUTLS_PRIORITIES;
41   	    }
42   	
43   	    if (cred_type == GNUTLS_CRD_ANON) {
44   	        return pcmk__assert_asprintf("%s:+ANON-DH", prio_base);
45   	    } else if (cred_type == GNUTLS_CRD_PSK) {
46   	        return pcmk__assert_asprintf("%s:+DHE-PSK:+PSK", prio_base);
47   	    } else {
48   	        return strdup(prio_base);
49   	    }
50   	}
51   	
52   	static const char *
53   	tls_cred_str(gnutls_credentials_type_t cred_type)
54   	{
55   	    if (cred_type == GNUTLS_CRD_ANON) {
56   	        return "unauthenticated";
57   	    } else if (cred_type == GNUTLS_CRD_PSK) {
58   	        return "shared-key-authenticated";
59   	    } else if (cred_type == GNUTLS_CRD_CERTIFICATE) {
60   	        return "certificate-authenticated";
61   	    } else {
62   	        return "unknown";
63   	    }
64   	}
65   	
66   	static int
67   	tls_load_x509_data(pcmk__tls_t *tls)
68   	{
69   	    int rc;
70   	
71   	    CRM_CHECK(tls->cred_type == GNUTLS_CRD_CERTIFICATE, return EINVAL);
72   	
73   	    /* Load a trusted CA to be used to verify client certificates.  Use
74   	     * of this function instead of gnutls_certificate_set_x509_system_trust
75   	     * means we do not look at the system-wide authorities installed in
76   	     * /etc/pki somewhere.  This requires the cluster admin to set up their
77   	     * own CA.
78   	     */
79   	    rc = gnutls_certificate_set_x509_trust_file(tls->credentials.cert,
80   	                                                tls->ca_file,
81   	                                                GNUTLS_X509_FMT_PEM);
82   	    if (rc <= 0) {
83   	        pcmk__err("Failed to set X509 CA file: %s", gnutls_strerror(rc));
84   	        return ENODATA;
85   	    }
86   	
87   	    /* If a Certificate Revocation List (CRL) file was given in the environment,
88   	     * load that now so we know which clients have been banned.
89   	     */
90   	    if (tls->crl_file != NULL) {
91   	        rc = gnutls_certificate_set_x509_crl_file(tls->credentials.cert,
92   	                                                  tls->crl_file,
93   	                                                  GNUTLS_X509_FMT_PEM);
94   	        if (rc < 0) {
95   	            pcmk__err("Failed to set X509 CRL file: %s", gnutls_strerror(rc));
96   	            return ENODATA;
97   	        }
98   	    }
99   	
100  	    /* NULL = no password for the key, GNUTLS_PKCS_PLAIN = unencrypted key
101  	     * file
102  	     */
103  	    rc = gnutls_certificate_set_x509_key_file2(tls->credentials.cert,
104  	                                               tls->cert_file, tls->key_file,
105  	                                               GNUTLS_X509_FMT_PEM, NULL,
106  	                                               GNUTLS_PKCS_PLAIN);
107  	    if (rc < 0) {
108  	        pcmk__err("Failed to set X509 cert/key pair: %s", gnutls_strerror(rc));
109  	        return ENODATA;
110  	    }
111  	
112  	    return pcmk_rc_ok;
113  	}
114  	
115  	static void
116  	_gnutls_log_func(int level, const char *msg)
117  	{
118  	    pcmk__trace("%s", msg);
119  	}
120  	
121  	void
122  	pcmk__free_tls(pcmk__tls_t *tls)
123  	{
124  	    if (tls == NULL) {
125  	        return;
126  	    }
127  	
128  	    /* This is only set on the server side. */
129  	    if (tls->server) {
130  	        gnutls_dh_params_deinit(tls->dh_params);
131  	    }
132  	
133  	    if (tls->cred_type == GNUTLS_CRD_ANON) {
134  	        if (tls->server) {
135  	            gnutls_anon_free_server_credentials(tls->credentials.anon_s);
136  	        } else {
137  	            gnutls_anon_free_client_credentials(tls->credentials.anon_c);
138  	        }
139  	    } else if (tls->cred_type == GNUTLS_CRD_CERTIFICATE) {
140  	        gnutls_certificate_free_credentials(tls->credentials.cert);
141  	    } else if (tls->cred_type == GNUTLS_CRD_PSK) {
142  	        if (tls->server) {
143  	            gnutls_psk_free_server_credentials(tls->credentials.psk_s);
144  	        } else {
145  	            gnutls_psk_free_client_credentials(tls->credentials.psk_c);
146  	        }
147  	    }
148  	
149  	    free(tls);
150  	}
151  	
152  	int
153  	pcmk__init_tls(pcmk__tls_t **tls, bool server, bool have_psk)
154  	{
155  	    int rc = pcmk_rc_ok;
156  	
(1) Event path: Condition "*tls != NULL", taking false branch.
157  	    if (*tls != NULL) {
158  	        return rc;
159  	    }
160  	
161  	    *tls = pcmk__assert_alloc(1, sizeof(pcmk__tls_t));
162  	
163  	    signal(SIGPIPE, SIG_IGN);
164  	
165  	    gnutls_global_set_log_level(8);
166  	    gnutls_global_set_log_function(_gnutls_log_func);
167  	
(2) Event path: Condition "server", taking true branch.
168  	    if (server) {
169  	        rc = pcmk__init_tls_dh(&(*tls)->dh_params);
(3) Event path: Condition "rc != pcmk_rc_ok", taking true branch.
170  	        if (rc != pcmk_rc_ok) {
CID (unavailable; MK=81ade510660048f65bd5a8ddb85e76b1) (#1 of 2): Inconsistent C union access (INCONSISTENT_UNION_ACCESS):
(4) Event assign_union_field: The union field "in" of "_pp" is written.
(5) Event inconsistent_union_field_access: In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in".
171  	            g_clear_pointer(tls, pcmk__free_tls);
172  	            return rc;
173  	        }
174  	    }
175  	
176  	    if (pcmk__x509_enabled()) {
177  	        (*tls)->cred_type = GNUTLS_CRD_CERTIFICATE;
178  	
179  	    } else if (have_psk) {
180  	        (*tls)->cred_type = GNUTLS_CRD_PSK;
181  	
182  	    } else {
183  	        (*tls)->cred_type = GNUTLS_CRD_ANON;
184  	    }
185  	
186  	    (*tls)->server = server;
187  	
188  	    if ((*tls)->cred_type == GNUTLS_CRD_ANON) {
189  	        pcmk__warn("Using anonymous authentication.  This is insecure and will "
190  	                   "be removed in a future release.  Use X509 certificates or PSK "
191  	                   "instead.");
192  	
193  	        if (server) {
194  	            gnutls_anon_allocate_server_credentials(&(*tls)->credentials.anon_s);
195  	            gnutls_anon_set_server_dh_params((*tls)->credentials.anon_s,
196  	                                             (*tls)->dh_params);
197  	        } else {
198  	            gnutls_anon_allocate_client_credentials(&(*tls)->credentials.anon_c);
199  	        }
200  	
201  	    } else if ((*tls)->cred_type == GNUTLS_CRD_CERTIFICATE) {
202  	        /* Try the PCMK_ version of each environment variable first, and if
203  	         * it's not set then try the CIB_ version.
204  	         */
205  	        (*tls)->ca_file = pcmk__env_option(PCMK__ENV_CA_FILE);
206  	        if (pcmk__str_empty((*tls)->ca_file)) {
207  	            (*tls)->ca_file = getenv("CIB_ca_file");
208  	        }
209  	
210  	        (*tls)->cert_file = pcmk__env_option(PCMK__ENV_CERT_FILE);
211  	        if (pcmk__str_empty((*tls)->cert_file)) {
212  	            (*tls)->cert_file = getenv("CIB_cert_file");
213  	        }
214  	
215  	        (*tls)->crl_file = pcmk__env_option(PCMK__ENV_CRL_FILE);
216  	        if (pcmk__str_empty((*tls)->crl_file)) {
217  	            (*tls)->crl_file = getenv("CIB_crl_file");
218  	        }
219  	
220  	        (*tls)->key_file = pcmk__env_option(PCMK__ENV_KEY_FILE);
221  	        if (pcmk__str_empty((*tls)->key_file)) {
222  	            (*tls)->key_file = getenv("CIB_key_file");
223  	        }
224  	
225  	        gnutls_certificate_allocate_credentials(&(*tls)->credentials.cert);
226  	
227  	        if (server) {
228  	            gnutls_certificate_set_dh_params((*tls)->credentials.cert,
229  	                                             (*tls)->dh_params);
230  	
231  	        }
232  	
233  	        rc = tls_load_x509_data(*tls);
234  	        if (rc != pcmk_rc_ok) {
235  	            g_clear_pointer(tls, pcmk__free_tls);
236  	            return rc;
237  	        }
238  	    } else {    // GNUTLS_CRD_PSK
239  	        if (server) {
240  	            gnutls_psk_allocate_server_credentials(&(*tls)->credentials.psk_s);
241  	            gnutls_psk_set_server_dh_params((*tls)->credentials.psk_s,
242  	                                            (*tls)->dh_params);
243  	        } else {
244  	            gnutls_psk_allocate_client_credentials(&(*tls)->credentials.psk_c);
245  	        }
246  	    }
247  	
248  	    return rc;
249  	}
250  	
251  	int
252  	pcmk__init_tls_dh(gnutls_dh_params_t *dh_params)
253  	{
254  	    int rc = GNUTLS_E_SUCCESS;
255  	    unsigned int dh_bits = 0;
256  	    int dh_max_bits = 0;
257  	
258  	    rc = gnutls_dh_params_init(dh_params);
259  	    if (rc != GNUTLS_E_SUCCESS) {
260  	        goto error;
261  	    }
262  	
263  	    dh_bits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH,
264  	                                          GNUTLS_SEC_PARAM_NORMAL);
265  	    if (dh_bits == 0) {
266  	        rc = GNUTLS_E_DH_PRIME_UNACCEPTABLE;
267  	        goto error;
268  	    }
269  	
270  	    pcmk__scan_min_int(pcmk__env_option(PCMK__ENV_DH_MAX_BITS), &dh_max_bits, 0);
271  	    if ((dh_max_bits > 0) && (dh_bits > dh_max_bits)) {
272  	        dh_bits = dh_max_bits;
273  	    }
274  	
275  	    pcmk__info("Generating Diffie-Hellman parameters with %u-bit prime for TLS",
276  	               dh_bits);
277  	    rc = gnutls_dh_params_generate2(*dh_params, dh_bits);
278  	    if (rc != GNUTLS_E_SUCCESS) {
279  	        goto error;
280  	    }
281  	
282  	    return pcmk_rc_ok;
283  	
284  	error:
285  	    pcmk__err("Could not initialize Diffie-Hellman parameters for TLS: %s "
286  	              QB_XS " rc=%d",
287  	              gnutls_strerror(rc), rc);
288  	    return EPROTO;
289  	}
290  	
291  	gnutls_session_t
292  	pcmk__new_tls_session(pcmk__tls_t *tls, int csock)
293  	{
294  	    unsigned int conn_type = GNUTLS_CLIENT;
295  	    int rc = GNUTLS_E_SUCCESS;
296  	    char *prio = NULL;
297  	    gnutls_session_t session = NULL;
298  	
299  	    CRM_CHECK((tls != NULL) && (csock >= 0), return NULL);
300  	
301  	    if (tls->server) {
302  	        conn_type = GNUTLS_SERVER;
303  	    }
304  	
305  	    rc = gnutls_init(&session, conn_type);
306  	    if (rc != GNUTLS_E_SUCCESS) {
307  	        goto error;
308  	    }
309  	
310  	    /* Determine list of acceptable ciphers, etc. Pacemaker always adds the
311  	     * values required for its functionality.
312  	     *
313  	     * For an example of anonymous authentication, see:
314  	     * http://www.manpagez.com/info/gnutls/gnutls-2.10.4/gnutls_81.php#Echo-Server-with-anonymous-authentication
315  	     */
316  	    prio = get_gnutls_priorities(tls->cred_type);
317  	
318  	    /* @TODO On the server side, it would be more efficient to cache the
319  	     * priority with gnutls_priority_init2() and set it with
320  	     * gnutls_priority_set() for all sessions.
321  	     */
322  	    rc = gnutls_priority_set_direct(session, prio, NULL);
323  	    if (rc != GNUTLS_E_SUCCESS) {
324  	        goto error;
325  	    }
326  	
327  	    gnutls_transport_set_int(session, csock);
328  	
329  	    /* gnutls does not make this easy */
330  	    if (tls->cred_type == GNUTLS_CRD_ANON && tls->server) {
331  	        rc = gnutls_credentials_set(session, tls->cred_type, tls->credentials.anon_s);
332  	    } else if (tls->cred_type == GNUTLS_CRD_ANON) {
333  	        rc = gnutls_credentials_set(session, tls->cred_type, tls->credentials.anon_c);
334  	    } else if (tls->cred_type == GNUTLS_CRD_CERTIFICATE) {
335  	        rc = gnutls_credentials_set(session, tls->cred_type, tls->credentials.cert);
336  	    } else if (tls->cred_type == GNUTLS_CRD_PSK && tls->server) {
337  	        rc = gnutls_credentials_set(session, tls->cred_type, tls->credentials.psk_s);
338  	    } else if (tls->cred_type == GNUTLS_CRD_PSK) {
339  	        rc = gnutls_credentials_set(session, tls->cred_type, tls->credentials.psk_c);
340  	    } else {
341  	        pcmk__err("Unknown credential type: %d", tls->cred_type);
342  	        rc = EINVAL;
343  	        goto error;
344  	    }
345  	
346  	    if (rc != GNUTLS_E_SUCCESS) {
347  	        goto error;
348  	    }
349  	
350  	    free(prio);
351  	
352  	    if (tls->cred_type == GNUTLS_CRD_CERTIFICATE) {
353  	        if (conn_type == GNUTLS_SERVER) {
354  	            /* Require the client to send a certificate for the server to verify. */
355  	            gnutls_certificate_server_set_request(session, GNUTLS_CERT_REQUIRE);
356  	        }
357  	
358  	        // Register a function to verify the peer's certificate
359  	        gnutls_session_set_verify_cert(session, NULL, 0);
360  	    }
361  	
362  	    return session;
363  	
364  	error:
365  	    pcmk__err("Could not initialize %s TLS %s session: %s "
366  	              QB_XS " rc=%d priority='%s'",
367  	              tls_cred_str(tls->cred_type),
368  	              ((conn_type == GNUTLS_SERVER)? "server" : "client"),
369  	              gnutls_strerror(rc), rc, prio);
370  	    free(prio);
371  	    if (session != NULL) {
372  	        gnutls_deinit(session);
373  	    }
374  	    return NULL;
375  	}
376  	
377  	int
378  	pcmk__tls_get_client_sock(const pcmk__remote_t *remote)
379  	{
380  	    pcmk__assert((remote != NULL) && (remote->tls_session != NULL));
381  	
382  	    return gnutls_transport_get_int(remote->tls_session);
383  	}
384  	
385  	int
386  	pcmk__read_handshake_data(const pcmk__client_t *client)
387  	{
388  	    int rc = 0;
389  	
390  	    pcmk__assert((client != NULL) && (client->remote != NULL)
391  	                 && (client->remote->tls_session != NULL));
392  	
393  	    do {
394  	        rc = gnutls_handshake(client->remote->tls_session);
395  	    } while (rc == GNUTLS_E_INTERRUPTED);
396  	
397  	    if (rc == GNUTLS_E_AGAIN) {
398  	        /* No more data is available at the moment. This function should be
399  	         * invoked again once the client sends more.
400  	         */
401  	        return EAGAIN;
402  	    } else if (rc == GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR) {
403  	        int type = gnutls_certificate_type_get(client->remote->tls_session);
404  	        unsigned int status = gnutls_session_get_verify_cert_status(client->remote->tls_session);
405  	        gnutls_datum_t out;
406  	
407  	        gnutls_certificate_verification_status_print(status, type, &out, 0);
408  	        pcmk__err("Certificate verification failed: %s", out.data);
409  	        gnutls_free(out.data);
410  	    } else if (rc != GNUTLS_E_SUCCESS) {
411  	        pcmk__err("TLS handshake with remote client failed: %s " QB_XS " rc=%d",
412  	                  gnutls_strerror(rc), rc);
413  	        return EPROTO;
414  	    }
415  	    return pcmk_rc_ok;
416  	}
417  	
418  	void
419  	pcmk__tls_client_add_psk_key(pcmk__tls_t *tls, const char *username,
420  	                             gnutls_datum_t *key, bool raw)
421  	{
422  	    gnutls_psk_set_client_credentials(tls->credentials.psk_c, username, key,
423  	                                      raw ? GNUTLS_PSK_KEY_RAW : GNUTLS_PSK_KEY_HEX);
424  	}
425  	
426  	void
427  	pcmk__tls_check_cert_expiration(gnutls_session_t session)
428  	{
429  	    gnutls_x509_crt_t cert;
430  	    const gnutls_datum_t *datum = NULL;
431  	    time_t expiry;
432  	
433  	    if (session == NULL) {
434  	        return;
435  	    }
436  	
437  	    if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509) {
438  	        return;
439  	    }
440  	
441  	    datum = gnutls_certificate_get_ours(session);
442  	    if (datum == NULL) {
443  	        return;
444  	    }
445  	
446  	    gnutls_x509_crt_init(&cert);
447  	    gnutls_x509_crt_import(cert, datum, GNUTLS_X509_FMT_DER);
448  	
449  	    expiry = gnutls_x509_crt_get_expiration_time(cert);
450  	
451  	    if (expiry != -1) {
452  	        time_t now = time(NULL);
453  	
454  	        /* If the cert is going to expire within ~ one month (30 days), log it */
455  	        if (expiry - now <= 60 * 60 * 24 * 30) {
456  	            crm_time_t *expiry_t = pcmk__copy_timet(expiry);
457  	
458  	            pcmk__time_log(LOG_WARNING, "TLS certificate will expire on",
459  	                           expiry_t, crm_time_log_date|crm_time_log_timeofday);
460  	            crm_time_free(expiry_t);
461  	        }
462  	    }
463  	
464  	    gnutls_x509_crt_deinit(cert);
465  	}
466  	
467  	int
468  	pcmk__tls_client_try_handshake(pcmk__remote_t *remote, int *gnutls_rc)
469  	{
470  	    int rc = pcmk_rc_ok;
471  	
472  	    pcmk__assert(gnutls_rc != NULL);
473  	    *gnutls_rc = GNUTLS_E_SUCCESS;
474  	
475  	    rc = gnutls_handshake(remote->tls_session);
476  	
477  	    switch (rc) {
478  	        case GNUTLS_E_SUCCESS:
479  	            rc = pcmk_rc_ok;
480  	            break;
481  	
482  	        case GNUTLS_E_INTERRUPTED:
483  	        case GNUTLS_E_AGAIN:
484  	            rc = EAGAIN;
485  	            break;
486  	
487  	        case GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR: {
488  	            int type = gnutls_certificate_type_get(remote->tls_session);
489  	            unsigned int status = gnutls_session_get_verify_cert_status(remote->tls_session);
490  	            gnutls_datum_t out;
491  	
492  	            gnutls_certificate_verification_status_print(status, type, &out, 0);
493  	            pcmk__err("Certificate verification failed: %s", out.data);
494  	            gnutls_free(out.data);
495  	
496  	            *gnutls_rc = rc;
497  	            rc = EPROTO;
498  	            break;
499  	        }
500  	
501  	        default:
502  	            *gnutls_rc = rc;
503  	            rc = EPROTO;
504  	            break;
505  	    }
506  	
507  	    return rc;
508  	}
509  	
510  	int
511  	pcmk__tls_client_handshake(pcmk__remote_t *remote, int timeout_sec,
512  	                           int *gnutls_rc)
513  	{
514  	    const time_t time_limit = time(NULL) + timeout_sec;
515  	
516  	    do {
517  	        int rc = pcmk__tls_client_try_handshake(remote, gnutls_rc);
518  	
519  	        if (rc != EAGAIN) {
520  	            return rc;
521  	        }
522  	    } while (time(NULL) < time_limit);
523  	
524  	    return ETIME;
525  	}
526  	
527  	bool
528  	pcmk__x509_enabled(void)
529  	{
530  	    /* Environment variables for servers come through the sysconfig file, and
531  	     * have names like PCMK_<whatever>.  Environment variables for clients come
532  	     * from the environment and have names like CIB_<whatever>.  This function
533  	     * is used for both, so we need to check both.
534  	     */
535  	    return (!pcmk__str_empty(pcmk__env_option(PCMK__ENV_CERT_FILE)) ||
536  	            !pcmk__str_empty(getenv("CIB_cert_file"))) &&
537  	           (!pcmk__str_empty(pcmk__env_option(PCMK__ENV_CA_FILE)) ||
538  	            !pcmk__str_empty(getenv("CIB_ca_file"))) &&
539  	           (!pcmk__str_empty(pcmk__env_option(PCMK__ENV_KEY_FILE)) ||
540  	            !pcmk__str_empty(getenv("CIB_key_file")));
541  	}
542  	
543  	void
544  	pcmk__copy_key(gnutls_datum_t *dest, const gnutls_datum_t *source)
545  	{
546  	    pcmk__assert((dest != NULL) && (source != NULL) && (source->data != NULL));
547  	
548  	    dest->data = gnutls_malloc(source->size);
549  	    pcmk__mem_assert(dest->data);
550  	
551  	    memcpy(dest->data, source->data, source->size);
552  	    dest->size = source->size;
553  	}
554  	
555  	int
556  	pcmk__load_key(const char *location, gnutls_datum_t *key, bool raw)
557  	{
558  	    gchar *contents = NULL;
559  	    gsize len = 0;
560  	    int rc = pcmk_rc_ok;
561  	
562  	    pcmk__assert((location != NULL) && (key != NULL));
563  	
564  	    if (!g_file_get_contents(location, &contents, &len, NULL)) {
565  	        return ENOKEY;
566  	    }
567  	
568  	    if (!raw) {
569  	        /* This is a plain text key.  gnutls expects only hex characters, so
570  	         * remove any whitespace and check that the characters are valid.
571  	         */
572  	        contents = g_strstrip(contents);
573  	
574  	        for (const char *c = contents; *c != '\0'; c++) {
575  	            if (!isxdigit(*c)) {
576  	                pcmk__err("Key file %s contains characters besides hex digits",
577  	                          location);
578  	                rc = ENOKEY;
579  	                goto done;
580  	            }
581  	        }
582  	
583  	        len = strlen(contents);
584  	    }
585  	
586  	    key->size = len;
587  	    key->data = gnutls_malloc(key->size);
588  	    pcmk__mem_assert(key->data);
589  	    memcpy(key->data, contents, key->size);
590  	
591  	done:
592  	    g_free(contents);
593  	    return rc;
594  	}
595  	
596  	bool
597  	pcmk__cred_file_useable(const char *location, bool *file_exists)
598  	{
599  	    int rc = pcmk_rc_ok;
600  	    struct stat sb;
601  	    uid_t euid;
602  	
603  	    pcmk__assert((location != NULL) && (file_exists != NULL));
604  	
605  	    rc = stat(location, &sb);
606  	    if ((rc != 0) || !S_ISREG(sb.st_mode)) {
607  	        *file_exists = false;
608  	        return false;
609  	    }
610  	
611  	    *file_exists = true;
612  	
613  	    euid = geteuid();
614  	    if (sb.st_uid != euid) {
615  	        struct passwd *pwent = getpwuid(euid);
616  	
617  	        if (pwent == NULL) {
618  	            pcmk__err("Refusing to use PSK credentials file %s because it is "
619  	                      "not owned by UID %lld", (long long) euid);
620  	        } else {
621  	            pcmk__err("Refusing to use PSK credentials file %s because it is "
622  	                      "not owned by %s", location, pwent->pw_name);
623  	        }
624  	
625  	        return false;
626  	    }
627  	
628  	    if ((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) {
629  	        pcmk__err("Refusing to use PSK credentials file %s because it has "
630  	                  "group and/or other permissions set", location);
631  	        return false;
632  	    }
633  	
634  	    return true;
635  	}
636