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 #include <stdio.h> 28 #include <stdlib.h> 29 #include <unistd.h> 30 #include <fcntl.h> 31 #include <errno.h> 32 #include <strings.h> 33 #include <alloca.h> 34 #include <door.h> 35 #include <pthread.h> 36 #include <synch.h> 37 #include <pwd.h> 38 #include <auth_list.h> 39 #include <auth_attr.h> 40 #include <bsm/adt.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" 48 49 /* 50 * Buffer management for results. 51 */ 52 typedef struct i_buffer { 53 uint64_t seqnum; 54 char *buffer; 55 struct i_buffer *next; 56 } i_buffer_t; 57 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; 61 62 /* 63 * Door file descriptor. 64 */ 65 static int door_fd = -1; 66 67 /* 68 * Function prototypes. 69 */ 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, 81 int); 82 static void audit_setprivate(ucred_t *, char *, char *, char *, char *, 83 int); 84 85 /* 86 * door_server_init() 87 * 88 * Create the door file, and initialize the door server. 89 */ 90 boolean_t 91 door_server_init(void) 92 { 93 int fd; 94 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"); 99 } else { 100 log_err("Cannot open door file '%s': %s\n", 101 HOTPLUGD_DOOR, strerror(errno)); 102 } 103 return (B_FALSE); 104 } 105 (void) close(fd); 106 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)); 111 return (B_FALSE); 112 } 113 114 /* Cleanup stale door associations */ 115 (void) fdetach(HOTPLUGD_DOOR); 116 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, 120 strerror(errno)); 121 (void) door_revoke(door_fd); 122 (void) fdetach(HOTPLUGD_DOOR); 123 door_fd = -1; 124 return (B_FALSE); 125 } 126 127 return (B_TRUE); 128 } 129 130 /* 131 * door_server_fini() 132 * 133 * Terminate and cleanup the door server. 134 */ 135 void 136 door_server_fini(void) 137 { 138 if (door_fd != -1) { 139 (void) door_revoke(door_fd); 140 (void) fdetach(HOTPLUGD_DOOR); 141 } 142 143 (void) unlink(HOTPLUGD_DOOR); 144 } 145 146 /* 147 * door_server() 148 * 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. 153 * 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. 159 */ 160 /*ARGSUSED*/ 161 static void 162 door_server(void *cookie, char *argp, size_t sz, door_desc_t *dp, uint_t ndesc) 163 { 164 nvlist_t *args = NULL; 165 nvlist_t *results = NULL; 166 hp_cmd_t cmd; 167 int rv; 168 169 dprintf("Door call: cookie=%p, argp=%p, sz=%d\n", cookie, (void *)argp, 170 sz); 171 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); 176 return; 177 } 178 179 /* Unpack the arguments nvlist */ 180 if (nvlist_unpack(argp, sz, &args, 0) != 0) { 181 log_err("Cannot unpack door arguments.\n"); 182 rv = EINVAL; 183 goto fail; 184 } 185 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"); 189 rv = EINVAL; 190 goto fail; 191 } 192 193 /* Implement the command */ 194 switch (cmd) { 195 case HP_CMD_GETINFO: 196 rv = cmd_getinfo(args, &results); 197 break; 198 case HP_CMD_CHANGESTATE: 199 rv = cmd_changestate(args, &results); 200 break; 201 case HP_CMD_SETPRIVATE: 202 case HP_CMD_GETPRIVATE: 203 rv = cmd_private(cmd, args, &results); 204 break; 205 default: 206 rv = EINVAL; 207 break; 208 } 209 210 /* The arguments nvlist is no longer needed */ 211 nvlist_free(args); 212 args = NULL; 213 214 /* 215 * If an nvlist was constructed for the results, 216 * then pack the results nvlist and return it. 217 */ 218 if (results != NULL) { 219 uint64_t seqnum; 220 char *buf = NULL; 221 size_t len = 0; 222 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"); 227 rv = EFAULT; 228 goto fail; 229 } 230 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"); 235 rv = EFAULT; 236 goto fail; 237 } 238 239 /* Link results buffer into list */ 240 add_buffer(seqnum, buf); 241 242 /* The results nvlist is no longer needed */ 243 nvlist_free(results); 244 245 /* Return the results */ 246 (void) door_return(buf, len, NULL, 0); 247 return; 248 } 249 250 /* Return result code (when no nvlist) */ 251 (void) door_return((char *)&rv, sizeof (int), NULL, 0); 252 return; 253 254 fail: 255 log_err("Door call failed (%s)\n", strerror(rv)); 256 nvlist_free(args); 257 nvlist_free(results); 258 (void) door_return((char *)&rv, sizeof (int), NULL, 0); 259 } 260 261 /* 262 * check_auth() 263 * 264 * Perform an RBAC authorization check. 265 */ 266 static int 267 check_auth(ucred_t *ucred, const char *auth) 268 { 269 struct passwd pwd; 270 uid_t euid; 271 char buf[MAXPATHLEN]; 272 273 euid = ucred_geteuid(ucred); 274 275 if ((getpwuid_r(euid, &pwd, buf, sizeof (buf)) == NULL) || 276 (chkauthattr(auth, pwd.pw_name) == 0)) { 277 log_info("Unauthorized door call.\n"); 278 return (-1); 279 } 280 281 return (0); 282 } 283 284 /* 285 * cmd_getinfo() 286 * 287 * Implements the door command to get a hotplug information snapshot. 288 */ 289 static int 290 cmd_getinfo(nvlist_t *args, nvlist_t **resultsp) 291 { 292 hp_node_t root; 293 nvlist_t *results; 294 char *path; 295 char *connection; 296 char *buf = NULL; 297 size_t len = 0; 298 uint_t flags; 299 int rv; 300 301 dprintf("cmd_getinfo:\n"); 302 303 /* Get arguments */ 304 if (nvlist_lookup_string(args, HPD_PATH, &path) != 0) { 305 dprintf("cmd_getinfo: invalid arguments.\n"); 306 return (EINVAL); 307 } 308 if (nvlist_lookup_string(args, HPD_CONNECTION, &connection) != 0) 309 connection = NULL; 310 if (nvlist_lookup_uint32(args, HPD_FLAGS, (uint32_t *)&flags) != 0) 311 flags = 0; 312 313 /* Get and pack the requested snapshot */ 314 if ((rv = getinfo(path, connection, flags, &root)) == 0) { 315 rv = hp_pack(root, &buf, &len); 316 hp_fini(root); 317 } 318 dprintf("cmd_getinfo: getinfo(): rv = %d, buf = %p.\n", rv, 319 (void *)buf); 320 321 /* 322 * If the above failed or there is no snapshot, 323 * then only return a status code. 324 */ 325 if (rv != 0) 326 return (rv); 327 if (buf == NULL) 328 return (EFAULT); 329 330 /* Allocate nvlist for results */ 331 if (nvlist_alloc(&results, NV_UNIQUE_NAME_TYPE, 0) != 0) { 332 dprintf("cmd_getinfo: nvlist_alloc() failed.\n"); 333 free(buf); 334 return (ENOMEM); 335 } 336 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); 343 free(buf); 344 return (ENOMEM); 345 } 346 347 /* Packed snapshot no longer needed */ 348 free(buf); 349 350 /* Success */ 351 *resultsp = results; 352 return (0); 353 } 354 355 /* 356 * cmd_changestate() 357 * 358 * Implements the door command to initate a state change operation. 359 * 360 * NOTE: requires 'modify' authorization. 361 */ 362 static int 363 cmd_changestate(nvlist_t *args, nvlist_t **resultsp) 364 { 365 hp_node_t root = NULL; 366 nvlist_t *results = NULL; 367 char *path, *connection; 368 ucred_t *uc = NULL; 369 uint_t flags; 370 int rv, state, old_state, status; 371 372 dprintf("cmd_changestate:\n"); 373 374 /* Get arguments */ 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"); 379 return (EINVAL); 380 } 381 if (nvlist_lookup_uint32(args, HPD_FLAGS, (uint32_t *)&flags) != 0) 382 flags = 0; 383 384 /* Get caller's credentials */ 385 if (door_ucred(&uc) != 0) { 386 log_err("Cannot get door credentials (%s)\n", strerror(errno)); 387 return (EACCES); 388 } 389 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); 395 ucred_free(uc); 396 return (EACCES); 397 } 398 399 /* Perform the state change operation */ 400 status = changestate(path, connection, state, flags, &old_state, &root); 401 dprintf("cmd_changestate: changestate() == %d\n", status); 402 403 /* Audit the operation */ 404 audit_changestate(uc, HP_MODIFY_AUTH, path, connection, state, 405 old_state, status); 406 407 /* Caller's credentials no longer needed */ 408 ucred_free(uc); 409 410 /* 411 * Pack the results into an nvlist if there is an error snapshot. 412 * 413 * If any error occurs while packing the results, the original 414 * error code from changestate() above is still returned. 415 */ 416 if (root != NULL) { 417 char *buf = NULL; 418 size_t len = 0; 419 420 dprintf("cmd_changestate: results nvlist required.\n"); 421 422 /* Pack and discard the error snapshot */ 423 rv = hp_pack(root, &buf, &len); 424 hp_fini(root); 425 if (rv != 0) { 426 dprintf("cmd_changestate: hp_pack() failed (%s).\n", 427 strerror(rv)); 428 return (status); 429 } 430 431 /* Allocate nvlist for results */ 432 if (nvlist_alloc(&results, NV_UNIQUE_NAME_TYPE, 0) != 0) { 433 dprintf("cmd_changestate: nvlist_alloc() failed.\n"); 434 free(buf); 435 return (status); 436 } 437 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, 441 len) != 0)) { 442 dprintf("cmd_changestate: nvlist add failed.\n"); 443 nvlist_free(results); 444 free(buf); 445 return (status); 446 } 447 448 *resultsp = results; 449 } 450 451 return (status); 452 } 453 454 /* 455 * cmd_private() 456 * 457 * Implementation of the door command to set or get bus private options. 458 * 459 * NOTE: requires 'modify' authorization for the 'set' command. 460 */ 461 static int 462 cmd_private(hp_cmd_t cmd, nvlist_t *args, nvlist_t **resultsp) 463 { 464 nvlist_t *results = NULL; 465 ucred_t *uc = NULL; 466 char *path, *connection, *options; 467 char *values = NULL; 468 int status; 469 470 dprintf("cmd_private:\n"); 471 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)); 475 return (EACCES); 476 } 477 478 /* Get arguments */ 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"); 483 return (EINVAL); 484 } 485 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); 492 ucred_free(uc); 493 return (EACCES); 494 } 495 496 /* Perform the operation */ 497 status = private_options(path, connection, cmd, options, &values); 498 dprintf("cmd_private: private_options() == %d\n", status); 499 500 /* Audit the operation */ 501 if (cmd == HP_CMD_SETPRIVATE) { 502 audit_setprivate(uc, HP_MODIFY_AUTH, path, connection, options, 503 status); 504 ucred_free(uc); 505 } 506 507 /* Construct an nvlist if values were returned */ 508 if (values != NULL) { 509 510 /* Allocate nvlist for results */ 511 if (nvlist_alloc(&results, NV_UNIQUE_NAME_TYPE, 0) != 0) { 512 dprintf("cmd_private: nvlist_alloc() failed.\n"); 513 free(values); 514 return (ENOMEM); 515 } 516 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); 522 free(values); 523 return (ENOMEM); 524 } 525 526 /* The values string is no longer needed */ 527 free(values); 528 529 *resultsp = results; 530 } 531 532 return (status); 533 } 534 535 /* 536 * get_seqnum() 537 * 538 * Allocate the next unique sequence number for a results buffer. 539 */ 540 static uint64_t 541 get_seqnum(void) 542 { 543 uint64_t seqnum; 544 545 (void) pthread_mutex_lock(&buffer_lock); 546 547 seqnum = buffer_seqnum++; 548 549 (void) pthread_mutex_unlock(&buffer_lock); 550 551 return (seqnum); 552 } 553 554 /* 555 * add_buffer() 556 * 557 * Link a results buffer into the list containing all buffers. 558 */ 559 static void 560 add_buffer(uint64_t seqnum, char *buf) 561 { 562 i_buffer_t *node; 563 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", 567 strerror(errno)); 568 return; 569 } 570 571 node->seqnum = seqnum; 572 node->buffer = buf; 573 574 (void) pthread_mutex_lock(&buffer_lock); 575 576 node->next = buffer_list; 577 buffer_list = node; 578 579 (void) pthread_mutex_unlock(&buffer_lock); 580 } 581 582 /* 583 * free_buffer() 584 * 585 * Remove a results buffer from the list containing all buffers. 586 */ 587 static void 588 free_buffer(uint64_t seqnum) 589 { 590 i_buffer_t *node, *prev; 591 592 (void) pthread_mutex_lock(&buffer_lock); 593 594 prev = NULL; 595 node = buffer_list; 596 597 while (node) { 598 if (node->seqnum == seqnum) { 599 dprintf("Free buffer %lld\n", seqnum); 600 if (prev) { 601 prev->next = node->next; 602 } else { 603 buffer_list = node->next; 604 } 605 free(node->buffer); 606 free(node); 607 break; 608 } 609 prev = node; 610 node = node->next; 611 } 612 613 (void) pthread_mutex_unlock(&buffer_lock); 614 } 615 616 /* 617 * audit_session() 618 * 619 * Initialize an audit session. 620 */ 621 static int 622 audit_session(ucred_t *ucred, adt_session_data_t **sessionp) 623 { 624 adt_session_data_t *session; 625 626 if (adt_start_session(&session, NULL, 0) != 0) { 627 log_err("Cannot start audit session.\n"); 628 return (-1); 629 } 630 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); 634 return (-1); 635 } 636 637 *sessionp = session; 638 return (0); 639 } 640 641 /* 642 * audit_changestate() 643 * 644 * Audit a 'changestate' door command. 645 */ 646 static void 647 audit_changestate(ucred_t *ucred, char *auth, char *path, char *connection, 648 int new_state, int old_state, int result) 649 { 650 adt_session_data_t *session; 651 adt_event_data_t *event; 652 int pass_fail, fail_reason; 653 654 if (audit_session(ucred, &session) != 0) 655 return; 656 657 if ((event = adt_alloc_event(session, ADT_hotplug_state)) == NULL) { 658 (void) adt_end_session(session); 659 return; 660 } 661 662 if (result == 0) { 663 pass_fail = ADT_SUCCESS; 664 fail_reason = ADT_SUCCESS; 665 } else { 666 pass_fail = ADT_FAILURE; 667 fail_reason = result; 668 } 669 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); 675 676 /* Put the event */ 677 if (adt_put_event(event, pass_fail, fail_reason) != 0) 678 log_err("Cannot put audit event.\n"); 679 680 adt_free_event(event); 681 (void) adt_end_session(session); 682 } 683 684 /* 685 * audit_setprivate() 686 * 687 * Audit a 'set private' door command. 688 */ 689 static void 690 audit_setprivate(ucred_t *ucred, char *auth, char *path, char *connection, 691 char *options, int result) 692 { 693 adt_session_data_t *session; 694 adt_event_data_t *event; 695 int pass_fail, fail_reason; 696 697 if (audit_session(ucred, &session) != 0) 698 return; 699 700 if ((event = adt_alloc_event(session, ADT_hotplug_set)) == NULL) { 701 (void) adt_end_session(session); 702 return; 703 } 704 705 if (result == 0) { 706 pass_fail = ADT_SUCCESS; 707 fail_reason = ADT_SUCCESS; 708 } else { 709 pass_fail = ADT_FAILURE; 710 fail_reason = result; 711 } 712 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; 717 718 /* Put the event */ 719 if (adt_put_event(event, pass_fail, fail_reason) != 0) 720 log_err("Cannot put audit event.\n"); 721 722 adt_free_event(event); 723 (void) adt_end_session(session); 724 } 725 726 /* 727 * state_str() 728 * 729 * Convert a state from integer to string. 730 */ 731 static char * 732 state_str(int state) 733 { 734 switch (state) { 735 case DDI_HP_CN_STATE_EMPTY: 736 return ("EMPTY"); 737 case DDI_HP_CN_STATE_PRESENT: 738 return ("PRESENT"); 739 case DDI_HP_CN_STATE_POWERED: 740 return ("POWERED"); 741 case DDI_HP_CN_STATE_ENABLED: 742 return ("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: 748 return ("OFFLINE"); 749 case DDI_HP_CN_STATE_ATTACHED: 750 return ("ATTACHED"); 751 case DDI_HP_CN_STATE_MAINTENANCE: 752 return ("MAINTENANCE"); 753 case DDI_HP_CN_STATE_ONLINE: 754 return ("ONLINE"); 755 default: 756 return ("UNKNOWN"); 757 } 758 } 759