1 #include "config.h"
2
3 #include <stdio.h>
4 #include <string.h>
5 #include <stdint.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <sys/time.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <net/if.h>
13 #include <arpa/inet.h>
14 #include <errno.h>
15
16 #include "simpleconfig.h"
17 #include "static_map.h"
18 #include "mcast.h"
19 #include "xvm.h"
20 #include "server_plugin.h"
21 #include "simple_auth.h"
22
23
24 static int
25 yesno(const char *prompt, int dfl)
26 {
27 char result[10];
28
29 printf("%s [%c/%c]? ", prompt, dfl?'Y':'y', dfl?'n':'N');
30 fflush(stdout);
31
32 memset(result, 0, sizeof(result));
33 if (fgets(result, 9, stdin) == NULL)
34 return dfl;
35
36 if (result[0] == 'y' || result[0] == 'Y')
37 return 1;
38 if (result[0] == 'n' || result[0] == 'N')
39 return 0;
40
41 return dfl;
42 }
43
44
45 static int
46 text_input(const char *prompt, const char *dfl, char *input, size_t len)
47 {
48 const char *tmpdfl = dfl;
49 const char *nulldfl = "";
50
51 if (dfl == NULL) {
52 tmpdfl = nulldfl;
53 }
54
55 printf("%s [%s]: ", prompt, tmpdfl);
56 fflush(stdout);
57
58 memset(input, 0, len);
59 if (fgets(input, len, stdin) == NULL) {
60 strncpy(input, tmpdfl, len);
61 return 0;
62 }
63 if (input[strlen(input)-1] == '\n')
64 input[strlen(input)-1] = 0;
65
66 if (strlen(input) == 0) {
67 strncpy(input, tmpdfl, len);
68 return 0;
69 }
70
71 return 0;
72 }
73
74
75 static int
76 plugin_path_configure(config_object_t *config)
77 {
78 char val[4096];
79 char inp[4096];
80 int done = 0;
81
82 if (sc_get(config, "fence_virtd/@module_path", val,
83 sizeof(val))) {
84 #ifdef MODULE_PATH
85 snprintf(val, sizeof(val), MODULE_PATH);
86 #else
87 printf("Failed to determine module search path.\n");
88 #endif
89 }
90
91 do {
92 text_input("Module search path", val, inp, sizeof(inp));
93
94 printf("\n");
95 done = plugin_search(inp);
96 if (done > 0) {
97 plugin_dump();
98 done = 1;
99 } else {
100 done = 0;
101 printf("No modules found in %s!\n", inp);
102 if (yesno("Use this value anyway", 0) == 1)
103 done = 1;
104 }
105 } while (!done);
106
107 sc_set(config, "fence_virtd/@module_path", inp);
108
109 return 0;
110 }
111
112
113 static int
114 backend_config_libvirt(config_object_t *config)
115 {
116 char val[4096];
117 char inp[4096];
118
119 printf("\n");
120 printf("The libvirt backend module is designed for single desktops or\n"
121 "servers. Do not use in environments where virtual machines\n"
122 "may be migrated between hosts.\n\n");
123
124 /* Default backend plugin */
125 if (sc_get(config, "backends/libvirt/@uri", val,
126 sizeof(val))) {
127 strncpy(val, DEFAULT_HYPERVISOR_URI, sizeof(val));
128 }
129
130 text_input("Libvirt URI", val, inp, sizeof(inp));
131
132 sc_set(config, "backends/libvirt/@uri", inp);
133
134 return 0;
135 }
136
137
138 static int
139 backend_config_cpg(config_object_t *config)
140 {
141 char val[4096];
142 char inp[4096];
143 int done = 0;
144
145 printf("\n");
146 printf("The CPG backend module is designed for use in clusters\n"
147 "running corosync and libvirt. It utilizes the CPG API to \n"
148 "route fencing requests, finally utilizing libvirt to perform\n"
149 "fencing actions.\n\n");
150
151 if (sc_get(config, "backends/cpg/@uri", val,
152 sizeof(val))) {
153 strncpy(val, DEFAULT_HYPERVISOR_URI, sizeof(val));
154 }
155
156 text_input("Libvirt URI", val, inp, sizeof(inp));
157
158 sc_set(config, "backends/cpg/@uri", inp);
159
160 printf("\n");
161 printf("The name mode is how the cpg plugin stores and \n"
162 "references virtual machines. Since virtual machine names\n"
163 "are not guaranteed to be unique cluster-wide, use of UUIDs\n"
164 "is strongly recommended. However, for compatibility with \n"
165 "fence_xvmd, the use of 'name' mode is also supported.\n\n");
166
167 if (sc_get(config, "backends/cpg/@name_mode", val,
168 sizeof(val))) {
169 strncpy(val, "uuid", sizeof(val));
170 }
171
172 do {
173 text_input("VM naming/tracking mode (name or uuid)",
174 val, inp, sizeof(inp));
175 if (!strcasecmp(inp, "uuid")) {
176 done = 1;
177 } else if (!strcasecmp(inp, "name")) {
178 done = 0;
179 printf("This can be dangerous if you do not take care to"
180 "ensure that\n"
181 "virtual machine names are unique "
182 "cluster-wide.\n");
183 if (yesno("Use name mode anyway", 1) == 1)
184 done = 1;
185 }
186 } while (!done);
187
188 sc_set(config, "backends/cpg/@name_mode", inp);
189
190 return 0;
191 }
192
193
194 static int
195 listener_config_multicast(config_object_t *config)
196 {
197 char val[4096];
198 char inp[4096];
199 const char *family = "ipv4";
200 struct in_addr sin;
201 struct in6_addr sin6;
202 int done = 0;
203
204 printf("\n");
205 printf("The multicast listener module is designed for use environments\n"
206 "where the guests and hosts may communicate over a network using\n"
207 "multicast.\n\n");
208
209
210 /* MULTICAST IP ADDRESS/FAMILY */
211 printf("The multicast address is the address that a client will use to\n"
212 "send fencing requests to fence_virtd.\n\n");
213
214 if (sc_get(config, "listeners/multicast/@address",
215 val, sizeof(val)-1)) {
216 strncpy(val, IPV4_MCAST_DEFAULT, sizeof(val));
217 }
218
219 done = 0;
220 do {
221 text_input("Multicast IP Address", val, inp, sizeof(inp));
222
223 if (inet_pton(AF_INET, inp, &sin) == 1) {
224 printf("\nUsing ipv4 as family.\n\n");
225 family = "ipv4";
226 done = 1;
227 } else if (inet_pton(AF_INET6, inp, &sin6) == 1) {
228 printf("\nUsing ipv6 as family.\n\n");
229 family = "ipv6";
230 done = 1;
231 } else
232 printf("'%s' is not a valid IP address!\n", inp);
233 } while (!done);
234
235 sc_set(config, "listeners/multicast/@family", family);
236 sc_set(config, "listeners/multicast/@address", inp);
237
238 /* MULTICAST IP PORT */
239 if (sc_get(config, "listeners/multicast/@port",
240 val, sizeof(val)-1)) {
241 snprintf(val, sizeof(val), "%d", DEFAULT_MCAST_PORT);
242 }
243
244 done = 0;
245 do {
246 char *p;
247 int ret;
248
249 text_input("Multicast IP Port", val, inp, sizeof(inp));
250 ret = strtol(inp, &p, 0);
251 if (*p != '\0' || ret <= 0 || ret >= 65536) {
252 printf("Port value '%s' is out of range\n", val);
253 continue;
254 } else
255 done = 1;
256 } while (!done);
257
258 sc_set(config, "listeners/multicast/@port", inp);
259
260 /* MULTICAST INTERFACE */
261 printf("\nSetting a preferred interface causes fence_virtd to listen only\n"
262 "on that interface. Normally, it listens on all interfaces.\n"
263 "In environments where the virtual machines are using the host\n"
264 "machine as a gateway, this *must* be set (typically to virbr0).\n"
265 "Set to 'none' for no interface.\n\n"
266 );
267
268 if (sc_get(config, "listeners/multicast/@interface",
269 val, sizeof(val)-1)) {
270 strncpy(val, "none", sizeof(val));
271 }
272
273 done = 0;
274 do {
275 text_input("Interface", val, inp, sizeof(inp));
276
277 if (!strcasecmp(inp, "none")) {
278 break;
279 }
280
281 if (strlen(inp) > 0) {
282 int ret;
283
284 ret = if_nametoindex(inp);
285 if (ret < 0) {
286 printf("Invalid interface: %s\n", inp);
287 if (yesno("Use anyway", 1) == 1)
288 done = 1;
289 } else
290 done = 1;
291 } else
292 printf("No interface given\n");
293 } while (!done);
294
295 if (!strcasecmp(inp, "none")) {
296 sc_set(config, "listeners/multicast/@interface", NULL);
297 } else {
298 sc_set(config, "listeners/multicast/@interface", inp);
299 }
300
301
302 /* KEY FILE */
303 printf("\nThe key file is the shared key information which is used to\n"
304 "authenticate fencing requests. The contents of this file must\n"
305 "be distributed to each physical host and virtual machine within\n"
306 "a cluster.\n\n");
307
308 if (sc_get(config, "listeners/multicast/@key_file",
309 val, sizeof(val)-1)) {
310 strncpy(val, DEFAULT_KEY_FILE, sizeof(val));
311 }
312
313 done = 0;
314 do {
315 text_input("Key File", val, inp, sizeof(inp));
316
317 if (!strcasecmp(inp, "none")) {
318 break;
319 }
320
321 if (strlen(inp) > 0) {
322 if (inp[0] != '/') {
323 printf("Invalid key file: %s\n", inp);
324 if (yesno("Use anyway", 1) == 1)
325 done = 1;
326 } else
327 done = 1;
328 } else
329 printf("No key file given\n");
330 } while (!done);
331
332 if (!strcasecmp(inp, "none")) {
333 sc_set(config, "listeners/multicast/@key_file", NULL);
334 } else {
335 sc_set(config, "listeners/multicast/@key_file", inp);
336 }
337
338 return 0;
339 }
340
341 static int
342 listener_config_tcp(config_object_t *config)
343 {
344 char val[4096];
345 char inp[4096];
346 const char *family = "ipv4";
347 struct in_addr sin;
348 struct in6_addr sin6;
349 int done = 0;
350
351 printf("\n");
352 printf("The TCP listener module is designed for use in environments\n"
353 "where the guests and hosts communicate over viosproxy.\n\n");
354
355 /* IP ADDRESS/FAMILY */
356 printf("The IP address is the address that a client will use to\n"
357 "send fencing requests to fence_virtd.\n\n");
358
359 if (sc_get(config, "listeners/tcp/@address",
360 val, sizeof(val)-1)) {
361 strncpy(val, IPV4_MCAST_DEFAULT, sizeof(val));
362 }
363
364 done = 0;
365 do {
366 text_input("TCP Listen IP Address", val, inp, sizeof(inp));
367
368 if (inet_pton(AF_INET, inp, &sin) == 1) {
369 printf("\nUsing ipv4 as family.\n\n");
370 family = "ipv4";
371 done = 1;
372 } else if (inet_pton(AF_INET6, inp, &sin6) == 1) {
373 printf("\nUsing ipv6 as family.\n\n");
374 family = "ipv6";
375 done = 1;
376 } else {
377 printf("'%s' is not a valid IP address!\n", inp);
378 continue;
379 }
380 } while (!done);
381
382 sc_set(config, "listeners/tcp/@family", family);
383 sc_set(config, "listeners/tcp/@address", inp);
384
385 /* MULTICAST IP PORT */
386 if (sc_get(config, "listeners/tcp/@port",
387 val, sizeof(val)-1)) {
388 snprintf(val, sizeof(val), "%d", DEFAULT_MCAST_PORT);
389 }
390
391 done = 0;
392 do {
393 char *p;
394 int ret;
395
396 text_input("TCP Listen Port", val, inp, sizeof(inp));
397
398 ret = strtol(inp, &p, 0);
399 if (*p != '\0' || ret <= 0 || ret >= 65536) {
400 printf("Port value '%s' is out of range\n", val);
401 continue;
402 }
403 done = 1;
404 } while (!done);
405 sc_set(config, "listeners/tcp/@port", inp);
406
407 /* KEY FILE */
408 printf("\nThe key file is the shared key information which is used to\n"
409 "authenticate fencing requests. The contents of this file must\n"
410 "be distributed to each physical host and virtual machine within\n"
411 "a cluster.\n\n");
412
413 if (sc_get(config, "listeners/tcp/@key_file",
414 val, sizeof(val)-1)) {
415 strncpy(val, DEFAULT_KEY_FILE, sizeof(val));
416 }
417
418 done = 0;
419 do {
420 text_input("Key File", val, inp, sizeof(inp));
421
422 if (!strcasecmp(inp, "none")) {
423 break;
424 }
425
426 if (strlen(inp) > 0) {
427 if (inp[0] != '/') {
428 printf("Invalid key file: %s\n", inp);
429 if (yesno("Use anyway", 1) == 1)
430 done = 1;
431 } else
432 done = 1;
433 } else
434 printf("No key file given\n");
435 } while (!done);
436
437 if (!strcasecmp(inp, "none")) {
438 sc_set(config, "listeners/tcp/@key_file", NULL);
439 } else {
440 sc_set(config, "listeners/tcp/@key_file", inp);
441 }
442
443 return 0;
444 }
445
446 static int
447 listener_config_serial(config_object_t *config)
448 {
449 char val[4096];
450 char inp[4096];
451 int done;
452
453 printf("\n");
454 printf("The serial plugin allows fence_virtd to communicate with\n"
455 "guests using serial or guest-forwarding VMChannel instead\n"
456 "of using TCP/IP networking.\n\n");
457 printf("Special configuration of virtual machines is required. See\n"
458 "fence_virt.conf(5) for more details.\n\n");
459
460 if (sc_get(config, "listeners/serial/@uri",
461 val, sizeof(val)-1)) {
462 strncpy(val, DEFAULT_HYPERVISOR_URI, sizeof(val));
463 }
464
465 text_input("Libvirt URI", val, inp, sizeof(inp));
466
467 printf("\nSetting a socket path prevents fence_virtd from taking\n"
468 "hold of all Unix domain sockets created when the guest\n"
469 "is started. A value like /var/run/cluster/fence might\n"
470 "be a good value. Don't forget to create the directory!\n\n");
471
472 if (sc_get(config, "listeners/serial/@path",
473 val, sizeof(val)-1)) {
474 strncpy(val, "none", sizeof(val));
475 }
476
477 text_input("Socket directory", val, inp, sizeof(inp));
478 if (!strcasecmp(inp, "none")) {
479 sc_set(config, "listeners/serial/@path", NULL);
480 } else {
481 sc_set(config, "listeners/serial/@path", inp);
482 }
483
484 printf("\nThe serial plugin allows two types of guest to host\n"
485 "configurations. One is via a serial port; the other is\n"
486 "utilizing the newer VMChannel.\n\n");
487
488 if (sc_get(config, "listeners/serial/@mode",
489 val, sizeof(val)-1)) {
490 strncpy(val, "serial", sizeof(val));
491 }
492
493 if (!strcasecmp(inp, "none")) {
494 sc_set(config, "listeners/serial/@path", NULL);
495 } else {
496 sc_set(config, "listeners/serial/@path", inp);
497 }
498
499 done = 0;
500 do {
501 text_input("Mode (serial or vmchannel)", val, inp,
502 sizeof(inp));
503
504 if (strcasecmp(inp, "serial") && strcasecmp(inp, "vmchannel")) {
505 printf("Invalid mode: %s\n", inp);
506 if (yesno("Use anyway", 1) == 1)
507 done = 1;
508 } else
509 done = 1;
510 } while (!done);
511
512 sc_set(config, "listeners/serial/@mode", inp);
513 return 0;
514 }
515
516
517 static int
518 backend_configure(config_object_t *config)
519 {
520 char val[4096];
521 char inp[4096];
522 int done;
523
524 printf("\n");
525 printf("Backend modules are responsible for routing requests to\n"
526 "the appropriate hypervisor or management layer.\n\n");
527
528 /* Default backend plugin */
529 if (sc_get(config, "fence_virtd/@backend", val,
530 sizeof(val))) {
531 strncpy(val, "libvirt", sizeof(val));
532 }
533
534 done = 0;
535 do {
536 text_input("Backend module", val, inp, sizeof(inp));
537 if (plugin_find_backend(inp) == NULL) {
538 printf("No backend module named %s found!\n", inp);
539 if (yesno("Use this value anyway", 0) == 1)
540 done = 1;
541 } else
542 done = 1;
543 } while (!done);
544
545 sc_set(config, "fence_virtd/@backend", inp);
546
547 if (!strcmp(inp, "libvirt")) {
548 backend_config_libvirt(config);
549 } else if (!strcmp(inp, "cpg")) {
550 backend_config_cpg(config);
551 }
552
553 return 0;
554 }
555
556
557 static int
558 listener_configure(config_object_t *config)
559 {
560 char val[4096];
561 char inp[4096];
562 int done;
563
564 printf("\n");
565 printf("Listener modules are responsible for accepting requests\n"
566 "from fencing clients.\n\n");
567
568 /* Default backend plugin */
569 if (sc_get(config, "fence_virtd/@listener", val,
570 sizeof(val))) {
571 strncpy(val, "multicast", sizeof(val));
572 }
573
574 done = 0;
575 do {
576 text_input("Listener module", val, inp, sizeof(inp));
577 if (plugin_find_listener(inp) == NULL) {
578 printf("No listener module named %s found!\n", inp);
579 if (yesno("Use this value anyway", 0) == 1)
580 done = 1;
581 } else
582 done = 1;
583 } while (!done);
584
585 sc_set(config, "fence_virtd/@listener", inp);
586 if (!strcmp(inp, "multicast"))
587 listener_config_multicast(config);
588 else if (!strcmp(inp, "tcp"))
589 listener_config_tcp(config);
590 else if (!strcmp(inp, "serial"))
591 listener_config_serial(config);
592 else
593 printf("Unable to configure unknown listner module '%s'\n", inp);
594
595 return 0;
596 }
597
598
599 int
600 check_file_permissions(const char *fname)
601 {
602 struct stat st;
603 mode_t file_perms = 0600;
604 int ret;
605
|
(1) Event fs_check_call: |
Calling function "stat" to perform check on "fname". |
| Also see events: |
[toctou] |
606 ret = stat(fname, &st);
|
(2) Event cond_false: |
Condition "ret != 0", taking false branch. |
607 if (ret != 0) {
608 printf("stat failed on file '%s': %s\n",
609 fname, strerror(errno));
610 return 1;
|
(3) Event if_end: |
End of if statement. |
611 }
612
|
(4) Event cond_true: |
Condition "(st.st_mode & 511) != file_perms", taking true branch. |
613 if ((st.st_mode & 0777) != file_perms) {
614 printf("Insecure permissions on file "
615 "'%s': changing from 0%o to 0%o.\n", fname,
616 (unsigned int)(st.st_mode & 0777),
617 (unsigned int)file_perms);
|
(5) Event toctou: |
Calling function "chmod" that uses "fname" after a check function. This can cause a time-of-check, time-of-use race condition. |
| Also see events: |
[fs_check_call] |
618 if (chmod(fname, file_perms) != 0) {
619 printf("Unable to change permissions for file '%s'",
620 fname);
621 return 1;
622 }
623 }
624
625 return 0;
626 }
627
628 int
629 do_configure(config_object_t *config, const char *config_file)
630 {
631 FILE *fp = NULL;
632 char message[80];
633 char tmp_filename[4096];
634 int tmp_fd = -1;
635 mode_t old_umask;
636
637 if (sc_parse(config, config_file) != 0) {
638 printf("Parsing of %s failed.\n", config_file);
639 if (yesno("Start from scratch", 0) == 0) {
640 return 1;
641 }
642 }
643
644 plugin_path_configure(config);
645 listener_configure(config);
646 backend_configure(config);
647
648 printf("\nConfiguration complete.\n\n");
649
650 printf("=== Begin Configuration ===\n");
651 sc_dump(config, stdout);
652 printf("=== End Configuration ===\n");
653
654 snprintf(message, sizeof(message), "Replace %s with the above",
655 config_file);
656 if (yesno(message, 0) == 0) {
657 return 1;
658 }
659
660 snprintf(tmp_filename, sizeof(tmp_filename),
661 "%s.XXXXXX", config_file);
662
663 old_umask = umask(077);
664 tmp_fd = mkstemp(tmp_filename);
665 umask(old_umask);
666
667 if (tmp_fd < 0) {
668 perror("fopen");
669 printf("Failed to write configuration file!\n");
670 return 1;
671 }
672
673 fp = fdopen(tmp_fd, "w+");
674 if (fp == NULL)
675 goto out_fail;
676
677 sc_dump(config, fp);
678
679 if (rename(tmp_filename, config_file) < 0) {
680 perror("rename");
681 goto out_fail;
682 }
683
684 fclose(fp);
685 close(tmp_fd);
686
687 return 0;
688
689 out_fail:
690 if (fp)
691 fclose(fp);
692 if (tmp_fd >= 0)
693 close(tmp_fd);
694 if (strlen(tmp_filename))
695 unlink(tmp_filename);
696 printf("Failed to write configuration file!\n");
697 return 1;
698 }
699