/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "conditions.h" #include "events.h" #include "ncp.h" #include "ncu.h" #include "objects.h" #include "util.h" /* * door_if.c * This file contains functions which implement the command interface to * nwam via the door NWAM_DOOR. Doors provide a LPC mechanism that allows * for threads in one process to cause code to execute in another process. * Doors also provide the ability to pass data and file descriptors. See * libdoor(3LIB) for more information. * * This file exports two functions, nwamd_door_initialize() (which sets up * the door) and nwamd_door_fini(), which removes it. * * It sets up the static routine nwamd_door_switch() to be called when a client * calls the door (via door_call(3C)). The structure nwam_request_t is * passed as data and contains data to specify the type of action requested * and any data need to meet that request. A table consisting of entries * for each door request, the associated authorization and the function to * process that request is used to handle the various requests. */ struct nwamd_door_req_entry { int ndre_type; char *ndre_auth; nwam_error_t (*ndre_fn)(nwamd_door_arg_t *, ucred_t *, struct passwd *); }; static nwam_error_t nwamd_door_req_event_register(nwamd_door_arg_t *, ucred_t *, struct passwd *); static nwam_error_t nwamd_door_req_event_unregister(nwamd_door_arg_t *, ucred_t *, struct passwd *); static nwam_error_t nwamd_door_req_wlan_scan(nwamd_door_arg_t *, ucred_t *, struct passwd *); static nwam_error_t nwamd_door_req_wlan_scan_results(nwamd_door_arg_t *, ucred_t *, struct passwd *); static nwam_error_t nwamd_door_req_wlan_select(nwamd_door_arg_t *, ucred_t *, struct passwd *); static nwam_error_t nwamd_door_req_wlan_set_key(nwamd_door_arg_t *, ucred_t *, struct passwd *); static nwam_error_t nwamd_door_req_action(nwamd_door_arg_t *, ucred_t *, struct passwd *); static nwam_error_t nwamd_door_req_state(nwamd_door_arg_t *, ucred_t *, struct passwd *); static nwam_error_t nwamd_door_req_priority_group(nwamd_door_arg_t *, ucred_t *, struct passwd *); /* * This table defines the set of door commands available, the required * authorizations for each command, and the function that carries out * each command. */ struct nwamd_door_req_entry door_req_table[] = { { NWAM_REQUEST_TYPE_EVENT_REGISTER, AUTOCONF_READ_AUTH, nwamd_door_req_event_register }, { NWAM_REQUEST_TYPE_EVENT_UNREGISTER, AUTOCONF_READ_AUTH, nwamd_door_req_event_unregister }, { NWAM_REQUEST_TYPE_WLAN_SCAN, AUTOCONF_WLAN_AUTH, nwamd_door_req_wlan_scan }, { NWAM_REQUEST_TYPE_WLAN_SCAN_RESULTS, AUTOCONF_READ_AUTH, nwamd_door_req_wlan_scan_results }, { NWAM_REQUEST_TYPE_WLAN_SELECT, AUTOCONF_WLAN_AUTH, nwamd_door_req_wlan_select }, { NWAM_REQUEST_TYPE_WLAN_SET_KEY, AUTOCONF_WLAN_AUTH, nwamd_door_req_wlan_set_key }, /* Requires WRITE, SELECT or WLAN auth depending on action */ { NWAM_REQUEST_TYPE_ACTION, NULL, nwamd_door_req_action }, { NWAM_REQUEST_TYPE_STATE, AUTOCONF_READ_AUTH, nwamd_door_req_state }, { NWAM_REQUEST_TYPE_PRIORITY_GROUP, AUTOCONF_READ_AUTH, nwamd_door_req_priority_group }, }; int doorfd = -1; /* ARGSUSED */ static nwam_error_t nwamd_door_req_event_register(nwamd_door_arg_t *req, ucred_t *ucr, struct passwd *pwd) { nwam_error_t err; err = nwam_event_queue_init (req->nwda_data.nwdad_register_info.nwdad_name); if (err != NWAM_SUCCESS) { nlog(LOG_ERR, "nwamd_door_req_event_register: " "could not register events for %s", req->nwda_data.nwdad_register_info.nwdad_name); } return (err); } /* ARGSUSED */ static nwam_error_t nwamd_door_req_event_unregister(nwamd_door_arg_t *req, ucred_t *ucr, struct passwd *pwd) { nwam_event_queue_fini(req->nwda_data.nwdad_register_info.nwdad_name); return (NWAM_SUCCESS); } /* ARGSUSED1 */ static nwam_error_t nwamd_door_req_wlan_scan(nwamd_door_arg_t *req, ucred_t *ucr, struct passwd *pwd) { nlog(LOG_DEBUG, "nwamd_door_req_wlan_scan: processing WLAN scan request: " "link %s", req->nwda_data.nwdad_wlan_info.nwdad_name); return (nwamd_wlan_scan(req->nwda_data.nwdad_wlan_info.nwdad_name)); } /* ARGSUSED */ static nwam_error_t nwamd_door_req_wlan_scan_results(nwamd_door_arg_t *req, ucred_t *ucr, struct passwd *pwd) { nwamd_object_t obj; nwamd_ncu_t *ncu; nwamd_link_t *link; uint_t num_wlans; nlog(LOG_DEBUG, "nwamd_door_req_wlan_scan_results: processing WLAN " "scan results request: link %s", req->nwda_data.nwdad_wlan_info.nwdad_name); obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, req->nwda_data.nwdad_wlan_info.nwdad_name); if (obj == NULL) { nlog(LOG_ERR, "nwamd_door_req_wlan_scan_results: link %s not found", req->nwda_data.nwdad_wlan_info.nwdad_name); return (NWAM_ENTITY_NOT_FOUND); } ncu = obj->nwamd_object_data; link = &ncu->ncu_link; num_wlans = link->nwamd_link_wifi_scan.nwamd_wifi_scan_curr_num; if (num_wlans > 0) { (void) memcpy (req->nwda_data.nwdad_wlan_info.nwdad_wlans, link->nwamd_link_wifi_scan.nwamd_wifi_scan_curr, num_wlans * sizeof (nwam_wlan_t)); } req->nwda_data.nwdad_wlan_info.nwdad_num_wlans = num_wlans; nlog(LOG_DEBUG, "nwamd_door_req_wlan_scan_results: returning %d scan results", num_wlans); nwamd_object_release(obj); return (NWAM_SUCCESS); } /* ARGSUSED */ static nwam_error_t nwamd_door_req_wlan_select(nwamd_door_arg_t *req, ucred_t *ucr, struct passwd *pwd) { nlog(LOG_DEBUG, "nwamd_door_req_wlan_select: processing WLAN selection : " "link %s ESSID %s , BSSID %s", req->nwda_data.nwdad_wlan_info.nwdad_name, req->nwda_data.nwdad_wlan_info.nwdad_essid, req->nwda_data.nwdad_wlan_info.nwdad_bssid); return (nwamd_wlan_select (req->nwda_data.nwdad_wlan_info.nwdad_name, req->nwda_data.nwdad_wlan_info.nwdad_essid, req->nwda_data.nwdad_wlan_info.nwdad_bssid, req->nwda_data.nwdad_wlan_info.nwdad_security_mode, req->nwda_data.nwdad_wlan_info.nwdad_add_to_known_wlans)); } /* ARGSUSED */ static nwam_error_t nwamd_door_req_wlan_set_key(nwamd_door_arg_t *req, ucred_t *ucr, struct passwd *pwd) { nlog(LOG_DEBUG, "nwamd_door_req_wlan_set_key: processing WLAN key input : " "link %s ESSID %s BSSID %s", req->nwda_data.nwdad_wlan_info.nwdad_name, req->nwda_data.nwdad_wlan_info.nwdad_essid, req->nwda_data.nwdad_wlan_info.nwdad_bssid); return (nwamd_wlan_set_key (req->nwda_data.nwdad_wlan_info.nwdad_name, req->nwda_data.nwdad_wlan_info.nwdad_essid, req->nwda_data.nwdad_wlan_info.nwdad_bssid, req->nwda_data.nwdad_wlan_info.nwdad_security_mode, req->nwda_data.nwdad_wlan_info.nwdad_keyslot, req->nwda_data.nwdad_wlan_info.nwdad_key)); } static nwam_error_t nwamd_door_req_action(nwamd_door_arg_t *req, ucred_t *ucr, struct passwd *pwd) { char name[NWAM_MAX_NAME_LEN]; char parent[NWAM_MAX_NAME_LEN]; nwam_action_t action = req->nwda_data.nwdad_object_action.nwdad_action; nwam_object_type_t object_type = req->nwda_data.nwdad_object_action.nwdad_object_type; char *obj_type_str = (char *)nwam_object_type_to_string(object_type); nwam_error_t err; /* Check for name, parent overrun */ if (strlcpy(name, req->nwda_data.nwdad_object_action.nwdad_name, sizeof (name)) == NWAM_MAX_NAME_LEN || strlcpy(parent, req->nwda_data.nwdad_object_action.nwdad_parent, sizeof (parent)) == NWAM_MAX_NAME_LEN) return (NWAM_INVALID_ARG); /* * Check authorizations against actions. * - ENABLE/DISABLE requires SELECT auth * - ADD/DESTROY/REFRESH on Known WLANs requires WLAN auth * - ADD/DESTROY on other objects requires WRITE auth * - REFRESH on other objects requires either WRITE or SELECT auth */ if (action == NWAM_ACTION_ENABLE || action == NWAM_ACTION_DISABLE) { if (chkauthattr(AUTOCONF_SELECT_AUTH, pwd->pw_name) == 0) { nwam_record_audit_event(ucr, action == NWAM_ACTION_ENABLE ? ADT_nwam_enable : ADT_nwam_disable, name, obj_type_str, ADT_FAILURE, ADT_FAIL_VALUE_AUTH); nlog(LOG_ERR, "nwamd_door_req_action: " "need %s for %s action", AUTOCONF_SELECT_AUTH, nwam_action_to_string(action)); return (NWAM_PERMISSION_DENIED); } } else if (object_type == NWAM_OBJECT_TYPE_KNOWN_WLAN) { if (chkauthattr(AUTOCONF_WLAN_AUTH, pwd->pw_name) == 0) { nlog(LOG_ERR, "nwamd_door_req_action: " "need %s for %s action on Known WLAN", AUTOCONF_WLAN_AUTH, nwam_action_to_string(action)); return (NWAM_PERMISSION_DENIED); } } else if (action == NWAM_ACTION_ADD || action == NWAM_ACTION_DESTROY) { if (chkauthattr(AUTOCONF_WRITE_AUTH, pwd->pw_name) == 0) { nlog(LOG_ERR, "nwamd_door_req_action: " "need %s for %s action", AUTOCONF_WRITE_AUTH, nwam_action_to_string(action)); return (NWAM_PERMISSION_DENIED); } } else if (action == NWAM_ACTION_REFRESH) { if (chkauthattr(AUTOCONF_WRITE_AUTH, pwd->pw_name) == 0 && chkauthattr(AUTOCONF_SELECT_AUTH, pwd->pw_name) == 0) { nlog(LOG_ERR, "nwamd_door_req_action: " "need either %s or %s for %s action", AUTOCONF_WRITE_AUTH, AUTOCONF_SELECT_AUTH, nwam_action_to_string(action)); return (NWAM_PERMISSION_DENIED); } } else { nlog(LOG_ERR, "nwamd_door_req_action: received unknown " "action %d (%s)", action, nwam_action_to_string(action)); return (NWAM_INVALID_ARG); } switch (action) { case NWAM_ACTION_ENABLE: case NWAM_ACTION_DISABLE: nwam_record_audit_event(ucr, action == NWAM_ACTION_ENABLE ? ADT_nwam_enable : ADT_nwam_disable, name, obj_type_str, ADT_SUCCESS, ADT_SUCCESS); nlog(LOG_DEBUG, "nwamd_door_req_action: %s %s", action == NWAM_ACTION_ENABLE ? "enabling" : "disabling", name); switch (object_type) { case NWAM_OBJECT_TYPE_ENM: err = nwamd_enm_action(name, action); break; case NWAM_OBJECT_TYPE_LOC: err = nwamd_loc_action(name, action); break; case NWAM_OBJECT_TYPE_NCU: err = nwamd_ncu_action(name, parent, action); break; case NWAM_OBJECT_TYPE_NCP: if (action == NWAM_ACTION_DISABLE) { nlog(LOG_ERR, "nwamd_door_req_action: " "NCPs cannot be disabled"); err = NWAM_INVALID_ARG; } else { err = nwamd_ncp_action(name, action); } break; default: nlog(LOG_ERR, "nwamd_door_req_action: received invalid " "object type %d (%s)", object_type, nwam_object_type_to_string(object_type)); return (NWAM_INVALID_ARG); } break; case NWAM_ACTION_ADD: case NWAM_ACTION_REFRESH: /* * Called whenever an object is committed in the library. * Reread that committed object into nwamd. */ nlog(LOG_DEBUG, "door_switch: refreshing %s", name); switch (object_type) { case NWAM_OBJECT_TYPE_ENM: err = nwamd_enm_action(name, action); break; case NWAM_OBJECT_TYPE_LOC: err = nwamd_loc_action(name, action); break; case NWAM_OBJECT_TYPE_KNOWN_WLAN: err = nwamd_known_wlan_action(name, action); break; case NWAM_OBJECT_TYPE_NCU: err = nwamd_ncu_action(name, parent, action); break; case NWAM_OBJECT_TYPE_NCP: err = nwamd_ncp_action(name, action); break; default: nlog(LOG_ERR, "nwamd_door_req_action: received invalid " "object type %d (%s)", object_type, nwam_object_type_to_string(object_type)); err = NWAM_INVALID_ARG; break; } break; case NWAM_ACTION_DESTROY: /* Object was destroyed, remove from nwamd */ nlog(LOG_DEBUG, "door_switch: removing %s", name); switch (object_type) { case NWAM_OBJECT_TYPE_ENM: err = nwamd_enm_action(name, NWAM_ACTION_DESTROY); break; case NWAM_OBJECT_TYPE_LOC: err = nwamd_loc_action(name, NWAM_ACTION_DESTROY); break; case NWAM_OBJECT_TYPE_KNOWN_WLAN: err = nwamd_known_wlan_action(name, NWAM_ACTION_DESTROY); break; case NWAM_OBJECT_TYPE_NCU: err = nwamd_ncu_action(name, parent, NWAM_ACTION_DESTROY); break; case NWAM_OBJECT_TYPE_NCP: (void) pthread_mutex_lock(&active_ncp_mutex); if (strcmp(name, active_ncp) == 0) { nlog(LOG_ERR, "nwamd_door_req_action: %s is " "active, cannot destroy", parent); err = NWAM_ENTITY_IN_USE; } else { err = nwamd_ncp_action(name, NWAM_ACTION_DESTROY); } (void) pthread_mutex_unlock(&active_ncp_mutex); break; default: nlog(LOG_ERR, "nwamd_door_req_action: received invalid " "object type %d (%s)", object_type, nwam_object_type_to_string(object_type)); err = NWAM_INVALID_ARG; break; } break; default: nlog(LOG_ERR, "nwamd_door_req_action: received unknown " "action %d (%s)", action, nwam_action_to_string(action)); err = NWAM_INVALID_ARG; break; } if (err == NWAM_SUCCESS) { /* * At this point, we've successfully carried out an action. * Configuration may have changed, so we need to recheck * conditions, however we want to avoid a flurry of condition * check events, so we enqueue a triggered condition check * if none is due in the next few seconds. */ nwamd_create_triggered_condition_check_event(NEXT_FEW_SECONDS); } else { nlog(LOG_ERR, "nwamd_door_req_action: could not carry out " "%s action on %s: %s", nwam_action_to_string(action), name, nwam_strerror(err)); } return (err); } /* ARGSUSED */ static nwam_error_t nwamd_door_req_state(nwamd_door_arg_t *req, ucred_t *ucr, struct passwd *pwd) { char name[NWAM_MAX_NAME_LEN]; nwamd_object_t obj; nwam_object_type_t object_type = req->nwda_data.nwdad_object_state.nwdad_object_type; boolean_t is_active = B_FALSE; /* Check for name, parent overrun */ if (strlcpy(name, req->nwda_data.nwdad_object_state.nwdad_name, sizeof (name)) == NWAM_MAX_NAME_LEN) return (NWAM_INVALID_ARG); switch (object_type) { case NWAM_OBJECT_TYPE_NCP: (void) pthread_mutex_lock(&active_ncp_mutex); is_active = (strcmp(active_ncp, name) == 0); (void) pthread_mutex_unlock(&active_ncp_mutex); if (is_active) { req->nwda_data.nwdad_object_state.nwdad_state = NWAM_STATE_ONLINE; req->nwda_data.nwdad_object_state. nwdad_aux_state = NWAM_AUX_STATE_ACTIVE; nlog(LOG_DEBUG, "nwamd_door_req_state: NCP %s is active", name); } else { req->nwda_data.nwdad_object_state.nwdad_state = NWAM_STATE_DISABLED; req->nwda_data.nwdad_object_state. nwdad_aux_state = NWAM_AUX_STATE_MANUAL_DISABLE; nlog(LOG_DEBUG, "nwamd_door_req_state: " "NCP %s is inactive", name); } break; case NWAM_OBJECT_TYPE_LOC: case NWAM_OBJECT_TYPE_NCU: case NWAM_OBJECT_TYPE_ENM: obj = nwamd_object_find(object_type, name); if (obj == NULL) { nlog(LOG_ERR, "nwamd_door_req_state: %s %s not found", nwam_object_type_to_string(object_type), name); return (NWAM_ENTITY_NOT_FOUND); } nlog(LOG_DEBUG, "nwamd_door_req_state: %s %s is %s", nwam_object_type_to_string(object_type), name, nwam_state_to_string(obj->nwamd_object_state)); req->nwda_data.nwdad_object_state.nwdad_state = obj->nwamd_object_state; req->nwda_data.nwdad_object_state.nwdad_aux_state = obj->nwamd_object_aux_state; nwamd_object_release(obj); break; default: nlog(LOG_ERR, "nwamd_door_req_state: received invalid " "object type %d (%s)", object_type, nwam_object_type_to_string(object_type)); req->nwda_status = NWAM_REQUEST_STATUS_UNKNOWN; return (NWAM_INVALID_ARG); } return (NWAM_SUCCESS); } /* ARGSUSED */ static nwam_error_t nwamd_door_req_priority_group(nwamd_door_arg_t *req, ucred_t *ucr, struct passwd *pwd) { (void) pthread_mutex_lock(&active_ncp_mutex); nlog(LOG_DEBUG, "nwamd_door_req_priority_group: " "retrieving active priority-group: %d", current_ncu_priority_group); req->nwda_data.nwdad_priority_group_info.nwdad_priority = current_ncu_priority_group; (void) pthread_mutex_unlock(&active_ncp_mutex); return (NWAM_SUCCESS); } /* ARGSUSED */ static void nwamd_door_switch(void *cookie, char *argp, size_t arg_size, door_desc_t *dp, uint_t n_desc) { nwamd_door_arg_t *req; ucred_t *ucr = NULL; uid_t uid; struct passwd *pwd = NULL; boolean_t found = B_FALSE; int i; /* LINTED E_BAD_PTR_CAST_ALIGN */ req = (nwamd_door_arg_t *)argp; req->nwda_error = NWAM_SUCCESS; if (door_ucred(&ucr) != 0) { nlog(LOG_ERR, "nwamd_door_switch: door_ucred failed: %s", strerror(errno)); req->nwda_error = NWAM_ERROR_INTERNAL; req->nwda_status = NWAM_REQUEST_STATUS_FAILED; goto done; } uid = ucred_getruid(ucr); if ((pwd = getpwuid(uid)) == NULL) { nlog(LOG_ERR, "nwamd_door_switch: getpwuid failed: %s", strerror(errno)); endpwent(); req->nwda_error = NWAM_ERROR_INTERNAL; req->nwda_status = NWAM_REQUEST_STATUS_FAILED; goto done; } /* * Find door request entry in table, check auths and call the function * handling the request. */ for (i = 0; i < sizeof (door_req_table) / sizeof (struct nwamd_door_req_entry); i++) { if (req->nwda_type != door_req_table[i].ndre_type) continue; found = B_TRUE; if (door_req_table[i].ndre_auth != NULL && chkauthattr(door_req_table[i].ndre_auth, pwd->pw_name) == 0) { nlog(LOG_ERR, "nwamd_door_switch: need %s for request type %d", door_req_table[i].ndre_auth, req->nwda_type); req->nwda_error = NWAM_PERMISSION_DENIED; break; } req->nwda_error = door_req_table[i].ndre_fn(req, ucr, pwd); break; } if (!found) { nlog(LOG_ERR, "nwamd_door_switch: received unknown request type %d", req->nwda_type); req->nwda_status = NWAM_REQUEST_STATUS_UNKNOWN; } else { if (req->nwda_error == NWAM_SUCCESS) req->nwda_status = NWAM_REQUEST_STATUS_OK; else req->nwda_status = NWAM_REQUEST_STATUS_FAILED; } done: ucred_free(ucr); endpwent(); if (door_return((char *)req, sizeof (nwamd_door_arg_t), NULL, 0) == -1) { nlog(LOG_ERR, "door_switch: type %d door_return failed: %s", req->nwda_type, strerror(errno)); } } /* * We initialize the nwamd door here. Failure to have this happen is critical * to the daemon so we log a message and pass up notice to the caller who * will most likely abort trying to start. This routine is meant to only * be called once. */ void nwamd_door_init(void) { const int door_mode = 0644; struct stat buf; if ((doorfd = door_create(nwamd_door_switch, NULL, DOOR_NO_CANCEL | DOOR_REFUSE_DESC)) == -1) pfail("Unable to create door: %s", strerror(errno)); if (stat(NWAM_DOOR, &buf) < 0) { int nwam_door_fd; if ((nwam_door_fd = creat(NWAM_DOOR, door_mode)) < 0) { int err = errno; (void) door_revoke(doorfd); doorfd = -1; pfail("Couldn't create door: %s", strerror(err)); } (void) close(nwam_door_fd); } else { if (buf.st_mode != door_mode) { if (chmod(NWAM_DOOR, door_mode) == -1) { nlog(LOG_ERR, "couldn't change mode of %s: %s", NWAM_DOOR, strerror(errno)); } } } /* cleanup anything hanging around from a previous invocation */ (void) fdetach(NWAM_DOOR); /* Place our door in the file system so that others can find us. */ if (fattach(doorfd, NWAM_DOOR) < 0) { int err = errno; (void) door_revoke(doorfd); doorfd = -1; pfail("Couldn't attach door: %s", strerror(err)); } } void nwamd_door_fini(void) { if (doorfd != -1) { nlog(LOG_DEBUG, "nwamd_door_fini: closing door"); (void) door_revoke(doorfd); doorfd = -1; } (void) unlink(NWAM_DOOR); }