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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)347 _info(struct modinfo *modinfop)
348 {
349 SCKM_DEBUG0(D_INIT, "in _info");
350 return (mod_info(&modlinkage, modinfop));
351 }
352
353 static int
sckm_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)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
sckm_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)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
sckm_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)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
sckm_open(dev_t * devp,int flag,int otyp,struct cred * cred)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
sckm_close(dev_t dev,int flag,int otyp,struct cred * cred)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
sckm_copyin_ioctl_getreq(intptr_t userarg,sckm_ioctl_getreq_t * driverarg,int flag)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
sckm_copyout_ioctl_getreq(sckm_ioctl_getreq_t * driverarg,intptr_t userarg,int flag)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
sckm_ioctl(dev_t dev,int cmd,intptr_t data,int flag,cred_t * cred,int * rvalp)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
sckm_mbox_callback(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
sckm_mbox_task(void * ignored)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
sckm_process_msg(uint32_t cmd,uint64_t transid,uint32_t len,sckm_mbox_req_hdr_t * req_data,sckm_mbox_rep_hdr_t * rep_data)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