Home | History | Annotate | Download | only in nwamd
      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