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