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
];
272 struct passwd
*result
;
274 euid
= ucred_geteuid(ucred
);
276 getpwuid_r(euid
, &pwd
, buf
, sizeof (buf
), &result
);
277 if (!result
|| (chkauthattr(auth
, result
->pw_name
) == 0)) {
278 log_info("Unauthorized door call.\n");
288 * Implements the door command to get a hotplug information snapshot.
291 cmd_getinfo(nvlist_t
*args
, nvlist_t
**resultsp
)
302 dprintf("cmd_getinfo:\n");
305 if (nvlist_lookup_string(args
, HPD_PATH
, &path
) != 0) {
306 dprintf("cmd_getinfo: invalid arguments.\n");
309 if (nvlist_lookup_string(args
, HPD_CONNECTION
, &connection
) != 0)
311 if (nvlist_lookup_uint32(args
, HPD_FLAGS
, (uint32_t *)&flags
) != 0)
314 /* Get and pack the requested snapshot */
315 if ((rv
= getinfo(path
, connection
, flags
, &root
)) == 0) {
316 rv
= hp_pack(root
, &buf
, &len
);
319 dprintf("cmd_getinfo: getinfo(): rv = %d, buf = %p.\n", rv
,
323 * If the above failed or there is no snapshot,
324 * then only return a status code.
331 /* Allocate nvlist for results */
332 if (nvlist_alloc(&results
, NV_UNIQUE_NAME_TYPE
, 0) != 0) {
333 dprintf("cmd_getinfo: nvlist_alloc() failed.\n");
338 /* Add snapshot and successful status to results */
339 if ((nvlist_add_int32(results
, HPD_STATUS
, 0) != 0) ||
340 (nvlist_add_byte_array(results
, HPD_INFO
,
341 (uchar_t
*)buf
, len
) != 0)) {
342 dprintf("cmd_getinfo: nvlist add failure.\n");
343 nvlist_free(results
);
348 /* Packed snapshot no longer needed */
359 * Implements the door command to initate a state change operation.
361 * NOTE: requires 'modify' authorization.
364 cmd_changestate(nvlist_t
*args
, nvlist_t
**resultsp
)
366 hp_node_t root
= NULL
;
367 nvlist_t
*results
= NULL
;
368 char *path
, *connection
;
371 int rv
, state
, old_state
, status
;
373 dprintf("cmd_changestate:\n");
376 if ((nvlist_lookup_string(args
, HPD_PATH
, &path
) != 0) ||
377 (nvlist_lookup_string(args
, HPD_CONNECTION
, &connection
) != 0) ||
378 (nvlist_lookup_int32(args
, HPD_STATE
, &state
) != 0)) {
379 dprintf("cmd_changestate: invalid arguments.\n");
382 if (nvlist_lookup_uint32(args
, HPD_FLAGS
, (uint32_t *)&flags
) != 0)
385 /* Get caller's credentials */
386 if (door_ucred(&uc
) != 0) {
387 log_err("Cannot get door credentials (%s)\n", strerror(errno
));
391 /* Check authorization */
392 if (check_auth(uc
, HP_MODIFY_AUTH
) != 0) {
393 dprintf("cmd_changestate: access denied.\n");
394 audit_changestate(uc
, HP_MODIFY_AUTH
, path
, connection
,
395 state
, -1, ADT_FAIL_VALUE_AUTH
);
400 /* Perform the state change operation */
401 status
= changestate(path
, connection
, state
, flags
, &old_state
, &root
);
402 dprintf("cmd_changestate: changestate() == %d\n", status
);
404 /* Audit the operation */
405 audit_changestate(uc
, HP_MODIFY_AUTH
, path
, connection
, state
,
408 /* Caller's credentials no longer needed */
412 * Pack the results into an nvlist if there is an error snapshot.
414 * If any error occurs while packing the results, the original
415 * error code from changestate() above is still returned.
421 dprintf("cmd_changestate: results nvlist required.\n");
423 /* Pack and discard the error snapshot */
424 rv
= hp_pack(root
, &buf
, &len
);
427 dprintf("cmd_changestate: hp_pack() failed (%s).\n",
432 /* Allocate nvlist for results */
433 if (nvlist_alloc(&results
, NV_UNIQUE_NAME_TYPE
, 0) != 0) {
434 dprintf("cmd_changestate: nvlist_alloc() failed.\n");
439 /* Add the results into the nvlist */
440 if ((nvlist_add_int32(results
, HPD_STATUS
, status
) != 0) ||
441 (nvlist_add_byte_array(results
, HPD_INFO
, (uchar_t
*)buf
,
443 dprintf("cmd_changestate: nvlist add failed.\n");
444 nvlist_free(results
);
458 * Implementation of the door command to set or get bus private options.
460 * NOTE: requires 'modify' authorization for the 'set' command.
463 cmd_private(hp_cmd_t cmd
, nvlist_t
*args
, nvlist_t
**resultsp
)
465 nvlist_t
*results
= NULL
;
467 char *path
, *connection
, *options
;
471 dprintf("cmd_private:\n");
473 /* Get caller's credentials */
474 if ((cmd
== HP_CMD_SETPRIVATE
) && (door_ucred(&uc
) != 0)) {
475 log_err("Cannot get door credentials (%s)\n", strerror(errno
));
480 if ((nvlist_lookup_string(args
, HPD_PATH
, &path
) != 0) ||
481 (nvlist_lookup_string(args
, HPD_CONNECTION
, &connection
) != 0) ||
482 (nvlist_lookup_string(args
, HPD_OPTIONS
, &options
) != 0)) {
483 dprintf("cmd_private: invalid arguments.\n");
487 /* Check authorization */
488 if ((cmd
== HP_CMD_SETPRIVATE
) &&
489 (check_auth(uc
, HP_MODIFY_AUTH
) != 0)) {
490 dprintf("cmd_private: access denied.\n");
491 audit_setprivate(uc
, HP_MODIFY_AUTH
, path
, connection
, options
,
492 ADT_FAIL_VALUE_AUTH
);
497 /* Perform the operation */
498 status
= private_options(path
, connection
, cmd
, options
, &values
);
499 dprintf("cmd_private: private_options() == %d\n", status
);
501 /* Audit the operation */
502 if (cmd
== HP_CMD_SETPRIVATE
) {
503 audit_setprivate(uc
, HP_MODIFY_AUTH
, path
, connection
, options
,
508 /* Construct an nvlist if values were returned */
509 if (values
!= NULL
) {
511 /* Allocate nvlist for results */
512 if (nvlist_alloc(&results
, NV_UNIQUE_NAME_TYPE
, 0) != 0) {
513 dprintf("cmd_private: nvlist_alloc() failed.\n");
518 /* Add values and status to the results */
519 if ((nvlist_add_int32(results
, HPD_STATUS
, status
) != 0) ||
520 (nvlist_add_string(results
, HPD_OPTIONS
, values
) != 0)) {
521 dprintf("cmd_private: nvlist add failed.\n");
522 nvlist_free(results
);
527 /* The values string is no longer needed */
539 * Allocate the next unique sequence number for a results buffer.
546 (void) pthread_mutex_lock(&buffer_lock
);
548 seqnum
= buffer_seqnum
++;
550 (void) pthread_mutex_unlock(&buffer_lock
);
558 * Link a results buffer into the list containing all buffers.
561 add_buffer(uint64_t seqnum
, char *buf
)
565 if ((node
= (i_buffer_t
*)malloc(sizeof (i_buffer_t
))) == NULL
) {
566 /* The consequence is a memory leak. */
567 log_err("Cannot allocate results buffer: %s\n",
572 node
->seqnum
= seqnum
;
575 (void) pthread_mutex_lock(&buffer_lock
);
577 node
->next
= buffer_list
;
580 (void) pthread_mutex_unlock(&buffer_lock
);
586 * Remove a results buffer from the list containing all buffers.
589 free_buffer(uint64_t seqnum
)
591 i_buffer_t
*node
, *prev
;
593 (void) pthread_mutex_lock(&buffer_lock
);
599 if (node
->seqnum
== seqnum
) {
600 dprintf("Free buffer %lld\n", seqnum
);
602 prev
->next
= node
->next
;
604 buffer_list
= node
->next
;
614 (void) pthread_mutex_unlock(&buffer_lock
);
620 * Initialize an audit session.
623 audit_session(ucred_t
*ucred
, adt_session_data_t
**sessionp
)
625 adt_session_data_t
*session
;
627 if (adt_start_session(&session
, NULL
, 0) != 0) {
628 log_err("Cannot start audit session.\n");
632 if (adt_set_from_ucred(session
, ucred
, ADT_NEW
) != 0) {
633 log_err("Cannot set audit session from ucred.\n");
634 (void) adt_end_session(session
);
643 * audit_changestate()
645 * Audit a 'changestate' door command.
648 audit_changestate(ucred_t
*ucred
, char *auth
, char *path
, char *connection
,
649 int new_state
, int old_state
, int result
)
651 adt_session_data_t
*session
;
652 adt_event_data_t
*event
;
653 int pass_fail
, fail_reason
;
655 if (audit_session(ucred
, &session
) != 0)
658 if ((event
= adt_alloc_event(session
, ADT_hotplug_state
)) == NULL
) {
659 (void) adt_end_session(session
);
664 pass_fail
= ADT_SUCCESS
;
665 fail_reason
= ADT_SUCCESS
;
667 pass_fail
= ADT_FAILURE
;
668 fail_reason
= result
;
671 event
->adt_hotplug_state
.auth_used
= auth
;
672 event
->adt_hotplug_state
.device_path
= path
;
673 event
->adt_hotplug_state
.connection
= connection
;
674 event
->adt_hotplug_state
.new_state
= state_str(new_state
);
675 event
->adt_hotplug_state
.old_state
= state_str(old_state
);
678 if (adt_put_event(event
, pass_fail
, fail_reason
) != 0)
679 log_err("Cannot put audit event.\n");
681 adt_free_event(event
);
682 (void) adt_end_session(session
);
688 * Audit a 'set private' door command.
691 audit_setprivate(ucred_t
*ucred
, char *auth
, char *path
, char *connection
,
692 char *options
, int result
)
694 adt_session_data_t
*session
;
695 adt_event_data_t
*event
;
696 int pass_fail
, fail_reason
;
698 if (audit_session(ucred
, &session
) != 0)
701 if ((event
= adt_alloc_event(session
, ADT_hotplug_set
)) == NULL
) {
702 (void) adt_end_session(session
);
707 pass_fail
= ADT_SUCCESS
;
708 fail_reason
= ADT_SUCCESS
;
710 pass_fail
= ADT_FAILURE
;
711 fail_reason
= result
;
714 event
->adt_hotplug_set
.auth_used
= auth
;
715 event
->adt_hotplug_set
.device_path
= path
;
716 event
->adt_hotplug_set
.connection
= connection
;
717 event
->adt_hotplug_set
.options
= options
;
720 if (adt_put_event(event
, pass_fail
, fail_reason
) != 0)
721 log_err("Cannot put audit event.\n");
723 adt_free_event(event
);
724 (void) adt_end_session(session
);
730 * Convert a state from integer to string.
736 case DDI_HP_CN_STATE_EMPTY
:
738 case DDI_HP_CN_STATE_PRESENT
:
740 case DDI_HP_CN_STATE_POWERED
:
742 case DDI_HP_CN_STATE_ENABLED
:
744 case DDI_HP_CN_STATE_PORT_EMPTY
:
745 return ("PORT-EMPTY");
746 case DDI_HP_CN_STATE_PORT_PRESENT
:
747 return ("PORT-PRESENT");
748 case DDI_HP_CN_STATE_OFFLINE
:
750 case DDI_HP_CN_STATE_ATTACHED
:
752 case DDI_HP_CN_STATE_MAINTENANCE
:
753 return ("MAINTENANCE");
754 case DDI_HP_CN_STATE_ONLINE
: