1*26947304SEvan Yan /* 2*26947304SEvan Yan * CDDL HEADER START 3*26947304SEvan Yan * 4*26947304SEvan Yan * The contents of this file are subject to the terms of the 5*26947304SEvan Yan * Common Development and Distribution License (the "License"). 6*26947304SEvan Yan * You may not use this file except in compliance with the License. 7*26947304SEvan Yan * 8*26947304SEvan Yan * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*26947304SEvan Yan * or http://www.opensolaris.org/os/licensing. 10*26947304SEvan Yan * See the License for the specific language governing permissions 11*26947304SEvan Yan * and limitations under the License. 12*26947304SEvan Yan * 13*26947304SEvan Yan * When distributing Covered Code, include this CDDL HEADER in each 14*26947304SEvan Yan * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*26947304SEvan Yan * If applicable, add the following below this CDDL HEADER, with the 16*26947304SEvan Yan * fields enclosed by brackets "[]" replaced with your own identifying 17*26947304SEvan Yan * information: Portions Copyright [yyyy] [name of copyright owner] 18*26947304SEvan Yan * 19*26947304SEvan Yan * CDDL HEADER END 20*26947304SEvan Yan */ 21*26947304SEvan Yan 22*26947304SEvan Yan /* 23*26947304SEvan Yan * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24*26947304SEvan Yan * Use is subject to license terms. 25*26947304SEvan Yan */ 26*26947304SEvan Yan 27*26947304SEvan Yan #include <stdio.h> 28*26947304SEvan Yan #include <stdlib.h> 29*26947304SEvan Yan #include <unistd.h> 30*26947304SEvan Yan #include <fcntl.h> 31*26947304SEvan Yan #include <errno.h> 32*26947304SEvan Yan #include <strings.h> 33*26947304SEvan Yan #include <alloca.h> 34*26947304SEvan Yan #include <door.h> 35*26947304SEvan Yan #include <pthread.h> 36*26947304SEvan Yan #include <synch.h> 37*26947304SEvan Yan #include <pwd.h> 38*26947304SEvan Yan #include <auth_list.h> 39*26947304SEvan Yan #include <auth_attr.h> 40*26947304SEvan Yan #include <bsm/adt.h> 41*26947304SEvan Yan #include <bsm/adt_event.h> 42*26947304SEvan Yan #include <sys/sunddi.h> 43*26947304SEvan Yan #include <sys/ddi_hp.h> 44*26947304SEvan Yan #include <libnvpair.h> 45*26947304SEvan Yan #include <libhotplug.h> 46*26947304SEvan Yan #include <libhotplug_impl.h> 47*26947304SEvan Yan #include "hotplugd_impl.h" 48*26947304SEvan Yan 49*26947304SEvan Yan /* 50*26947304SEvan Yan * Buffer management for results. 51*26947304SEvan Yan */ 52*26947304SEvan Yan typedef struct i_buffer { 53*26947304SEvan Yan uint64_t seqnum; 54*26947304SEvan Yan char *buffer; 55*26947304SEvan Yan struct i_buffer *next; 56*26947304SEvan Yan } i_buffer_t; 57*26947304SEvan Yan 58*26947304SEvan Yan static uint64_t buffer_seqnum = 1; 59*26947304SEvan Yan static i_buffer_t *buffer_list = NULL; 60*26947304SEvan Yan static pthread_mutex_t buffer_lock = PTHREAD_MUTEX_INITIALIZER; 61*26947304SEvan Yan 62*26947304SEvan Yan /* 63*26947304SEvan Yan * Door file descriptor. 64*26947304SEvan Yan */ 65*26947304SEvan Yan static int door_fd = -1; 66*26947304SEvan Yan 67*26947304SEvan Yan /* 68*26947304SEvan Yan * Function prototypes. 69*26947304SEvan Yan */ 70*26947304SEvan Yan static void door_server(void *, char *, size_t, door_desc_t *, uint_t); 71*26947304SEvan Yan static int check_auth(ucred_t *, const char *); 72*26947304SEvan Yan static int cmd_getinfo(nvlist_t *, nvlist_t **); 73*26947304SEvan Yan static int cmd_changestate(nvlist_t *, nvlist_t **); 74*26947304SEvan Yan static int cmd_private(hp_cmd_t, nvlist_t *, nvlist_t **); 75*26947304SEvan Yan static void add_buffer(uint64_t, char *); 76*26947304SEvan Yan static void free_buffer(uint64_t); 77*26947304SEvan Yan static uint64_t get_seqnum(void); 78*26947304SEvan Yan static char *state_str(int); 79*26947304SEvan Yan static int audit_session(ucred_t *, adt_session_data_t **); 80*26947304SEvan Yan static void audit_changestate(ucred_t *, char *, char *, char *, int, int, 81*26947304SEvan Yan int); 82*26947304SEvan Yan static void audit_setprivate(ucred_t *, char *, char *, char *, char *, 83*26947304SEvan Yan int); 84*26947304SEvan Yan 85*26947304SEvan Yan /* 86*26947304SEvan Yan * door_server_init() 87*26947304SEvan Yan * 88*26947304SEvan Yan * Create the door file, and initialize the door server. 89*26947304SEvan Yan */ 90*26947304SEvan Yan boolean_t 91*26947304SEvan Yan door_server_init(void) 92*26947304SEvan Yan { 93*26947304SEvan Yan int fd; 94*26947304SEvan Yan 95*26947304SEvan Yan /* Create the door file */ 96*26947304SEvan Yan if ((fd = open(HOTPLUGD_DOOR, O_CREAT|O_EXCL|O_RDONLY, 0644)) == -1) { 97*26947304SEvan Yan if (errno == EEXIST) { 98*26947304SEvan Yan log_err("Door service is already running.\n"); 99*26947304SEvan Yan } else { 100*26947304SEvan Yan log_err("Cannot open door file '%s': %s\n", 101*26947304SEvan Yan HOTPLUGD_DOOR, strerror(errno)); 102*26947304SEvan Yan } 103*26947304SEvan Yan return (B_FALSE); 104*26947304SEvan Yan } 105*26947304SEvan Yan (void) close(fd); 106*26947304SEvan Yan 107*26947304SEvan Yan /* Initialize the door service */ 108*26947304SEvan Yan if ((door_fd = door_create(door_server, NULL, 109*26947304SEvan Yan DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) { 110*26947304SEvan Yan log_err("Cannot create door service: %s\n", strerror(errno)); 111*26947304SEvan Yan return (B_FALSE); 112*26947304SEvan Yan } 113*26947304SEvan Yan 114*26947304SEvan Yan /* Cleanup stale door associations */ 115*26947304SEvan Yan (void) fdetach(HOTPLUGD_DOOR); 116*26947304SEvan Yan 117*26947304SEvan Yan /* Associate door service with door file */ 118*26947304SEvan Yan if (fattach(door_fd, HOTPLUGD_DOOR) != 0) { 119*26947304SEvan Yan log_err("Cannot attach to door file '%s': %s\n", HOTPLUGD_DOOR, 120*26947304SEvan Yan strerror(errno)); 121*26947304SEvan Yan (void) door_revoke(door_fd); 122*26947304SEvan Yan (void) fdetach(HOTPLUGD_DOOR); 123*26947304SEvan Yan door_fd = -1; 124*26947304SEvan Yan return (B_FALSE); 125*26947304SEvan Yan } 126*26947304SEvan Yan 127*26947304SEvan Yan return (B_TRUE); 128*26947304SEvan Yan } 129*26947304SEvan Yan 130*26947304SEvan Yan /* 131*26947304SEvan Yan * door_server_fini() 132*26947304SEvan Yan * 133*26947304SEvan Yan * Terminate and cleanup the door server. 134*26947304SEvan Yan */ 135*26947304SEvan Yan void 136*26947304SEvan Yan door_server_fini(void) 137*26947304SEvan Yan { 138*26947304SEvan Yan if (door_fd != -1) { 139*26947304SEvan Yan (void) door_revoke(door_fd); 140*26947304SEvan Yan (void) fdetach(HOTPLUGD_DOOR); 141*26947304SEvan Yan } 142*26947304SEvan Yan 143*26947304SEvan Yan (void) unlink(HOTPLUGD_DOOR); 144*26947304SEvan Yan } 145*26947304SEvan Yan 146*26947304SEvan Yan /* 147*26947304SEvan Yan * door_server() 148*26947304SEvan Yan * 149*26947304SEvan Yan * This routine is the handler which responds to each door call. 150*26947304SEvan Yan * Each incoming door call is expected to send a packed nvlist 151*26947304SEvan Yan * of arguments which describe the requested action. And each 152*26947304SEvan Yan * response is sent back as a packed nvlist of results. 153*26947304SEvan Yan * 154*26947304SEvan Yan * Results are always allocated on the heap. A global list of 155*26947304SEvan Yan * allocated result buffers is managed, and each one is tracked 156*26947304SEvan Yan * by a unique sequence number. The final step in the protocol 157*26947304SEvan Yan * is for the caller to send a short response using the sequence 158*26947304SEvan Yan * number when the buffer can be released. 159*26947304SEvan Yan */ 160*26947304SEvan Yan /*ARGSUSED*/ 161*26947304SEvan Yan static void 162*26947304SEvan Yan door_server(void *cookie, char *argp, size_t sz, door_desc_t *dp, uint_t ndesc) 163*26947304SEvan Yan { 164*26947304SEvan Yan nvlist_t *args = NULL; 165*26947304SEvan Yan nvlist_t *results = NULL; 166*26947304SEvan Yan hp_cmd_t cmd; 167*26947304SEvan Yan int rv; 168*26947304SEvan Yan 169*26947304SEvan Yan dprintf("Door call: cookie=%p, argp=%p, sz=%d\n", cookie, (void *)argp, 170*26947304SEvan Yan sz); 171*26947304SEvan Yan 172*26947304SEvan Yan /* Special case to free a results buffer */ 173*26947304SEvan Yan if (sz == sizeof (uint64_t)) { 174*26947304SEvan Yan free_buffer(*(uint64_t *)(uintptr_t)argp); 175*26947304SEvan Yan (void) door_return(NULL, 0, NULL, 0); 176*26947304SEvan Yan return; 177*26947304SEvan Yan } 178*26947304SEvan Yan 179*26947304SEvan Yan /* Unpack the arguments nvlist */ 180*26947304SEvan Yan if (nvlist_unpack(argp, sz, &args, 0) != 0) { 181*26947304SEvan Yan log_err("Cannot unpack door arguments.\n"); 182*26947304SEvan Yan rv = EINVAL; 183*26947304SEvan Yan goto fail; 184*26947304SEvan Yan } 185*26947304SEvan Yan 186*26947304SEvan Yan /* Extract the requested command */ 187*26947304SEvan Yan if (nvlist_lookup_int32(args, HPD_CMD, (int32_t *)&cmd) != 0) { 188*26947304SEvan Yan log_err("Cannot decode door command.\n"); 189*26947304SEvan Yan rv = EINVAL; 190*26947304SEvan Yan goto fail; 191*26947304SEvan Yan } 192*26947304SEvan Yan 193*26947304SEvan Yan /* Implement the command */ 194*26947304SEvan Yan switch (cmd) { 195*26947304SEvan Yan case HP_CMD_GETINFO: 196*26947304SEvan Yan rv = cmd_getinfo(args, &results); 197*26947304SEvan Yan break; 198*26947304SEvan Yan case HP_CMD_CHANGESTATE: 199*26947304SEvan Yan rv = cmd_changestate(args, &results); 200*26947304SEvan Yan break; 201*26947304SEvan Yan case HP_CMD_SETPRIVATE: 202*26947304SEvan Yan case HP_CMD_GETPRIVATE: 203*26947304SEvan Yan rv = cmd_private(cmd, args, &results); 204*26947304SEvan Yan break; 205*26947304SEvan Yan default: 206*26947304SEvan Yan rv = EINVAL; 207*26947304SEvan Yan break; 208*26947304SEvan Yan } 209*26947304SEvan Yan 210*26947304SEvan Yan /* The arguments nvlist is no longer needed */ 211*26947304SEvan Yan nvlist_free(args); 212*26947304SEvan Yan args = NULL; 213*26947304SEvan Yan 214*26947304SEvan Yan /* 215*26947304SEvan Yan * If an nvlist was constructed for the results, 216*26947304SEvan Yan * then pack the results nvlist and return it. 217*26947304SEvan Yan */ 218*26947304SEvan Yan if (results != NULL) { 219*26947304SEvan Yan uint64_t seqnum; 220*26947304SEvan Yan char *buf = NULL; 221*26947304SEvan Yan size_t len = 0; 222*26947304SEvan Yan 223*26947304SEvan Yan /* Add a sequence number to the results */ 224*26947304SEvan Yan seqnum = get_seqnum(); 225*26947304SEvan Yan if (nvlist_add_uint64(results, HPD_SEQNUM, seqnum) != 0) { 226*26947304SEvan Yan log_err("Cannot add sequence number.\n"); 227*26947304SEvan Yan rv = EFAULT; 228*26947304SEvan Yan goto fail; 229*26947304SEvan Yan } 230*26947304SEvan Yan 231*26947304SEvan Yan /* Pack the results nvlist */ 232*26947304SEvan Yan if (nvlist_pack(results, &buf, &len, 233*26947304SEvan Yan NV_ENCODE_NATIVE, 0) != 0) { 234*26947304SEvan Yan log_err("Cannot pack door results.\n"); 235*26947304SEvan Yan rv = EFAULT; 236*26947304SEvan Yan goto fail; 237*26947304SEvan Yan } 238*26947304SEvan Yan 239*26947304SEvan Yan /* Link results buffer into list */ 240*26947304SEvan Yan add_buffer(seqnum, buf); 241*26947304SEvan Yan 242*26947304SEvan Yan /* The results nvlist is no longer needed */ 243*26947304SEvan Yan nvlist_free(results); 244*26947304SEvan Yan 245*26947304SEvan Yan /* Return the results */ 246*26947304SEvan Yan (void) door_return(buf, len, NULL, 0); 247*26947304SEvan Yan return; 248*26947304SEvan Yan } 249*26947304SEvan Yan 250*26947304SEvan Yan /* Return result code (when no nvlist) */ 251*26947304SEvan Yan (void) door_return((char *)&rv, sizeof (int), NULL, 0); 252*26947304SEvan Yan return; 253*26947304SEvan Yan 254*26947304SEvan Yan fail: 255*26947304SEvan Yan log_err("Door call failed (%s)\n", strerror(rv)); 256*26947304SEvan Yan nvlist_free(args); 257*26947304SEvan Yan nvlist_free(results); 258*26947304SEvan Yan (void) door_return((char *)&rv, sizeof (int), NULL, 0); 259*26947304SEvan Yan } 260*26947304SEvan Yan 261*26947304SEvan Yan /* 262*26947304SEvan Yan * check_auth() 263*26947304SEvan Yan * 264*26947304SEvan Yan * Perform an RBAC authorization check. 265*26947304SEvan Yan */ 266*26947304SEvan Yan static int 267*26947304SEvan Yan check_auth(ucred_t *ucred, const char *auth) 268*26947304SEvan Yan { 269*26947304SEvan Yan struct passwd pwd; 270*26947304SEvan Yan uid_t euid; 271*26947304SEvan Yan char buf[MAXPATHLEN]; 272*26947304SEvan Yan 273*26947304SEvan Yan euid = ucred_geteuid(ucred); 274*26947304SEvan Yan 275*26947304SEvan Yan if ((getpwuid_r(euid, &pwd, buf, sizeof (buf)) == NULL) || 276*26947304SEvan Yan (chkauthattr(auth, pwd.pw_name) == 0)) { 277*26947304SEvan Yan log_info("Unauthorized door call.\n"); 278*26947304SEvan Yan return (-1); 279*26947304SEvan Yan } 280*26947304SEvan Yan 281*26947304SEvan Yan return (0); 282*26947304SEvan Yan } 283*26947304SEvan Yan 284*26947304SEvan Yan /* 285*26947304SEvan Yan * cmd_getinfo() 286*26947304SEvan Yan * 287*26947304SEvan Yan * Implements the door command to get a hotplug information snapshot. 288*26947304SEvan Yan */ 289*26947304SEvan Yan static int 290*26947304SEvan Yan cmd_getinfo(nvlist_t *args, nvlist_t **resultsp) 291*26947304SEvan Yan { 292*26947304SEvan Yan hp_node_t root; 293*26947304SEvan Yan nvlist_t *results; 294*26947304SEvan Yan char *path; 295*26947304SEvan Yan char *connection; 296*26947304SEvan Yan char *buf = NULL; 297*26947304SEvan Yan size_t len = 0; 298*26947304SEvan Yan uint_t flags; 299*26947304SEvan Yan int rv; 300*26947304SEvan Yan 301*26947304SEvan Yan dprintf("cmd_getinfo:\n"); 302*26947304SEvan Yan 303*26947304SEvan Yan /* Get arguments */ 304*26947304SEvan Yan if (nvlist_lookup_string(args, HPD_PATH, &path) != 0) { 305*26947304SEvan Yan dprintf("cmd_getinfo: invalid arguments.\n"); 306*26947304SEvan Yan return (EINVAL); 307*26947304SEvan Yan } 308*26947304SEvan Yan if (nvlist_lookup_string(args, HPD_CONNECTION, &connection) != 0) 309*26947304SEvan Yan connection = NULL; 310*26947304SEvan Yan if (nvlist_lookup_uint32(args, HPD_FLAGS, (uint32_t *)&flags) != 0) 311*26947304SEvan Yan flags = 0; 312*26947304SEvan Yan 313*26947304SEvan Yan /* Get and pack the requested snapshot */ 314*26947304SEvan Yan if ((rv = getinfo(path, connection, flags, &root)) == 0) { 315*26947304SEvan Yan rv = hp_pack(root, &buf, &len); 316*26947304SEvan Yan hp_fini(root); 317*26947304SEvan Yan } 318*26947304SEvan Yan dprintf("cmd_getinfo: getinfo(): rv = %d, buf = %p.\n", rv, 319*26947304SEvan Yan (void *)buf); 320*26947304SEvan Yan 321*26947304SEvan Yan /* 322*26947304SEvan Yan * If the above failed or there is no snapshot, 323*26947304SEvan Yan * then only return a status code. 324*26947304SEvan Yan */ 325*26947304SEvan Yan if (rv != 0) 326*26947304SEvan Yan return (rv); 327*26947304SEvan Yan if (buf == NULL) 328*26947304SEvan Yan return (EFAULT); 329*26947304SEvan Yan 330*26947304SEvan Yan /* Allocate nvlist for results */ 331*26947304SEvan Yan if (nvlist_alloc(&results, NV_UNIQUE_NAME_TYPE, 0) != 0) { 332*26947304SEvan Yan dprintf("cmd_getinfo: nvlist_alloc() failed.\n"); 333*26947304SEvan Yan free(buf); 334*26947304SEvan Yan return (ENOMEM); 335*26947304SEvan Yan } 336*26947304SEvan Yan 337*26947304SEvan Yan /* Add snapshot and successful status to results */ 338*26947304SEvan Yan if ((nvlist_add_int32(results, HPD_STATUS, 0) != 0) || 339*26947304SEvan Yan (nvlist_add_byte_array(results, HPD_INFO, 340*26947304SEvan Yan (uchar_t *)buf, len) != 0)) { 341*26947304SEvan Yan dprintf("cmd_getinfo: nvlist add failure.\n"); 342*26947304SEvan Yan nvlist_free(results); 343*26947304SEvan Yan free(buf); 344*26947304SEvan Yan return (ENOMEM); 345*26947304SEvan Yan } 346*26947304SEvan Yan 347*26947304SEvan Yan /* Packed snapshot no longer needed */ 348*26947304SEvan Yan free(buf); 349*26947304SEvan Yan 350*26947304SEvan Yan /* Success */ 351*26947304SEvan Yan *resultsp = results; 352*26947304SEvan Yan return (0); 353*26947304SEvan Yan } 354*26947304SEvan Yan 355*26947304SEvan Yan /* 356*26947304SEvan Yan * cmd_changestate() 357*26947304SEvan Yan * 358*26947304SEvan Yan * Implements the door command to initate a state change operation. 359*26947304SEvan Yan * 360*26947304SEvan Yan * NOTE: requires 'modify' authorization. 361*26947304SEvan Yan */ 362*26947304SEvan Yan static int 363*26947304SEvan Yan cmd_changestate(nvlist_t *args, nvlist_t **resultsp) 364*26947304SEvan Yan { 365*26947304SEvan Yan hp_node_t root = NULL; 366*26947304SEvan Yan nvlist_t *results = NULL; 367*26947304SEvan Yan char *path, *connection; 368*26947304SEvan Yan ucred_t *uc = NULL; 369*26947304SEvan Yan uint_t flags; 370*26947304SEvan Yan int rv, state, old_state, status; 371*26947304SEvan Yan 372*26947304SEvan Yan dprintf("cmd_changestate:\n"); 373*26947304SEvan Yan 374*26947304SEvan Yan /* Get arguments */ 375*26947304SEvan Yan if ((nvlist_lookup_string(args, HPD_PATH, &path) != 0) || 376*26947304SEvan Yan (nvlist_lookup_string(args, HPD_CONNECTION, &connection) != 0) || 377*26947304SEvan Yan (nvlist_lookup_int32(args, HPD_STATE, &state) != 0)) { 378*26947304SEvan Yan dprintf("cmd_changestate: invalid arguments.\n"); 379*26947304SEvan Yan return (EINVAL); 380*26947304SEvan Yan } 381*26947304SEvan Yan if (nvlist_lookup_uint32(args, HPD_FLAGS, (uint32_t *)&flags) != 0) 382*26947304SEvan Yan flags = 0; 383*26947304SEvan Yan 384*26947304SEvan Yan /* Get caller's credentials */ 385*26947304SEvan Yan if (door_ucred(&uc) != 0) { 386*26947304SEvan Yan log_err("Cannot get door credentials (%s)\n", strerror(errno)); 387*26947304SEvan Yan return (EACCES); 388*26947304SEvan Yan } 389*26947304SEvan Yan 390*26947304SEvan Yan /* Check authorization */ 391*26947304SEvan Yan if (check_auth(uc, HP_MODIFY_AUTH) != 0) { 392*26947304SEvan Yan dprintf("cmd_changestate: access denied.\n"); 393*26947304SEvan Yan audit_changestate(uc, HP_MODIFY_AUTH, path, connection, 394*26947304SEvan Yan state, -1, ADT_FAIL_VALUE_AUTH); 395*26947304SEvan Yan ucred_free(uc); 396*26947304SEvan Yan return (EACCES); 397*26947304SEvan Yan } 398*26947304SEvan Yan 399*26947304SEvan Yan /* Perform the state change operation */ 400*26947304SEvan Yan status = changestate(path, connection, state, flags, &old_state, &root); 401*26947304SEvan Yan dprintf("cmd_changestate: changestate() == %d\n", status); 402*26947304SEvan Yan 403*26947304SEvan Yan /* Audit the operation */ 404*26947304SEvan Yan audit_changestate(uc, HP_MODIFY_AUTH, path, connection, state, 405*26947304SEvan Yan old_state, status); 406*26947304SEvan Yan 407*26947304SEvan Yan /* Caller's credentials no longer needed */ 408*26947304SEvan Yan ucred_free(uc); 409*26947304SEvan Yan 410*26947304SEvan Yan /* 411*26947304SEvan Yan * Pack the results into an nvlist if there is an error snapshot. 412*26947304SEvan Yan * 413*26947304SEvan Yan * If any error occurs while packing the results, the original 414*26947304SEvan Yan * error code from changestate() above is still returned. 415*26947304SEvan Yan */ 416*26947304SEvan Yan if (root != NULL) { 417*26947304SEvan Yan char *buf = NULL; 418*26947304SEvan Yan size_t len = 0; 419*26947304SEvan Yan 420*26947304SEvan Yan dprintf("cmd_changestate: results nvlist required.\n"); 421*26947304SEvan Yan 422*26947304SEvan Yan /* Pack and discard the error snapshot */ 423*26947304SEvan Yan rv = hp_pack(root, &buf, &len); 424*26947304SEvan Yan hp_fini(root); 425*26947304SEvan Yan if (rv != 0) { 426*26947304SEvan Yan dprintf("cmd_changestate: hp_pack() failed (%s).\n", 427*26947304SEvan Yan strerror(rv)); 428*26947304SEvan Yan return (status); 429*26947304SEvan Yan } 430*26947304SEvan Yan 431*26947304SEvan Yan /* Allocate nvlist for results */ 432*26947304SEvan Yan if (nvlist_alloc(&results, NV_UNIQUE_NAME_TYPE, 0) != 0) { 433*26947304SEvan Yan dprintf("cmd_changestate: nvlist_alloc() failed.\n"); 434*26947304SEvan Yan free(buf); 435*26947304SEvan Yan return (status); 436*26947304SEvan Yan } 437*26947304SEvan Yan 438*26947304SEvan Yan /* Add the results into the nvlist */ 439*26947304SEvan Yan if ((nvlist_add_int32(results, HPD_STATUS, status) != 0) || 440*26947304SEvan Yan (nvlist_add_byte_array(results, HPD_INFO, (uchar_t *)buf, 441*26947304SEvan Yan len) != 0)) { 442*26947304SEvan Yan dprintf("cmd_changestate: nvlist add failed.\n"); 443*26947304SEvan Yan nvlist_free(results); 444*26947304SEvan Yan free(buf); 445*26947304SEvan Yan return (status); 446*26947304SEvan Yan } 447*26947304SEvan Yan 448*26947304SEvan Yan *resultsp = results; 449*26947304SEvan Yan } 450*26947304SEvan Yan 451*26947304SEvan Yan return (status); 452*26947304SEvan Yan } 453*26947304SEvan Yan 454*26947304SEvan Yan /* 455*26947304SEvan Yan * cmd_private() 456*26947304SEvan Yan * 457*26947304SEvan Yan * Implementation of the door command to set or get bus private options. 458*26947304SEvan Yan * 459*26947304SEvan Yan * NOTE: requires 'modify' authorization for the 'set' command. 460*26947304SEvan Yan */ 461*26947304SEvan Yan static int 462*26947304SEvan Yan cmd_private(hp_cmd_t cmd, nvlist_t *args, nvlist_t **resultsp) 463*26947304SEvan Yan { 464*26947304SEvan Yan nvlist_t *results = NULL; 465*26947304SEvan Yan ucred_t *uc = NULL; 466*26947304SEvan Yan char *path, *connection, *options; 467*26947304SEvan Yan char *values = NULL; 468*26947304SEvan Yan int status; 469*26947304SEvan Yan 470*26947304SEvan Yan dprintf("cmd_private:\n"); 471*26947304SEvan Yan 472*26947304SEvan Yan /* Get caller's credentials */ 473*26947304SEvan Yan if ((cmd == HP_CMD_SETPRIVATE) && (door_ucred(&uc) != 0)) { 474*26947304SEvan Yan log_err("Cannot get door credentials (%s)\n", strerror(errno)); 475*26947304SEvan Yan return (EACCES); 476*26947304SEvan Yan } 477*26947304SEvan Yan 478*26947304SEvan Yan /* Get arguments */ 479*26947304SEvan Yan if ((nvlist_lookup_string(args, HPD_PATH, &path) != 0) || 480*26947304SEvan Yan (nvlist_lookup_string(args, HPD_CONNECTION, &connection) != 0) || 481*26947304SEvan Yan (nvlist_lookup_string(args, HPD_OPTIONS, &options) != 0)) { 482*26947304SEvan Yan dprintf("cmd_private: invalid arguments.\n"); 483*26947304SEvan Yan return (EINVAL); 484*26947304SEvan Yan } 485*26947304SEvan Yan 486*26947304SEvan Yan /* Check authorization */ 487*26947304SEvan Yan if ((cmd == HP_CMD_SETPRIVATE) && 488*26947304SEvan Yan (check_auth(uc, HP_MODIFY_AUTH) != 0)) { 489*26947304SEvan Yan dprintf("cmd_private: access denied.\n"); 490*26947304SEvan Yan audit_setprivate(uc, HP_MODIFY_AUTH, path, connection, options, 491*26947304SEvan Yan ADT_FAIL_VALUE_AUTH); 492*26947304SEvan Yan ucred_free(uc); 493*26947304SEvan Yan return (EACCES); 494*26947304SEvan Yan } 495*26947304SEvan Yan 496*26947304SEvan Yan /* Perform the operation */ 497*26947304SEvan Yan status = private_options(path, connection, cmd, options, &values); 498*26947304SEvan Yan dprintf("cmd_private: private_options() == %d\n", status); 499*26947304SEvan Yan 500*26947304SEvan Yan /* Audit the operation */ 501*26947304SEvan Yan if (cmd == HP_CMD_SETPRIVATE) { 502*26947304SEvan Yan audit_setprivate(uc, HP_MODIFY_AUTH, path, connection, options, 503*26947304SEvan Yan status); 504*26947304SEvan Yan ucred_free(uc); 505*26947304SEvan Yan } 506*26947304SEvan Yan 507*26947304SEvan Yan /* Construct an nvlist if values were returned */ 508*26947304SEvan Yan if (values != NULL) { 509*26947304SEvan Yan 510*26947304SEvan Yan /* Allocate nvlist for results */ 511*26947304SEvan Yan if (nvlist_alloc(&results, NV_UNIQUE_NAME_TYPE, 0) != 0) { 512*26947304SEvan Yan dprintf("cmd_private: nvlist_alloc() failed.\n"); 513*26947304SEvan Yan free(values); 514*26947304SEvan Yan return (ENOMEM); 515*26947304SEvan Yan } 516*26947304SEvan Yan 517*26947304SEvan Yan /* Add values and status to the results */ 518*26947304SEvan Yan if ((nvlist_add_int32(results, HPD_STATUS, status) != 0) || 519*26947304SEvan Yan (nvlist_add_string(results, HPD_OPTIONS, values) != 0)) { 520*26947304SEvan Yan dprintf("cmd_private: nvlist add failed.\n"); 521*26947304SEvan Yan nvlist_free(results); 522*26947304SEvan Yan free(values); 523*26947304SEvan Yan return (ENOMEM); 524*26947304SEvan Yan } 525*26947304SEvan Yan 526*26947304SEvan Yan /* The values string is no longer needed */ 527*26947304SEvan Yan free(values); 528*26947304SEvan Yan 529*26947304SEvan Yan *resultsp = results; 530*26947304SEvan Yan } 531*26947304SEvan Yan 532*26947304SEvan Yan return (status); 533*26947304SEvan Yan } 534*26947304SEvan Yan 535*26947304SEvan Yan /* 536*26947304SEvan Yan * get_seqnum() 537*26947304SEvan Yan * 538*26947304SEvan Yan * Allocate the next unique sequence number for a results buffer. 539*26947304SEvan Yan */ 540*26947304SEvan Yan static uint64_t 541*26947304SEvan Yan get_seqnum(void) 542*26947304SEvan Yan { 543*26947304SEvan Yan uint64_t seqnum; 544*26947304SEvan Yan 545*26947304SEvan Yan (void) pthread_mutex_lock(&buffer_lock); 546*26947304SEvan Yan 547*26947304SEvan Yan seqnum = buffer_seqnum++; 548*26947304SEvan Yan 549*26947304SEvan Yan (void) pthread_mutex_unlock(&buffer_lock); 550*26947304SEvan Yan 551*26947304SEvan Yan return (seqnum); 552*26947304SEvan Yan } 553*26947304SEvan Yan 554*26947304SEvan Yan /* 555*26947304SEvan Yan * add_buffer() 556*26947304SEvan Yan * 557*26947304SEvan Yan * Link a results buffer into the list containing all buffers. 558*26947304SEvan Yan */ 559*26947304SEvan Yan static void 560*26947304SEvan Yan add_buffer(uint64_t seqnum, char *buf) 561*26947304SEvan Yan { 562*26947304SEvan Yan i_buffer_t *node; 563*26947304SEvan Yan 564*26947304SEvan Yan if ((node = (i_buffer_t *)malloc(sizeof (i_buffer_t))) == NULL) { 565*26947304SEvan Yan /* The consequence is a memory leak. */ 566*26947304SEvan Yan log_err("Cannot allocate results buffer: %s\n", 567*26947304SEvan Yan strerror(errno)); 568*26947304SEvan Yan return; 569*26947304SEvan Yan } 570*26947304SEvan Yan 571*26947304SEvan Yan node->seqnum = seqnum; 572*26947304SEvan Yan node->buffer = buf; 573*26947304SEvan Yan 574*26947304SEvan Yan (void) pthread_mutex_lock(&buffer_lock); 575*26947304SEvan Yan 576*26947304SEvan Yan node->next = buffer_list; 577*26947304SEvan Yan buffer_list = node; 578*26947304SEvan Yan 579*26947304SEvan Yan (void) pthread_mutex_unlock(&buffer_lock); 580*26947304SEvan Yan } 581*26947304SEvan Yan 582*26947304SEvan Yan /* 583*26947304SEvan Yan * free_buffer() 584*26947304SEvan Yan * 585*26947304SEvan Yan * Remove a results buffer from the list containing all buffers. 586*26947304SEvan Yan */ 587*26947304SEvan Yan static void 588*26947304SEvan Yan free_buffer(uint64_t seqnum) 589*26947304SEvan Yan { 590*26947304SEvan Yan i_buffer_t *node, *prev; 591*26947304SEvan Yan 592*26947304SEvan Yan (void) pthread_mutex_lock(&buffer_lock); 593*26947304SEvan Yan 594*26947304SEvan Yan prev = NULL; 595*26947304SEvan Yan node = buffer_list; 596*26947304SEvan Yan 597*26947304SEvan Yan while (node) { 598*26947304SEvan Yan if (node->seqnum == seqnum) { 599*26947304SEvan Yan dprintf("Free buffer %lld\n", seqnum); 600*26947304SEvan Yan if (prev) { 601*26947304SEvan Yan prev->next = node->next; 602*26947304SEvan Yan } else { 603*26947304SEvan Yan buffer_list = node->next; 604*26947304SEvan Yan } 605*26947304SEvan Yan free(node->buffer); 606*26947304SEvan Yan free(node); 607*26947304SEvan Yan break; 608*26947304SEvan Yan } 609*26947304SEvan Yan prev = node; 610*26947304SEvan Yan node = node->next; 611*26947304SEvan Yan } 612*26947304SEvan Yan 613*26947304SEvan Yan (void) pthread_mutex_unlock(&buffer_lock); 614*26947304SEvan Yan } 615*26947304SEvan Yan 616*26947304SEvan Yan /* 617*26947304SEvan Yan * audit_session() 618*26947304SEvan Yan * 619*26947304SEvan Yan * Initialize an audit session. 620*26947304SEvan Yan */ 621*26947304SEvan Yan static int 622*26947304SEvan Yan audit_session(ucred_t *ucred, adt_session_data_t **sessionp) 623*26947304SEvan Yan { 624*26947304SEvan Yan adt_session_data_t *session; 625*26947304SEvan Yan 626*26947304SEvan Yan if (adt_start_session(&session, NULL, 0) != 0) { 627*26947304SEvan Yan log_err("Cannot start audit session.\n"); 628*26947304SEvan Yan return (-1); 629*26947304SEvan Yan } 630*26947304SEvan Yan 631*26947304SEvan Yan if (adt_set_from_ucred(session, ucred, ADT_NEW) != 0) { 632*26947304SEvan Yan log_err("Cannot set audit session from ucred.\n"); 633*26947304SEvan Yan (void) adt_end_session(session); 634*26947304SEvan Yan return (-1); 635*26947304SEvan Yan } 636*26947304SEvan Yan 637*26947304SEvan Yan *sessionp = session; 638*26947304SEvan Yan return (0); 639*26947304SEvan Yan } 640*26947304SEvan Yan 641*26947304SEvan Yan /* 642*26947304SEvan Yan * audit_changestate() 643*26947304SEvan Yan * 644*26947304SEvan Yan * Audit a 'changestate' door command. 645*26947304SEvan Yan */ 646*26947304SEvan Yan static void 647*26947304SEvan Yan audit_changestate(ucred_t *ucred, char *auth, char *path, char *connection, 648*26947304SEvan Yan int new_state, int old_state, int result) 649*26947304SEvan Yan { 650*26947304SEvan Yan adt_session_data_t *session; 651*26947304SEvan Yan adt_event_data_t *event; 652*26947304SEvan Yan int pass_fail, fail_reason; 653*26947304SEvan Yan 654*26947304SEvan Yan if (audit_session(ucred, &session) != 0) 655*26947304SEvan Yan return; 656*26947304SEvan Yan 657*26947304SEvan Yan if ((event = adt_alloc_event(session, ADT_hotplug_state)) == NULL) { 658*26947304SEvan Yan (void) adt_end_session(session); 659*26947304SEvan Yan return; 660*26947304SEvan Yan } 661*26947304SEvan Yan 662*26947304SEvan Yan if (result == 0) { 663*26947304SEvan Yan pass_fail = ADT_SUCCESS; 664*26947304SEvan Yan fail_reason = ADT_SUCCESS; 665*26947304SEvan Yan } else { 666*26947304SEvan Yan pass_fail = ADT_FAILURE; 667*26947304SEvan Yan fail_reason = result; 668*26947304SEvan Yan } 669*26947304SEvan Yan 670*26947304SEvan Yan event->adt_hotplug_state.auth_used = auth; 671*26947304SEvan Yan event->adt_hotplug_state.device_path = path; 672*26947304SEvan Yan event->adt_hotplug_state.connection = connection; 673*26947304SEvan Yan event->adt_hotplug_state.new_state = state_str(new_state); 674*26947304SEvan Yan event->adt_hotplug_state.old_state = state_str(old_state); 675*26947304SEvan Yan 676*26947304SEvan Yan /* Put the event */ 677*26947304SEvan Yan if (adt_put_event(event, pass_fail, fail_reason) != 0) 678*26947304SEvan Yan log_err("Cannot put audit event.\n"); 679*26947304SEvan Yan 680*26947304SEvan Yan adt_free_event(event); 681*26947304SEvan Yan (void) adt_end_session(session); 682*26947304SEvan Yan } 683*26947304SEvan Yan 684*26947304SEvan Yan /* 685*26947304SEvan Yan * audit_setprivate() 686*26947304SEvan Yan * 687*26947304SEvan Yan * Audit a 'set private' door command. 688*26947304SEvan Yan */ 689*26947304SEvan Yan static void 690*26947304SEvan Yan audit_setprivate(ucred_t *ucred, char *auth, char *path, char *connection, 691*26947304SEvan Yan char *options, int result) 692*26947304SEvan Yan { 693*26947304SEvan Yan adt_session_data_t *session; 694*26947304SEvan Yan adt_event_data_t *event; 695*26947304SEvan Yan int pass_fail, fail_reason; 696*26947304SEvan Yan 697*26947304SEvan Yan if (audit_session(ucred, &session) != 0) 698*26947304SEvan Yan return; 699*26947304SEvan Yan 700*26947304SEvan Yan if ((event = adt_alloc_event(session, ADT_hotplug_set)) == NULL) { 701*26947304SEvan Yan (void) adt_end_session(session); 702*26947304SEvan Yan return; 703*26947304SEvan Yan } 704*26947304SEvan Yan 705*26947304SEvan Yan if (result == 0) { 706*26947304SEvan Yan pass_fail = ADT_SUCCESS; 707*26947304SEvan Yan fail_reason = ADT_SUCCESS; 708*26947304SEvan Yan } else { 709*26947304SEvan Yan pass_fail = ADT_FAILURE; 710*26947304SEvan Yan fail_reason = result; 711*26947304SEvan Yan } 712*26947304SEvan Yan 713*26947304SEvan Yan event->adt_hotplug_set.auth_used = auth; 714*26947304SEvan Yan event->adt_hotplug_set.device_path = path; 715*26947304SEvan Yan event->adt_hotplug_set.connection = connection; 716*26947304SEvan Yan event->adt_hotplug_set.options = options; 717*26947304SEvan Yan 718*26947304SEvan Yan /* Put the event */ 719*26947304SEvan Yan if (adt_put_event(event, pass_fail, fail_reason) != 0) 720*26947304SEvan Yan log_err("Cannot put audit event.\n"); 721*26947304SEvan Yan 722*26947304SEvan Yan adt_free_event(event); 723*26947304SEvan Yan (void) adt_end_session(session); 724*26947304SEvan Yan } 725*26947304SEvan Yan 726*26947304SEvan Yan /* 727*26947304SEvan Yan * state_str() 728*26947304SEvan Yan * 729*26947304SEvan Yan * Convert a state from integer to string. 730*26947304SEvan Yan */ 731*26947304SEvan Yan static char * 732*26947304SEvan Yan state_str(int state) 733*26947304SEvan Yan { 734*26947304SEvan Yan switch (state) { 735*26947304SEvan Yan case DDI_HP_CN_STATE_EMPTY: 736*26947304SEvan Yan return ("EMPTY"); 737*26947304SEvan Yan case DDI_HP_CN_STATE_PRESENT: 738*26947304SEvan Yan return ("PRESENT"); 739*26947304SEvan Yan case DDI_HP_CN_STATE_POWERED: 740*26947304SEvan Yan return ("POWERED"); 741*26947304SEvan Yan case DDI_HP_CN_STATE_ENABLED: 742*26947304SEvan Yan return ("ENABLED"); 743*26947304SEvan Yan case DDI_HP_CN_STATE_PORT_EMPTY: 744*26947304SEvan Yan return ("PORT-EMPTY"); 745*26947304SEvan Yan case DDI_HP_CN_STATE_PORT_PRESENT: 746*26947304SEvan Yan return ("PORT-PRESENT"); 747*26947304SEvan Yan case DDI_HP_CN_STATE_OFFLINE: 748*26947304SEvan Yan return ("OFFLINE"); 749*26947304SEvan Yan case DDI_HP_CN_STATE_ATTACHED: 750*26947304SEvan Yan return ("ATTACHED"); 751*26947304SEvan Yan case DDI_HP_CN_STATE_MAINTENANCE: 752*26947304SEvan Yan return ("MAINTENANCE"); 753*26947304SEvan Yan case DDI_HP_CN_STATE_ONLINE: 754*26947304SEvan Yan return ("ONLINE"); 755*26947304SEvan Yan default: 756*26947304SEvan Yan return ("UNKNOWN"); 757*26947304SEvan Yan } 758*26947304SEvan Yan } 759