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 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * This file contains the routines that service the libdoor(3LIB) interface. 29 * This interface is intended for use by an external GUI utility to provide 30 * status information to users and allow control over nwam behavior in certain 31 * situations. 32 * 33 * The daemon has one active thread for each door call. Typically, a client 34 * will make a blocking call to await new events, and if an active client is 35 * busy, we will enqueue a small number of events here. If too many are 36 * enqueued, then we begin dropping events, and a single special "lost" event 37 * is placed in the queue. Clients are expected to start over at that point. 38 * 39 * For client events that require a response from the client, the server must 40 * assume a response if "lost" occurs or if there are no clients. 41 * 42 * When no clients are present, we just drop events. No history is maintained. 43 * 44 * Thread cancellation notes: In general, we disable cancellation for all 45 * calls, as allowing cancellation would require special handlers throughout 46 * the nwamd code to deal with the release of locks taken in various contexts. 47 * Instead, we allow it to run to completion on the assumption that all calls 48 * are expected to run without significant blocking. 49 * 50 * The one exception to this is the event-wait function, which intentionally 51 * blocks indefinitely. This request must enable cancellation so that an idle 52 * client can be terminated cleanly. 53 */ 54 55 #include <stdlib.h> 56 #include <assert.h> 57 #include <unistd.h> 58 #include <pthread.h> 59 #include <door.h> 60 #include <alloca.h> 61 #include <sys/types.h> 62 #include <sys/stat.h> 63 #include <fcntl.h> 64 #include <errno.h> 65 #include <syslog.h> 66 #include <string.h> 67 #include <pwd.h> 68 #include <auth_attr.h> 69 #include <auth_list.h> 70 #include <secdb.h> 71 #include <bsm/adt.h> 72 #include <bsm/adt_event.h> 73 74 #include "defines.h" 75 #include "structures.h" 76 #include "functions.h" 77 #include "variables.h" 78 79 /* Idle time before declaring a client to be dead. */ 80 uint_t door_idle_time = 10; 81 82 static int door_fd = -1; 83 84 static uid_t cur_user = (uid_t)-1; 85 static adt_session_data_t *cur_ah; 86 87 /* 88 * event_queue is a simple circular queue of fixed size. 'evput' is the next 89 * location to write, and 'evget' is the next waiting event. The queue size is 90 * chosen so that it's extremely unlikely that a functioning GUI could get this 91 * far behind on events and still be at all usable. (Too large, and we'd wait 92 * too long backing off to automatic mode on a broken GUI. Too small, and an 93 * interface up/down transient would cause us to switch to automatic mode too 94 * easily.) 95 */ 96 #define MAX_DESCR_EVENTS 64 97 static nwam_descr_event_t event_queue[MAX_DESCR_EVENTS]; 98 static nwam_descr_event_t *evput = event_queue, *evget = event_queue; 99 static struct wireless_lan *current_wlans; 100 static size_t current_wlansize; 101 102 /* 103 * This lock protects the event queue and the current_wlans list. 104 */ 105 static pthread_mutex_t event_lock = PTHREAD_MUTEX_INITIALIZER; 106 107 /* 108 * sleep_cv is used to block waiting for new events to appear in an empty 109 * queue. client_cv is used to wait for event client threads to wake up and 110 * return before shutting down the daemon. 111 */ 112 static pthread_cond_t sleep_cv, client_cv; 113 static uint_t sleeping_clients; 114 static boolean_t active_clients; 115 static uint32_t client_expire; 116 117 /* 118 * Register a "user logout" event with the auditing system. 119 * A "logout" occurs when the GUI stops calling the event wait system (detected 120 * either by idle timer or queue overflow), or when a different authorized user 121 * calls the daemon (the previous one is logged out), or when the daemon itself 122 * is shut down. 123 */ 124 static void 125 audit_detach(void) 126 { 127 adt_event_data_t *event; 128 129 event = adt_alloc_event(cur_ah, ADT_nwam_detach); 130 if (event == NULL) 131 syslog(LOG_ERR, "audit failure: detach allocation: %m"); 132 else if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) 133 syslog(LOG_ERR, "audit failure: detach put: %m"); 134 adt_free_event(event); 135 (void) adt_end_session(cur_ah); 136 cur_ah = NULL; 137 cur_user = (uid_t)-1; 138 } 139 140 /* 141 * Register either a normal "user login" event (if 'attached' is set) or a 142 * failed login (if 'attached' is not set) with auditing. 143 */ 144 static void 145 audit_attach(ucred_t *ucr, boolean_t attached) 146 { 147 adt_session_data_t *ah; 148 adt_event_data_t *event; 149 int retv, status, retval; 150 151 if (adt_start_session(&ah, NULL, 0) != 0) { 152 syslog(LOG_ERR, "audit failure: session start: %m"); 153 return; 154 } 155 156 if (adt_set_from_ucred(ah, ucr, ADT_NEW) != 0) { 157 syslog(LOG_ERR, "audit failure: session credentials: %m"); 158 goto failure; 159 } 160 if ((event = adt_alloc_event(ah, ADT_nwam_attach)) == NULL) { 161 syslog(LOG_ERR, "audit failure: audit allocation: %m"); 162 goto failure; 163 } 164 event->adt_nwam_attach.auth_used = NET_AUTOCONF_AUTH; 165 if (attached) { 166 status = ADT_SUCCESS; 167 retval = ADT_SUCCESS; 168 } else { 169 status = ADT_FAILURE; 170 retval = ADT_FAIL_VALUE_AUTH; 171 } 172 retv = adt_put_event(event, status, retval); 173 adt_free_event(event); 174 if (retv != 0) { 175 syslog(LOG_ERR, "audit failure: attach put: %m"); 176 goto failure; 177 } 178 179 /* 180 * Only successful attach records result in a detach. All else have 181 * (at most) a single failure record, and nothing else. Thus, we do 182 * not set cur_ah until we know we've written an attach record. 183 */ 184 if (attached) { 185 cur_ah = ah; 186 return; 187 } 188 189 failure: 190 (void) adt_end_session(ah); 191 } 192 193 /* Convert descriptive event to a text name for debug log */ 194 static const char * 195 descr_event_name(libnwam_descr_evtype_t evt) 196 { 197 /* 198 * Cast to int so that compiler and lint don't complain about extra 199 * 'default' case, and so that we can handle stray values. 200 */ 201 switch ((int)evt) { 202 case deInitial: 203 return ("Initial"); 204 case deInterfaceUp: 205 return ("InterfaceUp"); 206 case deInterfaceDown: 207 return ("InterfaceDown"); 208 case deInterfaceAdded: 209 return ("InterfaceAdded"); 210 case deInterfaceRemoved: 211 return ("InterfaceRemoved"); 212 case deWlanConnectFail: 213 return ("WlanConnectFail"); 214 case deWlanDisconnect: 215 return ("WlanDisconnect"); 216 case deWlanConnected: 217 return ("WlanConnected"); 218 case deLLPSelected: 219 return ("LLPSelected"); 220 case deLLPUnselected: 221 return ("LLPUnselected"); 222 case deULPActivated: 223 return ("ULPActivated"); 224 case deULPDeactivated: 225 return ("ULPDeactivated"); 226 case deScanChange: 227 return ("ScanChange"); 228 case deScanSame: 229 return ("ScanSame"); 230 case deWlanKeyNeeded: 231 return ("WlanKeyNeeded"); 232 case deWlanSelectionNeeded: 233 return ("WlanSelectionNeeded"); 234 default: 235 return ("unknown"); 236 } 237 } 238 239 /* 240 * This is called only by ndcWaitEvent, which holds event_lock until it has 241 * copied out the data from the entry. 242 */ 243 static const nwam_descr_event_t * 244 get_descr_event(void) 245 { 246 nwam_descr_event_t *nde; 247 static const nwam_descr_event_t init = { deInitial }; 248 249 if (!active_clients) { 250 syslog(LOG_INFO, "new active door client detected"); 251 active_clients = B_TRUE; 252 return (&init); 253 } 254 if ((nde = evget) == evput) 255 return (NULL); 256 if ((evget = nde + 1) >= event_queue + MAX_DESCR_EVENTS) 257 evget = event_queue; 258 /* If this event has a new WLAN snapshot, then update */ 259 if (nde->nde_wlans != NULL) { 260 free(current_wlans); 261 current_wlans = nde->nde_wlans; 262 current_wlansize = nde->nde_wlansize; 263 nde->nde_wlans = NULL; 264 } 265 return (nde); 266 } 267 268 /* 269 * {start,put}_descr_event are called by the reporting functions. This 270 * function starts a new descriptive event and returns with the lock held (if 271 * the return value is non-NULL). 272 */ 273 static nwam_descr_event_t * 274 start_descr_event(libnwam_descr_evtype_t evt) 275 { 276 nwam_descr_event_t *nde, *ndenext; 277 278 if (!active_clients || pthread_mutex_lock(&event_lock) != 0) { 279 dprintf("dropping event %s; no active client", 280 descr_event_name(evt)); 281 return (NULL); 282 } 283 nde = evput; 284 if ((ndenext = nde + 1) >= event_queue + MAX_DESCR_EVENTS) 285 ndenext = event_queue; 286 if (ndenext == evget) { 287 syslog(LOG_INFO, "descr event queue overflow"); 288 active_clients = B_FALSE; 289 (void) np_queue_add_event(EV_RESELECT, NULL); 290 evput = evget = event_queue; 291 audit_detach(); 292 (void) pthread_mutex_unlock(&event_lock); 293 return (NULL); 294 } else { 295 nde->nde_type = evt; 296 return (nde); 297 } 298 } 299 300 /* Finish reporting the event; must not be called if nde is NULL */ 301 static void 302 put_descr_event(nwam_descr_event_t *nde, const char *ifname) 303 { 304 if (ifname != NULL) { 305 dprintf("putting descr event %s %s", 306 descr_event_name(nde->nde_type), ifname); 307 (void) strlcpy(nde->nde_interface, ifname, 308 sizeof (nde->nde_interface)); 309 if (++nde >= event_queue + MAX_DESCR_EVENTS) 310 nde = event_queue; 311 evput = nde; 312 (void) pthread_cond_signal(&sleep_cv); 313 } 314 /* Cannot drop the lock unless we've acquired it. */ 315 assert(nde != NULL); 316 (void) pthread_mutex_unlock(&event_lock); 317 } 318 319 /* 320 * Finish reporting an event that sets the WLAN snapshot. If there's no 321 * client, then update the saved snapshot right now, as we won't be queuing the 322 * event. 323 */ 324 static boolean_t 325 commit_wlans(nwam_descr_event_t *nde, const struct wireless_lan *wlans, 326 int wlan_cnt, const char *ifname) 327 { 328 size_t wlansize; 329 struct wireless_lan *saved_wlans; 330 331 wlansize = sizeof (*saved_wlans) * wlan_cnt; 332 if ((saved_wlans = malloc(wlansize)) == NULL) { 333 if (nde != NULL) 334 put_descr_event(nde, NULL); 335 return (B_FALSE); 336 } 337 (void) memcpy(saved_wlans, wlans, wlansize); 338 339 if (nde != NULL) { 340 nde->nde_wlansize = wlansize; 341 nde->nde_wlans = saved_wlans; 342 put_descr_event(nde, ifname); 343 return (B_TRUE); 344 } else { 345 /* If the UI isn't running, then save the cached results */ 346 if (pthread_mutex_lock(&event_lock) == 0) { 347 free(current_wlans); 348 current_wlans = saved_wlans; 349 current_wlansize = wlansize; 350 (void) pthread_mutex_unlock(&event_lock); 351 } else { 352 free(saved_wlans); 353 } 354 return (B_FALSE); 355 } 356 } 357 358 void 359 report_interface_up(const char *ifname, struct in_addr addr, int prefixlen) 360 { 361 nwam_descr_event_t *nde; 362 363 if ((nde = start_descr_event(deInterfaceUp)) != NULL) { 364 nde->nde_v4address = addr; 365 nde->nde_prefixlen = prefixlen; 366 put_descr_event(nde, ifname); 367 } 368 } 369 370 void 371 report_interface_down(const char *ifname, libnwam_diag_cause_t cause) 372 { 373 nwam_descr_event_t *nde; 374 375 if ((nde = start_descr_event(deInterfaceDown)) != NULL) { 376 nde->nde_cause = cause; 377 put_descr_event(nde, ifname); 378 } 379 } 380 381 void 382 report_interface_added(const char *ifname) 383 { 384 nwam_descr_event_t *nde; 385 386 if ((nde = start_descr_event(deInterfaceAdded)) != NULL) 387 put_descr_event(nde, ifname); 388 } 389 390 void 391 report_interface_removed(const char *ifname) 392 { 393 nwam_descr_event_t *nde; 394 395 if ((nde = start_descr_event(deInterfaceRemoved)) != NULL) 396 put_descr_event(nde, ifname); 397 } 398 399 void 400 report_wlan_connect_fail(const char *ifname) 401 { 402 nwam_descr_event_t *nde; 403 404 if ((nde = start_descr_event(deWlanConnectFail)) != NULL) 405 put_descr_event(nde, ifname); 406 } 407 408 void 409 report_wlan_disconnect(const struct wireless_lan *wlan) 410 { 411 nwam_descr_event_t *nde; 412 413 if ((nde = start_descr_event(deWlanDisconnect)) != NULL) { 414 nde->nde_attrs = wlan->attrs; 415 put_descr_event(nde, wlan->wl_if_name); 416 } 417 } 418 419 void 420 report_wlan_connected(const struct wireless_lan *wlan) 421 { 422 nwam_descr_event_t *nde; 423 424 if ((nde = start_descr_event(deWlanConnected)) != NULL) { 425 nde->nde_attrs = wlan->attrs; 426 put_descr_event(nde, wlan->wl_if_name); 427 } 428 } 429 430 void 431 report_llp_selected(const char *ifname) 432 { 433 nwam_descr_event_t *nde; 434 435 if ((nde = start_descr_event(deLLPSelected)) != NULL) 436 put_descr_event(nde, ifname); 437 } 438 439 void 440 report_llp_unselected(const char *ifname, libnwam_diag_cause_t cause) 441 { 442 nwam_descr_event_t *nde; 443 444 if ((nde = start_descr_event(deLLPUnselected)) != NULL) { 445 nde->nde_cause = cause; 446 put_descr_event(nde, ifname); 447 } 448 } 449 450 void 451 report_ulp_activated(const char *ulpname) 452 { 453 nwam_descr_event_t *nde; 454 455 if ((nde = start_descr_event(deULPActivated)) != NULL) 456 put_descr_event(nde, ulpname); 457 } 458 459 void 460 report_ulp_deactivated(const char *ulpname) 461 { 462 nwam_descr_event_t *nde; 463 464 if ((nde = start_descr_event(deULPDeactivated)) != NULL) 465 put_descr_event(nde, ulpname); 466 } 467 468 void 469 report_scan_complete(const char *ifname, boolean_t changed, 470 const struct wireless_lan *wlans, int wlan_cnt) 471 { 472 nwam_descr_event_t *nde; 473 474 nde = start_descr_event(changed ? deScanChange : deScanSame); 475 (void) commit_wlans(nde, wlans, wlan_cnt, ifname); 476 } 477 478 boolean_t 479 request_wlan_key(struct wireless_lan *wlan) 480 { 481 nwam_descr_event_t *nde; 482 483 if ((nde = start_descr_event(deWlanKeyNeeded)) != NULL) { 484 nde->nde_attrs = wlan->attrs; 485 put_descr_event(nde, wlan->wl_if_name); 486 return (B_TRUE); 487 } else { 488 return (B_FALSE); 489 } 490 } 491 492 boolean_t 493 request_wlan_selection(const char *ifname, const struct wireless_lan *wlans, 494 int wlan_cnt) 495 { 496 nwam_descr_event_t *nde; 497 498 nde = start_descr_event(deWlanSelectionNeeded); 499 return (commit_wlans(nde, wlans, wlan_cnt, ifname)); 500 } 501 502 /* ARGSUSED */ 503 static void 504 thread_cancel_handler(void *arg) 505 { 506 if (--sleeping_clients == 0) { 507 client_expire = NSEC_TO_SEC(gethrtime()) + door_idle_time; 508 (void) pthread_cond_signal(&client_cv); 509 /* 510 * On the wrong thread; must call start_timer from the main 511 * thread. 512 */ 513 if (client_expire < timer_expire) 514 (void) np_queue_add_event(EV_DOOR_TIME, NULL); 515 } 516 (void) pthread_mutex_unlock(&event_lock); 517 } 518 519 /* 520 * A timer is set when there are waiting event collectors. If there haven't 521 * been any collectors for "a long time," then we assume that the user 522 * interface has been terminated or is jammed. 523 */ 524 void 525 check_door_life(uint32_t now) 526 { 527 if (active_clients && sleeping_clients == 0) { 528 if (client_expire > now) { 529 start_timer(now, client_expire - now); 530 } else { 531 syslog(LOG_INFO, 532 "no active door clients left; flushing queue"); 533 if (pthread_mutex_lock(&event_lock) == 0) { 534 active_clients = B_FALSE; 535 if (evput != evget) { 536 (void) np_queue_add_event(EV_RESELECT, 537 NULL); 538 } 539 evput = evget = event_queue; 540 audit_detach(); 541 (void) pthread_mutex_unlock(&event_lock); 542 } 543 } 544 } 545 } 546 547 /* 548 * This is called for an unrecognized UID. We check to see if the user is 549 * authorized to issue commands to the NWAM daemon. 550 */ 551 static boolean_t 552 update_cur_user(ucred_t *ucr) 553 { 554 struct passwd *pwd; 555 uid_t uid = ucred_getruid(ucr); 556 boolean_t attached = B_FALSE; 557 558 if ((pwd = getpwuid(uid)) == NULL) { 559 syslog(LOG_DEBUG, "unable to translate uid %d to a name", uid); 560 } else if (chkauthattr(NET_AUTOCONF_AUTH, pwd->pw_name) == 0) { 561 syslog(LOG_DEBUG, "user %s (%d) does not have %s", pwd->pw_name, 562 uid, NET_AUTOCONF_AUTH); 563 } else { 564 attached = B_TRUE; 565 } 566 if (pthread_mutex_lock(&event_lock) == 0) { 567 if (attached) { 568 audit_detach(); 569 cur_user = uid; 570 } 571 audit_attach(ucr, attached); 572 (void) pthread_mutex_unlock(&event_lock); 573 } else { 574 attached = B_FALSE; 575 } 576 return (attached); 577 } 578 579 /* ARGSUSED */ 580 static void 581 nwam_door_server(void *cookie, char *argp, size_t arg_size, door_desc_t *dp, 582 uint_t ndesc) 583 { 584 /* LINTED: alignment */ 585 nwam_door_cmd_t *ndc = (nwam_door_cmd_t *)argp; 586 int retv = -1; 587 ucred_t *ucr = NULL; 588 libnwam_interface_type_t ift; 589 590 if (arg_size < sizeof (*ndc) || door_ucred(&ucr) != 0) { 591 retv = EINVAL; 592 (void) door_return((char *)&retv, sizeof (retv), NULL, 0); 593 return; 594 } 595 596 if (ucred_getruid(ucr) != cur_user && !update_cur_user(ucr)) { 597 ucred_free(ucr); 598 retv = EPERM; 599 (void) door_return((char *)&retv, sizeof (retv), NULL, 0); 600 return; 601 } 602 ucred_free(ucr); 603 604 /* 605 * Only the blocking event wait can be canceled, and then only when 606 * headed for a block. 607 */ 608 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); 609 610 switch (ndc->ndc_type) { 611 case ndcNull: 612 dprintf("door: null event from client"); 613 retv = 0; 614 break; 615 616 case ndcWaitEvent: { 617 const nwam_descr_event_t *nde; 618 nwam_descr_event_t ndcopy; 619 620 if ((retv = pthread_mutex_lock(&event_lock)) != 0) 621 break; 622 if ((nde = get_descr_event()) != NULL) { 623 ndcopy = *nde; 624 (void) pthread_mutex_unlock(&event_lock); 625 dprintf("door: returning waiting event %s", 626 descr_event_name(ndcopy.nde_type)); 627 (void) door_return((char *)&ndcopy, sizeof (ndcopy), 628 NULL, 0); 629 return; 630 } 631 632 (void) pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); 633 pthread_cleanup_push(thread_cancel_handler, NULL); 634 sleeping_clients++; 635 while ((nde = get_descr_event()) == NULL && 636 door_fd != -1) { 637 if (pthread_cond_wait(&sleep_cv, &event_lock) != 0) 638 break; 639 } 640 if (nde != NULL) 641 ndcopy = *nde; 642 pthread_cleanup_pop(1); 643 if (nde == NULL) { 644 retv = EBADF; 645 break; 646 } 647 dprintf("door: returning waited-for event %s", 648 descr_event_name(ndcopy.nde_type)); 649 (void) door_return((char *)&ndcopy, sizeof (ndcopy), NULL, 0); 650 return; 651 } 652 653 case ndcGetLLPList: { 654 nwam_llp_data_t *nld; 655 llp_t *llplist, *llpstack, *llp; 656 size_t llpsize; 657 uint_t count; 658 char selected[LIFNAMSIZ], locked[LIFNAMSIZ]; 659 660 /* 661 * Note that door_return never returns here, so we can't just 662 * use malloc'd memory. Copy over to a stack-allocated buffer 663 * and do the string pointer fix-ups. 664 */ 665 if ((retv = pthread_mutex_lock(&machine_lock)) != 0) 666 break; 667 errno = 0; 668 llplist = get_llp_list(&llpsize, &count, selected, locked); 669 (void) pthread_mutex_unlock(&machine_lock); 670 if (llplist != NULL) { 671 nld = alloca(sizeof (*nld) + llpsize); 672 nld->nld_count = count; 673 (void) strlcpy(nld->nld_selected, selected, 674 sizeof (nld->nld_selected)); 675 (void) strlcpy(nld->nld_locked, locked, 676 sizeof (nld->nld_locked)); 677 llpstack = (llp_t *)(nld + 1); 678 (void) memcpy(llpstack, llplist, llpsize); 679 llp = llpstack; 680 while (count-- > 0) { 681 if (llp->llp_ipv4addrstr != NULL) 682 llp->llp_ipv4addrstr -= 683 (uintptr_t)llplist; 684 if (llp->llp_ipv6addrstr != NULL) 685 llp->llp_ipv6addrstr -= 686 (uintptr_t)llplist; 687 llp++; 688 } 689 free(llplist); 690 llpsize += sizeof (*nld); 691 } else { 692 retv = errno; 693 dprintf("door: no LLP list to get"); 694 break; 695 } 696 dprintf("door: get llp list returning %d entries", 697 nld->nld_count); 698 (void) door_return((char *)nld, llpsize, NULL, 0); 699 return; 700 } 701 702 case ndcSetLLPPriority: 703 if ((retv = pthread_mutex_lock(&machine_lock)) != 0) 704 break; 705 dprintf("door: set priority on %s to %d", 706 ndc->ndc_interface, ndc->ndc_priority); 707 retv = set_llp_priority(ndc->ndc_interface, ndc->ndc_priority); 708 (void) pthread_mutex_unlock(&machine_lock); 709 break; 710 711 case ndcLockLLP: 712 if ((retv = pthread_mutex_lock(&machine_lock)) != 0) 713 break; 714 if (ndc->ndc_interface[0] == '\0') 715 dprintf("door: unlocking llp selection"); 716 else 717 dprintf("door: locking to %s", ndc->ndc_interface); 718 retv = set_locked_llp(ndc->ndc_interface); 719 (void) pthread_mutex_unlock(&machine_lock); 720 break; 721 722 case ndcGetWlanList: { 723 char *wlans; 724 size_t wlansize; 725 726 /* 727 * We protect ourselves here against a malicious or confused 728 * user. The list is stable only while we're holding the lock, 729 * and the lock can't be held during the door return. 730 */ 731 if ((retv = pthread_mutex_lock(&event_lock)) != 0) 732 break; 733 if (current_wlans == NULL) { 734 (void) pthread_mutex_unlock(&event_lock); 735 dprintf("door: no WLAN list to get"); 736 retv = ENXIO; 737 break; 738 } 739 wlans = alloca(wlansize = current_wlansize); 740 (void) memcpy(wlans, current_wlans, wlansize); 741 (void) pthread_mutex_unlock(&event_lock); 742 dprintf("door: get wlan list returning %lu bytes", 743 (ulong_t)wlansize); 744 (void) door_return(wlans, wlansize, NULL, 0); 745 return; 746 } 747 748 case ndcGetKnownAPList: { 749 nwam_known_ap_t *nka; 750 libnwam_known_ap_t *kalist, *kastack, *kap; 751 size_t kasize; 752 uint_t count; 753 754 /* 755 * Note that door_return never returns here, so we can't just 756 * use malloc'd memory. Copy over to a stack-allocated buffer 757 * and do the string pointer fix-ups. 758 */ 759 errno = 0; 760 kalist = get_known_ap_list(&kasize, &count); 761 if (kalist != NULL) { 762 nka = alloca(sizeof (*nka) + kasize); 763 nka->nka_count = count; 764 kastack = (libnwam_known_ap_t *)(nka + 1); 765 (void) memcpy(kastack, kalist, kasize); 766 kap = kastack; 767 while (count-- > 0) { 768 kap->ka_bssid -= (uintptr_t)kalist; 769 kap->ka_essid -= (uintptr_t)kalist; 770 kap++; 771 } 772 free(kalist); 773 kasize += sizeof (*nka); 774 } else { 775 retv = errno; 776 dprintf("door: no known AP list to get"); 777 break; 778 } 779 dprintf("door: get known AP list returning %u entries", 780 nka->nka_count); 781 (void) door_return((char *)nka, kasize, NULL, 0); 782 return; 783 } 784 785 case ndcAddKnownAP: 786 dprintf("door: adding known AP %s %s", 787 ndc->ndc_essid, ndc->ndc_bssid); 788 retv = add_known_ap(ndc->ndc_essid, ndc->ndc_bssid); 789 break; 790 791 case ndcDeleteKnownAP: 792 dprintf("door: removing known AP %s %s", 793 ndc->ndc_essid, ndc->ndc_bssid); 794 retv = delete_known_ap(ndc->ndc_essid, ndc->ndc_bssid); 795 break; 796 797 case ndcSelectWlan: 798 if ((retv = pthread_mutex_lock(&machine_lock)) != 0) 799 break; 800 dprintf("door: selecting WLAN on %s as %s %s", 801 ndc->ndc_interface, ndc->ndc_essid, ndc->ndc_bssid); 802 /* 803 * Check if we're already connected to the requested 804 * ESSID/BSSID. If so, then this request succeeds without 805 * changing anything. Otherwise, tear down the interface 806 * (disconnecting from the WLAN) and set up again. 807 */ 808 if (check_wlan_connected(ndc->ndc_interface, ndc->ndc_essid, 809 ndc->ndc_bssid)) { 810 retv = 0; 811 } else { 812 takedowninterface(ndc->ndc_interface, dcSelect); 813 if (link_layer_profile != NULL) 814 link_layer_profile->llp_waiting = B_TRUE; 815 retv = set_specific_lan(ndc->ndc_interface, 816 ndc->ndc_essid, ndc->ndc_bssid); 817 } 818 (void) pthread_mutex_unlock(&machine_lock); 819 break; 820 821 case ndcWlanKey: 822 if ((retv = pthread_mutex_lock(&machine_lock)) != 0) 823 break; 824 dprintf("door: selecting WLAN key on %s for %s %s", 825 ndc->ndc_interface, ndc->ndc_essid, ndc->ndc_bssid); 826 retv = set_wlan_key(ndc->ndc_interface, ndc->ndc_essid, 827 ndc->ndc_bssid, ndc->ndc_key, ndc->ndc_secmode); 828 (void) pthread_mutex_unlock(&machine_lock); 829 break; 830 831 case ndcStartRescan: 832 dprintf("door: rescan requested on %s", 833 ndc->ndc_interface); 834 ift = get_if_type(ndc->ndc_interface); 835 if (ift != IF_UNKNOWN && ift != IF_WIRELESS) { 836 retv = EINVAL; 837 break; 838 } 839 if ((retv = pthread_mutex_lock(&machine_lock)) != 0) 840 break; 841 retv = launch_wireless_scan(ndc->ndc_interface); 842 (void) pthread_mutex_unlock(&machine_lock); 843 break; 844 845 default: 846 dprintf("door: unknown request type %d", (int)ndc->ndc_type); 847 break; 848 } 849 if (retv != 0) 850 dprintf("door: returning to caller with error %d (%s)", 851 retv, strerror(retv)); 852 (void) door_return((char *)&retv, sizeof (retv), NULL, 0); 853 } 854 855 static void 856 door_cleanup(void) 857 { 858 if (door_fd != -1) { 859 syslog(LOG_DEBUG, "closing door"); 860 (void) door_revoke(door_fd); 861 door_fd = -1; 862 } 863 (void) unlink(DOOR_FILENAME); 864 } 865 866 void 867 terminate_door(void) 868 { 869 door_cleanup(); 870 if (pthread_mutex_lock(&event_lock) != 0) 871 return; 872 if (sleeping_clients != 0) 873 syslog(LOG_DEBUG, "waiting on %d sleeping clients", 874 sleeping_clients); 875 while (sleeping_clients != 0) { 876 (void) pthread_cond_broadcast(&sleep_cv); 877 if (pthread_cond_wait(&client_cv, &event_lock) != 0) 878 break; 879 } 880 free(current_wlans); 881 current_wlans = NULL; 882 audit_detach(); 883 (void) pthread_mutex_unlock(&event_lock); 884 } 885 886 void 887 initialize_door(void) 888 { 889 int did; 890 891 /* Do a low-overhead "touch" on the file that will be the door node. */ 892 syslog(LOG_DEBUG, "opening door"); 893 did = open(DOOR_FILENAME, 894 O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW | O_NONBLOCK, 895 DOOR_FILEMODE); 896 if (did != -1) { 897 (void) close(did); 898 } else if (errno != EEXIST) { 899 syslog(LOG_ERR, "unable to create control door node: %m"); 900 exit(EXIT_FAILURE); 901 } 902 903 (void) atexit(door_cleanup); 904 905 /* Create the door. */ 906 door_fd = door_create(nwam_door_server, NULL, DOOR_REFUSE_DESC); 907 if (door_fd == -1) { 908 syslog(LOG_ERR, "unable to create control door: %m"); 909 exit(EXIT_FAILURE); 910 } 911 912 /* Attach the door to the file. */ 913 (void) fdetach(DOOR_FILENAME); 914 if (fattach(door_fd, DOOR_FILENAME) == -1) { 915 syslog(LOG_ERR, "unable to attach control door: %m"); 916 exit(EXIT_FAILURE); 917 } 918 } 919