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