/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libnwam_impl.h" #include #include /* * 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)); }