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