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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
38 #include <auth_list.h>
39 #include <auth_attr.h>
41 #include <bsm/adt_event.h>
42 #include <sys/sunddi.h>
43 #include <sys/ddi_hp.h>
44 #include <libnvpair.h>
45 #include <libhotplug.h>
46 #include <libhotplug_impl.h>
47 #include "hotplugd_impl.h"
50 * Buffer management for results.
52 typedef struct i_buffer
{
55 struct i_buffer
*next
;
58 static uint64_t buffer_seqnum
= 1;
59 static i_buffer_t
*buffer_list
= NULL
;
60 static pthread_mutex_t buffer_lock
= PTHREAD_MUTEX_INITIALIZER
;
63 * Door file descriptor.
65 static int door_fd
= -1;
68 * Function prototypes.
70 static void door_server(void *, char *, size_t, door_desc_t
*, uint_t
);
71 static int check_auth(ucred_t
*, const char *);
72 static int cmd_getinfo(nvlist_t
*, nvlist_t
**);
73 static int cmd_changestate(nvlist_t
*, nvlist_t
**);
74 static int cmd_private(hp_cmd_t
, nvlist_t
*, nvlist_t
**);
75 static void add_buffer(uint64_t, char *);
76 static void free_buffer(uint64_t);
77 static uint64_t get_seqnum(void);
78 static char *state_str(int);
79 static int audit_session(ucred_t
*, adt_session_data_t
**);
80 static void audit_changestate(ucred_t
*, char *, char *, char *, int, int,
82 static void audit_setprivate(ucred_t
*, char *, char *, char *, char *,
88 * Create the door file, and initialize the door server.
91 door_server_init(void)
95 /* Create the door file */
96 if ((fd
= open(HOTPLUGD_DOOR
, O_CREAT
|O_EXCL
|O_RDONLY
, 0644)) == -1) {
97 if (errno
== EEXIST
) {
98 log_err("Door service is already running.\n");
100 log_err("Cannot open door file '%s': %s\n",
101 HOTPLUGD_DOOR
, strerror(errno
));
107 /* Initialize the door service */
108 if ((door_fd
= door_create(door_server
, NULL
,
109 DOOR_REFUSE_DESC
| DOOR_NO_CANCEL
)) == -1) {
110 log_err("Cannot create door service: %s\n", strerror(errno
));
114 /* Cleanup stale door associations */
115 (void) fdetach(HOTPLUGD_DOOR
);
117 /* Associate door service with door file */
118 if (fattach(door_fd
, HOTPLUGD_DOOR
) != 0) {
119 log_err("Cannot attach to door file '%s': %s\n", HOTPLUGD_DOOR
,
121 (void) door_revoke(door_fd
);
122 (void) fdetach(HOTPLUGD_DOOR
);
133 * Terminate and cleanup the door server.
136 door_server_fini(void)
139 (void) door_revoke(door_fd
);
140 (void) fdetach(HOTPLUGD_DOOR
);
143 (void) unlink(HOTPLUGD_DOOR
);
149 * This routine is the handler which responds to each door call.
150 * Each incoming door call is expected to send a packed nvlist
151 * of arguments which describe the requested action. And each
152 * response is sent back as a packed nvlist of results.
154 * Results are always allocated on the heap. A global list of
155 * allocated result buffers is managed, and each one is tracked
156 * by a unique sequence number. The final step in the protocol
157 * is for the caller to send a short response using the sequence
158 * number when the buffer can be released.
162 door_server(void *cookie
, char *argp
, size_t sz
, door_desc_t
*dp
, uint_t ndesc
)
164 nvlist_t
*args
= NULL
;
165 nvlist_t
*results
= NULL
;
169 dprintf("Door call: cookie=%p, argp=%p, sz=%d\n", cookie
, (void *)argp
,
172 /* Special case to free a results buffer */
173 if (sz
== sizeof (uint64_t)) {
174 free_buffer(*(uint64_t *)(uintptr_t)argp
);
175 (void) door_return(NULL
, 0, NULL
, 0);
179 /* Unpack the arguments nvlist */
180 if (nvlist_unpack(argp
, sz
, &args
, 0) != 0) {
181 log_err("Cannot unpack door arguments.\n");
186 /* Extract the requested command */
187 if (nvlist_lookup_int32(args
, HPD_CMD
, (int32_t *)&cmd
) != 0) {
188 log_err("Cannot decode door command.\n");
193 /* Implement the command */
196 rv
= cmd_getinfo(args
, &results
);
198 case HP_CMD_CHANGESTATE
:
199 rv
= cmd_changestate(args
, &results
);
201 case HP_CMD_SETPRIVATE
:
202 case HP_CMD_GETPRIVATE
:
203 rv
= cmd_private(cmd
, args
, &results
);
210 /* The arguments nvlist is no longer needed */
215 * If an nvlist was constructed for the results,
216 * then pack the results nvlist and return it.
218 if (results
!= NULL
) {
223 /* Add a sequence number to the results */
224 seqnum
= get_seqnum();
225 if (nvlist_add_uint64(results
, HPD_SEQNUM
, seqnum
) != 0) {
226 log_err("Cannot add sequence number.\n");
231 /* Pack the results nvlist */
232 if (nvlist_pack(results
, &buf
, &len
,
233 NV_ENCODE_NATIVE
, 0) != 0) {
234 log_err("Cannot pack door results.\n");
239 /* Link results buffer into list */
240 add_buffer(seqnum
, buf
);
242 /* The results nvlist is no longer needed */
243 nvlist_free(results
);
245 /* Return the results */
246 (void) door_return(buf
, len
, NULL
, 0);
250 /* Return result code (when no nvlist) */
251 (void) door_return((char *)&rv
, sizeof (int), NULL
, 0);
255 log_err("Door call failed (%s)\n", strerror(rv
));
257 nvlist_free(results
);
258 (void) door_return((char *)&rv
, sizeof (int), NULL
, 0);
264 * Perform an RBAC authorization check.
267 check_auth(ucred_t
*ucred
, const char *auth
)
271 char buf
[MAXPATHLEN
];
273 euid
= ucred_geteuid(ucred
);
275 if ((getpwuid_r(euid
, &pwd
, buf
, sizeof (buf
)) == NULL
) ||
276 (chkauthattr(auth
, pwd
.pw_name
) == 0)) {
277 log_info("Unauthorized door call.\n");
287 * Implements the door command to get a hotplug information snapshot.
290 cmd_getinfo(nvlist_t
*args
, nvlist_t
**resultsp
)
301 dprintf("cmd_getinfo:\n");
304 if (nvlist_lookup_string(args
, HPD_PATH
, &path
) != 0) {
305 dprintf("cmd_getinfo: invalid arguments.\n");
308 if (nvlist_lookup_string(args
, HPD_CONNECTION
, &connection
) != 0)
310 if (nvlist_lookup_uint32(args
, HPD_FLAGS
, (uint32_t *)&flags
) != 0)
313 /* Get and pack the requested snapshot */
314 if ((rv
= getinfo(path
, connection
, flags
, &root
)) == 0) {
315 rv
= hp_pack(root
, &buf
, &len
);
318 dprintf("cmd_getinfo: getinfo(): rv = %d, buf = %p.\n", rv
,
322 * If the above failed or there is no snapshot,
323 * then only return a status code.
330 /* Allocate nvlist for results */
331 if (nvlist_alloc(&results
, NV_UNIQUE_NAME_TYPE
, 0) != 0) {
332 dprintf("cmd_getinfo: nvlist_alloc() failed.\n");
337 /* Add snapshot and successful status to results */
338 if ((nvlist_add_int32(results
, HPD_STATUS
, 0) != 0) ||
339 (nvlist_add_byte_array(results
, HPD_INFO
,
340 (uchar_t
*)buf
, len
) != 0)) {
341 dprintf("cmd_getinfo: nvlist add failure.\n");
342 nvlist_free(results
);
347 /* Packed snapshot no longer needed */
358 * Implements the door command to initate a state change operation.
360 * NOTE: requires 'modify' authorization.
363 cmd_changestate(nvlist_t
*args
, nvlist_t
**resultsp
)
365 hp_node_t root
= NULL
;
366 nvlist_t
*results
= NULL
;
367 char *path
, *connection
;
370 int rv
, state
, old_state
, status
;
372 dprintf("cmd_changestate:\n");
375 if ((nvlist_lookup_string(args
, HPD_PATH
, &path
) != 0) ||
376 (nvlist_lookup_string(args
, HPD_CONNECTION
, &connection
) != 0) ||
377 (nvlist_lookup_int32(args
, HPD_STATE
, &state
) != 0)) {
378 dprintf("cmd_changestate: invalid arguments.\n");
381 if (nvlist_lookup_uint32(args
, HPD_FLAGS
, (uint32_t *)&flags
) != 0)
384 /* Get caller's credentials */
385 if (door_ucred(&uc
) != 0) {
386 log_err("Cannot get door credentials (%s)\n", strerror(errno
));
390 /* Check authorization */
391 if (check_auth(uc
, HP_MODIFY_AUTH
) != 0) {
392 dprintf("cmd_changestate: access denied.\n");
393 audit_changestate(uc
, HP_MODIFY_AUTH
, path
, connection
,
394 state
, -1, ADT_FAIL_VALUE_AUTH
);
399 /* Perform the state change operation */
400 status
= changestate(path
, connection
, state
, flags
, &old_state
, &root
);
401 dprintf("cmd_changestate: changestate() == %d\n", status
);
403 /* Audit the operation */
404 audit_changestate(uc
, HP_MODIFY_AUTH
, path
, connection
, state
,
407 /* Caller's credentials no longer needed */
411 * Pack the results into an nvlist if there is an error snapshot.
413 * If any error occurs while packing the results, the original
414 * error code from changestate() above is still returned.
420 dprintf("cmd_changestate: results nvlist required.\n");
422 /* Pack and discard the error snapshot */
423 rv
= hp_pack(root
, &buf
, &len
);
426 dprintf("cmd_changestate: hp_pack() failed (%s).\n",
431 /* Allocate nvlist for results */
432 if (nvlist_alloc(&results
, NV_UNIQUE_NAME_TYPE
, 0) != 0) {
433 dprintf("cmd_changestate: nvlist_alloc() failed.\n");
438 /* Add the results into the nvlist */
439 if ((nvlist_add_int32(results
, HPD_STATUS
, status
) != 0) ||
440 (nvlist_add_byte_array(results
, HPD_INFO
, (uchar_t
*)buf
,
442 dprintf("cmd_changestate: nvlist add failed.\n");
443 nvlist_free(results
);
457 * Implementation of the door command to set or get bus private options.
459 * NOTE: requires 'modify' authorization for the 'set' command.
462 cmd_private(hp_cmd_t cmd
, nvlist_t
*args
, nvlist_t
**resultsp
)
464 nvlist_t
*results
= NULL
;
466 char *path
, *connection
, *options
;
470 dprintf("cmd_private:\n");
472 /* Get caller's credentials */
473 if ((cmd
== HP_CMD_SETPRIVATE
) && (door_ucred(&uc
) != 0)) {
474 log_err("Cannot get door credentials (%s)\n", strerror(errno
));
479 if ((nvlist_lookup_string(args
, HPD_PATH
, &path
) != 0) ||
480 (nvlist_lookup_string(args
, HPD_CONNECTION
, &connection
) != 0) ||
481 (nvlist_lookup_string(args
, HPD_OPTIONS
, &options
) != 0)) {
482 dprintf("cmd_private: invalid arguments.\n");
486 /* Check authorization */
487 if ((cmd
== HP_CMD_SETPRIVATE
) &&
488 (check_auth(uc
, HP_MODIFY_AUTH
) != 0)) {
489 dprintf("cmd_private: access denied.\n");
490 audit_setprivate(uc
, HP_MODIFY_AUTH
, path
, connection
, options
,
491 ADT_FAIL_VALUE_AUTH
);
496 /* Perform the operation */
497 status
= private_options(path
, connection
, cmd
, options
, &values
);
498 dprintf("cmd_private: private_options() == %d\n", status
);
500 /* Audit the operation */
501 if (cmd
== HP_CMD_SETPRIVATE
) {
502 audit_setprivate(uc
, HP_MODIFY_AUTH
, path
, connection
, options
,
507 /* Construct an nvlist if values were returned */
508 if (values
!= NULL
) {
510 /* Allocate nvlist for results */
511 if (nvlist_alloc(&results
, NV_UNIQUE_NAME_TYPE
, 0) != 0) {
512 dprintf("cmd_private: nvlist_alloc() failed.\n");
517 /* Add values and status to the results */
518 if ((nvlist_add_int32(results
, HPD_STATUS
, status
) != 0) ||
519 (nvlist_add_string(results
, HPD_OPTIONS
, values
) != 0)) {
520 dprintf("cmd_private: nvlist add failed.\n");
521 nvlist_free(results
);
526 /* The values string is no longer needed */
538 * Allocate the next unique sequence number for a results buffer.
545 (void) pthread_mutex_lock(&buffer_lock
);
547 seqnum
= buffer_seqnum
++;
549 (void) pthread_mutex_unlock(&buffer_lock
);
557 * Link a results buffer into the list containing all buffers.
560 add_buffer(uint64_t seqnum
, char *buf
)
564 if ((node
= (i_buffer_t
*)malloc(sizeof (i_buffer_t
))) == NULL
) {
565 /* The consequence is a memory leak. */
566 log_err("Cannot allocate results buffer: %s\n",
571 node
->seqnum
= seqnum
;
574 (void) pthread_mutex_lock(&buffer_lock
);
576 node
->next
= buffer_list
;
579 (void) pthread_mutex_unlock(&buffer_lock
);
585 * Remove a results buffer from the list containing all buffers.
588 free_buffer(uint64_t seqnum
)
590 i_buffer_t
*node
, *prev
;
592 (void) pthread_mutex_lock(&buffer_lock
);
598 if (node
->seqnum
== seqnum
) {
599 dprintf("Free buffer %lld\n", seqnum
);
601 prev
->next
= node
->next
;
603 buffer_list
= node
->next
;
613 (void) pthread_mutex_unlock(&buffer_lock
);
619 * Initialize an audit session.
622 audit_session(ucred_t
*ucred
, adt_session_data_t
**sessionp
)
624 adt_session_data_t
*session
;
626 if (adt_start_session(&session
, NULL
, 0) != 0) {
627 log_err("Cannot start audit session.\n");
631 if (adt_set_from_ucred(session
, ucred
, ADT_NEW
) != 0) {
632 log_err("Cannot set audit session from ucred.\n");
633 (void) adt_end_session(session
);
642 * audit_changestate()
644 * Audit a 'changestate' door command.
647 audit_changestate(ucred_t
*ucred
, char *auth
, char *path
, char *connection
,
648 int new_state
, int old_state
, int result
)
650 adt_session_data_t
*session
;
651 adt_event_data_t
*event
;
652 int pass_fail
, fail_reason
;
654 if (audit_session(ucred
, &session
) != 0)
657 if ((event
= adt_alloc_event(session
, ADT_hotplug_state
)) == NULL
) {
658 (void) adt_end_session(session
);
663 pass_fail
= ADT_SUCCESS
;
664 fail_reason
= ADT_SUCCESS
;
666 pass_fail
= ADT_FAILURE
;
667 fail_reason
= result
;
670 event
->adt_hotplug_state
.auth_used
= auth
;
671 event
->adt_hotplug_state
.device_path
= path
;
672 event
->adt_hotplug_state
.connection
= connection
;
673 event
->adt_hotplug_state
.new_state
= state_str(new_state
);
674 event
->adt_hotplug_state
.old_state
= state_str(old_state
);
677 if (adt_put_event(event
, pass_fail
, fail_reason
) != 0)
678 log_err("Cannot put audit event.\n");
680 adt_free_event(event
);
681 (void) adt_end_session(session
);
687 * Audit a 'set private' door command.
690 audit_setprivate(ucred_t
*ucred
, char *auth
, char *path
, char *connection
,
691 char *options
, int result
)
693 adt_session_data_t
*session
;
694 adt_event_data_t
*event
;
695 int pass_fail
, fail_reason
;
697 if (audit_session(ucred
, &session
) != 0)
700 if ((event
= adt_alloc_event(session
, ADT_hotplug_set
)) == NULL
) {
701 (void) adt_end_session(session
);
706 pass_fail
= ADT_SUCCESS
;
707 fail_reason
= ADT_SUCCESS
;
709 pass_fail
= ADT_FAILURE
;
710 fail_reason
= result
;
713 event
->adt_hotplug_set
.auth_used
= auth
;
714 event
->adt_hotplug_set
.device_path
= path
;
715 event
->adt_hotplug_set
.connection
= connection
;
716 event
->adt_hotplug_set
.options
= options
;
719 if (adt_put_event(event
, pass_fail
, fail_reason
) != 0)
720 log_err("Cannot put audit event.\n");
722 adt_free_event(event
);
723 (void) adt_end_session(session
);
729 * Convert a state from integer to string.
735 case DDI_HP_CN_STATE_EMPTY
:
737 case DDI_HP_CN_STATE_PRESENT
:
739 case DDI_HP_CN_STATE_POWERED
:
741 case DDI_HP_CN_STATE_ENABLED
:
743 case DDI_HP_CN_STATE_PORT_EMPTY
:
744 return ("PORT-EMPTY");
745 case DDI_HP_CN_STATE_PORT_PRESENT
:
746 return ("PORT-PRESENT");
747 case DDI_HP_CN_STATE_OFFLINE
:
749 case DDI_HP_CN_STATE_ATTACHED
:
751 case DDI_HP_CN_STATE_MAINTENANCE
:
752 return ("MAINTENANCE");
753 case DDI_HP_CN_STATE_ONLINE
: