OpenSolaris_b135/lib/libnwam/common/libnwam_backend.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <assert.h>
#include <auth_attr.h>
#include <auth_list.h>
#include <bsm/adt.h>
#include <bsm/adt_event.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <pwd.h>
#include <secdb.h>
#include <stdlib.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <strings.h>
#include <unistd.h>

#include "libnwam_impl.h"
#include <libnwam_priv.h>
#include <libnwam.h>

/*
 * Communicate with and implement library backend (running in netcfgd) to
 * retrieve or change NWAM configuration.
 */

static int backend_door_client_fd = -1;

/*
 * Check if uid has proper auths.  flags is used to check auths for
 * enable/disable of profiles and manipulation of Known WLANs.
 */
static nwam_error_t
nwam_check_auths(uid_t uid, boolean_t write, uint64_t flags)
{
	struct passwd *pwd;
	nwam_error_t err = NWAM_SUCCESS;

	if ((pwd = getpwuid(uid)) == NULL) {
		endpwent();
		return (NWAM_PERMISSION_DENIED);
	}

	if (flags & NWAM_FLAG_ENTITY_ENABLE) {
		/* Enabling/disabling profile - need SELECT auth */
		if (chkauthattr(AUTOCONF_SELECT_AUTH, pwd->pw_name) == 0)
			err = NWAM_PERMISSION_DENIED;

	} else if (flags & NWAM_FLAG_ENTITY_KNOWN_WLAN) {
		/* Known WLAN activity - need WLAN auth */
		if (chkauthattr(AUTOCONF_WLAN_AUTH, pwd->pw_name) == 0)
			err = NWAM_PERMISSION_DENIED;

	} else {
		/*
		 * First, check for WRITE, since it implies READ.  If this
		 * auth is not present, and write is true, fail, otherwise
		 * check for READ.
		 */
		if (chkauthattr(AUTOCONF_WRITE_AUTH, pwd->pw_name) == 0) {
			if (write) {
				err = NWAM_PERMISSION_DENIED;
			} else {
				if (chkauthattr(AUTOCONF_READ_AUTH,
				    pwd->pw_name) == 0)
					err = NWAM_PERMISSION_DENIED;
			}
		}
	}

	endpwent();
	return (err);
}

static nwam_error_t
nwam_create_backend_door_arg(nwam_backend_door_cmd_t cmd,
    const char *dbname, const char *objname, uint64_t flags,
    void *obj, nwam_backend_door_arg_t *arg)
{
	nwam_error_t err;
	size_t datalen = 0;
	caddr_t dataptr;

	switch (cmd) {
	case NWAM_BACKEND_DOOR_CMD_READ_REQ:
		/*
		 * For a read request,  we want the full buffer to be
		 * available for the backend door to write to.
		 */
		datalen = NWAM_BACKEND_DOOR_ARG_SIZE;
		break;

	case NWAM_BACKEND_DOOR_CMD_UPDATE_REQ:
		/*
		 * An update request may either specify an object list
		 * (which we pack into the buffer immediately after the
		 * backend door request) or may not specify an object
		 * (signifying a request to create the container of the
		 * object).
		 */
		if (obj == NULL) {
			datalen = 0;
			break;
		}
		/* Data immediately follows the descriptor */
		dataptr = (caddr_t)arg + sizeof (nwam_backend_door_arg_t);
		datalen = NWAM_BACKEND_DOOR_ARG_SIZE;
		/* pack object list for update request,  adjusting datalen */
		if ((err = nwam_pack_object_list(obj, (char **)&dataptr,
		    &datalen)) != NWAM_SUCCESS)
			return (err);
		break;

	case NWAM_BACKEND_DOOR_CMD_REMOVE_REQ:
		/* A remove request has no associated object list. */
		datalen = 0;
		break;

	default:
		return (NWAM_INVALID_ARG);
	}

	arg->nwbda_cmd = cmd;
	arg->nwbda_flags = flags;
	arg->nwbda_datalen = datalen;
	arg->nwbda_result = NWAM_SUCCESS;

	if (dbname != NULL)
		(void) strlcpy(arg->nwbda_dbname, dbname, MAXPATHLEN);
	else
		arg->nwbda_dbname[0] = '\0';

	if (objname != NULL)
		(void) strlcpy(arg->nwbda_object, objname, NWAM_MAX_NAME_LEN);
	else
		arg->nwbda_object[0] = '\0';

	return (NWAM_SUCCESS);
}

