root/daemons/execd/remoted_tls.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. remoted__read_handshake_data
  2. lrmd_remote_client_msg
  3. lrmd_remote_client_destroy
  4. lrmd_auth_timeout_cb
  5. lrmd_remote_listen
  6. tls_server_dropped
  7. lrmd_tls_server_key_cb
  8. bind_and_listen
  9. get_address_info
  10. lrmd_init_remote_tls_server
  11. execd_stop_tls_server

   1 /*
   2  * Copyright 2012-2024 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 <glib.h>
  13 #include <unistd.h>
  14 
  15 #include <crm/crm.h>
  16 #include <crm/common/mainloop.h>
  17 #include <crm/common/xml.h>
  18 #include <crm/common/remote_internal.h>
  19 #include <crm/common/tls_internal.h>
  20 #include <crm/lrmd_internal.h>
  21 
  22 #include <netdb.h>
  23 #include <sys/socket.h>
  24 #include <netinet/in.h>
  25 #include <netinet/ip.h>
  26 #include <arpa/inet.h>
  27 
  28 #include "pacemaker-execd.h"
  29 
  30 #include <gnutls/gnutls.h>
  31 
  32 #define LRMD_REMOTE_AUTH_TIMEOUT 10000
  33 
  34 static pcmk__tls_t *tls = NULL;
  35 static int ssock = -1;
  36 extern int lrmd_call_id;
  37 
  38 /*!
  39  * \internal
  40  * \brief Read (more) TLS handshake data from client
  41  *
  42  * \param[in,out] client  IPC client doing handshake
  43  *
  44  * \return 0 on success or more data needed, -1 on error
  45  */
  46 static int
  47 remoted__read_handshake_data(pcmk__client_t *client)
     /* [previous][next][first][last][top][bottom][index][help] */
  48 {
  49     int rc = pcmk__read_handshake_data(client);
  50 
  51     if (rc == EAGAIN) {
  52         /* No more data is available at the moment. Just return for now;
  53          * we'll get invoked again once the client sends more.
  54          */
  55         return 0;
  56     } else if (rc != pcmk_rc_ok) {
  57         return -1;
  58     }
  59 
  60     if (client->remote->auth_timeout) {
  61         g_source_remove(client->remote->auth_timeout);
  62     }
  63     client->remote->auth_timeout = 0;
  64 
  65     pcmk__set_client_flags(client, pcmk__client_tls_handshake_complete);
  66     crm_notice("Remote client connection accepted");
  67 
  68     /* Now that the handshake is done, see if any client TLS certificate is
  69      * close to its expiration date and log if so.  If a TLS certificate is not
  70      * in use, this function will just return so we don't need to check for the
  71      * session type here.
  72      */
  73     pcmk__tls_check_cert_expiration(client->remote->tls_session);
  74 
  75     /* Only a client with access to the TLS key can connect, so we can treat
  76      * it as privileged.
  77      */
  78     pcmk__set_client_flags(client, pcmk__client_privileged);
  79 
  80     // Alert other clients of the new connection
  81     notify_of_new_client(client);
  82     return 0;
  83 }
  84 
  85 static int
  86 lrmd_remote_client_msg(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
  87 {
  88     int id = 0;
  89     int rc = pcmk_rc_ok;
  90     xmlNode *request = NULL;
  91     pcmk__client_t *client = data;
  92 
  93     if (!pcmk_is_set(client->flags, pcmk__client_tls_handshake_complete)) {
  94         return remoted__read_handshake_data(client);
  95     }
  96 
  97     rc = pcmk__remote_ready(client->remote, 0);
  98     switch (rc) {
  99         case pcmk_rc_ok:
 100             break;
 101 
 102         case ETIME:
 103             /* No message available to read */
 104             return 0;
 105 
 106         default:
 107             /* Error */
 108             crm_info("Error polling remote client: %s", pcmk_rc_str(rc));
 109             return -1;
 110     }
 111 
 112     rc = pcmk__read_available_remote_data(client->remote);
 113     switch (rc) {
 114         case pcmk_rc_ok:
 115             break;
 116 
 117         case EAGAIN:
 118             /* We haven't read the whole message yet */
 119             return 0;
 120 
 121         default:
 122             /* Error */
 123             crm_info("Error reading from remote client: %s", pcmk_rc_str(rc));
 124             return -1;
 125     }
 126 
 127     request = pcmk__remote_message_xml(client->remote);
 128     if (request == NULL) {
 129         return 0;
 130     }
 131 
 132     crm_element_value_int(request, PCMK__XA_LRMD_REMOTE_MSG_ID, &id);
 133     crm_trace("Processing remote client request %d", id);
 134     if (!client->name) {
 135         client->name = crm_element_value_copy(request,
 136                                               PCMK__XA_LRMD_CLIENTNAME);
 137     }
 138 
 139     lrmd_call_id++;
 140     if (lrmd_call_id < 1) {
 141         lrmd_call_id = 1;
 142     }
 143 
 144     crm_xml_add(request, PCMK__XA_LRMD_CLIENTID, client->id);
 145     crm_xml_add(request, PCMK__XA_LRMD_CLIENTNAME, client->name);
 146     crm_xml_add_int(request, PCMK__XA_LRMD_CALLID, lrmd_call_id);
 147 
 148     process_lrmd_message(client, id, request);
 149     pcmk__xml_free(request);
 150 
 151     return 0;
 152 }
 153 
 154 static void
 155 lrmd_remote_client_destroy(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 156 {
 157     pcmk__client_t *client = user_data;
 158 
 159     if (client == NULL) {
 160         return;
 161     }
 162 
 163     crm_notice("Cleaning up after remote client %s disconnected",
 164                pcmk__client_name(client));
 165 
 166     ipc_proxy_remove_provider(client);
 167 
 168     /* if this is the last remote connection, stop recurring
 169      * operations */
 170     if (pcmk__ipc_client_count() == 1) {
 171         client_disconnect_cleanup(NULL);
 172     }
 173 
 174     if (client->remote->tls_session) {
 175         void *sock_ptr;
 176         int csock;
 177 
 178         sock_ptr = gnutls_transport_get_ptr(client->remote->tls_session);
 179         csock = GPOINTER_TO_INT(sock_ptr);
 180 
 181         gnutls_bye(client->remote->tls_session, GNUTLS_SHUT_RDWR);
 182         gnutls_deinit(client->remote->tls_session);
 183         client->remote->tls_session = NULL;
 184         close(csock);
 185     }
 186 
 187     lrmd_client_destroy(client);
 188     return;
 189 }
 190 
 191 static gboolean
 192 lrmd_auth_timeout_cb(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 193 {
 194     pcmk__client_t *client = data;
 195 
 196     client->remote->auth_timeout = 0;
 197 
 198     if (pcmk_is_set(client->flags,
 199                     pcmk__client_tls_handshake_complete)) {
 200         return FALSE;
 201     }
 202 
 203     mainloop_del_fd(client->remote->source);
 204     client->remote->source = NULL;
 205     crm_err("Remote client authentication timed out");
 206 
 207     return FALSE;
 208 }
 209 
 210 // Dispatch callback for remote server socket
 211 static int
 212 lrmd_remote_listen(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 213 {
 214     int csock = -1;
 215     gnutls_session_t session = NULL;
 216     pcmk__client_t *new_client = NULL;
 217 
 218     // For client socket
 219     static struct mainloop_fd_callbacks lrmd_remote_fd_cb = {
 220         .dispatch = lrmd_remote_client_msg,
 221         .destroy = lrmd_remote_client_destroy,
 222     };
 223 
 224     CRM_CHECK(ssock >= 0, return TRUE);
 225 
 226     if (pcmk__accept_remote_connection(ssock, &csock) != pcmk_rc_ok) {
 227         return TRUE;
 228     }
 229 
 230     session = pcmk__new_tls_session(tls, csock);
 231     if (session == NULL) {
 232         close(csock);
 233         return TRUE;
 234     }
 235 
 236     new_client = pcmk__new_unauth_client(NULL);
 237     new_client->remote = pcmk__assert_alloc(1, sizeof(pcmk__remote_t));
 238     pcmk__set_client_flags(new_client, pcmk__client_tls);
 239     new_client->remote->tls_session = session;
 240 
 241     // Require the client to authenticate within this time
 242     new_client->remote->auth_timeout = pcmk__create_timer(LRMD_REMOTE_AUTH_TIMEOUT,
 243                                                           lrmd_auth_timeout_cb,
 244                                                           new_client);
 245     crm_info("Remote client pending authentication "
 246              QB_XS " %p id: %s", new_client, new_client->id);
 247 
 248     new_client->remote->source =
 249         mainloop_add_fd("pacemaker-remote-client", G_PRIORITY_DEFAULT, csock,
 250                         new_client, &lrmd_remote_fd_cb);
 251     return TRUE;
 252 }
 253 
 254 static void
 255 tls_server_dropped(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 256 {
 257     crm_notice("TLS server session ended");
 258     return;
 259 }
 260 
 261 // \return 0 on success, -1 on error (gnutls_psk_server_credentials_function)
 262 static int
 263 lrmd_tls_server_key_cb(gnutls_session_t session, const char *username, gnutls_datum_t * key)
     /* [previous][next][first][last][top][bottom][index][help] */
 264 {
 265     return (lrmd__init_remote_key(key) == pcmk_rc_ok)? 0 : -1;
 266 }
 267 
 268 static int
 269 bind_and_listen(struct addrinfo *addr)
     /* [previous][next][first][last][top][bottom][index][help] */
 270 {
 271     int optval;
 272     int fd;
 273     int rc;
 274     char buffer[INET6_ADDRSTRLEN] = { 0, };
 275 
 276     pcmk__sockaddr2str(addr->ai_addr, buffer);
 277     crm_trace("Attempting to bind to address %s", buffer);
 278 
 279     fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
 280     if (fd < 0) {
 281         rc = errno;
 282         crm_err("Listener socket creation failed: %", pcmk_rc_str(rc));
 283         return -rc;
 284     }
 285 
 286     /* reuse address */
 287     optval = 1;
 288     rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
 289     if (rc < 0) {
 290         rc = errno;
 291         crm_err("Local address reuse not allowed on %s: %s", buffer, pcmk_rc_str(rc));
 292         close(fd);
 293         return -rc;
 294     }
 295 
 296     if (addr->ai_family == AF_INET6) {
 297         optval = 0;
 298         rc = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval));
 299         if (rc < 0) {
 300             rc = errno;
 301             crm_err("Couldn't disable IPV6-only on %s: %s", buffer, pcmk_rc_str(rc));
 302             close(fd);
 303             return -rc;
 304         }
 305     }
 306 
 307     if (bind(fd, addr->ai_addr, addr->ai_addrlen) != 0) {
 308         rc = errno;
 309         crm_err("Cannot bind to %s: %s", buffer, pcmk_rc_str(rc));
 310         close(fd);
 311         return -rc;
 312     }
 313 
 314     if (listen(fd, 10) == -1) {
 315         rc = errno;
 316         crm_err("Cannot listen on %s: %s", buffer, pcmk_rc_str(rc));
 317         close(fd);
 318         return -rc;
 319     }
 320     return fd;
 321 }
 322 
 323 static int
 324 get_address_info(const char *bind_name, int port, struct addrinfo **res)
     /* [previous][next][first][last][top][bottom][index][help] */
 325 {
 326     int rc;
 327     char port_str[6]; // at most "65535"
 328     struct addrinfo hints;
 329 
 330     memset(&hints, 0, sizeof(struct addrinfo));
 331     hints.ai_flags = AI_PASSIVE;
 332     hints.ai_family = AF_UNSPEC; // IPv6 or IPv4
 333     hints.ai_socktype = SOCK_STREAM;
 334     hints.ai_protocol = IPPROTO_TCP;
 335 
 336     snprintf(port_str, sizeof(port_str), "%d", port);
 337     rc = getaddrinfo(bind_name, port_str, &hints, res);
 338     rc = pcmk__gaierror2rc(rc);
 339 
 340     if (rc != pcmk_rc_ok) {
 341         crm_err("Unable to get IP address(es) for %s: %s",
 342                 (bind_name? bind_name : "local node"), pcmk_rc_str(rc));
 343         return rc;
 344     }
 345 
 346     return pcmk_rc_ok;
 347 }
 348 
 349 int
 350 lrmd_init_remote_tls_server(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 351 {
 352     int rc = pcmk_rc_ok;
 353     int filter;
 354     int port = crm_default_remote_port();
 355     struct addrinfo *res = NULL, *iter;
 356     const char *bind_name = pcmk__env_option(PCMK__ENV_REMOTE_ADDRESS);
 357     bool use_cert = pcmk__x509_enabled();
 358 
 359     static struct mainloop_fd_callbacks remote_listen_fd_callbacks = {
 360         .dispatch = lrmd_remote_listen,
 361         .destroy = tls_server_dropped,
 362     };
 363 
 364     CRM_CHECK(ssock == -1, return ssock);
 365 
 366     crm_debug("Starting TLS listener on %s port %d",
 367               (bind_name? bind_name : "all addresses on"), port);
 368 
 369     rc = pcmk__init_tls(&tls, true, use_cert ? GNUTLS_CRD_CERTIFICATE : GNUTLS_CRD_PSK);
 370     if (rc != pcmk_rc_ok) {
 371         return -1;
 372     }
 373 
 374     if (!use_cert) {
 375         gnutls_datum_t psk_key = { NULL, 0 };
 376 
 377         pcmk__tls_add_psk_callback(tls, lrmd_tls_server_key_cb);
 378 
 379         /* The key callback won't get called until the first client connection
 380          * attempt. Do it once here, so we can warn the user at start-up if we can't
 381          * read the key. We don't error out, though, because it's fine if the key is
 382          * going to be added later.
 383          */
 384         if (lrmd__init_remote_key(&psk_key) != pcmk_rc_ok) {
 385             crm_warn("A cluster connection will not be possible until the key is available");
 386         }
 387 
 388         gnutls_free(psk_key.data);
 389     }
 390 
 391     if (get_address_info(bind_name, port, &res) != pcmk_rc_ok) {
 392         return -1;
 393     }
 394 
 395     /* Currently we listen on only one address from the resulting list (the
 396      * first IPv6 address we can bind to if possible, otherwise the first IPv4
 397      * address we can bind to). When bind_name is NULL, this should be the
 398      * respective wildcard address.
 399      *
 400      * @TODO If there is demand for specifying more than one address, allow
 401      * bind_name to be a space-separated list, call getaddrinfo() for each,
 402      * and create a socket for each result (set IPV6_V6ONLY on IPv6 sockets
 403      * since IPv4 listeners will have their own sockets).
 404      */
 405     iter = res;
 406     filter = AF_INET6;
 407     while (iter) {
 408         if (iter->ai_family == filter) {
 409             ssock = bind_and_listen(iter);
 410         }
 411         if (ssock >= 0) {
 412             break;
 413         }
 414 
 415         iter = iter->ai_next;
 416         if (iter == NULL && filter == AF_INET6) {
 417             iter = res;
 418             filter = AF_INET;
 419         }
 420     }
 421 
 422     if (ssock >= 0) {
 423         mainloop_add_fd("pacemaker-remote-server", G_PRIORITY_DEFAULT, ssock,
 424                         NULL, &remote_listen_fd_callbacks);
 425         crm_debug("Started TLS listener on %s port %d",
 426                   (bind_name? bind_name : "all addresses on"), port);
 427     }
 428     freeaddrinfo(res);
 429     return ssock;
 430 }
 431 
 432 void
 433 execd_stop_tls_server(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 434 {
 435     if (tls != NULL) {
 436         pcmk__free_tls(tls);
 437         tls = NULL;
 438     }
 439 
 440     if (ssock >= 0) {
 441         close(ssock);
 442         ssock = -1;
 443     }
 444 }

/* [previous][next][first][last][top][bottom][index][help] */