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]
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
45 #include <sys/types.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>
59 #else /* SCKMD_DEBUG */
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 */
74 static int standalone
;
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
, ...);
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.
97 main(int argc
, char **argv
)
101 sckm_ioctl_getreq_t msg
;
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
) {
130 sckmd_log(LOG_ERR
, "unknown command line option\n");
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",
146 (void) close(keysock
);
147 sckmd_log(LOG_ERR
, "PF_KEY socket for IPsec load succeeded.\n");
150 if (geteuid() != 0) {
151 sckmd_log(LOG_ERR
, "must run as root\n");
155 if (standalone
== 0) {
159 for (i
= 0; i
< NOFILE
; i
++) {
170 /* reinitialize syslog after closing all fds */
171 openlog("sckmd", LOG_CONS
| LOG_NDELAY
, LOG_DAEMON
);
175 if ((fd
= open(KM_DEV
, O_RDONLY
)) == -1) {
176 sckmd_log(LOG_ERR
, "error initializing km driver: %s\n",
182 * Main processing loop
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
));
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");
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
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
;
231 sckmd_log(LOG_ERR
, "invalid message\n");
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",
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",
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
;
293 * Only update, add, and delete are supported. Pass the
294 * message directly to PF_KEY.
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",
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
;
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) {
324 if (err
== SCKM_IOCTL_STAT_ERR_PFKEY
) {
325 reply
.sadb_msg_errno
= errno
;
328 sckmd_log(LOG_DEBUG
, "PF_KEY operation succeeded\n");
329 reply
.status
= SCKM_IOCTL_STAT_SUCCESS
;
333 return (send_sckm_status(fd
, &reply
));
340 * Send a sckm status message to the sckm driver
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",
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
366 get_pfkey_reply(uint32_t req_seq
, uint8_t req_type
, int *err
)
375 struct sadb_msg
*msg
;
377 static char *pfkey_msg_type
[] = {
394 sckmd_log(LOG_DEBUG
, "waiting for key engine reply\n");
396 timeout
= SCKMD_PFKEY_TIMEOUT
;
401 while (timeout
> 0) {
403 before
= times(&unused
);
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 "
412 *err
= SCKM_IOCTL_STAT_ERR_TIMEOUT
;
416 /* read in the next PF_KEY message */
417 msg
= read_pfkey_msg();
420 *err
= SCKM_IOCTL_STAT_ERR_OTHER
;
424 /* check if the message is intended for us */
425 if (msg
->sadb_msg_seq
== req_seq
&&
426 msg
->sadb_msg_pid
== mypid
) {
430 after
= times(&unused
);
432 diff
= (double)(after
- before
)/(double)CLK_TCK
;
433 timeout
-= (int)(diff
* 1000);
436 /* check for a timeout */
438 sckmd_log(LOG_NOTICE
, "timed out waiting for PF_KEY "
440 *err
= SCKM_IOCTL_STAT_ERR_TIMEOUT
;
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",
448 *err
= SCKM_IOCTL_STAT_ERR_OTHER
;
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;
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",
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
;
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
*
506 static uint64_t *offset
;
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
));
518 sckmd_log(LOG_ERR
, "PF_KEY read: %s\n",
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
));
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.
557 convert_pfkey_msg(struct sadb_msg
*msg
)
559 sckmd_log(LOG_DEBUG
, "PF_KEY conversion necessary...\n");
561 switch (msg
->sadb_msg_version
) {
565 * Current supported version:
566 * No conversion required
570 sckmd_log(LOG_ERR
, "No conversion possible for "
571 "PF_KEY version %d\n", msg
->sadb_msg_version
);
582 * Log a message using the syslog facility. If sckmd is running in
583 * standalone mode (global flag 'standalone' set), messages are also
587 sckmd_log(int priority
, char *fmt
, ...)
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)) {
599 vsnprintf(err
, SCKMD_ERR_MSG_SIZE
, fmt
, 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
);