/*
 * If the arg datalen is non-zero,  unpack the object list associated with
 * the backend door argument.
 */
static nwam_error_t
nwam_read_object_from_backend_door_arg(nwam_backend_door_arg_t *arg,
    char *dbname, char *name, void *objp)
{
	nwam_error_t err;
	caddr_t dataptr = (caddr_t)arg + sizeof (nwam_backend_door_arg_t);

	if (arg->nwbda_result != NWAM_SUCCESS)
		return (arg->nwbda_result);

	if (arg->nwbda_datalen > 0) {
		if ((err = nwam_unpack_object_list((char *)dataptr,
		    arg->nwbda_datalen, objp)) != NWAM_SUCCESS)
			return (err);
	} else {
		*((char **)objp) = NULL;
	}

	/*
	 * If "dbname" and "name" are non-NULL, copy in the actual dbname
	 * and name values from the door arg since both may have been changed
	 * from case-insensitive to case-sensitive matches.  They will be the
	 * same length as they only differ in case.
	 */
	if (dbname != NULL && strcmp(dbname, arg->nwbda_dbname) != 0)
		(void) strlcpy(dbname, arg->nwbda_dbname, strlen(dbname) + 1);
	if (name != NULL && strcmp(name, arg->nwbda_object) != 0)
		(void) strlcpy(name, arg->nwbda_object, strlen(name) + 1);

	return (NWAM_SUCCESS);
}

