This source file includes following definitions.
- cib_remote_inputfd
- cib_remote_set_connection_dnotify
- cib_remote_register_notification
- cib_remote_new
- cib_tls_close
- cib_tls_signon
- cib_remote_connection_destroy
- cib_remote_command_dispatch
- cib_remote_callback_dispatch
- cib_remote_signon
- cib_remote_signoff
- cib_remote_free
- cib_remote_perform_op
- cib__set_output
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <unistd.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <stdarg.h>
16 #include <string.h>
17 #include <netdb.h>
18 #include <termios.h>
19 #include <sys/socket.h>
20
21 #include <glib.h>
22
23 #include <crm/crm.h>
24 #include <crm/cib/internal.h>
25 #include <crm/msg_xml.h>
26 #include <crm/common/ipc_internal.h>
27 #include <crm/common/mainloop.h>
28 #include <crm/common/remote_internal.h>
29 #include <crm/common/output_internal.h>
30
31 #ifdef HAVE_GNUTLS_GNUTLS_H
32
33 # include <gnutls/gnutls.h>
34
35 # define TLS_HANDSHAKE_TIMEOUT_MS 5000
36
37 static gnutls_anon_client_credentials_t anon_cred_c;
38 static gboolean remote_gnutls_credentials_init = FALSE;
39
40 #else
41
42 typedef void gnutls_session_t;
43
44 #endif
45
46 #include <arpa/inet.h>
47
48 #define DH_BITS 1024
49
50 typedef struct cib_remote_opaque_s {
51 int flags;
52 int socket;
53 int port;
54 char *server;
55 char *user;
56 char *passwd;
57 gboolean encrypted;
58 pcmk__remote_t command;
59 pcmk__remote_t callback;
60 pcmk__output_t *out;
61
62 } cib_remote_opaque_t;
63
64 void cib_remote_connection_destroy(gpointer user_data);
65 int cib_remote_callback_dispatch(gpointer user_data);
66 int cib_remote_command_dispatch(gpointer user_data);
67 int cib_remote_signon(cib_t * cib, const char *name, enum cib_conn_type type);
68 int cib_remote_signoff(cib_t * cib);
69 int cib_remote_free(cib_t * cib);
70
71 int cib_remote_perform_op(cib_t * cib, const char *op, const char *host, const char *section,
72 xmlNode * data, xmlNode ** output_data, int call_options,
73 const char *name);
74
75 static int
76 cib_remote_inputfd(cib_t * cib)
77 {
78 cib_remote_opaque_t *private = cib->variant_opaque;
79
80 return private->callback.tcp_socket;
81 }
82
83 static int
84 cib_remote_set_connection_dnotify(cib_t * cib, void (*dnotify) (gpointer user_data))
85 {
86 return -EPROTONOSUPPORT;
87 }
88
89 static int
90 cib_remote_register_notification(cib_t * cib, const char *callback, int enabled)
91 {
92 xmlNode *notify_msg = create_xml_node(NULL, "cib_command");
93 cib_remote_opaque_t *private = cib->variant_opaque;
94
95 crm_xml_add(notify_msg, F_CIB_OPERATION, T_CIB_NOTIFY);
96 crm_xml_add(notify_msg, F_CIB_NOTIFY_TYPE, callback);
97 crm_xml_add_int(notify_msg, F_CIB_NOTIFY_ACTIVATE, enabled);
98 pcmk__remote_send_xml(&private->callback, notify_msg);
99 free_xml(notify_msg);
100 return pcmk_ok;
101 }
102
103 cib_t *
104 cib_remote_new(const char *server, const char *user, const char *passwd, int port,
105 gboolean encrypted)
106 {
107 cib_remote_opaque_t *private = NULL;
108 cib_t *cib = cib_new_variant();
109
110 if (cib == NULL) {
111 return NULL;
112 }
113
114 private = calloc(1, sizeof(cib_remote_opaque_t));
115
116 if (private == NULL) {
117 free(cib);
118 return NULL;
119 }
120
121 cib->variant = cib_remote;
122 cib->variant_opaque = private;
123
124 pcmk__str_update(&private->server, server);
125 pcmk__str_update(&private->user, user);
126 pcmk__str_update(&private->passwd, passwd);
127
128 private->port = port;
129 private->encrypted = encrypted;
130
131
132 cib->delegate_fn = cib_remote_perform_op;
133 cib->cmds->signon = cib_remote_signon;
134 cib->cmds->signoff = cib_remote_signoff;
135 cib->cmds->free = cib_remote_free;
136 cib->cmds->inputfd = cib_remote_inputfd;
137
138 cib->cmds->register_notification = cib_remote_register_notification;
139 cib->cmds->set_connection_dnotify = cib_remote_set_connection_dnotify;
140
141 return cib;
142 }
143
144 static int
145 cib_tls_close(cib_t * cib)
146 {
147 cib_remote_opaque_t *private = cib->variant_opaque;
148
149 #ifdef HAVE_GNUTLS_GNUTLS_H
150 if (private->encrypted) {
151 if (private->command.tls_session) {
152 gnutls_bye(*(private->command.tls_session), GNUTLS_SHUT_RDWR);
153 gnutls_deinit(*(private->command.tls_session));
154 gnutls_free(private->command.tls_session);
155 }
156
157 if (private->callback.tls_session) {
158 gnutls_bye(*(private->callback.tls_session), GNUTLS_SHUT_RDWR);
159 gnutls_deinit(*(private->callback.tls_session));
160 gnutls_free(private->callback.tls_session);
161 }
162 private->command.tls_session = NULL;
163 private->callback.tls_session = NULL;
164 if (remote_gnutls_credentials_init) {
165 gnutls_anon_free_client_credentials(anon_cred_c);
166 gnutls_global_deinit();
167 remote_gnutls_credentials_init = FALSE;
168 }
169 }
170 #endif
171
172 if (private->command.tcp_socket) {
173 shutdown(private->command.tcp_socket, SHUT_RDWR);
174 close(private->command.tcp_socket);
175 }
176 if (private->callback.tcp_socket) {
177 shutdown(private->callback.tcp_socket, SHUT_RDWR);
178 close(private->callback.tcp_socket);
179 }
180 private->command.tcp_socket = 0;
181 private->callback.tcp_socket = 0;
182
183 free(private->command.buffer);
184 free(private->callback.buffer);
185 private->command.buffer = NULL;
186 private->callback.buffer = NULL;
187
188 return 0;
189 }
190
191 static int
192 cib_tls_signon(cib_t *cib, pcmk__remote_t *connection, gboolean event_channel)
193 {
194 cib_remote_opaque_t *private = cib->variant_opaque;
195 int rc;
196
197 xmlNode *answer = NULL;
198 xmlNode *login = NULL;
199
200 static struct mainloop_fd_callbacks cib_fd_callbacks = { 0, };
201
202 cib_fd_callbacks.dispatch =
203 event_channel ? cib_remote_callback_dispatch : cib_remote_command_dispatch;
204 cib_fd_callbacks.destroy = cib_remote_connection_destroy;
205
206 connection->tcp_socket = -1;
207 #ifdef HAVE_GNUTLS_GNUTLS_H
208 connection->tls_session = NULL;
209 #endif
210 rc = pcmk__connect_remote(private->server, private->port, 0, NULL,
211 &(connection->tcp_socket), NULL, NULL);
212 if (rc != pcmk_rc_ok) {
213 crm_info("Remote connection to %s:%d failed: %s " CRM_XS " rc=%d",
214 private->server, private->port, pcmk_rc_str(rc), rc);
215 return -ENOTCONN;
216 }
217
218 if (private->encrypted) {
219
220 #ifdef HAVE_GNUTLS_GNUTLS_H
221 if (remote_gnutls_credentials_init == FALSE) {
222 crm_gnutls_global_init();
223 gnutls_anon_allocate_client_credentials(&anon_cred_c);
224 remote_gnutls_credentials_init = TRUE;
225 }
226
227
228 connection->tls_session = pcmk__new_tls_session(connection->tcp_socket,
229 GNUTLS_CLIENT,
230 GNUTLS_CRD_ANON,
231 anon_cred_c);
232 if (connection->tls_session == NULL) {
233 cib_tls_close(cib);
234 return -1;
235 }
236
237 if (pcmk__tls_client_handshake(connection, TLS_HANDSHAKE_TIMEOUT_MS)
238 != pcmk_rc_ok) {
239 crm_err("Session creation for %s:%d failed", private->server, private->port);
240
241 gnutls_deinit(*connection->tls_session);
242 gnutls_free(connection->tls_session);
243 connection->tls_session = NULL;
244 cib_tls_close(cib);
245 return -1;
246 }
247 #else
248 return -EPROTONOSUPPORT;
249 #endif
250 }
251
252
253 login = create_xml_node(NULL, "cib_command");
254 crm_xml_add(login, "op", "authenticate");
255 crm_xml_add(login, "user", private->user);
256 crm_xml_add(login, "password", private->passwd);
257 crm_xml_add(login, "hidden", "password");
258
259 pcmk__remote_send_xml(connection, login);
260 free_xml(login);
261
262 rc = pcmk_ok;
263 if (pcmk__read_remote_message(connection, -1) == ENOTCONN) {
264 rc = -ENOTCONN;
265 }
266
267 answer = pcmk__remote_message_xml(connection);
268
269 crm_log_xml_trace(answer, "Reply");
270 if (answer == NULL) {
271 rc = -EPROTO;
272
273 } else {
274
275 const char *msg_type = crm_element_value(answer, F_CIB_OPERATION);
276 const char *tmp_ticket = crm_element_value(answer, F_CIB_CLIENTID);
277
278 if (!pcmk__str_eq(msg_type, CRM_OP_REGISTER, pcmk__str_casei)) {
279 crm_err("Invalid registration message: %s", msg_type);
280 rc = -EPROTO;
281
282 } else if (tmp_ticket == NULL) {
283 rc = -EPROTO;
284
285 } else {
286 connection->token = strdup(tmp_ticket);
287 }
288 }
289 free_xml(answer);
290 answer = NULL;
291
292 if (rc != 0) {
293 cib_tls_close(cib);
294 return rc;
295 }
296
297 crm_trace("remote client connection established");
298 connection->source = mainloop_add_fd("cib-remote", G_PRIORITY_HIGH,
299 connection->tcp_socket, cib,
300 &cib_fd_callbacks);
301 return rc;
302 }
303
304 void
305 cib_remote_connection_destroy(gpointer user_data)
306 {
307 crm_err("Connection destroyed");
308 #ifdef HAVE_GNUTLS_GNUTLS_H
309 cib_tls_close(user_data);
310 #endif
311 return;
312 }
313
314 int
315 cib_remote_command_dispatch(gpointer user_data)
316 {
317 int rc;
318 cib_t *cib = user_data;
319 cib_remote_opaque_t *private = cib->variant_opaque;
320
321 rc = pcmk__read_remote_message(&private->command, -1);
322
323 free(private->command.buffer);
324 private->command.buffer = NULL;
325 crm_err("received late reply for remote cib connection, discarding");
326
327 if (rc == ENOTCONN) {
328 return -1;
329 }
330 return 0;
331 }
332
333 int
334 cib_remote_callback_dispatch(gpointer user_data)
335 {
336 int rc;
337 cib_t *cib = user_data;
338 cib_remote_opaque_t *private = cib->variant_opaque;
339
340 xmlNode *msg = NULL;
341
342 crm_info("Message on callback channel");
343
344 rc = pcmk__read_remote_message(&private->callback, -1);
345
346 msg = pcmk__remote_message_xml(&private->callback);
347 while (msg) {
348 const char *type = crm_element_value(msg, F_TYPE);
349
350 crm_trace("Activating %s callbacks...", type);
351
352 if (pcmk__str_eq(type, T_CIB, pcmk__str_casei)) {
353 cib_native_callback(cib, msg, 0, 0);
354
355 } else if (pcmk__str_eq(type, T_CIB_NOTIFY, pcmk__str_casei)) {
356 g_list_foreach(cib->notify_list, cib_native_notify, msg);
357
358 } else {
359 crm_err("Unknown message type: %s", type);
360 }
361
362 free_xml(msg);
363 msg = pcmk__remote_message_xml(&private->callback);
364 }
365
366 if (rc == ENOTCONN) {
367 return -1;
368 }
369
370 return 0;
371 }
372
373 int
374 cib_remote_signon(cib_t * cib, const char *name, enum cib_conn_type type)
375 {
376 int rc = pcmk_ok;
377 cib_remote_opaque_t *private = cib->variant_opaque;
378
379 if (private->passwd == NULL) {
380 if (private->out == NULL) {
381
382
383
384 pcmk__text_prompt("Password", false, &(private->passwd));
385 } else {
386 private->out->prompt("Password", false, &(private->passwd));
387 }
388 }
389
390 if (private->server == NULL || private->user == NULL) {
391 rc = -EINVAL;
392 }
393
394 if (rc == pcmk_ok) {
395 rc = cib_tls_signon(cib, &(private->command), FALSE);
396 }
397
398 if (rc == pcmk_ok) {
399 rc = cib_tls_signon(cib, &(private->callback), TRUE);
400 }
401
402 if (rc == pcmk_ok) {
403 xmlNode *hello =
404 cib_create_op(0, private->callback.token, CRM_OP_REGISTER, NULL, NULL, NULL, 0, NULL);
405 crm_xml_add(hello, F_CIB_CLIENTNAME, name);
406 pcmk__remote_send_xml(&private->command, hello);
407 free_xml(hello);
408 }
409
410 if (rc == pcmk_ok) {
411 crm_info("Opened connection to %s:%d for %s",
412 private->server, private->port, name);
413 cib->state = cib_connected_command;
414 cib->type = cib_command;
415
416 } else {
417 crm_info("Connection to %s:%d for %s failed: %s\n",
418 private->server, private->port, name, pcmk_strerror(rc));
419 }
420
421 return rc;
422 }
423
424 int
425 cib_remote_signoff(cib_t * cib)
426 {
427 int rc = pcmk_ok;
428
429
430
431 crm_debug("Disconnecting from the CIB manager");
432 #ifdef HAVE_GNUTLS_GNUTLS_H
433 cib_tls_close(cib);
434 #endif
435
436 cib->state = cib_disconnected;
437 cib->type = cib_no_connection;
438
439 return rc;
440 }
441
442 int
443 cib_remote_free(cib_t * cib)
444 {
445 int rc = pcmk_ok;
446
447 crm_warn("Freeing CIB");
448 if (cib->state != cib_disconnected) {
449 rc = cib_remote_signoff(cib);
450 if (rc == pcmk_ok) {
451 cib_remote_opaque_t *private = cib->variant_opaque;
452
453 free(private->server);
454 free(private->user);
455 free(private->passwd);
456 free(cib->cmds);
457 free(private);
458 free(cib);
459 }
460 }
461
462 return rc;
463 }
464
465 int
466 cib_remote_perform_op(cib_t * cib, const char *op, const char *host, const char *section,
467 xmlNode * data, xmlNode ** output_data, int call_options, const char *name)
468 {
469 int rc;
470 int remaining_time = 0;
471 time_t start_time;
472
473 xmlNode *op_msg = NULL;
474 xmlNode *op_reply = NULL;
475
476 cib_remote_opaque_t *private = cib->variant_opaque;
477
478 if (cib->state == cib_disconnected) {
479 return -ENOTCONN;
480 }
481
482 if (output_data != NULL) {
483 *output_data = NULL;
484 }
485
486 if (op == NULL) {
487 crm_err("No operation specified");
488 return -EINVAL;
489 }
490
491 cib->call_id++;
492 if (cib->call_id < 1) {
493 cib->call_id = 1;
494 }
495
496 op_msg =
497 cib_create_op(cib->call_id, private->callback.token, op, host, section, data, call_options,
498 NULL);
499 if (op_msg == NULL) {
500 return -EPROTO;
501 }
502
503 crm_trace("Sending %s message to the CIB manager", op);
504 if (!(call_options & cib_sync_call)) {
505 pcmk__remote_send_xml(&private->callback, op_msg);
506 } else {
507 pcmk__remote_send_xml(&private->command, op_msg);
508 }
509 free_xml(op_msg);
510
511 if ((call_options & cib_discard_reply)) {
512 crm_trace("Discarding reply");
513 return pcmk_ok;
514
515 } else if (!(call_options & cib_sync_call)) {
516 return cib->call_id;
517 }
518
519 crm_trace("Waiting for a synchronous reply");
520
521 start_time = time(NULL);
522 remaining_time = cib->call_timeout ? cib->call_timeout : 60;
523
524 rc = pcmk_rc_ok;
525 while (remaining_time > 0 && (rc != ENOTCONN)) {
526 int reply_id = -1;
527 int msg_id = cib->call_id;
528
529 rc = pcmk__read_remote_message(&private->command,
530 remaining_time * 1000);
531 op_reply = pcmk__remote_message_xml(&private->command);
532
533 if (!op_reply) {
534 break;
535 }
536
537 crm_element_value_int(op_reply, F_CIB_CALLID, &reply_id);
538
539 if (reply_id == msg_id) {
540 break;
541
542 } else if (reply_id < msg_id) {
543 crm_debug("Received old reply: %d (wanted %d)", reply_id, msg_id);
544 crm_log_xml_trace(op_reply, "Old reply");
545
546 } else if ((reply_id - 10000) > msg_id) {
547
548 crm_debug("Received old reply: %d (wanted %d)", reply_id, msg_id);
549 crm_log_xml_trace(op_reply, "Old reply");
550 } else {
551 crm_err("Received a __future__ reply:" " %d (wanted %d)", reply_id, msg_id);
552 }
553
554 free_xml(op_reply);
555 op_reply = NULL;
556
557
558 remaining_time = time(NULL) - start_time;
559 }
560
561
562
563
564
565
566
567 if (rc == ENOTCONN) {
568 crm_err("Disconnected while waiting for reply.");
569 return -ENOTCONN;
570 } else if (op_reply == NULL) {
571 crm_err("No reply message - empty");
572 return -ENOMSG;
573 }
574
575 crm_trace("Synchronous reply received");
576
577
578 if (crm_element_value_int(op_reply, F_CIB_RC, &rc) != 0) {
579 rc = -EPROTO;
580 }
581
582 if (rc == -pcmk_err_diff_resync) {
583
584 rc = pcmk_ok;
585 }
586
587 if (rc == pcmk_ok || rc == -EPERM) {
588 crm_log_xml_debug(op_reply, "passed");
589
590 } else {
591
592 crm_err("Call failed: %s", pcmk_strerror(rc));
593 crm_log_xml_warn(op_reply, "failed");
594 }
595
596 if (output_data == NULL) {
597
598
599 } else if (!(call_options & cib_discard_reply)) {
600 xmlNode *tmp = get_message_xml(op_reply, F_CIB_CALLDATA);
601
602 if (tmp == NULL) {
603 crm_trace("No output in reply to \"%s\" command %d", op, cib->call_id - 1);
604 } else {
605 *output_data = copy_xml(tmp);
606 }
607 }
608
609 free_xml(op_reply);
610
611 return rc;
612 }
613
614 void
615 cib__set_output(cib_t *cib, pcmk__output_t *out)
616 {
617 cib_remote_opaque_t *private;
618
619 if (cib->variant != cib_remote) {
620 return;
621 }
622
623 private = cib->variant_opaque;
624 private->out = out;
625 }