1 /*
2 * Copyright 2004-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 General Public License version 2
7 * or later (GPLv2+) WITHOUT ANY WARRANTY.
8 */
9
10 #include <crm_internal.h>
11
12 #include <arpa/inet.h> // htons
13 #include <errno.h> // errno, EAGAIN
14 #include <grp.h> // getgrgid, getgrnam, group
15 #include <inttypes.h> // PRIx64
16 #include <netinet/in.h> // sockaddr_in, INADDR_ANY
17 #include <stdbool.h>
18 #include <stddef.h> // NULL
19 #include <stdlib.h> // calloc, free, getenv
20 #include <string.h> // memset
21 #include <sys/socket.h> // sockaddr{,_storage}, AF_INET, etc.
22 #include <unistd.h> // close
23
24 #include <glib.h> // gboolean, gpointer, g_source_remove, etc.
25 #include <gnutls/gnutls.h> // gnutls_bye, gnutls_deinit
26 #include <libxml/tree.h> // xmlNode
27 #include <qb/qblog.h> // QB_XS
28
29 #include <crm_config.h> // CRM_DAEMON_GROUP
30 #include <crm/common/internal.h> // pcmk__client_t, etc.
31 #include <crm/common/logging.h> // CRM_CHECK
32 #include <crm/common/mainloop.h> // mainloop_*
33 #include <crm/common/results.h> // pcmk_rc_*
34 #include <crm/common/xml.h> // PCMK_XA_*
35 #include <crm/crm.h> // CRM_OP_REGISTER
36
37 #include "pacemaker-based.h"
38
39 #if HAVE_SECURITY_PAM_APPL_H
40 #include <security/pam_appl.h> // pam_*, PAM_*
41 #define HAVE_PAM 1
42 #elif HAVE_PAM_PAM_APPL_H
43 #include <pam/pam_appl.h> // pam_*, PAM_*
44 #define HAVE_PAM 1
45 #endif
46
47 #define CREDFILE PACEMAKER_CONFIG_DIR "/cib-credentials"
48
49 static pcmk__tls_t *tls = NULL;
50
51 int remote_fd = 0;
52 int remote_tls_fd = 0;
53
54 // @TODO This is rather short for someone to type their password
55 #define REMOTE_AUTH_TIMEOUT 10000
56
57 /*!
58 * \internal
59 * \brief Destroy a client if not authenticated after the timeout has expired
60 *
61 * This is used as a callback that runs \c REMOTE_AUTH_TIMEOUT milliseconds
62 * after a new remote CIB client connects.
63 *
64 * If the client is not authenticated, drop it by removing its source from the
65 * mainloop. It will be freed by \c based_remote_client_destroy() via
66 * \c remote_client_fd_callbacks.
67 *
68 * \param[in,out] data Remote CIB client (\c pcmk__client_t)
69 *
70 * \return \c G_SOURCE_REMOVE (to destroy the timeout)
71 */
72 static gboolean
73 remote_auth_timeout_cb(gpointer data)
74 {
75 pcmk__client_t *client = data;
76
77 client->remote->auth_timeout = 0;
78
79 if (pcmk__is_set(client->flags, pcmk__client_authenticated)) {
80 return G_SOURCE_REMOVE;
81 }
82
83 mainloop_del_fd(client->remote->source);
84 pcmk__err("Remote client authentication timed out");
85 return G_SOURCE_REMOVE;
86 }
87
88 /*!
89 * \internal
90 * \brief Check whether a given user is a member of \c CRM_DAEMON_GROUP
91 *
92 * \param[in] user User name
93 *
94 * \return \c true if \p user is a member of \c CRM_DAEMON_GROUP, or \c false
95 * otherwise
96 */
97 static bool
98 is_daemon_group_member(const char *user)
99 {
100 int rc = pcmk_rc_ok;
101 gid_t gid = 0;
102 const struct group *group = NULL;
103
104 /* group->gr_mem only contains those users that are listed in /etc/group.
105 * It won't list the user if the group is their primary (that is, it's in
106 * the GID field in /etc/passwd (or passwd->pw_gid as returned by getpwent).
107 * So, we first need to perform a primary group check.
108 */
109 rc = pcmk__lookup_user(user, NULL, &gid);
110 if (rc != pcmk_rc_ok) {
111 pcmk__notice("Rejecting remote client: could not find user '%s': %s",
112 user, pcmk_rc_str(rc));
113 return false;
114 }
115
116 group = getgrnam(CRM_DAEMON_GROUP);
117 if (group == NULL) {
118 pcmk__err("Rejecting remote client: " CRM_DAEMON_GROUP " is not a "
119 "valid group");
120 return false;
121 }
122
123 if (group->gr_gid == gid) {
124 return true;
125 }
126
127 /* If that didn't work, check if CRM_DAEMON_GROUP is a secondary group for
128 * the user.
129 */
130 for (const char *const *member = (const char *const *) group->gr_mem;
131 *member != NULL; member++) {
132
133 if (pcmk__str_eq(user, *member, pcmk__str_none)) {
134 return true;
135 }
136 }
137
138 pcmk__notice("Rejecting remote client: User %s is not a member of group %s",
139 user, CRM_DAEMON_GROUP);
140 return false;
141 }
142
143 #ifdef HAVE_PAM
144 /*!
145 * \internal
146 * \brief Pass remote user's password to PAM
147 *
148 * \param[in] num_msg Number of entries in \p msg
149 * \param[in] msg Array of PAM messages
150 * \param[out] response Where to set response to PAM
151 * \param[in] data User data (the password string)
152 *
153 * \return PAM return code (PAM_BUF_ERR for memory errors, PAM_CONV_ERR for all
154 * other errors, or PAM_SUCCESS on success)
155 * \note See pam_conv(3) for more explanation
156 */
157 static int
158 construct_pam_passwd(int num_msg, const struct pam_message **msg,
159 struct pam_response **response, void *data)
160 {
161 /* In theory, multiple messages are allowed, but due to OS compatibility
162 * issues, PAM implementations are recommended to only send one message at a
163 * time. We can require that here for simplicity.
164 */
165 CRM_CHECK((num_msg == 1) && (msg != NULL) && (response != NULL)
166 && (data != NULL), return PAM_CONV_ERR);
167
168 switch (msg[0]->msg_style) {
169 case PAM_PROMPT_ECHO_OFF:
170 case PAM_PROMPT_ECHO_ON:
171 // Password requested
172 break;
173 case PAM_TEXT_INFO:
174 pcmk__info("PAM: %s", msg[0]->msg);
175 data = NULL;
176 break;
177 case PAM_ERROR_MSG:
178 /* In theory we should show msg[0]->msg, but that might
179 * contain the password, which we don't want in the logs
180 */
181 pcmk__err("PAM reported an error");
182 data = NULL;
183 break;
184 default:
185 pcmk__warn("Ignoring PAM message of unrecognized type %d",
186 msg[0]->msg_style);
187 return PAM_CONV_ERR;
188 }
189
190 *response = calloc(1, sizeof(struct pam_response));
191 if (*response == NULL) {
192 return PAM_BUF_ERR;
193 }
194 (*response)->resp_retcode = 0;
195 (*response)->resp = pcmk__str_copy((const char *) data); // Caller will free
196 return PAM_SUCCESS;
197 }
198 #endif
199
200 /*!
201 * \internal
202 * \brief Verify the username and password passed for a remote CIB connection
203 *
204 * \param[in] user Username passed for remote CIB connection
205 * \param[in] passwd Password passed for remote CIB connection
206 *
207 * \return \c true if the username and password are accepted, otherwise \c false
208 * \note This function rejects all credentials when built without PAM support.
209 */
210 static bool
211 authenticate_user(const char *user, const char *passwd)
212 {
213 #ifdef HAVE_PAM
214 int rc = 0;
215 bool pass = false;
216 const void *p_user = NULL;
217 struct pam_conv p_conv;
218 struct pam_handle *pam_h = NULL;
219
220 static const char *pam_name = NULL;
221
222 if (pam_name == NULL) {
223 pam_name = getenv("CIB_pam_service");
224 if (pam_name == NULL) {
225 pam_name = "login";
226 }
227 }
228
229 p_conv.conv = construct_pam_passwd;
230 p_conv.appdata_ptr = (void *) passwd;
231
232 rc = pam_start(pam_name, user, &p_conv, &pam_h);
233 if (rc != PAM_SUCCESS) {
234 pcmk__warn("Rejecting remote client for user %s because PAM "
235 "initialization failed: %s",
236 user, pam_strerror(pam_h, rc));
237 goto bail;
238 }
239
240 // Check user credentials
241 rc = pam_authenticate(pam_h, PAM_SILENT);
242 if (rc != PAM_SUCCESS) {
243 pcmk__notice("Access for remote user %s denied: %s", user,
244 pam_strerror(pam_h, rc));
245 goto bail;
246 }
247
248 /* Get the authenticated user name (PAM modules can map the original name to
249 * something else). Since the CIB manager runs as the daemon user (not
250 * root), that is the only user that can be successfully authenticated.
251 */
252 rc = pam_get_item(pam_h, PAM_USER, &p_user);
253 if (rc != PAM_SUCCESS) {
254 pcmk__warn("Rejecting remote client for user %s because PAM failed to "
255 "return final user name: %s",
256 user, pam_strerror(pam_h, rc));
257 goto bail;
258 }
259 if (p_user == NULL) {
260 pcmk__warn("Rejecting remote client for user %s because PAM returned "
261 "no final user name",
262 user);
263 goto bail;
264 }
265
266 // @TODO Why do we require these to match?
267 if (!pcmk__str_eq(p_user, user, pcmk__str_none)) {
268 pcmk__warn("Rejecting remote client for user %s because PAM returned "
269 "different final user name %s",
270 user, p_user);
271 goto bail;
272 }
273
274 // Check user account restrictions (expiration, etc.)
275 rc = pam_acct_mgmt(pam_h, PAM_SILENT);
276 if (rc != PAM_SUCCESS) {
277 pcmk__notice("Access for remote user %s denied: %s", user,
278 pam_strerror(pam_h, rc));
279 goto bail;
280 }
281 pass = true;
282
283 bail:
284 pam_end(pam_h, rc);
285 return pass;
286 #else
287 // @TODO Implement for non-PAM environments
288 pcmk__warn("Rejecting remote user %s because this build does not have PAM "
289 "support",
290 user);
291 return false;
292 #endif
293 }
294
295 static bool
296 cib_remote_auth(xmlNode * login)
297 {
298 const char *user = NULL;
299 const char *pass = NULL;
300 const char *tmp = NULL;
301
302 if (login == NULL) {
303 return false;
304 }
305
306 if (!pcmk__xe_is(login, PCMK__XE_CIB_COMMAND)) {
307 pcmk__warn("Rejecting remote client: Unrecognizable message (element "
308 "'%s' not '" PCMK__XE_CIB_COMMAND "')",
309 login->name);
310 pcmk__log_xml_debug(login, "bad");
311 return false;
312 }
313
314 tmp = pcmk__xe_get(login, PCMK_XA_OP);
315 if (!pcmk__str_eq(tmp, "authenticate", pcmk__str_casei)) {
316 pcmk__warn("Rejecting remote client: Unrecognizable message (operation "
317 "'%s' not 'authenticate')",
318 tmp);
319 pcmk__log_xml_debug(login, "bad");
320 return false;
321 }
322
323 user = pcmk__xe_get(login, PCMK_XA_USER);
324 pass = pcmk__xe_get(login, PCMK__XA_PASSWORD);
325 if (!user || !pass) {
326 pcmk__warn("Rejecting remote client: No %s given",
327 ((user == NULL)? "username" : "password"));
328 pcmk__log_xml_debug(login, "bad");
329 return false;
330 }
331
332 pcmk__log_xml_debug(login, "auth");
333
334 return is_daemon_group_member(user) && authenticate_user(user, pass);
335 }
336
337 static void
338 cib_handle_remote_msg(pcmk__client_t *client, xmlNode *command)
339 {
340 int rc = pcmk_rc_ok;
341 uint32_t call_options = cib_none;
342 const char *op = pcmk__xe_get(command, PCMK__XA_CIB_OP);
343
344 if (!pcmk__xe_is(command, PCMK__XE_CIB_COMMAND)) {
345 pcmk__log_xml_trace(command, "bad");
346 return;
347 }
348
349 if (client->name == NULL) {
350 client->name = pcmk__str_copy(client->id);
351 }
352
353 /* unset dangerous options */
354 pcmk__xe_remove_attr(command, PCMK__XA_SRC);
355 pcmk__xe_remove_attr(command, PCMK__XA_CIB_HOST);
356 pcmk__xe_remove_attr(command, PCMK__XA_CIB_UPDATE);
357
358 pcmk__xe_set(command, PCMK__XA_T, PCMK__VALUE_CIB);
359 pcmk__xe_set(command, PCMK__XA_CIB_CLIENTID, client->id);
360 pcmk__xe_set(command, PCMK__XA_CIB_CLIENTNAME, client->name);
361 pcmk__xe_set(command, PCMK__XA_CIB_USER, client->user);
362
363 if (pcmk__xe_get(command, PCMK__XA_CIB_CALLID) == NULL) {
364 char *call_uuid = pcmk__generate_uuid();
365
366 /* fix the command */
367 pcmk__xe_set(command, PCMK__XA_CIB_CALLID, call_uuid);
368 free(call_uuid);
369 }
370
371 rc = pcmk__xe_get_flags(command, PCMK__XA_CIB_CALLOPT, &call_options,
372 cib_none);
373 if (rc != pcmk_rc_ok) {
374 pcmk__warn("Couldn't parse options from request from remote client %s: "
375 "%s", client->name, pcmk_rc_str(rc));
376 pcmk__log_xml_info(command, "bad-call-opts");
377 }
378
379 /* Requests with cib_transaction set should not be sent to based directly
380 * (that is, outside of a commit-transaction request)
381 */
382 if (pcmk__is_set(call_options, cib_transaction)) {
383 pcmk__warn("Ignoring CIB request from remote client %s with "
384 "cib_transaction flag set outside of any transaction",
385 client->name);
386 pcmk__log_xml_info(command, "no-transaction");
387 return;
388 }
389
390 pcmk__log_xml_trace(command, "remote-request");
391
392 if (pcmk__str_eq(op, PCMK__VALUE_CIB_NOTIFY, pcmk__str_none)) {
393 based_update_notify_flags(command, client);
394 }
395
396 based_process_request(command, true, client);
397 }
398
399 static int
400 cib_remote_msg(gpointer data)
401 {
402 xmlNode *command = NULL;
403 pcmk__client_t *client = data;
404 int rc;
405 const char *client_name = pcmk__client_name(client);
406
407 pcmk__trace("Remote %s message received for client %s",
408 pcmk__client_type_str(PCMK__CLIENT_TYPE(client)), client_name);
409
410 if ((PCMK__CLIENT_TYPE(client) == pcmk__client_tls)
411 && !pcmk__is_set(client->flags, pcmk__client_tls_handshake_complete)) {
412
413 int rc = pcmk__read_handshake_data(client);
414
415 if (rc == EAGAIN) {
416 /* No more data is available at the moment. Just return for now;
417 * we'll get invoked again once the client sends more.
418 */
419 return 0;
420 } else if (rc != pcmk_rc_ok) {
421 return -1;
422 }
423
424 pcmk__debug("Completed TLS handshake with remote client %s",
425 client_name);
426 pcmk__set_client_flags(client, pcmk__client_tls_handshake_complete);
427 if (client->remote->auth_timeout) {
428 g_source_remove(client->remote->auth_timeout);
429 }
430
431 /* Now that the handshake is done, see if any client TLS certificate is
432 * close to its expiration date and log if so. If a TLS certificate is not
433 * in use, this function will just return so we don't need to check for the
434 * session type here.
435 */
436 pcmk__tls_check_cert_expiration(client->remote->tls_session);
437
438 // Require the client to authenticate within this time
439 client->remote->auth_timeout = pcmk__create_timer(REMOTE_AUTH_TIMEOUT,
440 remote_auth_timeout_cb,
441 client);
442 return 0;
443 }
444
445 rc = pcmk__read_available_remote_data(client->remote);
446 switch (rc) {
447 case pcmk_rc_ok:
448 break;
449
450 case EAGAIN:
451 /* We haven't read the whole message yet */
452 return 0;
453
454 default:
455 /* Error */
456 pcmk__trace("Error reading from remote client: %s",
457 pcmk_rc_str(rc));
458 return -1;
459 }
460
461 /* must pass auth before we will process anything else */
462 if (!pcmk__is_set(client->flags, pcmk__client_authenticated)) {
463 xmlNode *reg;
464 const char *user = NULL;
465
466 command = pcmk__remote_message_xml(client->remote);
467 if (!cib_remote_auth(command)) {
468 pcmk__xml_free(command);
469 return -1;
470 }
471
472 pcmk__set_client_flags(client, pcmk__client_authenticated);
473 g_source_remove(client->remote->auth_timeout);
474 client->remote->auth_timeout = 0;
475 client->name = pcmk__xe_get_copy(command, PCMK_XA_NAME);
476
477 user = pcmk__xe_get(command, PCMK_XA_USER);
478 if (user) {
479 client->user = pcmk__str_copy(user);
480 }
481
482 pcmk__notice("Remote connection accepted for authenticated user %s "
483 QB_XS " client %s",
484 pcmk__s(user, ""), client_name);
485
486 /* send ACK */
487 reg = pcmk__xe_create(NULL, PCMK__XE_CIB_RESULT);
488 pcmk__xe_set(reg, PCMK__XA_CIB_OP, CRM_OP_REGISTER);
489 pcmk__xe_set(reg, PCMK__XA_CIB_CLIENTID, client->id);
490 pcmk__remote_send_xml(client->remote, reg);
491 pcmk__xml_free(reg);
492 pcmk__xml_free(command);
493 }
494
495 command = pcmk__remote_message_xml(client->remote);
496 if (command != NULL) {
497 pcmk__trace("Remote message received from client %s", client_name);
498 cib_handle_remote_msg(client, command);
499 pcmk__xml_free(command);
500 }
501
502 return 0;
503 }
504
505 static void
506 based_remote_client_destroy(gpointer user_data)
507 {
508 pcmk__client_t *client = user_data;
509 int csock = -1;
510
|
(1) Event path: |
Condition "client == NULL", taking false branch. |
511 if (client == NULL) {
512 return;
513 }
514
|
(2) Event path: |
Switch case default. |
|
(3) Event path: |
Condition "trace_cs == NULL", taking true branch. |
|
(4) Event path: |
Condition "crm_is_callsite_active(trace_cs, _level, 0)", taking false branch. |
|
(5) Event path: |
Breaking from switch. |
515 pcmk__trace("Cleaning up after client %s disconnect",
516 pcmk__client_name(client));
517
|
(6) Event path: |
Switch case value "pcmk__client_tls". |
518 switch (PCMK__CLIENT_TYPE(client)) {
519 case pcmk__client_tcp:
520 csock = client->remote->tcp_socket;
521 break;
522 case pcmk__client_tls:
|
(7) Event path: |
Condition "client->remote->tls_session", taking true branch. |
523 if (client->remote->tls_session) {
524 csock = pcmk__tls_get_client_sock(client->remote);
525
|
(8) Event path: |
Condition "pcmk__is_set(client->flags, pcmk__client_tls_handshake_complete)", taking true branch. |
526 if (pcmk__is_set(client->flags,
527 pcmk__client_tls_handshake_complete)) {
528 gnutls_bye(client->remote->tls_session, GNUTLS_SHUT_WR);
529 }
530
|
CID (unavailable; MK=f03b6464a2c4c33831bec16dc378d3f6) (#1 of 1): Inconsistent C union access (INCONSISTENT_UNION_ACCESS): |
|
(9) Event assign_union_field: |
The union field "in" of "_pp" is written. |
|
(10) Event inconsistent_union_field_access: |
In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in". |
531 g_clear_pointer(&client->remote->tls_session, gnutls_deinit);
532 }
533 break;
534 default:
535 pcmk__warn("Unknown transport for client %s "
536 QB_XS " flags=%#016" PRIx64,
537 pcmk__client_name(client), client->flags);
538 }
539
540 if (csock >= 0) {
541 close(csock);
542 }
543
544 pcmk__free_client(client);
545
546 pcmk__trace("Freed the cib client");
547
548 if (cib_shutdown_flag) {
549 based_shutdown(0);
550 }
551 }
552
553 static int
554 cib_remote_listen(gpointer data)
555 {
556 int csock = -1;
557 unsigned laddr;
558 struct sockaddr_storage addr;
559 char ipstr[INET6_ADDRSTRLEN];
560 int ssock = *(int *)data;
561 int rc;
562
563 pcmk__client_t *new_client = NULL;
564
565 static struct mainloop_fd_callbacks remote_client_fd_callbacks = {
566 .dispatch = cib_remote_msg,
567 .destroy = based_remote_client_destroy,
568 };
569
570 /* accept the connection */
571 laddr = sizeof(addr);
572 memset(&addr, 0, sizeof(addr));
573 csock = accept(ssock, (struct sockaddr *)&addr, &laddr);
574 if (csock == -1) {
575 pcmk__warn("Could not accept remote connection: %s",
576 pcmk_rc_str(errno));
577 return 0;
578 }
579
580 pcmk__sockaddr2str(&addr, ipstr);
581
582 rc = pcmk__set_nonblocking(csock);
583 if (rc != pcmk_rc_ok) {
584 pcmk__warn("Dropping remote connection from %s because it could not be "
585 "set to non-blocking: %s",
586 ipstr, pcmk_rc_str(rc));
587 close(csock);
588 return 0;
589 }
590
591 new_client = pcmk__new_unauth_client(NULL);
592 new_client->remote = pcmk__assert_alloc(1, sizeof(pcmk__remote_t));
593
594 if (ssock == remote_tls_fd) {
595 pcmk__set_client_flags(new_client, pcmk__client_tls);
596
597 /* create gnutls session for the server socket */
598 new_client->remote->tls_session = pcmk__new_tls_session(tls, csock);
599 if (new_client->remote->tls_session == NULL) {
600 close(csock);
601 return 0;
602 }
603 } else {
604 pcmk__set_client_flags(new_client, pcmk__client_tcp);
605 new_client->remote->tcp_socket = csock;
606 }
607
608 // Require the client to authenticate within this time
609 new_client->remote->auth_timeout = pcmk__create_timer(REMOTE_AUTH_TIMEOUT,
610 remote_auth_timeout_cb,
611 new_client);
612 pcmk__info("%s connection from %s pending authentication for client %s",
613 ((ssock == remote_tls_fd)? "Encrypted" : "Clear-text"), ipstr,
614 new_client->id);
615
616 new_client->remote->source =
617 mainloop_add_fd("cib-remote-client", G_PRIORITY_DEFAULT, csock, new_client,
618 &remote_client_fd_callbacks);
619
620 return 0;
621 }
622
623 static void
624 based_remote_listener_destroy(gpointer user_data)
625 {
626 pcmk__info("No longer listening for remote connections");
627 }
628
629 static int
630 init_remote_listener(int port)
631 {
632 int rc;
633 int *ssock = NULL;
634 struct sockaddr_in saddr;
635 int optval;
636
637 static struct mainloop_fd_callbacks remote_listen_fd_callbacks = {
638 .dispatch = cib_remote_listen,
639 .destroy = based_remote_listener_destroy,
640 };
641
642 #ifndef HAVE_PAM
643 pcmk__warn("This build does not support remote administrators because PAM "
644 "support is not available");
645 #endif
646
647 /* create server socket */
648 ssock = pcmk__assert_alloc(1, sizeof(int));
649 *ssock = socket(AF_INET, SOCK_STREAM, 0);
650 if (*ssock == -1) {
651 pcmk__err("Listener socket creation failed: %s", pcmk_rc_str(errno));
652 free(ssock);
653 return -1;
654 }
655
656 /* reuse address */
657 optval = 1;
658 rc = setsockopt(*ssock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
659 if (rc < 0) {
660 pcmk__err("Local address reuse not allowed on listener socket: %s",
661 pcmk_rc_str(errno));
662 }
663
664 /* bind server socket */
665 memset(&saddr, '\0', sizeof(saddr));
666 saddr.sin_family = AF_INET;
667 saddr.sin_addr.s_addr = INADDR_ANY;
668 saddr.sin_port = htons(port);
669 if (bind(*ssock, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) {
670 pcmk__err("Cannot bind to listener socket: %s", pcmk_rc_str(errno));
671 close(*ssock);
672 free(ssock);
673 return -2;
674 }
675 if (listen(*ssock, 10) == -1) {
676 pcmk__err("Cannot listen on socket: %s", pcmk_rc_str(errno));
677 close(*ssock);
678 free(ssock);
679 return -3;
680 }
681
682 mainloop_add_fd("cib-remote", G_PRIORITY_DEFAULT, *ssock, ssock, &remote_listen_fd_callbacks);
683 pcmk__debug("Started listener on port %d", port);
684
685 return *ssock;
686 }
687
688 /*!
689 * \internal
690 * \brief Initialize remote listeners using ports configured in the CIB
691 */
692 void
693 based_remote_init(void)
694 {
695 const char *port_s = NULL;
696 int port = 0;
697 int rc = pcmk_rc_ok;
698 bool have_psk = false;
699
700 port_s = pcmk__xe_get(the_cib, PCMK_XA_REMOTE_TLS_PORT);
701
702 if ((pcmk__scan_port(port_s, &port) != pcmk_rc_ok) || (port <= 0)) {
703 goto try_clear_port;
704 }
705
706 /* X509 certificates take precedence over PSK in pcmk__init_tls,
707 * so don't perform any of the following (potentially noisy) checks
708 * if we don't care about their results.
709 */
710 if (!pcmk__x509_enabled()) {
711 bool file_exists = false;
712
713 have_psk = pcmk__cred_file_useable(CREDFILE, &file_exists);
714
715 if (!have_psk && file_exists) {
716 /* The credential file exists but doesn't have the right owner
717 * or permissions. Don't fall back to anonymous on config
718 * errors.
719 */
720 pcmk__err("Not starting TLS listener on port %d", port);
721 goto try_clear_port;
722 }
723
724 if (!have_psk) {
725 pcmk__warn("Falling back to anonymous authentication for remote "
726 "CIB connections");
727 }
728 }
729
730 /* Now that we know whether to fall back to anonymous authentication
731 * or not, we can actually initialize TLS support.
732 */
733 rc = pcmk__init_tls(&tls, true, have_psk);
734 if (rc != pcmk_rc_ok) {
735 pcmk__err("Failed to initialize TLS: %s. Not starting TLS listener ",
736 "on port %d", pcmk_rc_str(rc), port);
737
738 remote_tls_fd = -1;
739 goto try_clear_port;
740 }
741
742 if (tls->cred_type == GNUTLS_CRD_PSK) {
743 gnutls_psk_set_server_credentials_file(tls->credentials.psk_s,
744 CREDFILE);
745 }
746
747 pcmk__notice("Starting TLS listener on port %d", port);
748 remote_tls_fd = init_remote_listener(port);
749
750 try_clear_port:
751 /* Regardless of whether or not we successfully enabled remote-tls-port,
752 * we also want to try to enable remote-clear-port as well.
753 */
754 port_s = pcmk__xe_get(the_cib, PCMK_XA_REMOTE_CLEAR_PORT);
755
756 if ((pcmk__scan_port(port_s, &port) == pcmk_rc_ok) && (port > 0)) {
757 pcmk__warn("Starting clear-text listener on port %d. This is insecure "
758 "and will be removed in a future release. Use "
759 PCMK_XA_REMOTE_TLS_PORT " instead.", port);
760 remote_fd = init_remote_listener(port);
761 }
762 }
763