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 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 28 /* 29 * Starcat IPSec Key Management Driver. 30 * 31 * This driver runs on a Starcat Domain. It processes requests received 32 * from the System Controller (SC) from IOSRAM, passes these requests 33 * to the sckmd daemon by means of an open/close/ioctl interface, and 34 * sends corresponding status information back to the SC. 35 * 36 * Requests received from the SC consist of IPsec security associations 37 * (SAs) needed to secure the communication between SC and Domain daemons 38 * communicating using the Management Network (MAN). 39 */ 40 41 #include <sys/types.h> 42 #include <sys/cmn_err.h> 43 #include <sys/kmem.h> 44 #include <sys/errno.h> 45 #include <sys/file.h> 46 #include <sys/open.h> 47 #include <sys/stat.h> 48 #include <sys/conf.h> 49 #include <sys/ddi.h> 50 #include <sys/cmn_err.h> 51 #include <sys/sunddi.h> 52 #include <sys/sunndi.h> 53 #include <sys/ddi_impldefs.h> 54 #include <sys/ndi_impldefs.h> 55 #include <sys/modctl.h> 56 #include <sys/disp.h> 57 #include <sys/async.h> 58 #include <sys/mboxsc.h> 59 #include <sys/sckm_msg.h> 60 #include <sys/sckm_io.h> 61 #include <sys/taskq.h> 62 #include <sys/note.h> 63 64 #ifdef DEBUG 65 static uint_t sckm_debug_flags = 0x0; 66 #define SCKM_DEBUG0(f, s) if ((f)& sckm_debug_flags) \ 67 cmn_err(CE_CONT, s) 68 #define SCKM_DEBUG1(f, s, a) if ((f)& sckm_debug_flags) \ 69 cmn_err(CE_CONT, s, a) 70 #define SCKM_DEBUG2(f, s, a, b) if ((f)& sckm_debug_flags) \ 71 cmn_err(CE_CONT, s, a, b) 72 #define SCKM_DEBUG3(f, s, a, b, c) if ((f)& sckm_debug_flags) \ 73 cmn_err(CE_CONT, s, a, b, c) 74 #define SCKM_DEBUG4(f, s, a, b, c, d) if ((f)& sckm_debug_flags) \ 75 cmn_err(CE_CONT, s, a, b, c, d) 76 #define SCKM_DEBUG5(f, s, a, b, c, d, e) if ((f)& sckm_debug_flags) \ 77 cmn_err(CE_CONT, s, a, b, c, d, e) 78 #define SCKM_DEBUG6(f, s, a, b, c, d, e, ff) if ((f)& sckm_debug_flags) \ 79 cmn_err(CE_CONT, s, a, b, c, d, e, ff) 80 #else 81 #define SCKM_DEBUG0(f, s) 82 #define SCKM_DEBUG1(f, s, a) 83 #define SCKM_DEBUG2(f, s, a, b) 84 #define SCKM_DEBUG3(f, s, a, b, c) 85 #define SCKM_DEBUG4(f, s, a, b, c, d) 86 #define SCKM_DEBUG5(f, s, a, b, c, d, e) 87 #define SCKM_DEBUG6(f, s, a, b, c, d, e, ff) 88 #endif /* DEBUG */ 89 90 #define D_INIT 0x00000001 /* _init/_fini/_info */ 91 #define D_ATTACH 0x00000002 /* attach/detach */ 92 #define D_OPEN 0x00000008 /* open/close */ 93 #define D_IOCTL 0x00010000 /* ioctl */ 94 #define D_TASK 0x00100000 /* mailbox task processing */ 95 #define D_CALLBACK 0x00200000 /* mailbox callback */ 96 97 static int sckm_open(dev_t *, int, int, struct cred *); 98 static int sckm_close(dev_t, int, int, struct cred *); 99 static int sckm_ioctl(dev_t, int, intptr_t, int, struct cred *, int *); 100 101 static struct cb_ops sckm_cb_ops = { 102 sckm_open, /* open */ 103 sckm_close, /* close */ 104 nodev, /* strategy */ 105 nodev, /* print */ 106 nodev, /* dump */ 107 nodev, /* read */ 108 nodev, /* write */ 109 sckm_ioctl, /* ioctl */ 110 nodev, /* devmap */ 111 nodev, /* mmap */ 112 nodev, /* segmap */ 113 nochpoll, /* poll */ 114 ddi_prop_op, /* prop_op */ 115 0, /* streamtab */ 116 D_NEW | D_MP /* Driver compatibility flag */ 117 }; 118 119 static int sckm_attach(dev_info_t *, ddi_attach_cmd_t); 120 static int sckm_detach(dev_info_t *, ddi_detach_cmd_t); 121 static int sckm_info(dev_info_t *, ddi_info_cmd_t, void *, void **); 122 123 static struct dev_ops sckm_ops = { 124 DEVO_REV, /* devo_rev, */ 125 0, /* refcnt */ 126 sckm_info, /* get_dev_info */ 127 nulldev, /* identify */ 128 nulldev, /* probe */ 129 sckm_attach, /* attach */ 130 sckm_detach, /* detach */ 131 nodev, /* reset */ 132 &sckm_cb_ops, /* driver operations */ 133 (struct bus_ops *)0, /* no bus operations */ 134 NULL, /* power */ 135 ddi_quiesce_not_needed, /* quiesce */ 136 }; 137 138 static struct modldrv modldrv = { 139 &mod_driverops, 140 "Key Management Driver", 141 &sckm_ops, 142 }; 143 144 static struct modlinkage modlinkage = { 145 MODREV_1, 146 &modldrv, 147 NULL 148 }; 149 150 /* 151 * Private definitions. 152 */ 153 #define SCKM_DEF_GETMSG_TIMEOUT 60 /* in seconds */ 154 #define SCKM_DAEMON_TIMEOUT 4000000 /* in microseconds */ 155 #define SCKM_NUM_TASKQ 2 /* # of task queue entries */ 156 157 /* 158 * For processing mailbox layer events. 159 */ 160 static kmutex_t sckm_task_mutex; 161 static kmutex_t sckm_taskq_ptr_mutex; 162 static clock_t sckm_getmsg_timeout = SCKM_DEF_GETMSG_TIMEOUT*1000; 163 static taskq_t *sckm_taskq = NULL; 164 static sckm_mbox_req_hdr_t *req_data = NULL; 165 static sckm_mbox_rep_hdr_t *rep_data = NULL; 166 167 168 /* 169 * For synchronization with key management daemon. 170 */ 171 static kmutex_t sckm_umutex; 172 static kcondvar_t sckm_udata_cv; /* daemon waits on data */ 173 static kcondvar_t sckm_cons_cv; /* wait for daemon to consume data */ 174 static boolean_t sckm_udata_req = B_FALSE; /* data available for daemon */ 175 static sckm_ioctl_getreq_t sckm_udata; /* request for daemon */ 176 static sckm_ioctl_status_t sckm_udata_status; /* status from daemon */ 177 178 /* 179 * Other misc private variables. 180 */ 181 static dev_info_t *sckm_devi = NULL; 182 static boolean_t sckm_oflag = B_FALSE; 183 184 /* 185 * Private functions prototypes. 186 */ 187 static void sckm_mbox_callback(void); 188 static void sckm_mbox_task(void *arg); 189 static void sckm_process_msg(uint32_t cmd, uint64_t transid, 190 uint32_t len, sckm_mbox_req_hdr_t *req_data, 191 sckm_mbox_rep_hdr_t *rep_data); 192 193 194 int 195 _init(void) 196 { 197 mboxsc_timeout_range_t timeout_range; 198 int ret; 199 200 SCKM_DEBUG0(D_INIT, "in _init"); 201 202 /* 203 * Initialize outgoing mailbox (KDSC) 204 */ 205 if ((ret = mboxsc_init(KEY_KDSC, MBOXSC_MBOX_OUT, NULL)) != 0) { 206 cmn_err(CE_WARN, "failed initializing outgoing mailbox " 207 "(%d)", ret); 208 return (ret); 209 } 210 211 /* 212 * Initialize incoming mailbox (SCKD) 213 */ 214 if ((ret = mboxsc_init(KEY_SCKD, MBOXSC_MBOX_IN, 215 sckm_mbox_callback)) != 0) { 216 cmn_err(CE_WARN, "failed initializing incoming mailbox " 217 "(%d)\n", ret); 218 (void) mboxsc_fini(KEY_KDSC); 219 return (ret); 220 } 221 222 if ((ret = mboxsc_ctrl(KEY_SCKD, MBOXSC_CMD_GETMSG_TIMEOUT_RANGE, 223 (void *)&timeout_range)) != 0) { 224 (void) mboxsc_fini(KEY_SCKD); 225 (void) mboxsc_fini(KEY_KDSC); 226 return (ret); 227 } 228 229 if (sckm_getmsg_timeout < timeout_range.min_timeout) { 230 sckm_getmsg_timeout = timeout_range.min_timeout; 231 cmn_err(CE_WARN, "resetting getmsg timeout to %lx", 232 sckm_getmsg_timeout); 233 } 234 235 if (sckm_getmsg_timeout > timeout_range.max_timeout) { 236 sckm_getmsg_timeout = timeout_range.max_timeout; 237 cmn_err(CE_WARN, "resetting getmsg timeout to %lx", 238 sckm_getmsg_timeout); 239 } 240 241 if ((ret = mod_install(&modlinkage)) != 0) { 242 (void) mboxsc_fini(KEY_KDSC); 243 (void) mboxsc_fini(KEY_SCKD); 244 return (ret); 245 } 246 247 /* 248 * Initialize variables needed for synchronization with daemon. 249 */ 250 sckm_udata.buf = kmem_alloc(SCKM_SCKD_MAXDATA, KM_SLEEP); 251 req_data = (sckm_mbox_req_hdr_t *)kmem_alloc(SCKM_SCKD_MAXDATA, 252 KM_SLEEP); 253 rep_data = (sckm_mbox_rep_hdr_t *)kmem_alloc(SCKM_KDSC_MAXDATA, 254 KM_SLEEP); 255 256 if ((sckm_udata.buf == NULL) || (req_data == NULL) || 257 (rep_data == NULL)) { 258 cmn_err(CE_WARN, "not enough memory during _init"); 259 260 /* free what was successfully allocated */ 261 if (sckm_udata.buf != NULL) 262 kmem_free(sckm_udata.buf, SCKM_SCKD_MAXDATA); 263 if (req_data != NULL) 264 kmem_free(req_data, SCKM_SCKD_MAXDATA); 265 if (rep_data != NULL) 266 kmem_free(rep_data, SCKM_KDSC_MAXDATA); 267 sckm_udata.buf = NULL; 268 req_data = NULL; 269 rep_data = NULL; 270 271 /* uninitialize mailboxes, remove module, and return error */ 272 (void) mboxsc_fini(KEY_KDSC); 273 (void) mboxsc_fini(KEY_SCKD); 274 (void) mod_remove(&modlinkage); 275 return (-1); 276 } 277 278 cv_init(&sckm_udata_cv, NULL, CV_DRIVER, NULL); 279 cv_init(&sckm_cons_cv, NULL, CV_DRIVER, NULL); 280 mutex_init(&sckm_umutex, NULL, MUTEX_DRIVER, NULL); 281 282 /* 283 * Create mutex for task processing, protection of taskq 284 * pointer, and create taskq. 285 */ 286 mutex_init(&sckm_task_mutex, NULL, MUTEX_DRIVER, NULL); 287 mutex_init(&sckm_taskq_ptr_mutex, NULL, MUTEX_DRIVER, NULL); 288 sckm_taskq = taskq_create("sckm_taskq", 1, minclsyspri, 289 SCKM_NUM_TASKQ, SCKM_NUM_TASKQ, TASKQ_PREPOPULATE); 290 291 SCKM_DEBUG1(D_INIT, "out _init ret=%d\n", ret); 292 return (ret); 293 } 294 295 int 296 _fini(void) 297 { 298 int ret; 299 300 SCKM_DEBUG0(D_INIT, "in _fini"); 301 302 if ((ret = mod_remove(&modlinkage)) != 0) { 303 return (ret); 304 } 305 306 /* 307 * Wait for scheduled tasks to complete, then destroy task queue. 308 */ 309 mutex_enter(&sckm_taskq_ptr_mutex); 310 if (sckm_taskq != NULL) { 311 taskq_destroy(sckm_taskq); 312 sckm_taskq = NULL; 313 } 314 mutex_exit(&sckm_taskq_ptr_mutex); 315 316 /* 317 * Terminate incoming and outgoing IOSRAM mailboxes 318 */ 319 (void) mboxsc_fini(KEY_KDSC); 320 (void) mboxsc_fini(KEY_SCKD); 321 322 /* 323 * Destroy module synchronization objects and free memory 324 */ 325 mutex_destroy(&sckm_task_mutex); 326 mutex_destroy(&sckm_taskq_ptr_mutex); 327 mutex_destroy(&sckm_umutex); 328 cv_destroy(&sckm_cons_cv); 329 330 if (sckm_udata.buf != NULL) { 331 kmem_free(sckm_udata.buf, SCKM_SCKD_MAXDATA); 332 sckm_udata.buf = NULL; 333 } 334 if (rep_data != NULL) { 335 kmem_free(rep_data, SCKM_KDSC_MAXDATA); 336 rep_data = NULL; 337 } 338 if (req_data != NULL) { 339 kmem_free(req_data, SCKM_SCKD_MAXDATA); 340 req_data = NULL; 341 } 342 343 return (ret); 344 } 345 346 int 347 _info(struct modinfo *modinfop) 348 { 349 SCKM_DEBUG0(D_INIT, "in _info"); 350 return (mod_info(&modlinkage, modinfop)); 351 } 352 353 static int 354 sckm_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 355 { 356 SCKM_DEBUG1(D_ATTACH, "in sckm_attach, cmd=%d", cmd); 357 358 switch (cmd) { 359 case DDI_ATTACH: 360 SCKM_DEBUG0(D_ATTACH, "sckm_attach: DDI_ATTACH"); 361 if (ddi_create_minor_node(devi, "sckmdrv", S_IFCHR, 362 0, NULL, NULL) == DDI_FAILURE) { 363 cmn_err(CE_WARN, "ddi_create_minor_node failed"); 364 ddi_remove_minor_node(devi, NULL); 365 return (DDI_FAILURE); 366 } 367 sckm_devi = devi; 368 break; 369 case DDI_SUSPEND: 370 SCKM_DEBUG0(D_ATTACH, "sckm_attach: DDI_SUSPEND"); 371 break; 372 default: 373 cmn_err(CE_WARN, "sckm_attach: bad cmd %d\n", cmd); 374 return (DDI_FAILURE); 375 } 376 377 SCKM_DEBUG0(D_ATTACH, "out sckm_attach (DDI_SUCCESS)"); 378 return (DDI_SUCCESS); 379 } 380 381 static int 382 sckm_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 383 { 384 SCKM_DEBUG1(D_ATTACH, "in sckm_detach, cmd=%d", cmd); 385 386 switch (cmd) { 387 case DDI_DETACH: 388 SCKM_DEBUG0(D_ATTACH, "sckm_detach: DDI_DETACH"); 389 ddi_remove_minor_node(devi, NULL); 390 break; 391 case DDI_SUSPEND: 392 SCKM_DEBUG0(D_ATTACH, "sckm_detach: DDI_DETACH"); 393 break; 394 default: 395 cmn_err(CE_WARN, "sckm_detach: bad cmd %d\n", cmd); 396 return (DDI_FAILURE); 397 } 398 399 SCKM_DEBUG0(D_ATTACH, "out sckm_detach (DDI_SUCCESS)"); 400 return (DDI_SUCCESS); 401 } 402 403 /* ARGSUSED */ 404 static int 405 sckm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 406 void **result) 407 { 408 int rv; 409 410 SCKM_DEBUG1(D_ATTACH, "in sckm_info, infocmd=%d", infocmd); 411 412 switch (infocmd) { 413 case DDI_INFO_DEVT2DEVINFO: 414 *result = (void *)sckm_devi; 415 rv = DDI_SUCCESS; 416 break; 417 case DDI_INFO_DEVT2INSTANCE: 418 *result = (void *)0; 419 rv = DDI_SUCCESS; 420 break; 421 default: 422 rv = DDI_FAILURE; 423 } 424 425 SCKM_DEBUG1(D_ATTACH, "out sckm_info, rv=%d", rv); 426 return (rv); 427 } 428 429 /*ARGSUSED*/ 430 static int 431 sckm_open(dev_t *devp, int flag, int otyp, struct cred *cred) 432 { 433 SCKM_DEBUG0(D_OPEN, "in sckm_open"); 434 435 /* check credentials of calling process */ 436 if (drv_priv(cred)) { 437 SCKM_DEBUG0(D_OPEN, "sckm_open: attempt by non-root proc"); 438 return (EPERM); 439 } 440 441 /* enforce exclusive access */ 442 mutex_enter(&sckm_umutex); 443 if (sckm_oflag == B_TRUE) { 444 SCKM_DEBUG0(D_OPEN, "sckm_open: already open"); 445 mutex_exit(&sckm_umutex); 446 return (EBUSY); 447 } 448 sckm_oflag = B_TRUE; 449 mutex_exit(&sckm_umutex); 450 451 SCKM_DEBUG0(D_OPEN, "sckm_open: succcess"); 452 return (0); 453 } 454 455 /*ARGSUSED*/ 456 static int 457 sckm_close(dev_t dev, int flag, int otyp, struct cred *cred) 458 { 459 SCKM_DEBUG0(D_OPEN, "in sckm_close"); 460 461 mutex_enter(&sckm_umutex); 462 sckm_oflag = B_FALSE; 463 mutex_exit(&sckm_umutex); 464 465 return (0); 466 } 467 468 469 static int 470 sckm_copyin_ioctl_getreq(intptr_t userarg, sckm_ioctl_getreq_t *driverarg, 471 int flag) 472 { 473 #ifdef _MULTI_DATAMODEL 474 switch (ddi_model_convert_from(flag & FMODELS)) { 475 case DDI_MODEL_ILP32: { 476 sckm_ioctl_getreq32_t driverarg32; 477 if (ddi_copyin((caddr_t)userarg, &driverarg32, 478 sizeof (sckm_ioctl_getreq32_t), flag)) { 479 return (EFAULT); 480 } 481 driverarg->transid = driverarg32.transid; 482 driverarg->type = driverarg32.type; 483 driverarg->buf = (caddr_t)(uintptr_t)driverarg32.buf; 484 driverarg->buf_len = driverarg32.buf_len; 485 break; 486 } 487 case DDI_MODEL_NONE: { 488 if (ddi_copyin((caddr_t)userarg, &driverarg, 489 sizeof (sckm_ioctl_getreq_t), flag)) { 490 return (EFAULT); 491 } 492 break; 493 } 494 } 495 #else /* ! _MULTI_DATAMODEL */ 496 if (ddi_copyin((caddr_t)userarg, &driverarg, 497 sizeof (sckm_ioctl_getreq_t), flag)) { 498 return (EFAULT); 499 } 500 #endif /* _MULTI_DATAMODEL */ 501 return (0); 502 } 503 504 505 static int 506 sckm_copyout_ioctl_getreq(sckm_ioctl_getreq_t *driverarg, intptr_t userarg, 507 int flag) 508 { 509 #ifdef _MULTI_DATAMODEL 510 switch (ddi_model_convert_from(flag & FMODELS)) { 511 case DDI_MODEL_ILP32: { 512 sckm_ioctl_getreq32_t driverarg32; 513 driverarg32.transid = driverarg->transid; 514 driverarg32.type = driverarg->type; 515 driverarg32.buf = (caddr32_t)(uintptr_t)driverarg->buf; 516 driverarg32.buf_len = driverarg->buf_len; 517 if (ddi_copyout(&driverarg32, (caddr_t)userarg, 518 sizeof (sckm_ioctl_getreq32_t), flag)) { 519 return (EFAULT); 520 } 521 break; 522 } 523 case DDI_MODEL_NONE: 524 if (ddi_copyout(driverarg, (caddr_t)userarg, 525 sizeof (sckm_ioctl_getreq_t), flag)) { 526 return (EFAULT); 527 } 528 break; 529 } 530 #else /* ! _MULTI_DATAMODEL */ 531 if (ddi_copyout(driverarg, (caddr_t)userarg, 532 sizeof (sckm_ioctl_getreq_t), flag)) { 533 return (EFAULT); 534 } 535 #endif /* _MULTI_DATAMODEL */ 536 return (0); 537 } 538 539 540 /*ARGSUSED*/ 541 static int 542 sckm_ioctl(dev_t dev, int cmd, intptr_t data, int flag, 543 cred_t *cred, int *rvalp) 544 { 545 int rval = 0; 546 547 SCKM_DEBUG0(D_IOCTL, "in sckm_ioctl"); 548 549 switch (cmd) { 550 case SCKM_IOCTL_GETREQ: { 551 sckm_ioctl_getreq_t arg; 552 553 SCKM_DEBUG0(D_IOCTL, "sckm_ioctl: got SCKM_IOCTL_GETREQ"); 554 if (sckm_copyin_ioctl_getreq(data, &arg, flag)) { 555 return (EFAULT); 556 } 557 558 /* sanity check argument */ 559 if (arg.buf_len < SCKM_SCKD_MAXDATA) { 560 SCKM_DEBUG2(D_IOCTL, "sckm_ioctl: usr buffer too " 561 "small (%d < %d)", arg.buf_len, SCKM_SCKD_MAXDATA); 562 return (ENOSPC); 563 } 564 565 mutex_enter(&sckm_umutex); 566 567 /* wait for request from SC */ 568 while (!sckm_udata_req) { 569 SCKM_DEBUG0(D_IOCTL, "sckm_ioctl: waiting for msg"); 570 if (cv_wait_sig(&sckm_udata_cv, &sckm_umutex) == 0) { 571 mutex_exit(&sckm_umutex); 572 return (EINTR); 573 } 574 } 575 SCKM_DEBUG1(D_IOCTL, "sckm_ioctl: msg available " 576 "transid = 0x%lx", sckm_udata.transid); 577 578 arg.transid = sckm_udata.transid; 579 arg.type = sckm_udata.type; 580 if (ddi_copyout(sckm_udata.buf, arg.buf, 581 sckm_udata.buf_len, flag)) { 582 mutex_exit(&sckm_umutex); 583 return (EFAULT); 584 } 585 arg.buf_len = sckm_udata.buf_len; 586 587 mutex_exit(&sckm_umutex); 588 if (sckm_copyout_ioctl_getreq(&arg, data, flag)) { 589 return (EFAULT); 590 } 591 break; 592 } 593 case SCKM_IOCTL_STATUS: { 594 sckm_ioctl_status_t arg; 595 SCKM_DEBUG0(D_IOCTL, "sckm_ioctl: got SCKM_IOCTL_STATUS"); 596 if (ddi_copyin((caddr_t)data, &arg, 597 sizeof (sckm_ioctl_status_t), flag)) { 598 cmn_err(CE_WARN, "sckm_ioctl: ddi_copyin failed"); 599 return (EFAULT); 600 } 601 SCKM_DEBUG3(D_IOCTL, "sckm_ioctl: arg transid=0x%lx, " 602 "status=%d, sadb_msg_errno=%d", arg.transid, arg.status, 603 arg.sadb_msg_errno); 604 605 mutex_enter(&sckm_umutex); 606 607 /* fail if no status is expected, or if it does not match */ 608 if (!sckm_udata_req || sckm_udata.transid != arg.transid) { 609 mutex_exit(&sckm_umutex); 610 return (EINVAL); 611 } 612 613 /* update status information for event handler */ 614 bcopy(&arg, &sckm_udata_status, sizeof (sckm_ioctl_status_t)); 615 616 /* signal event handler that request has been processed */ 617 SCKM_DEBUG0(D_IOCTL, "sckm_ioctl: signaling event handler" 618 " that data has been processed"); 619 cv_signal(&sckm_cons_cv); 620 sckm_udata_req = B_FALSE; 621 622 mutex_exit(&sckm_umutex); 623 break; 624 } 625 default: 626 SCKM_DEBUG0(D_IOCTL, "sckm_ioctl: unknown command"); 627 rval = EINVAL; 628 } 629 630 SCKM_DEBUG1(D_IOCTL, "out sckm_ioctl, rval=%d", rval); 631 return (rval); 632 } 633 634 635 /* 636 * sckm_mbox_callback 637 * 638 * Callback routine registered with the IOSRAM mailbox protocol driver. 639 * Invoked when a message is received on the mailbox. 640 */ 641 static void 642 sckm_mbox_callback(void) 643 { 644 SCKM_DEBUG0(D_CALLBACK, "in sckm_mbox_callback()"); 645 646 mutex_enter(&sckm_taskq_ptr_mutex); 647 648 if (sckm_taskq == NULL) { 649 mutex_exit(&sckm_taskq_ptr_mutex); 650 return; 651 } 652 653 if (!taskq_dispatch(sckm_taskq, sckm_mbox_task, NULL, KM_NOSLEEP)) { 654 /* 655 * Too many tasks already pending. Do not queue a new 656 * request. 657 */ 658 SCKM_DEBUG0(D_CALLBACK, "failed dispatching task"); 659 } 660 661 mutex_exit(&sckm_taskq_ptr_mutex); 662 663 SCKM_DEBUG0(D_CALLBACK, "out sckm_mbox_callback()"); 664 } 665 666 667 /* 668 * sckm_mbox_task 669 * 670 * Dispatched on taskq from the IOSRAM mailbox callback 671 * sckm_mbox_callback when a message is received on the incoming 672 * mailbox. 673 */ 674 static void 675 sckm_mbox_task(void *ignored) 676 { 677 _NOTE(ARGUNUSED(ignored)) 678 uint32_t type, cmd, length; 679 uint64_t transid; 680 int rval; 681 682 SCKM_DEBUG0(D_TASK, "in sckm_mbox_task\n"); 683 684 mutex_enter(&sckm_task_mutex); 685 686 if (req_data == NULL || rep_data == NULL) { 687 SCKM_DEBUG0(D_TASK, "sckm_mbox_task: no buffers"); 688 mutex_exit(&sckm_task_mutex); 689 return; 690 } 691 692 /* 693 * Get mailbox message. 694 */ 695 696 type = MBOXSC_MSG_REQUEST; 697 length = SCKM_SCKD_MAXDATA; 698 cmd = 0; 699 transid = 0; 700 701 SCKM_DEBUG0(D_TASK, "sckm_mbox_task: " 702 "calling mboxsc_getmsg()\n"); 703 rval = mboxsc_getmsg(KEY_SCKD, &type, &cmd, &transid, 704 &length, req_data, sckm_getmsg_timeout); 705 706 if (rval != 0) { 707 SCKM_DEBUG1(D_TASK, "sckm_mbox_task: " 708 "mboxsc_getmsg() failed (%d)\n", rval); 709 mutex_exit(&sckm_task_mutex); 710 return; 711 } 712 713 SCKM_DEBUG4(D_TASK, "sckm_mbox_task: " 714 "type=0x%x cmd=0x%x length=%d transid=0x%lx\n", 715 type, cmd, length, transid); 716 717 /* check message length */ 718 if (length < sizeof (sckm_mbox_req_hdr_t)) { 719 /* protocol error, drop message */ 720 SCKM_DEBUG2(D_TASK, "received short " 721 "message of length %d, min %lu", 722 length, sizeof (sckm_mbox_req_hdr_t)); 723 mutex_exit(&sckm_task_mutex); 724 return; 725 } 726 727 /* check version of message received */ 728 if (req_data->sckm_version != SCKM_PROTOCOL_VERSION) { 729 SCKM_DEBUG2(D_TASK, "received protocol " 730 "version %d, expected %d", 731 req_data->sckm_version, SCKM_PROTOCOL_VERSION); 732 /* 733 * Send reply with SCKM_SADB_ERR_VERSION error 734 * so that SC can adopt correct protocol version 735 * for this domain. 736 */ 737 rep_data->sckm_version = SCKM_PROTOCOL_VERSION; 738 rep_data->status = SCKM_ERR_VERSION; 739 740 rval = mboxsc_putmsg(KEY_KDSC, MBOXSC_MSG_REPLY, 741 cmd, &transid, sizeof (sckm_mbox_rep_hdr_t), 742 rep_data, MBOXSC_PUTMSG_DEF_TIMEOUT); 743 744 if (rval != 0) { 745 SCKM_DEBUG1(D_TASK, "sckm_mbox_task: " 746 "mboxsc_putmsg() failed (%d)\n", rval); 747 mutex_exit(&sckm_task_mutex); 748 return; 749 } 750 } 751 752 /* process message */ 753 sckm_process_msg(cmd, transid, length, 754 req_data, rep_data); 755 756 mutex_exit(&sckm_task_mutex); 757 } 758 759 /* 760 * sckm_process_msg 761 * 762 * Process a message received from the SC. Invoked by sckm_event_task(). 763 */ 764 static void 765 sckm_process_msg(uint32_t cmd, uint64_t transid, 766 uint32_t len, sckm_mbox_req_hdr_t *req_data, 767 sckm_mbox_rep_hdr_t *rep_data) 768 { 769 int rv; 770 771 mutex_enter(&sckm_umutex); 772 773 switch (cmd) { 774 case SCKM_MSG_SADB: { 775 int sadb_msglen; 776 777 sadb_msglen = len-sizeof (sckm_mbox_req_hdr_t); 778 SCKM_DEBUG1(D_TASK, "received SCKM_MSG_SADB len=%d", 779 sadb_msglen); 780 781 /* sanity check request */ 782 if (len-sizeof (sckm_mbox_req_hdr_t) <= 0) { 783 SCKM_DEBUG0(D_TASK, "bad SADB message, " 784 "zero length"); 785 /* 786 * SADB message is too short, send corresponding 787 * error message to SC. 788 */ 789 rep_data->sckm_version = SCKM_PROTOCOL_VERSION; 790 rep_data->status = SCKM_ERR_SADB_MSG; 791 792 if ((rv = mboxsc_putmsg(KEY_KDSC, MBOXSC_MSG_REPLY, 793 cmd, &transid, sizeof (sckm_mbox_rep_hdr_t), 794 rep_data, MBOXSC_PUTMSG_DEF_TIMEOUT)) != 0) { 795 SCKM_DEBUG1(D_TASK, "sckm_mbox_task: " 796 "mboxsc_putmsg() failed (%d)\n", rv); 797 } 798 mutex_exit(&sckm_umutex); 799 return; 800 } 801 802 /* initialize request for daemon */ 803 sckm_udata.transid = transid; 804 sckm_udata.type = SCKM_IOCTL_REQ_SADB; 805 sckm_udata.buf_len = len-sizeof (sckm_mbox_req_hdr_t); 806 bcopy(req_data+1, sckm_udata.buf, sckm_udata.buf_len); 807 808 break; 809 } 810 default: 811 cmn_err(CE_WARN, "unknown cmd %x received from SC", cmd); 812 /* 813 * Received unknown command from SC. Send corresponding 814 * error message to SC. 815 */ 816 rep_data->sckm_version = SCKM_PROTOCOL_VERSION; 817 rep_data->status = SCKM_ERR_BAD_CMD; 818 819 if ((rv = mboxsc_putmsg(KEY_KDSC, MBOXSC_MSG_REPLY, 820 cmd, &transid, sizeof (sckm_mbox_rep_hdr_t), 821 rep_data, MBOXSC_PUTMSG_DEF_TIMEOUT)) != 0) { 822 SCKM_DEBUG1(D_TASK, "sckm_mbox_task: " 823 "mboxsc_putmsg() failed (%d)\n", rv); 824 } 825 mutex_exit(&sckm_umutex); 826 return; 827 } 828 829 /* 830 * At this point, we know that the request is valid, so pass 831 * the request to the daemon. 832 */ 833 SCKM_DEBUG0(D_TASK, "waking up daemon"); 834 sckm_udata_req = B_TRUE; 835 cv_signal(&sckm_udata_cv); 836 837 /* wait for daemon to process request */ 838 if (cv_reltimedwait(&sckm_cons_cv, &sckm_umutex, 839 drv_usectohz(SCKM_DAEMON_TIMEOUT), TR_CLOCK_TICK) == -1) { 840 /* 841 * Daemon did not process the data, report this 842 * error to the SC. 843 */ 844 SCKM_DEBUG0(D_TASK, "daemon timeout!!"); 845 rep_data->sckm_version = SCKM_PROTOCOL_VERSION; 846 rep_data->status = SCKM_ERR_DAEMON; 847 } else { 848 /* Daemon processed data, return status to SC */ 849 SCKM_DEBUG0(D_TASK, "daemon processed data"); 850 rep_data->sckm_version = SCKM_PROTOCOL_VERSION; 851 switch (sckm_udata_status.status) { 852 case SCKM_IOCTL_STAT_SUCCESS: 853 SCKM_DEBUG0(D_TASK, "daemon returned success"); 854 rep_data->status = SCKM_SUCCESS; 855 break; 856 case SCKM_IOCTL_STAT_ERR_PFKEY: 857 SCKM_DEBUG1(D_TASK, "daemon returned PF_KEY " 858 "error, errno=%d", 859 sckm_udata_status.sadb_msg_errno); 860 rep_data->status = SCKM_ERR_SADB_PFKEY; 861 rep_data->sadb_msg_errno = 862 sckm_udata_status.sadb_msg_errno; 863 break; 864 case SCKM_IOCTL_STAT_ERR_REQ: 865 SCKM_DEBUG0(D_TASK, "daemon returned " 866 "bad request"); 867 rep_data->status = SCKM_ERR_DAEMON; 868 break; 869 case SCKM_IOCTL_STAT_ERR_VERSION: 870 SCKM_DEBUG0(D_TASK, "PF_KEY version not " 871 "supported"); 872 rep_data->status = SCKM_ERR_SADB_VERSION; 873 rep_data->sadb_msg_version = 874 sckm_udata_status.sadb_msg_version; 875 break; 876 case SCKM_IOCTL_STAT_ERR_TIMEOUT: 877 SCKM_DEBUG0(D_TASK, "no response received " 878 "from key engine"); 879 rep_data->status = SCKM_ERR_SADB_TIMEOUT; 880 break; 881 case SCKM_IOCTL_STAT_ERR_OTHER: 882 SCKM_DEBUG0(D_TASK, "daemon encountered " 883 "an error"); 884 rep_data->status = SCKM_ERR_DAEMON; 885 break; 886 case SCKM_IOCTL_STAT_ERR_SADB_TYPE: 887 SCKM_DEBUG0(D_TASK, "daemon returned bad " 888 "SADB message type"); 889 rep_data->status = SCKM_ERR_SADB_BAD_TYPE; 890 break; 891 default: 892 cmn_err(CE_WARN, "SCKM daemon returned " 893 "invalid status %d", sckm_udata_status.status); 894 rep_data->status = SCKM_ERR_DAEMON; 895 } 896 } 897 898 /* send reply back to SC */ 899 if ((rv = mboxsc_putmsg(KEY_KDSC, MBOXSC_MSG_REPLY, 900 cmd, &transid, sizeof (sckm_mbox_rep_hdr_t), 901 rep_data, MBOXSC_PUTMSG_DEF_TIMEOUT)) != 0) { 902 SCKM_DEBUG1(D_TASK, "failed sending reply to SC (%d)", rv); 903 } else { 904 SCKM_DEBUG0(D_TASK, "reply sent to SC"); 905 } 906 907 sckm_udata_req = B_FALSE; 908 mutex_exit(&sckm_umutex); 909 } 910