root/usr/src/cmd/cmd-inet/lib/nwamd/door_if.c
/*
 * 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 <auth_attr.h>
#include <auth_list.h>
#include <bsm/adt.h>
#include <bsm/adt_event.h>
#include <door.h>
#include <errno.h>
#include <fcntl.h>
#include <libnwam_priv.h>
#include <libuutil.h>
#include <pthread.h>
#include <pwd.h>
#include <stdlib.h>
#include <sys/stat.h>

#include <sys/mman.h>
#include <syslog.h>
#include <unistd.h>

#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);
}