This source file includes following definitions.
- remote_connection_destroy
- init_remote_listener
- check_group_membership
- cib_remote_auth
- remote_auth_timeout_cb
- cib_remote_listen
- cib_remote_connection_destroy
- cib_handle_remote_msg
- cib_remote_msg
- construct_pam_passwd
- authenticate_user
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11 #include <crm/crm.h>
12
13 #include <sys/param.h>
14 #include <stdio.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
18 #include <inttypes.h>
19 #include <sys/socket.h>
20 #include <arpa/inet.h>
21
22 #include <netinet/ip.h>
23
24 #include <stdlib.h>
25 #include <errno.h>
26
27 #include <glib.h>
28 #include <libxml/tree.h>
29
30 #include <crm/common/ipc.h>
31 #include <crm/common/ipc_internal.h>
32 #include <crm/common/xml.h>
33 #include <crm/common/remote_internal.h>
34 #include <crm/common/tls_internal.h>
35 #include <crm/cib/internal.h>
36
37 #include "pacemaker-based.h"
38
39 #include <gnutls/gnutls.h>
40
41 #include <pwd.h>
42 #include <grp.h>
43 #if HAVE_SECURITY_PAM_APPL_H
44 # include <security/pam_appl.h>
45 # define HAVE_PAM 1
46 #elif HAVE_PAM_PAM_APPL_H
47 # include <pam/pam_appl.h>
48 # define HAVE_PAM 1
49 #endif
50
51 static pcmk__tls_t *tls = NULL;
52
53 extern int remote_tls_fd;
54 extern gboolean cib_shutdown_flag;
55
56 int init_remote_listener(int port, gboolean encrypted);
57 void cib_remote_connection_destroy(gpointer user_data);
58
59
60 #define REMOTE_AUTH_TIMEOUT 10000
61
62 int num_clients;
63 static bool authenticate_user(const char *user, const char *passwd);
64 static int cib_remote_listen(gpointer data);
65 static int cib_remote_msg(gpointer data);
66
67 static void
68 remote_connection_destroy(gpointer user_data)
69 {
70 crm_info("No longer listening for remote connections");
71 return;
72 }
73
74 int
75 init_remote_listener(int port, gboolean encrypted)
76 {
77 int rc;
78 int *ssock = NULL;
79 struct sockaddr_in saddr;
80 int optval;
81
82 static struct mainloop_fd_callbacks remote_listen_fd_callbacks = {
83 .dispatch = cib_remote_listen,
84 .destroy = remote_connection_destroy,
85 };
86
87 if (port <= 0) {
88
89 return 0;
90 }
91
92 if (encrypted) {
93 bool use_cert = pcmk__x509_enabled();
94
95 crm_notice("Starting TLS listener on port %d", port);
96
97 rc = pcmk__init_tls(&tls, true, use_cert ? GNUTLS_CRD_CERTIFICATE : GNUTLS_CRD_ANON);
98 if (rc != pcmk_rc_ok) {
99 return -1;
100 }
101 } else {
102 crm_warn("Starting plain-text listener on port %d", port);
103 }
104 #ifndef HAVE_PAM
105 crm_warn("This build does not support remote administrators "
106 "because PAM support is not available");
107 #endif
108
109
110 ssock = pcmk__assert_alloc(1, sizeof(int));
111 *ssock = socket(AF_INET, SOCK_STREAM, 0);
112 if (*ssock == -1) {
113 crm_err("Listener socket creation failed: %s", pcmk_rc_str(errno));
114 free(ssock);
115 return -1;
116 }
117
118
119 optval = 1;
120 rc = setsockopt(*ssock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
121 if (rc < 0) {
122 crm_err("Local address reuse not allowed on listener socket: %s",
123 pcmk_rc_str(errno));
124 }
125
126
127 memset(&saddr, '\0', sizeof(saddr));
128 saddr.sin_family = AF_INET;
129 saddr.sin_addr.s_addr = INADDR_ANY;
130 saddr.sin_port = htons(port);
131 if (bind(*ssock, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) {
132 crm_err("Cannot bind to listener socket: %s", pcmk_rc_str(errno));
133 close(*ssock);
134 free(ssock);
135 return -2;
136 }
137 if (listen(*ssock, 10) == -1) {
138 crm_err("Cannot listen on socket: %s", pcmk_rc_str(errno));
139 close(*ssock);
140 free(ssock);
141 return -3;
142 }
143
144 mainloop_add_fd("cib-remote", G_PRIORITY_DEFAULT, *ssock, ssock, &remote_listen_fd_callbacks);
145 crm_debug("Started listener on port %d", port);
146
147 return *ssock;
148 }
149
150 static int
151 check_group_membership(const char *usr, const char *grp)
152 {
153 int index = 0;
154 struct passwd *pwd = NULL;
155 struct group *group = NULL;
156
157 pwd = getpwnam(usr);
158 if (pwd == NULL) {
159 crm_notice("Rejecting remote client: '%s' is not a valid user", usr);
160 return FALSE;
161 }
162
163 group = getgrgid(pwd->pw_gid);
164 if (group != NULL && pcmk__str_eq(grp, group->gr_name, pcmk__str_none)) {
165 return TRUE;
166 }
167
168 group = getgrnam(grp);
169 if (group == NULL) {
170 crm_err("Rejecting remote client: '%s' is not a valid group", grp);
171 return FALSE;
172 }
173
174 while (TRUE) {
175 char *member = group->gr_mem[index++];
176
177 if (member == NULL) {
178 break;
179
180 } else if (pcmk__str_eq(usr, member, pcmk__str_none)) {
181 return TRUE;
182 }
183 }
184
185 crm_notice("Rejecting remote client: User '%s' is not a member of "
186 "group '%s'", usr, grp);
187 return FALSE;
188 }
189
190 static gboolean
191 cib_remote_auth(xmlNode * login)
192 {
193 const char *user = NULL;
194 const char *pass = NULL;
195 const char *tmp = NULL;
196
197 if (login == NULL) {
198 return FALSE;
199 }
200
201 if (!pcmk__xe_is(login, PCMK__XE_CIB_COMMAND)) {
202 crm_warn("Rejecting remote client: Unrecognizable message "
203 "(element '%s' not '" PCMK__XE_CIB_COMMAND "')", login->name);
204 crm_log_xml_debug(login, "bad");
205 return FALSE;
206 }
207
208 tmp = crm_element_value(login, PCMK_XA_OP);
209 if (!pcmk__str_eq(tmp, "authenticate", pcmk__str_casei)) {
210 crm_warn("Rejecting remote client: Unrecognizable message "
211 "(operation '%s' not 'authenticate')", tmp);
212 crm_log_xml_debug(login, "bad");
213 return FALSE;
214 }
215
216 user = crm_element_value(login, PCMK_XA_USER);
217 pass = crm_element_value(login, PCMK__XA_PASSWORD);
218 if (!user || !pass) {
219 crm_warn("Rejecting remote client: No %s given",
220 ((user == NULL)? "username" : "password"));
221 crm_log_xml_debug(login, "bad");
222 return FALSE;
223 }
224
225 crm_log_xml_debug(login, "auth");
226
227 return check_group_membership(user, CRM_DAEMON_GROUP)
228 && authenticate_user(user, pass);
229 }
230
231 static gboolean
232 remote_auth_timeout_cb(gpointer data)
233 {
234 pcmk__client_t *client = data;
235
236 client->remote->auth_timeout = 0;
237
238 if (pcmk_is_set(client->flags, pcmk__client_authenticated)) {
239 return FALSE;
240 }
241
242 mainloop_del_fd(client->remote->source);
243 crm_err("Remote client authentication timed out");
244
245 return FALSE;
246 }
247
248 static int
249 cib_remote_listen(gpointer data)
250 {
251 int csock = -1;
252 unsigned laddr;
253 struct sockaddr_storage addr;
254 char ipstr[INET6_ADDRSTRLEN];
255 int ssock = *(int *)data;
256 int rc;
257
258 pcmk__client_t *new_client = NULL;
259
260 static struct mainloop_fd_callbacks remote_client_fd_callbacks = {
261 .dispatch = cib_remote_msg,
262 .destroy = cib_remote_connection_destroy,
263 };
264
265
266 laddr = sizeof(addr);
267 memset(&addr, 0, sizeof(addr));
268 csock = accept(ssock, (struct sockaddr *)&addr, &laddr);
269 if (csock == -1) {
270 crm_warn("Could not accept remote connection: %s", pcmk_rc_str(errno));
271 return TRUE;
272 }
273
274 pcmk__sockaddr2str(&addr, ipstr);
275
276 rc = pcmk__set_nonblocking(csock);
277 if (rc != pcmk_rc_ok) {
278 crm_warn("Dropping remote connection from %s because "
279 "it could not be set to non-blocking: %s",
280 ipstr, pcmk_rc_str(rc));
281 close(csock);
282 return TRUE;
283 }
284
285 num_clients++;
286
287 new_client = pcmk__new_unauth_client(NULL);
288 new_client->remote = pcmk__assert_alloc(1, sizeof(pcmk__remote_t));
289
290 if (ssock == remote_tls_fd) {
291 pcmk__set_client_flags(new_client, pcmk__client_tls);
292
293
294 new_client->remote->tls_session = pcmk__new_tls_session(tls, csock);
295 if (new_client->remote->tls_session == NULL) {
296 close(csock);
297 return TRUE;
298 }
299 } else {
300 pcmk__set_client_flags(new_client, pcmk__client_tcp);
301 new_client->remote->tcp_socket = csock;
302 }
303
304
305 new_client->remote->auth_timeout = pcmk__create_timer(REMOTE_AUTH_TIMEOUT,
306 remote_auth_timeout_cb,
307 new_client);
308 crm_info("%s connection from %s pending authentication for client %s",
309 ((ssock == remote_tls_fd)? "Encrypted" : "Clear-text"),
310 ipstr, new_client->id);
311
312 new_client->remote->source =
313 mainloop_add_fd("cib-remote-client", G_PRIORITY_DEFAULT, csock, new_client,
314 &remote_client_fd_callbacks);
315
316 return TRUE;
317 }
318
319 void
320 cib_remote_connection_destroy(gpointer user_data)
321 {
322 pcmk__client_t *client = user_data;
323 int csock = -1;
324
325 if (client == NULL) {
326 return;
327 }
328
329 crm_trace("Cleaning up after client %s disconnect",
330 pcmk__client_name(client));
331
332 num_clients--;
333 crm_trace("Num unfree'd clients: %d", num_clients);
334
335 switch (PCMK__CLIENT_TYPE(client)) {
336 case pcmk__client_tcp:
337 csock = client->remote->tcp_socket;
338 break;
339 case pcmk__client_tls:
340 if (client->remote->tls_session) {
341 csock = pcmk__tls_get_client_sock(client->remote);
342
343 if (pcmk_is_set(client->flags,
344 pcmk__client_tls_handshake_complete)) {
345 gnutls_bye(client->remote->tls_session, GNUTLS_SHUT_WR);
346 }
347 gnutls_deinit(client->remote->tls_session);
348 client->remote->tls_session = NULL;
349 }
350 break;
351 default:
352 crm_warn("Unknown transport for client %s "
353 QB_XS " flags=%#016" PRIx64,
354 pcmk__client_name(client), client->flags);
355 }
356
357 if (csock >= 0) {
358 close(csock);
359 }
360
361 pcmk__free_client(client);
362
363 crm_trace("Freed the cib client");
364
365 if (cib_shutdown_flag) {
366 cib_shutdown(0);
367 }
368 return;
369 }
370
371 static void
372 cib_handle_remote_msg(pcmk__client_t *client, xmlNode *command)
373 {
374 if (!pcmk__xe_is(command, PCMK__XE_CIB_COMMAND)) {
375 crm_log_xml_trace(command, "bad");
376 return;
377 }
378
379 if (client->name == NULL) {
380 client->name = pcmk__str_copy(client->id);
381 }
382
383
384 pcmk__xe_remove_attr(command, PCMK__XA_SRC);
385 pcmk__xe_remove_attr(command, PCMK__XA_CIB_HOST);
386 pcmk__xe_remove_attr(command, PCMK__XA_CIB_UPDATE);
387
388 crm_xml_add(command, PCMK__XA_T, PCMK__VALUE_CIB);
389 crm_xml_add(command, PCMK__XA_CIB_CLIENTID, client->id);
390 crm_xml_add(command, PCMK__XA_CIB_CLIENTNAME, client->name);
391 crm_xml_add(command, PCMK__XA_CIB_USER, client->user);
392
393 if (crm_element_value(command, PCMK__XA_CIB_CALLID) == NULL) {
394 char *call_uuid = crm_generate_uuid();
395
396
397 crm_xml_add(command, PCMK__XA_CIB_CALLID, call_uuid);
398 free(call_uuid);
399 }
400
401 if (crm_element_value(command, PCMK__XA_CIB_CALLOPT) == NULL) {
402 crm_xml_add_int(command, PCMK__XA_CIB_CALLOPT, 0);
403 }
404
405 crm_log_xml_trace(command, "Remote command: ");
406 cib_common_callback_worker(0, 0, command, client, TRUE);
407 }
408
409 static int
410 cib_remote_msg(gpointer data)
411 {
412 xmlNode *command = NULL;
413 pcmk__client_t *client = data;
414 int rc;
415 const char *client_name = pcmk__client_name(client);
416
417 crm_trace("Remote %s message received for client %s",
418 pcmk__client_type_str(PCMK__CLIENT_TYPE(client)), client_name);
419
420 if ((PCMK__CLIENT_TYPE(client) == pcmk__client_tls)
421 && !pcmk_is_set(client->flags, pcmk__client_tls_handshake_complete)) {
422
423 int rc = pcmk__read_handshake_data(client);
424
425 if (rc == EAGAIN) {
426
427
428
429 return 0;
430 } else if (rc != pcmk_rc_ok) {
431 return -1;
432 }
433
434 crm_debug("Completed TLS handshake with remote client %s", client_name);
435 pcmk__set_client_flags(client, pcmk__client_tls_handshake_complete);
436 if (client->remote->auth_timeout) {
437 g_source_remove(client->remote->auth_timeout);
438 }
439
440
441
442
443
444
445 pcmk__tls_check_cert_expiration(client->remote->tls_session);
446
447
448 client->remote->auth_timeout = pcmk__create_timer(REMOTE_AUTH_TIMEOUT,
449 remote_auth_timeout_cb,
450 client);
451 return 0;
452 }
453
454 rc = pcmk__read_available_remote_data(client->remote);
455 switch (rc) {
456 case pcmk_rc_ok:
457 break;
458
459 case EAGAIN:
460
461 return 0;
462
463 default:
464
465 crm_trace("Error reading from remote client: %s", pcmk_rc_str(rc));
466 return -1;
467 }
468
469
470 if (!pcmk_is_set(client->flags, pcmk__client_authenticated)) {
471 xmlNode *reg;
472 const char *user = NULL;
473
474 command = pcmk__remote_message_xml(client->remote);
475 if (cib_remote_auth(command) == FALSE) {
476 pcmk__xml_free(command);
477 return -1;
478 }
479
480 pcmk__set_client_flags(client, pcmk__client_authenticated);
481 g_source_remove(client->remote->auth_timeout);
482 client->remote->auth_timeout = 0;
483 client->name = crm_element_value_copy(command, PCMK_XA_NAME);
484
485 user = crm_element_value(command, PCMK_XA_USER);
486 if (user) {
487 client->user = pcmk__str_copy(user);
488 }
489
490 crm_notice("Remote connection accepted for authenticated user %s "
491 QB_XS " client %s",
492 pcmk__s(user, ""), client_name);
493
494
495 reg = pcmk__xe_create(NULL, PCMK__XE_CIB_RESULT);
496 crm_xml_add(reg, PCMK__XA_CIB_OP, CRM_OP_REGISTER);
497 crm_xml_add(reg, PCMK__XA_CIB_CLIENTID, client->id);
498 pcmk__remote_send_xml(client->remote, reg);
499 pcmk__xml_free(reg);
500 pcmk__xml_free(command);
501 }
502
503 command = pcmk__remote_message_xml(client->remote);
504 if (command != NULL) {
505 crm_trace("Remote message received from client %s", client_name);
506 cib_handle_remote_msg(client, command);
507 pcmk__xml_free(command);
508 }
509
510 return 0;
511 }
512
513 #ifdef HAVE_PAM
514
515
516
517
518
519
520
521
522
523
524
525
526
527 static int
528 construct_pam_passwd(int num_msg, const struct pam_message **msg,
529 struct pam_response **response, void *data)
530 {
531
532
533
534
535 CRM_CHECK((num_msg == 1) && (msg != NULL) && (response != NULL)
536 && (data != NULL), return PAM_CONV_ERR);
537
538 switch (msg[0]->msg_style) {
539 case PAM_PROMPT_ECHO_OFF:
540 case PAM_PROMPT_ECHO_ON:
541
542 break;
543 case PAM_TEXT_INFO:
544 crm_info("PAM: %s", msg[0]->msg);
545 data = NULL;
546 break;
547 case PAM_ERROR_MSG:
548
549
550
551 crm_err("PAM reported an error");
552 data = NULL;
553 break;
554 default:
555 crm_warn("Ignoring PAM message of unrecognized type %d",
556 msg[0]->msg_style);
557 return PAM_CONV_ERR;
558 }
559
560 *response = calloc(1, sizeof(struct pam_response));
561 if (*response == NULL) {
562 return PAM_BUF_ERR;
563 }
564 (*response)->resp_retcode = 0;
565 (*response)->resp = pcmk__str_copy((const char *) data);
566 return PAM_SUCCESS;
567 }
568 #endif
569
570
571
572
573
574
575
576
577
578
579
580 static bool
581 authenticate_user(const char *user, const char *passwd)
582 {
583 #ifdef HAVE_PAM
584 int rc = 0;
585 bool pass = false;
586 const void *p_user = NULL;
587 struct pam_conv p_conv;
588 struct pam_handle *pam_h = NULL;
589
590 static const char *pam_name = NULL;
591
592 if (pam_name == NULL) {
593 pam_name = getenv("CIB_pam_service");
594 if (pam_name == NULL) {
595 pam_name = "login";
596 }
597 }
598
599 p_conv.conv = construct_pam_passwd;
600 p_conv.appdata_ptr = (void *) passwd;
601
602 rc = pam_start(pam_name, user, &p_conv, &pam_h);
603 if (rc != PAM_SUCCESS) {
604 crm_warn("Rejecting remote client for user %s "
605 "because PAM initialization failed: %s",
606 user, pam_strerror(pam_h, rc));
607 goto bail;
608 }
609
610
611 rc = pam_authenticate(pam_h, PAM_SILENT);
612 if (rc != PAM_SUCCESS) {
613 crm_notice("Access for remote user %s denied: %s",
614 user, pam_strerror(pam_h, rc));
615 goto bail;
616 }
617
618
619
620
621
622 rc = pam_get_item(pam_h, PAM_USER, &p_user);
623 if (rc != PAM_SUCCESS) {
624 crm_warn("Rejecting remote client for user %s "
625 "because PAM failed to return final user name: %s",
626 user, pam_strerror(pam_h, rc));
627 goto bail;
628 }
629 if (p_user == NULL) {
630 crm_warn("Rejecting remote client for user %s "
631 "because PAM returned no final user name", user);
632 goto bail;
633 }
634
635
636 if (!pcmk__str_eq(p_user, user, pcmk__str_none)) {
637 crm_warn("Rejecting remote client for user %s "
638 "because PAM returned different final user name %s",
639 user, p_user);
640 goto bail;
641 }
642
643
644 rc = pam_acct_mgmt(pam_h, PAM_SILENT);
645 if (rc != PAM_SUCCESS) {
646 crm_notice("Access for remote user %s denied: %s",
647 user, pam_strerror(pam_h, rc));
648 goto bail;
649 }
650 pass = true;
651
652 bail:
653 pam_end(pam_h, rc);
654 return pass;
655 #else
656
657 crm_warn("Rejecting remote user %s because this build does not have "
658 "PAM support", user);
659 return false;
660 #endif
661 }