/* ARGSUSED */
void
nwam_backend_door_server(void *cookie, char *arg, size_t arg_size,
    door_desc_t *dp, uint_t ndesc)
{
	/* LINTED: alignment */
	nwam_backend_door_arg_t *req = (nwam_backend_door_arg_t *)arg;
	nwam_error_t err;
	void *obj, *newobj = NULL;
	ucred_t *ucr = NULL;
	uid_t uid;
	boolean_t write = B_TRUE;

	/* Check arg size */
	if (arg_size < sizeof (nwam_backend_door_arg_t)) {
		req->nwbda_result = NWAM_INVALID_ARG;
		(void) door_return((char *)req,
		    sizeof (nwam_backend_door_arg_t), NULL, 0);
	}

	if (door_ucred(&ucr) != 0) {
		req->nwbda_result = NWAM_ERROR_INTERNAL;
		(void) door_return((char *)req, arg_size, NULL, 0);
	}

	/* Check auths */
	uid = ucred_getruid(ucr);

	if (req->nwbda_cmd == NWAM_BACKEND_DOOR_CMD_READ_REQ)
		write = B_FALSE;
	if ((err = nwam_check_auths(uid, write, req->nwbda_flags))
	    != NWAM_SUCCESS) {
		if (write) {
			nwam_record_audit_event(ucr,
			    req->nwbda_cmd == NWAM_BACKEND_DOOR_CMD_UPDATE_REQ ?
			    ADT_netcfg_update : ADT_netcfg_remove,
			    (char *)req->nwbda_object,
			    (char *)req->nwbda_dbname, ADT_FAILURE,
			    ADT_FAIL_VALUE_AUTH);
		}
		req->nwbda_result = err;
		goto door_return;
	}

	switch (req->nwbda_cmd) {
	case NWAM_BACKEND_DOOR_CMD_READ_REQ:
		if ((req->nwbda_result = nwam_read_object_from_files_backend
		    (strlen(req->nwbda_dbname) > 0 ? req->nwbda_dbname : NULL,
		    strlen(req->nwbda_object) > 0 ? req->nwbda_object : NULL,
		    req->nwbda_flags, &newobj)) != NWAM_SUCCESS) {
			break;
		}
		if (newobj != NULL) {
			size_t datalen = arg_size -
			    sizeof (nwam_backend_door_arg_t);
			caddr_t dataptr = (caddr_t)req +
			    sizeof (nwam_backend_door_arg_t);

			if ((req->nwbda_result = nwam_pack_object_list(newobj,
			    (char **)&dataptr, &datalen)) != NWAM_SUCCESS)
				req->nwbda_datalen = 0;
			else
				req->nwbda_datalen = datalen;
			nwam_free_object_list(newobj);
		} else {
			req->nwbda_datalen = 0;
		}
		break;

	case NWAM_BACKEND_DOOR_CMD_UPDATE_REQ:
		if (req->nwbda_datalen == 0) {
			obj = NULL;
		} else {
			if ((req->nwbda_result =
			    nwam_read_object_from_backend_door_arg
			    (req, NULL, NULL, &obj)) != NWAM_SUCCESS)
				break;
		}
		req->nwbda_result = nwam_update_object_in_files_backend(
		    req->nwbda_dbname[0] == 0 ? NULL : req->nwbda_dbname,
		    req->nwbda_object[0] == 0 ? NULL : req->nwbda_object,
		    req->nwbda_flags, obj);
		nwam_free_object_list(obj);
		if (req->nwbda_result == NWAM_SUCCESS) {
			req->nwbda_datalen = 0;
			nwam_record_audit_event(ucr, ADT_netcfg_update,
			    (char *)req->nwbda_object,
			    (char *)req->nwbda_dbname, ADT_SUCCESS,
			    ADT_SUCCESS);
		}
		break;

	case NWAM_BACKEND_DOOR_CMD_REMOVE_REQ:
		req->nwbda_result = nwam_remove_object_from_files_backend
		    (strlen(req->nwbda_dbname) > 0 ? req->nwbda_dbname : NULL,
		    strlen(req->nwbda_object) > 0 ? req->nwbda_object : NULL,
		    req->nwbda_flags);
		if (req->nwbda_result == NWAM_SUCCESS) {
			nwam_record_audit_event(ucr, ADT_netcfg_update,
			    (char *)req->nwbda_object,
			    (char *)req->nwbda_dbname, ADT_SUCCESS,
			    ADT_SUCCESS);
		}
		break;

	default:
		req->nwbda_result = NWAM_INVALID_ARG;
		break;
	}

door_return:
	ucred_free(ucr);

	(void) door_return((char *)req, arg_size, NULL, 0);
}

static int backend_door_fd = -1;

void
nwam_backend_fini(void)
{
	if (backend_door_fd != -1) {
		(void) door_revoke(backend_door_fd);
		backend_door_fd = -1;
	}
	(void) unlink(NWAM_BACKEND_DOOR_FILE);
}

nwam_error_t
nwam_backend_init(void)
{
	int did;
	struct stat statbuf;

	/* Create the door directory if it doesn't already exist */
	if (stat(NWAM_DOOR_DIR, &statbuf) < 0) {
		if (mkdir(NWAM_DOOR_DIR, (mode_t)0755) < 0)
			return (NWAM_ERROR_BACKEND_INIT);
	} else {
		if ((statbuf.st_mode & S_IFMT) != S_IFDIR)
			return (NWAM_ERROR_BACKEND_INIT);
	}

	if (chmod(NWAM_DOOR_DIR, 0755) < 0 ||
	    chown(NWAM_DOOR_DIR, UID_NETADM, GID_NETADM) < 0)
		return (NWAM_ERROR_BACKEND_INIT);

	/* Do a low-overhead "touch" on the file that will be the door node. */
	did = open(NWAM_BACKEND_DOOR_FILE,
	    O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW | O_NONBLOCK,
	    S_IRUSR | S_IRGRP | S_IROTH);

	if (did != -1)
		(void) close(did);
	else if (errno != EEXIST)
		return (NWAM_ERROR_BACKEND_INIT);

	/* Create the door. */
	backend_door_fd = door_create(nwam_backend_door_server, NULL,
	    DOOR_REFUSE_DESC);
	if (backend_door_fd == -1)
		return (NWAM_ERROR_BACKEND_INIT);

	/* Attach the door to the file. */
	(void) fdetach(NWAM_BACKEND_DOOR_FILE);
	if (fattach(backend_door_fd, NWAM_BACKEND_DOOR_FILE) == -1) {
		(void) door_revoke(backend_door_fd);
		return (NWAM_ERROR_BACKEND_INIT);
	}

	return (NWAM_SUCCESS);
}

