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