1    	/*
2    	  Copyright Red Hat, Inc. 2017
3    	
4    	  This program is free software; you can redistribute it and/or modify it
5    	  under the terms of the GNU General Public License as published by the
6    	  Free Software Foundation; either version 2, or (at your option) any
7    	  later version.
8    	
9    	  This program is distributed in the hope that it will be useful, but
10   	  WITHOUT ANY WARRANTY; without even the implied warranty of
11   	  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   	  General Public License for more details.
13   	
14   	  You should have received a copy of the GNU General Public License
15   	  along with this program; see the file COPYING.  If not, write to the
16   	  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
17   	  MA 02139, USA.
18   	*/
19   	#include "config.h"
20   	
21   	#include <unistd.h>
22   	#include <stdint.h>
23   	#include <stdlib.h>
24   	#include <stdio.h>
25   	#include <string.h>
26   	#include <signal.h>
27   	#include <errno.h>
28   	#include <nss.h>
29   	#include <sys/socket.h>
30   	#include <linux/vm_sockets.h>
31   	
32   	/* Local includes */
33   	#include "list.h"
34   	#include "simpleconfig.h"
35   	#include "static_map.h"
36   	#include "server_plugin.h"
37   	#include "history.h"
38   	#include "xvm.h"
39   	#include "simple_auth.h"
40   	#include "options.h"
41   	#include "mcast.h"
42   	#include "tcp.h"
43   	#include "tcp_listener.h"
44   	#include "debug.h"
45   	#include "fdops.h"
46   	
47   	#define NAME "vsock"
48   	#define VSOCK_VERSION "0.2"
49   	
50   	#define VSOCK_MAGIC 0xa32d27c1e
51   	
52   	#define VALIDATE(info) \
53   	do {\
54   		if (!info || info->magic != VSOCK_MAGIC)\
55   			return -EINVAL;\
56   	} while(0)
57   	
58   	typedef struct _vsock_options {
59   		char *key_file;
60   		int cid;
61   		unsigned int port;
62   		unsigned int hash;
63   		unsigned int auth;
64   		unsigned int flags;
65   	} vsock_options;
66   	
67   	
68   	typedef struct _vsock_info {
69   		uint64_t magic;
70   		void *priv;
71   		map_object_t *map;
72   		history_info_t *history;
73   		char key[MAX_KEY_LEN];
74   		vsock_options args;
75   		const fence_callbacks_t *cb;
76   		ssize_t key_len;
77   		int listen_sock;
78   	} vsock_info;
79   	
80   	
81   	struct vsock_hostlist_arg {
82   		map_object_t *map;
83   		int cid;
84   		int fd;
85   	};
86   	
87   	
88   	static int get_peer_cid(int fd, uint32_t *peer_cid) {
89   		struct sockaddr_vm svm;
90   		socklen_t len;
91   		int ret;
92   	
93   		if (!peer_cid)
94   			return -1;
95   	
96   		len = sizeof(svm);
97   		ret = getpeername(fd, (struct sockaddr *) &svm, &len);
98   		if (ret < 0) {
99   			printf("Error getting peer CID: %s\n", strerror(errno));
100  			return -1;
101  		}
102  	
103  		*peer_cid = svm.svm_cid;
104  		return 0;
105  	}
106  	
107  	
108  	/*
109  	 * See if we fenced this node recently (successfully)
110  	 * If so, ignore the request for a few seconds.
111  	 *
112  	 * We purge our history when the entries time out.
113  	 */
114  	static int
115  	check_history(void *a, void *b) {
116  		fence_req_t *old = a, *current = b;
117  	
118  		if (old->request == current->request &&
119  		    old->seqno == current->seqno &&
120  		    !strcasecmp((const char *)old->domain,
121  				(const char *)current->domain)) {
122  			return 1;
123  		}
124  		return 0;
125  	}
126  	
127  	
128  	static int 
129  	vsock_hostlist(const char *vm_name, const char *vm_uuid,
130  		       int state, void *priv)
131  	{
132  		struct vsock_hostlist_arg *arg = (struct vsock_hostlist_arg *) priv;
(1) Event var_decl: Declaring variable "hinfo" without initializer.
Also see events: [uninit_use_in_call]
133  		host_state_t hinfo;
134  		struct timeval tv;
135  		int ret;
136  		uint32_t peer_cid = 0;
137  		char peer_cid_str[24];
138  	
139  		ret = get_peer_cid(arg->fd, &peer_cid);
(2) Event cond_true: Condition "ret < 0", taking true branch.
140  		if (ret < 0) {
141  			printf("Unable to get peer CID: %s\n", strerror(errno));
142  			peer_cid_str[0] = '\0';
(3) Event if_fallthrough: Falling through to end of if statement.
143  		} else
(4) Event if_end: End of if statement.
144  			snprintf(peer_cid_str, sizeof(peer_cid_str), "%u", peer_cid);
145  	
146  		/* Noops if auth == AUTH_NONE */
147  	
(5) Event cond_false: Condition "arg->map->check(arg->map->info, peer_cid_str, vm_uuid, vm_name) == 0", taking false branch.
148  		if (map_check2(arg->map, peer_cid_str, vm_uuid, vm_name) == 0) {
149  			/* if we don't have access to fence this VM,
150  			 * we should not see it in a hostlist either */
151  			return 0;
(6) Event if_end: End of if statement.
152  		}
153  	
154  		strncpy((char *)hinfo.domain, vm_name, sizeof(hinfo.domain) - 1);
155  		strncpy((char *)hinfo.uuid, vm_uuid, sizeof(hinfo.uuid) - 1);
156  		hinfo.state = state;
157  	
158  		tv.tv_sec = 1;
159  		tv.tv_usec = 0;
(7) Event uninit_use_in_call: Using uninitialized value "hinfo". Field "hinfo.pad" is uninitialized when calling "_write_retry". [details]
Also see events: [var_decl]
160  		ret = _write_retry(arg->fd, &hinfo, sizeof(hinfo), &tv);
161  		if (ret == sizeof(hinfo))
162  			return 0;
163  		return 1;
164  	}
165  	
166  	
167  	static int
168  	vsock_hostlist_begin(int fd)
169  	{
170  		struct timeval tv;
171  		char val = (char) RESP_HOSTLIST;
172  	
173  		tv.tv_sec = 1;
174  		tv.tv_usec = 0;
175  		return _write_retry(fd, &val, 1, &tv);
176  	}
177  	
178  	
179  	static int 
180  	vsock_hostlist_end(int fd)
181  	{
182  		host_state_t hinfo;
183  		struct timeval tv;
184  		int ret;
185  	
186  		printf("Sending terminator packet\n");
187  	
188  		memset(&hinfo, 0, sizeof(hinfo));
189  	
190  		tv.tv_sec = 1;
191  		tv.tv_usec = 0;
192  		ret = _write_retry(fd, &hinfo, sizeof(hinfo), &tv);
193  		if (ret == sizeof(hinfo))
194  			return 0;
195  		return 1;
196  	}
197  	
198  	
199  	static int
200  	do_fence_request_vsock(int fd, fence_req_t *req, vsock_info *info)
201  	{
202  		char response = 1;
203  		struct vsock_hostlist_arg arg;
204  		uint32_t peer_cid = 0;
205  		char peer_cid_str[24];
206  		int ret;
207  	
208  		ret = get_peer_cid(fd, &peer_cid);
209  		if (ret < 0) {
210  			printf("Unable to get peer CID: %s\n", strerror(errno));
211  			return -1;
212  		}
213  	
214  		snprintf(peer_cid_str, sizeof(peer_cid_str), "%u", peer_cid);
215  	
216  		/* Noops if auth == AUTH_NONE */
217  		if (sock_response(fd, info->args.auth, info->key, info->key_len, 10) <= 0) {
218  			printf("CID %u Failed to respond to challenge\n", peer_cid);
219  			close(fd);
220  			return -1;
221  		}
222  	
223  		ret = sock_challenge(fd, info->args.auth, info->key, info->key_len, 10);
224  		if (ret <= 0) {
225  			printf("Remote CID %u failed challenge\n", peer_cid);
226  			close(fd);
227  			return -1;
228  		}
229  	
230  		dbg_printf(2, "Request %d seqno %d target %s from CID %u\n", 
231  			   req->request, req->seqno, req->domain, peer_cid);
232  	
233  		switch(req->request) {
234  		case FENCE_NULL:
235  			response = info->cb->null((char *)req->domain, info->priv);
236  			break;
237  		case FENCE_ON:
238  			if (map_check(info->map, peer_cid_str,
239  					     (const char *)req->domain) == 0) {
240  				response = RESP_PERM;
241  				break;
242  			}
243  			response = info->cb->on((char *)req->domain, peer_cid_str,
244  						req->seqno, info->priv);
245  			break;
246  		case FENCE_OFF:
247  			if (map_check(info->map, peer_cid_str,
248  					     (const char *)req->domain) == 0) {
249  				response = RESP_PERM;
250  				break;
251  			}
252  			response = info->cb->off((char *)req->domain, peer_cid_str,
253  						 req->seqno, info->priv);
254  			break;
255  		case FENCE_REBOOT:
256  			if (map_check(info->map, peer_cid_str,
257  					     (const char *)req->domain) == 0) {
258  				response = RESP_PERM;
259  				break;
260  			}
261  			response = info->cb->reboot((char *)req->domain, peer_cid_str,
262  						    req->seqno, info->priv);
263  			break;
264  		case FENCE_STATUS:
265  			if (map_check(info->map, peer_cid_str,
266  					     (const char *)req->domain) == 0) {
267  				response = RESP_PERM;
268  				break;
269  			}
270  			response = info->cb->status((char *)req->domain, info->priv);
271  			break;
272  		case FENCE_DEVSTATUS:
273  			response = info->cb->devstatus(info->priv);
274  			break;
275  		case FENCE_HOSTLIST:
276  			arg.map = info->map;
277  			arg.fd = fd;
278  	
279  			vsock_hostlist_begin(arg.fd);
280  			response = info->cb->hostlist(vsock_hostlist, &arg, info->priv);
281  			vsock_hostlist_end(arg.fd);
282  			break;
283  		}
284  	
285  		dbg_printf(3, "Sending response to caller CID %u...\n", peer_cid);
286  		if (_write_retry(fd, &response, 1, NULL) < 0)
287  			perror("write");
288  	
289  		history_record(info->history, req);
290  	
291  		if (fd != -1)
292  			close(fd);
293  	
294  		return 1;
295  	}
296  	
297  	
298  	static int
299  	vsock_dispatch(listener_context_t c, struct timeval *timeout)
300  	{
301  		vsock_info *info;
302  		fence_req_t data;
303  		fd_set rfds;
304  		int n;
305  		int client_fd;
306  	    int ret;
307  		struct timeval tv;
308  	
309  	    if (timeout != NULL)
310  	    	memcpy(&tv, timeout, sizeof(tv));
311  	    else {
312  	        tv.tv_sec = 1;
313  	        tv.tv_usec = 0;
314  	    }
315  	
316  		info = (vsock_info *) c;
317  		VALIDATE(info);
318  	
319  		FD_ZERO(&rfds);
320  		FD_SET(info->listen_sock, &rfds);
321  	
322  		n = select(info->listen_sock + 1, &rfds, NULL, NULL, timeout);
323  		if (n <= 0) {
324  			if (errno == EINTR || errno == EAGAIN)
325  				n = 0;
326  			else
327  				dbg_printf(2, "select: %s\n", strerror(errno));
328  			return n;
329  		}
330  	
331  		
332  		client_fd = accept(info->listen_sock, NULL, NULL);
333  		if (client_fd < 0) {
334  			perror("accept");
335  			return -1;
336  		}
337  	
338  		dbg_printf(3, "Accepted vsock client...\n");
339  	
340  		ret = _read_retry(client_fd, &data, sizeof(data), &tv);
341  		if (ret != sizeof(data)) {
342  			dbg_printf(3, "Invalid request (read %d bytes)\n", ret);
343  			close(client_fd);
344  			return 0;
345  		}
346  	
347  		swab_fence_req_t(&data);
348  	
349  		if (!verify_request(&data, info->args.hash, info->key, info->key_len)) {
350  			printf("Key mismatch; dropping client\n");
351  	        close(client_fd);
352  			return 0;
353  		}
354  	
355  		dbg_printf(3, "Request %d seqno %d domain %s\n",
356  			data.request, data.seqno, data.domain);
357  	
358  		if (history_check(info->history, &data) == 1) {
359  			printf("We just did this request; dropping client\n");
360  	        close(client_fd);
361  			return 0;
362  		}
363  			
364  		switch(info->args.auth) {
365  		case AUTH_NONE:
366  		case AUTH_SHA1:
367  		case AUTH_SHA256:
368  		case AUTH_SHA512:
369  			printf("VSOCK request\n");
370  			do_fence_request_vsock(client_fd, &data, info);
371  			break;
372  		default:
373  			printf("XXX Unhandled authentication\n");
374  		}
375  	
376  		return 0;
377  	}
378  	
379  	
380  	static int
381  	vsock_config(config_object_t *config, vsock_options *args)
382  	{
383  		char value[1024];
384  		int errors = 0;
385  	
386  		if (sc_get(config, "fence_virtd/@debug", value, sizeof(value))==0)
387  			dset(atoi(value));
388  	
389  		if (sc_get(config, "listeners/vsock/@key_file",
390  			   value, sizeof(value)-1) == 0) {
391  			dbg_printf(1, "Got %s for key_file\n", value);
392  			args->key_file = strdup(value);
393  		} else {
394  			args->key_file = strdup(DEFAULT_KEY_FILE);
395  			if (!args->key_file) {
396  				dbg_printf(1, "Failed to allocate memory\n");
397  				return -1;
398  			}
399  		}
400  	
401  		args->hash = DEFAULT_HASH;
402  		if (sc_get(config, "listeners/vsock/@hash",
403  			   value, sizeof(value)-1) == 0) {
404  			dbg_printf(1, "Got %s for hash\n", value);
405  			if (!strcasecmp(value, "none")) {
406  				args->hash = HASH_NONE;
407  			} else if (!strcasecmp(value, "sha1")) {
408  				args->hash = HASH_SHA1;
409  			} else if (!strcasecmp(value, "sha256")) {
410  				args->hash = HASH_SHA256;
411  			} else if (!strcasecmp(value, "sha512")) {
412  				args->hash = HASH_SHA512;
413  			} else {
414  				dbg_printf(1, "Unsupported hash: %s\n", value);
415  				++errors;
416  			}
417  		}
418  		
419  		args->auth = DEFAULT_AUTH;
420  		if (sc_get(config, "listeners/vsock/@auth",
421  			   value, sizeof(value)-1) == 0) {
422  			dbg_printf(1, "Got %s for auth\n", value);
423  			if (!strcasecmp(value, "none")) {
424  				args->hash = AUTH_NONE;
425  			} else if (!strcasecmp(value, "sha1")) {
426  				args->hash = AUTH_SHA1;
427  			} else if (!strcasecmp(value, "sha256")) {
428  				args->hash = AUTH_SHA256;
429  			} else if (!strcasecmp(value, "sha512")) {
430  				args->hash = AUTH_SHA512;
431  			} else {
432  				dbg_printf(1, "Unsupported auth: %s\n", value);
433  				++errors;
434  			}
435  		}
436  		
437  		args->port = DEFAULT_MCAST_PORT;
438  		if (sc_get(config, "listeners/vsock/@port",
439  			   value, sizeof(value)-1) == 0) {
440  			dbg_printf(1, "Got %s for port\n", value);
441  			args->port = atoi(value);
442  			if (args->port <= 0) {
443  				dbg_printf(1, "Invalid port: %s\n", value);
444  				++errors;
445  			}
446  		}
447  	
448  		return errors;
449  	}
450  	
451  	
452  	static int
453  	vsock_init(listener_context_t *c, const fence_callbacks_t *cb,
454  		   config_object_t *config, map_object_t *map, void *priv)
455  	{
456  		vsock_info *info;
457  		int listen_sock, ret;
458  		struct sockaddr_vm svm;
459  	
460  		if (NSS_NoDB_Init(NULL) != SECSuccess) {
461  			printf("Could not initialize NSS\n");
462  			return 1;
463  		}
464  	
465  		info = calloc(1, sizeof(*info));
466  		if (!info)
467  			return -1;
468  	
469  		info->priv = priv;
470  		info->cb = cb;
471  		info->map = map;
472  	
473  		ret = vsock_config(config, &info->args);
474  		if (ret < 0)
475  			perror("vsock_config");
476  		else if (ret > 0)
477  			printf("%d errors found during vsock listener configuration\n", ret);
478  	
479  		if (ret != 0) {
480  			if (info->args.key_file)
481  				free(info->args.key_file);
482  			free(info);
483  			return -1;
484  		}
485  	
486  		if (info->args.auth != AUTH_NONE || info->args.hash != HASH_NONE) {
487  			info->key_len = read_key_file(info->args.key_file,
488  						info->key, sizeof(info->key));
489  			if (info->key_len < 0) {
490  				printf("Could not read %s; operating without "
491  				       "authentication\n", info->args.key_file);
492  				info->args.auth = AUTH_NONE;
493  				info->args.hash = HASH_NONE;
494  				info->key_len = 0;
495  			}
496  		}
497  	
498  		listen_sock = socket(PF_VSOCK, SOCK_STREAM, 0);
499  		if (listen_sock < 0)
500  			goto out_fail;
501  	
502  		memset(&svm, 0, sizeof(svm));
503  		svm.svm_family = AF_VSOCK;
504  		svm.svm_cid = VMADDR_CID_ANY;
505  		svm.svm_port = info->args.port;
506  	
507  		if (bind(listen_sock, (struct sockaddr *) &svm, sizeof(svm)) < 0)
508  			goto out_fail;
509  	
510  		if (listen(listen_sock, 1) < 0)
511  			goto out_fail;
512  	
513  		info->magic = VSOCK_MAGIC;
514  		info->listen_sock = listen_sock;
515  		info->history = history_init(check_history, 10, sizeof(fence_req_t));
516  		*c = (listener_context_t)info;
517  		return 0;
518  	
519  	out_fail:
520  		printf("Could not set up listen socket: %s\n", strerror(errno));
521  		if (listen_sock >= 0)
522  			close(listen_sock);
523  		if (info->args.key_file)
524  			free(info->args.key_file);
525  		free(info);
526  		return -1;
527  	}
528  	
529  	
530  	static int
531  	vsock_shutdown(listener_context_t c)
532  	{
533  		vsock_info *info = (vsock_info *)c;
534  	
535  		VALIDATE(info);
536  		info->magic = 0;
537  		history_wipe(info->history);
538  		free(info->history);
539  		free(info->args.key_file);
540  		close(info->listen_sock);
541  		free(info);
542  	
543  		return 0;
544  	}
545  	
546  	
547  	static listener_plugin_t vsock_plugin = {
548  		.name = NAME,
549  		.version = VSOCK_VERSION,
550  		.init = vsock_init,
551  		.dispatch = vsock_dispatch,
552  		.cleanup = vsock_shutdown,
553  	};
554  	
555  	double
556  	LISTENER_VER_SYM(void)
557  	{
558  		return PLUGIN_VERSION_LISTENER;
559  	}
560  	
561  	const listener_plugin_t *
562  	LISTENER_INFO_SYM(void)
563  	{
564  		return &vsock_plugin;
565  	}
566