static nwam_error_t
nwam_backend_door_call(nwam_backend_door_cmd_t cmd, char *dbname,
    char *objname, uint64_t flags, void *obj)
{
	uchar_t reqbuf[NWAM_BACKEND_DOOR_ARG_SIZE];
	/* LINTED: alignment */
	nwam_backend_door_arg_t *req = (nwam_backend_door_arg_t *)&reqbuf;
	nwam_error_t err, reserr;

	if ((err = nwam_create_backend_door_arg(cmd, dbname, objname, flags,
	    obj, req)) != NWAM_SUCCESS)
		return (err);

	if (nwam_make_door_call(NWAM_BACKEND_DOOR_FILE, &backend_door_client_fd,
	    req, sizeof (reqbuf)) != 0)
		return (NWAM_ERROR_BIND);

	reserr = req->nwbda_result;

	if (cmd == NWAM_BACKEND_DOOR_CMD_READ_REQ) {
		err = nwam_read_object_from_backend_door_arg(req, dbname,
		    objname, obj);
	}

	return (err == NWAM_SUCCESS ? reserr : err);
}

/*
 * Read object specified by objname from backend dbname, retrieving an object
 * list representation.
 *
 * If dbname is NULL, obj is a list of string arrays consisting of the list
 * of backend dbnames.
 *
 * If objname is NULL, read all objects in the specified dbname and create
 * an object list containing a string array which represents each object.
 *
 * Otherwise obj will point to a list of the properties for the object
 * specified by objname in the backend dbname.
 */
/* ARGSUSED2 */
nwam_error_t
nwam_read_object_from_backend(char *dbname, char *objname,
    uint64_t flags, void *obj)
{
	nwam_error_t err = nwam_check_auths(getuid(), B_FALSE, flags);

	if (err != NWAM_SUCCESS)
		return (err);

	return (nwam_backend_door_call(NWAM_BACKEND_DOOR_CMD_READ_REQ,
	    dbname, objname, flags, obj));
}

/*
 * Read in all objects from backend dbname and update object corresponding
 * to objname with properties recorded in proplist, writing the results to
 * the backend dbname.
 */
nwam_error_t
nwam_update_object_in_backend(char *dbname, char *objname,
    uint64_t flags, void *obj)
{
	nwam_error_t err = nwam_check_auths(getuid(), B_TRUE, flags);

	if (err != NWAM_SUCCESS)
		return (err);

	return (nwam_backend_door_call(NWAM_BACKEND_DOOR_CMD_UPDATE_REQ,
	    dbname, objname, flags, obj));
}

/*
 * Remove specified object from backend by reading in the list of objects,
 * removing objname and writing the remainder.
 *
 * If objname is NULL, remove the backend dbname.
 */
nwam_error_t
nwam_remove_object_from_backend(char *dbname, char *objname, uint64_t flags)
{
	nwam_error_t err = nwam_check_auths(getuid(), B_TRUE, flags);

	if (err != NWAM_SUCCESS)
		return (err);

	return (nwam_backend_door_call(NWAM_BACKEND_DOOR_CMD_REMOVE_REQ,
	    dbname, objname, flags, NULL));
}