xref: /illumos-gate/usr/src/cmd/sckmd/sparc/sun4u/sckmd.c (revision 7a17cfad)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * sckmd - Starcat Key Management Daemon
28  *
29  * The sckmd is a daemon that runs on a domain and is responsible for
30  * establishing security associations (SAs) for secure communication
31  * with the System Controller (SC). All SAs are created on the SC
32  * and propogated to the sckmd through the sckm driver running on
33  * the domain. The sckmd then passes the SA to the key engine via the
34  * PF_KEY interface.
35  */
36 
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <stdarg.h>
40 #include <unistd.h>
41 #include <fcntl.h>
42 #include <string.h>
43 #include <errno.h>
44 #include <syslog.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <sys/times.h>
48 #include <sys/fcntl.h>
49 #include <sys/socket.h>
50 #include <net/pfkeyv2.h>
51 #include <netinet/in.h>
52 #include <ipsec_util.h>
53 
54 #include <sys/sckm_io.h>
55 
56 
57 #ifdef SCKMD_DEBUG
58 #define	OPT_STR	"ds"
59 #else /* SCKMD_DEBUG */
60 #define	OPT_STR	""
61 #endif /* SCKMD_DEBUG */
62 
63 #define	KM_DEV	"/dev/kmdrv"
64 
65 #define	SCKMD_MAX_MSG_SIZE	1024
66 #define	SCKMD_ERR_MSG_SIZE	512
67 #define	SCKMD_MSG_HDR_SIZE	sizeof (struct sadb_msg)
68 
69 #define	SCKMD_CURR_PFKEY_VER	PF_KEY_V2
70 #define	SCKMD_PFKEY_TIMEOUT	3000	/* 3 seconds */
71 
72 
73 static pid_t mypid;
74 static int standalone;
75 static int debug;
76 static int keysock;
77 static uint32_t seq = 0;
78 static uint64_t msg_buf[SCKMD_MAX_MSG_SIZE];
79 
80 
81 static int process_sckm_req(int fd, sckm_ioctl_getreq_t *msg);
82 static int send_sckm_status(int fd, sckm_ioctl_status_t *msg);
83 static int get_pfkey_reply(uint32_t req_seq, uint8_t req_type, int *err);
84 static struct sadb_msg *read_pfkey_msg(void);
85 static int convert_pfkey_msg(struct sadb_msg *msg);
86 static void sckmd_log(int priority, char *fmt, ...);
87 
88 
89 /*
90  * main:
91  *
92  * Initialize sckmd and enter an infinite loop. The loop waits for
93  * sckm messages from the sckm driver and dispatches each message
94  * to be processed synchronously.
95  */
96 int
main(int argc,char ** argv)97 main(int argc, char **argv)
98 {
99 	int			opt;
100 	int			fd;
101 	sckm_ioctl_getreq_t	msg;
102 
103 
104 	/*
105 	 * Set defaults
106 	 */
107 	standalone = 0;
108 	debug = 0;
109 	mypid = getpid();
110 
111 	openlog("sckmd", LOG_CONS | LOG_NDELAY, LOG_DAEMON);
112 
113 	/*
114 	 * Check command line options
115 	 */
116 	opterr = 0;	/* disable getopt error messages */
117 	while ((opt = getopt(argc, argv, OPT_STR)) != EOF) {
118 
119 		switch (opt) {
120 
121 		case 'd':
122 			debug++;
123 			break;
124 
125 		case 's':
126 			standalone++;
127 			break;
128 
129 		default:
130 			sckmd_log(LOG_ERR, "unknown command line option\n");
131 			exit(1);
132 		}
133 	}
134 
135 	sckmd_log(LOG_DEBUG, "starting sckmd...\n");
136 
137 	/*
138 	 * IPsec must get loaded in-kernel.  The easiest way to do this is
139 	 * to open (then close) a PF_KEY socket.
140 	 */
141 	if ((keysock = socket(PF_KEY, SOCK_RAW, SCKMD_CURR_PFKEY_VER)) == -1) {
142 		sckmd_log(LOG_DEBUG, "PF_KEY open for IPsec load failed: %s\n",
143 		    strerror(errno));
144 		exit(1);
145 	}
146 	(void) close(keysock);
147 	sckmd_log(LOG_ERR, "PF_KEY socket for IPsec load succeeded.\n");
148 
149 	/* must be root */
150 	if (geteuid() != 0) {
151 		sckmd_log(LOG_ERR, "must run as root\n");
152 		exit(1);
153 	}
154 
155 	if (standalone == 0) {
156 
157 		int	i;
158 
159 		for (i = 0; i < NOFILE; i++) {
160 			(void) close(i);
161 		}
162 
163 		(void) chdir("/");
164 		(void) umask(0);
165 		if (fork() != 0) {
166 			exit(0);
167 		}
168 		(void) setpgrp();
169 
170 		/* reinitialize syslog after closing all fds */
171 		openlog("sckmd", LOG_CONS | LOG_NDELAY, LOG_DAEMON);
172 	}
173 
174 	/* open driver */
175 	if ((fd = open(KM_DEV, O_RDONLY)) == -1) {
176 		sckmd_log(LOG_ERR, "error initializing km driver: %s\n",
177 		    strerror(errno));
178 		exit(1);
179 	}
180 
181 	/*
182 	 * Main processing loop
183 	 */
184 	for (;;) {
185 
186 		/* initialize the ioctl request */
187 		(void) memset(&msg, 0, sizeof (sckm_ioctl_getreq_t));
188 		msg.buf = (caddr_t)msg_buf;
189 		(void) memset(&msg_buf, 0, SCKMD_MAX_MSG_SIZE);
190 		msg.buf_len = SCKMD_MAX_MSG_SIZE;
191 
192 		/* wait for the next message */
193 		if (ioctl(fd, SCKM_IOCTL_GETREQ, &msg) == -1) {
194 			sckmd_log(LOG_ERR, "failed to receive sckm message: "
195 			    "%s\n", strerror(errno));
196 			continue;
197 		}
198 
199 		/* pass the message to pf_key */
200 		if (process_sckm_req(fd, &msg) == -1) {
201 			sckmd_log(LOG_DEBUG, "error processing sckm message\n");
202 			continue;
203 		}
204 	}
205 
206 	/*NOTREACHED*/
207 	return (0);
208 }
209 
210 
211 /*
212  * process_sckm_req:
213  *
214  * Process a sckm request message. If the message is valid, pass the
215  * included SADB message to PF_KEY and return status to the sckm driver.
216  * The function only fails if it is unable to return a status message
217  * to the driver.
218  */
219 static int
process_sckm_req(int fd,sckm_ioctl_getreq_t * msg)220 process_sckm_req(int fd, sckm_ioctl_getreq_t *msg)
221 {
222 	sckm_ioctl_status_t	reply;
223 	struct sadb_msg		*pfkey_msg;
224 	unsigned int		msg_ver;
225 	unsigned int		msg_type;
226 	unsigned int		msg_len;
227 	int			err;
228 
229 
230 	if (msg == NULL) {
231 		sckmd_log(LOG_ERR, "invalid message\n");
232 		return (-1);
233 	}
234 
235 	/* initialize a reply message */
236 	(void) memset(&reply, 0, sizeof (sckm_ioctl_status_t));
237 	reply.transid = msg->transid;
238 
239 	/* currently, we only support sadb messages */
240 	if (msg->type != SCKM_IOCTL_REQ_SADB) {
241 		sckmd_log(LOG_ERR, "unsupported message type (%d)\n",
242 		    msg->type);
243 		reply.status = SCKM_IOCTL_STAT_ERR_REQ;
244 		return (send_sckm_status(fd, &reply));
245 	}
246 
247 	/* check that we have at least the sadb header */
248 	if (msg->buf_len < sizeof (struct sadb_msg)) {
249 		sckmd_log(LOG_ERR, "incomplete sadb message received\n");
250 		reply.status = SCKM_IOCTL_STAT_ERR_REQ;
251 		return (send_sckm_status(fd, &reply));
252 	}
253 
254 	/* LINTED Pointer Cast Alignment Warning */
255 	pfkey_msg = (struct sadb_msg *)msg->buf;
256 	msg_ver = pfkey_msg->sadb_msg_version;
257 	msg_len = SADB_64TO8(pfkey_msg->sadb_msg_len);
258 	msg_type = pfkey_msg->sadb_msg_type;
259 
260 	/* check for an unsupported PF_KEY version */
261 	if ((msg_ver > SCKMD_CURR_PFKEY_VER) || (msg_ver < PF_KEY_V2)) {
262 
263 		sckmd_log(LOG_ERR, "unsupported PF_KEY version (%d)\n",
264 		    msg_ver);
265 		reply.status = SCKM_IOCTL_STAT_ERR_VERSION;
266 		reply.sadb_msg_version = SCKMD_CURR_PFKEY_VER;
267 		return (send_sckm_status(fd, &reply));
268 	}
269 
270 	/* convert the PF_KEY message if necessary */
271 	if (msg_ver != SCKMD_CURR_PFKEY_VER) {
272 
273 		if (convert_pfkey_msg(pfkey_msg) == -1) {
274 			reply.status = SCKM_IOCTL_STAT_ERR_VERSION;
275 			reply.sadb_msg_version = SCKMD_CURR_PFKEY_VER;
276 			return (send_sckm_status(fd, &reply));
277 		}
278 	}
279 
280 	/*
281 	 * Process the PF_KEY message
282 	 */
283 	pfkey_msg->sadb_msg_seq = ++seq;
284 	pfkey_msg->sadb_msg_pid = mypid;
285 
286 	switch (msg_type) {
287 
288 	case SADB_UPDATE:
289 	case SADB_ADD:
290 	case SADB_DELETE:
291 
292 		/*
293 		 * Only update, add, and delete are supported. Pass the
294 		 * message directly to PF_KEY.
295 		 */
296 		break;
297 
298 	default:
299 		sckmd_log(LOG_ERR, "received unsupported operation "
300 		    "from client (%d)\n", msg_type);
301 		reply.status = SCKM_IOCTL_STAT_ERR_SADB_TYPE;
302 		return (send_sckm_status(fd, &reply));
303 	}
304 
305 	/* initialize global key socket */
306 	if ((keysock = socket(PF_KEY, SOCK_RAW, SCKMD_CURR_PFKEY_VER)) == -1) {
307 		sckmd_log(LOG_ERR, "error initializing PF_KEY socket: %s\n",
308 		    strerror(errno));
309 		reply.status = SCKM_IOCTL_STAT_ERR_OTHER;
310 		return (send_sckm_status(fd, &reply));
311 	}
312 
313 	/* send the PF_KEY message */
314 	if (write(keysock, pfkey_msg, msg_len) != msg_len) {
315 		sckmd_log(LOG_ERR, "PF_KEY write failed\n");
316 		reply.status = SCKM_IOCTL_STAT_ERR_OTHER;
317 		close(keysock);
318 		return (send_sckm_status(fd, &reply));
319 	}
320 
321 	/* wait for key engine reply */
322 	if (get_pfkey_reply(pfkey_msg->sadb_msg_seq, msg_type, &err) == -1) {
323 		reply.status = err;
324 		if (err == SCKM_IOCTL_STAT_ERR_PFKEY) {
325 			reply.sadb_msg_errno = errno;
326 		}
327 	} else {
328 		sckmd_log(LOG_DEBUG, "PF_KEY operation succeeded\n");
329 		reply.status = SCKM_IOCTL_STAT_SUCCESS;
330 	}
331 
332 	close(keysock);
333 	return (send_sckm_status(fd, &reply));
334 }
335 
336 
337 /*
338  * send_sckm_status:
339  *
340  * Send a sckm status message to the sckm driver
341  */
342 static int
send_sckm_status(int fd,sckm_ioctl_status_t * msg)343 send_sckm_status(int fd, sckm_ioctl_status_t *msg)
344 {
345 	if (ioctl(fd, SCKM_IOCTL_STATUS, msg) == -1) {
346 		sckmd_log(LOG_ERR, "error sending sckm status message: %s\n",
347 		    strerror(errno));
348 		return (-1);
349 	}
350 
351 	return (0);
352 }
353 
354 
355 /*
356  * get_pfkey_reply:
357  *
358  * Wait for a reply from PF_KEY. Get the reply from the socket using
359  * the global file desciptor 'keysock'. If PF_KEY returns an error,
360  * the global errno is set to the error returned in the reply message.
361  * If an error occurs, the parameter 'err' is set to one of the error
362  * codes prefixed by SCKM_IOCTL_STAT_ERR to indicate the overall status
363  * of the operation.
364  */
365 static int
get_pfkey_reply(uint32_t req_seq,uint8_t req_type,int * err)366 get_pfkey_reply(uint32_t req_seq, uint8_t req_type, int *err)
367 {
368 	int		timeout;
369 	int		pollstatus;
370 	clock_t		before;
371 	clock_t		after;
372 	double		diff;
373 	struct tms	unused;
374 	struct pollfd	pfd;
375 	struct sadb_msg *msg;
376 
377 	static char *pfkey_msg_type[] = {
378 		"RESERVED",
379 		"GETSPI",
380 		"UPDATE",
381 		"ADD",
382 		"DELETE",
383 		"GET",
384 		"ACQUIRE",
385 		"REGISTER",
386 		"EXPIRE",
387 		"FLUSH",
388 		"DUMP",
389 		"X_PROMISC",
390 		"X_INVERSE_ACQUIRE",
391 	};
392 
393 
394 	sckmd_log(LOG_DEBUG, "waiting for key engine reply\n");
395 
396 	timeout = SCKMD_PFKEY_TIMEOUT;
397 
398 	pfd.fd = keysock;
399 	pfd.events = POLLIN;
400 
401 	while (timeout > 0) {
402 
403 		before = times(&unused);
404 
405 		pfd.revents = 0;
406 		pollstatus = poll(&pfd, 1, timeout);
407 
408 		/* check for a timeout */
409 		if (pollstatus == 0) {
410 			sckmd_log(LOG_NOTICE, "timed out waiting for PF_KEY "
411 			    "reply\n");
412 			*err = SCKM_IOCTL_STAT_ERR_TIMEOUT;
413 			return (-1);
414 		}
415 
416 		/* read in the next PF_KEY message */
417 		msg = read_pfkey_msg();
418 
419 		if (msg == NULL) {
420 			*err = SCKM_IOCTL_STAT_ERR_OTHER;
421 			return (-1);
422 		}
423 
424 		/* check if the message is intended for us */
425 		if (msg->sadb_msg_seq == req_seq &&
426 		    msg->sadb_msg_pid == mypid) {
427 			break;
428 		}
429 
430 		after = times(&unused);
431 
432 		diff = (double)(after - before)/(double)CLK_TCK;
433 		timeout -= (int)(diff * 1000);
434 	}
435 
436 	/* check for a timeout */
437 	if (timeout <= 0) {
438 		sckmd_log(LOG_NOTICE, "timed out waiting for PF_KEY "
439 		    "reply\n");
440 		*err = SCKM_IOCTL_STAT_ERR_TIMEOUT;
441 		return (-1);
442 	}
443 
444 	/* did we get what we were expecting? */
445 	if (msg->sadb_msg_type != req_type) {
446 		sckmd_log(LOG_ERR, "unexpected message type from PF_KEY: %d\n",
447 		    msg->sadb_msg_type);
448 		*err = SCKM_IOCTL_STAT_ERR_OTHER;
449 		return (-1);
450 	}
451 
452 	/*
453 	 * Check for errors in SADB message, but ignore the
454 	 * ESRCH error for DELETE operation. This can happen if the SP
455 	 * sends a DELETE request first before sending the ADD
456 	 * request, just to make sure the keys are installed without a failure.
457 	 */
458 	if ((msg->sadb_msg_errno != 0) && !((msg->sadb_msg_errno == ESRCH) &&
459 	    (msg->sadb_msg_type == SADB_DELETE))) {
460 
461 		char	   unknown_type_str[16];
462 		int	   unknown_type = 0;
463 		int	   arr_sz;
464 		const char *diagnostic_str;
465 
466 		arr_sz  = sizeof (pfkey_msg_type) / sizeof (*pfkey_msg_type);
467 
468 		/* generate unknown type string, if necessary */
469 		if (msg->sadb_msg_type >= arr_sz) {
470 			(void) snprintf(unknown_type_str,
471 			    sizeof (unknown_type_str), "UNKNOWN-%d",
472 			    msg->sadb_msg_type);
473 			unknown_type = 1;
474 		}
475 
476 		/* use libipsecutil to lookup the SADB diagnostic string */
477 		diagnostic_str = keysock_diag(msg->sadb_x_msg_diagnostic);
478 
479 		sckmd_log(LOG_ERR, "PF_KEY error: type=%s, errno=%d: %s, "
480 		    "diagnostic code=%d: %s\n",
481 		    (unknown_type) ? unknown_type_str :
482 		    pfkey_msg_type[msg->sadb_msg_type],
483 		    msg->sadb_msg_errno, strerror(msg->sadb_msg_errno),
484 		    msg->sadb_x_msg_diagnostic, diagnostic_str);
485 
486 		*err = SCKM_IOCTL_STAT_ERR_PFKEY;
487 		errno = msg->sadb_msg_errno;
488 		return (-1);
489 	}
490 
491 	return (0);
492 }
493 
494 
495 /*
496  * read_pfkey_msg:
497  *
498  * Get a PF_KEY message from the socket using the global file descriptor
499  * 'keysock'. Data is stored in the global buffer 'msg_buf'. The function
500  * returns a pointer to the next PF_KEY message. Note that this is not
501  * necessarily at the start of 'msg_buf'. NULL is returned for errors.
502  */
503 static struct sadb_msg *
read_pfkey_msg(void)504 read_pfkey_msg(void)
505 {
506 	static uint64_t	*offset;
507 	static int	len;
508 	struct sadb_msg	*retval;
509 
510 
511 	/* Assume offset and len are initialized to NULL and 0 */
512 
513 	if ((offset == NULL) || (offset - len == msg_buf)) {
514 		/* read a new block from the socket. */
515 		len = read(keysock, &msg_buf, sizeof (msg_buf));
516 
517 		if (len == -1) {
518 			sckmd_log(LOG_ERR, "PF_KEY read: %s\n",
519 			    strerror(errno));
520 
521 			offset = NULL;
522 			return (NULL);
523 		}
524 		offset = msg_buf;
525 		len = SADB_8TO64(len);
526 	}
527 
528 	retval = (struct sadb_msg *)offset;
529 	offset += retval->sadb_msg_len;
530 
531 	if (offset > msg_buf + len) {
532 		sckmd_log(LOG_ERR, "PF_KEY read: message corruption, "
533 		    "message length %d exceeds boundary %d\n",
534 		    SADB_64TO8(retval->sadb_msg_len),
535 		    SADB_64TO8((msg_buf + len) - (uint64_t *)retval));
536 
537 		offset = NULL;
538 		return (NULL);
539 	}
540 
541 	return (retval);
542 }
543 
544 
545 /*
546  * convert_pfkey_msg:
547  *
548  * Convert a lower version PF_KEY message to the current version
549  * being used by sckmd.
550  *
551  * Currently, there is only one implemented version of PF_KEY (v2).
552  * If future versions are added to the PF_KEY specification (RFC 2367),
553  * this function should be updated to provide backwards compatibility
554  * with version 2 and above.
555  */
556 static int
convert_pfkey_msg(struct sadb_msg * msg)557 convert_pfkey_msg(struct sadb_msg *msg)
558 {
559 	sckmd_log(LOG_DEBUG, "PF_KEY conversion necessary...\n");
560 
561 	switch (msg->sadb_msg_version) {
562 
563 	case PF_KEY_V2:
564 		/*
565 		 * Current supported version:
566 		 * No conversion required
567 		 */
568 		break;
569 	default:
570 		sckmd_log(LOG_ERR, "No conversion possible for "
571 		    "PF_KEY version %d\n", msg->sadb_msg_version);
572 		return (-1);
573 	}
574 
575 	return (0);
576 }
577 
578 
579 /*
580  * sckmd_log:
581  *
582  * Log a message using the syslog facility. If sckmd is running in
583  * standalone mode (global flag 'standalone' set), messages are also
584  * sent to stderr.
585  */
586 static void
sckmd_log(int priority,char * fmt,...)587 sckmd_log(int priority, char *fmt, ...)
588 {
589 	va_list	vap;
590 	char	err[SCKMD_ERR_MSG_SIZE];
591 
592 
593 	/* if this is a debug message, check if debugging is enabled */
594 	if ((priority == LOG_DEBUG) && (debug == 0)) {
595 		return;
596 	}
597 
598 	va_start(vap, fmt);
599 	vsnprintf(err, SCKMD_ERR_MSG_SIZE, fmt, vap);
600 	va_end(vap);
601 
602 	/* send message to stderr if in standalone mode */
603 	if (standalone != 0) {
604 		fprintf(stderr, err);
605 	}
606 
607 	/* always log the message */
608 	syslog(priority, err);
609 }
610