8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / sckmd / sparc / sun4u / sckmd.c
blob99ed3e9740aacd96e1da60cd103b8c5f25b8b777
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * sckmd - Starcat Key Management Daemon
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.
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>
54 #include <sys/sckm_io.h>
57 #ifdef SCKMD_DEBUG
58 #define OPT_STR "ds"
59 #else /* SCKMD_DEBUG */
60 #define OPT_STR ""
61 #endif /* SCKMD_DEBUG */
63 #define KM_DEV "/dev/kmdrv"
65 #define SCKMD_MAX_MSG_SIZE 1024
66 #define SCKMD_ERR_MSG_SIZE 512
67 #define SCKMD_MSG_HDR_SIZE sizeof (struct sadb_msg)
69 #define SCKMD_CURR_PFKEY_VER PF_KEY_V2
70 #define SCKMD_PFKEY_TIMEOUT 3000 /* 3 seconds */
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];
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, ...);
90 * main:
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.
96 int
97 main(int argc, char **argv)
99 int opt;
100 int fd;
101 sckm_ioctl_getreq_t msg;
105 * Set defaults
107 standalone = 0;
108 debug = 0;
109 mypid = getpid();
111 openlog("sckmd", LOG_CONS | LOG_NDELAY, LOG_DAEMON);
114 * Check command line options
116 opterr = 0; /* disable getopt error messages */
117 while ((opt = getopt(argc, argv, OPT_STR)) != EOF) {
119 switch (opt) {
121 case 'd':
122 debug++;
123 break;
125 case 's':
126 standalone++;
127 break;
129 default:
130 sckmd_log(LOG_ERR, "unknown command line option\n");
131 exit(1);
135 sckmd_log(LOG_DEBUG, "starting sckmd...\n");
138 * IPsec must get loaded in-kernel. The easiest way to do this is
139 * to open (then close) a PF_KEY socket.
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);
146 (void) close(keysock);
147 sckmd_log(LOG_ERR, "PF_KEY socket for IPsec load succeeded.\n");
149 /* must be root */
150 if (geteuid() != 0) {
151 sckmd_log(LOG_ERR, "must run as root\n");
152 exit(1);
155 if (standalone == 0) {
157 int i;
159 for (i = 0; i < NOFILE; i++) {
160 (void) close(i);
163 (void) chdir("/");
164 (void) umask(0);
165 if (fork() != 0) {
166 exit(0);
168 (void) setpgrp();
170 /* reinitialize syslog after closing all fds */
171 openlog("sckmd", LOG_CONS | LOG_NDELAY, LOG_DAEMON);
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);
182 * Main processing loop
184 for (;;) {
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;
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;
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;
206 /*NOTREACHED*/
207 return (0);
212 * process_sckm_req:
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.
219 static int
220 process_sckm_req(int fd, sckm_ioctl_getreq_t *msg)
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;
230 if (msg == NULL) {
231 sckmd_log(LOG_ERR, "invalid message\n");
232 return (-1);
235 /* initialize a reply message */
236 (void) memset(&reply, 0, sizeof (sckm_ioctl_status_t));
237 reply.transid = msg->transid;
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));
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));
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;
260 /* check for an unsupported PF_KEY version */
261 if ((msg_ver > SCKMD_CURR_PFKEY_VER) || (msg_ver < PF_KEY_V2)) {
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));
270 /* convert the PF_KEY message if necessary */
271 if (msg_ver != SCKMD_CURR_PFKEY_VER) {
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));
281 * Process the PF_KEY message
283 pfkey_msg->sadb_msg_seq = ++seq;
284 pfkey_msg->sadb_msg_pid = mypid;
286 switch (msg_type) {
288 case SADB_UPDATE:
289 case SADB_ADD:
290 case SADB_DELETE:
293 * Only update, add, and delete are supported. Pass the
294 * message directly to PF_KEY.
296 break;
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));
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));
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));
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;
327 } else {
328 sckmd_log(LOG_DEBUG, "PF_KEY operation succeeded\n");
329 reply.status = SCKM_IOCTL_STAT_SUCCESS;
332 close(keysock);
333 return (send_sckm_status(fd, &reply));
338 * send_sckm_status:
340 * Send a sckm status message to the sckm driver
342 static int
343 send_sckm_status(int fd, sckm_ioctl_status_t *msg)
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);
351 return (0);
356 * get_pfkey_reply:
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.
365 static int
366 get_pfkey_reply(uint32_t req_seq, uint8_t req_type, int *err)
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;
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",
394 sckmd_log(LOG_DEBUG, "waiting for key engine reply\n");
396 timeout = SCKMD_PFKEY_TIMEOUT;
398 pfd.fd = keysock;
399 pfd.events = POLLIN;
401 while (timeout > 0) {
403 before = times(&unused);
405 pfd.revents = 0;
406 pollstatus = poll(&pfd, 1, timeout);
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);
416 /* read in the next PF_KEY message */
417 msg = read_pfkey_msg();
419 if (msg == NULL) {
420 *err = SCKM_IOCTL_STAT_ERR_OTHER;
421 return (-1);
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;
430 after = times(&unused);
432 diff = (double)(after - before)/(double)CLK_TCK;
433 timeout -= (int)(diff * 1000);
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);
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);
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.
458 if ((msg->sadb_msg_errno != 0) && !((msg->sadb_msg_errno == ESRCH) &&
459 (msg->sadb_msg_type == SADB_DELETE))) {
461 char unknown_type_str[16];
462 int unknown_type = 0;
463 int arr_sz;
464 const char *diagnostic_str;
466 arr_sz = sizeof (pfkey_msg_type) / sizeof (*pfkey_msg_type);
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;
476 /* use libipsecutil to lookup the SADB diagnostic string */
477 diagnostic_str = keysock_diag(msg->sadb_x_msg_diagnostic);
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);
486 *err = SCKM_IOCTL_STAT_ERR_PFKEY;
487 errno = msg->sadb_msg_errno;
488 return (-1);
491 return (0);
496 * read_pfkey_msg:
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.
503 static struct sadb_msg *
504 read_pfkey_msg(void)
506 static uint64_t *offset;
507 static int len;
508 struct sadb_msg *retval;
511 /* Assume offset and len are initialized to NULL and 0 */
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));
517 if (len == -1) {
518 sckmd_log(LOG_ERR, "PF_KEY read: %s\n",
519 strerror(errno));
521 offset = NULL;
522 return (NULL);
524 offset = msg_buf;
525 len = SADB_8TO64(len);
528 retval = (struct sadb_msg *)offset;
529 offset += retval->sadb_msg_len;
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));
537 offset = NULL;
538 return (NULL);
541 return (retval);
546 * convert_pfkey_msg:
548 * Convert a lower version PF_KEY message to the current version
549 * being used by sckmd.
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.
556 static int
557 convert_pfkey_msg(struct sadb_msg *msg)
559 sckmd_log(LOG_DEBUG, "PF_KEY conversion necessary...\n");
561 switch (msg->sadb_msg_version) {
563 case PF_KEY_V2:
565 * Current supported version:
566 * No conversion required
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);
575 return (0);
580 * sckmd_log:
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.
586 static void
587 sckmd_log(int priority, char *fmt, ...)
589 va_list vap;
590 char err[SCKMD_ERR_MSG_SIZE];
593 /* if this is a debug message, check if debugging is enabled */
594 if ((priority == LOG_DEBUG) && (debug == 0)) {
595 return;
598 va_start(vap, fmt);
599 vsnprintf(err, SCKMD_ERR_MSG_SIZE, fmt, vap);
600 va_end(vap);
602 /* send message to stderr if in standalone mode */
603 if (standalone != 0) {
604 fprintf(stderr, err);
607 /* always log the message */
608 syslog(priority, err);