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