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