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 2010 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <assert.h> 28 #include <auth_attr.h> 29 #include <auth_list.h> 30 #include <bsm/adt.h> 31 #include <bsm/adt_event.h> 32 #include <ctype.h> 33 #include <errno.h> 34 #include <fcntl.h> 35 #include <libgen.h> 36 #include <pwd.h> 37 #include <secdb.h> 38 #include <stdlib.h> 39 #include <sys/param.h> 40 #include <sys/types.h> 41 #include <sys/stat.h> 42 #include <stdio.h> 43 #include <strings.h> 44 #include <unistd.h> 45 46 #include "libnwam_impl.h" 47 #include <libnwam_priv.h> 48 #include <libnwam.h> 49 50 /* 51 * Communicate with and implement library backend (running in netcfgd) to 52 * retrieve or change NWAM configuration. 53 */ 54 55 static int backend_door_client_fd = -1; 56 57 /* 58 * Check if uid has proper auths. flags is used to check auths for 59 * enable/disable of profiles and manipulation of Known WLANs. 60 */ 61 static nwam_error_t 62 nwam_check_auths(uid_t uid, boolean_t write, uint64_t flags) 63 { 64 struct passwd *pwd; 65 nwam_error_t err = NWAM_SUCCESS; 66 67 if ((pwd = getpwuid(uid)) == NULL) { 68 endpwent(); 69 return (NWAM_PERMISSION_DENIED); 70 } 71 72 if (flags & NWAM_FLAG_ENTITY_ENABLE) { 73 /* Enabling/disabling profile - need SELECT auth */ 74 if (chkauthattr(AUTOCONF_SELECT_AUTH, pwd->pw_name) == 0) 75 err = NWAM_PERMISSION_DENIED; 76 77 } else if (flags & NWAM_FLAG_ENTITY_KNOWN_WLAN) { 78 /* Known WLAN activity - need WLAN auth */ 79 if (chkauthattr(AUTOCONF_WLAN_AUTH, pwd->pw_name) == 0) 80 err = NWAM_PERMISSION_DENIED; 81 82 } else { 83 /* 84 * First, check for WRITE, since it implies READ. If this 85 * auth is not present, and write is true, fail, otherwise 86 * check for READ. 87 */ 88 if (chkauthattr(AUTOCONF_WRITE_AUTH, pwd->pw_name) == 0) { 89 if (write) { 90 err = NWAM_PERMISSION_DENIED; 91 } else { 92 if (chkauthattr(AUTOCONF_READ_AUTH, 93 pwd->pw_name) == 0) 94 err = NWAM_PERMISSION_DENIED; 95 } 96 } 97 } 98 99 endpwent(); 100 return (err); 101 } 102 103 static nwam_error_t 104 nwam_create_backend_door_arg(nwam_backend_door_cmd_t cmd, 105 const char *dbname, const char *objname, uint64_t flags, 106 void *obj, nwam_backend_door_arg_t *arg) 107 { 108 nwam_error_t err; 109 size_t datalen = 0; 110 caddr_t dataptr; 111 112 switch (cmd) { 113 case NWAM_BACKEND_DOOR_CMD_READ_REQ: 114 /* 115 * For a read request, we want the full buffer to be 116 * available for the backend door to write to. 117 */ 118 datalen = NWAM_BACKEND_DOOR_ARG_SIZE; 119 break; 120 121 case NWAM_BACKEND_DOOR_CMD_UPDATE_REQ: 122 /* 123 * An update request may either specify an object list 124 * (which we pack into the buffer immediately after the 125 * backend door request) or may not specify an object 126 * (signifying a request to create the container of the 127 * object). 128 */ 129 if (obj == NULL) { 130 datalen = 0; 131 break; 132 } 133 /* Data immediately follows the descriptor */ 134 dataptr = (caddr_t)arg + sizeof (nwam_backend_door_arg_t); 135 datalen = NWAM_BACKEND_DOOR_ARG_SIZE; 136 /* pack object list for update request, adjusting datalen */ 137 if ((err = nwam_pack_object_list(obj, (char **)&dataptr, 138 &datalen)) != NWAM_SUCCESS) 139 return (err); 140 break; 141 142 case NWAM_BACKEND_DOOR_CMD_REMOVE_REQ: 143 /* A remove request has no associated object list. */ 144 datalen = 0; 145 break; 146 147 default: 148 return (NWAM_INVALID_ARG); 149 } 150 151 arg->nwbda_cmd = cmd; 152 arg->nwbda_flags = flags; 153 arg->nwbda_datalen = datalen; 154 arg->nwbda_result = NWAM_SUCCESS; 155 156 if (dbname != NULL) 157 (void) strlcpy(arg->nwbda_dbname, dbname, MAXPATHLEN); 158 else 159 arg->nwbda_dbname[0] = '\0'; 160 161 if (objname != NULL) 162 (void) strlcpy(arg->nwbda_object, objname, NWAM_MAX_NAME_LEN); 163 else 164 arg->nwbda_object[0] = '\0'; 165 166 return (NWAM_SUCCESS); 167 } 168 169 /* 170 * If the arg datalen is non-zero, unpack the object list associated with 171 * the backend door argument. 172 */ 173 static nwam_error_t 174 nwam_read_object_from_backend_door_arg(nwam_backend_door_arg_t *arg, 175 char *dbname, char *name, void *objp) 176 { 177 nwam_error_t err; 178 caddr_t dataptr = (caddr_t)arg + sizeof (nwam_backend_door_arg_t); 179 180 if (arg->nwbda_result != NWAM_SUCCESS) 181 return (arg->nwbda_result); 182 183 if (arg->nwbda_datalen > 0) { 184 if ((err = nwam_unpack_object_list((char *)dataptr, 185 arg->nwbda_datalen, objp)) != NWAM_SUCCESS) 186 return (err); 187 } else { 188 *((char **)objp) = NULL; 189 } 190 191 /* 192 * If "dbname" and "name" are non-NULL, copy in the actual dbname 193 * and name values from the door arg since both may have been changed 194 * from case-insensitive to case-sensitive matches. They will be the 195 * same length as they only differ in case. 196 */ 197 if (dbname != NULL && strcmp(dbname, arg->nwbda_dbname) != 0) 198 (void) strlcpy(dbname, arg->nwbda_dbname, strlen(dbname) + 1); 199 if (name != NULL && strcmp(name, arg->nwbda_object) != 0) 200 (void) strlcpy(name, arg->nwbda_object, strlen(name) + 1); 201 202 return (NWAM_SUCCESS); 203 } 204 205 /* ARGSUSED */ 206 void 207 nwam_backend_door_server(void *cookie, char *arg, size_t arg_size, 208 door_desc_t *dp, uint_t ndesc) 209 { 210 /* LINTED: alignment */ 211 nwam_backend_door_arg_t *req = (nwam_backend_door_arg_t *)arg; 212 nwam_error_t err; 213 void *obj, *newobj = NULL; 214 ucred_t *ucr = NULL; 215 uid_t uid; 216 boolean_t write = B_TRUE; 217 218 /* Check arg size */ 219 if (arg_size < sizeof (nwam_backend_door_arg_t)) { 220 req->nwbda_result = NWAM_INVALID_ARG; 221 (void) door_return((char *)req, 222 sizeof (nwam_backend_door_arg_t), NULL, 0); 223 } 224 225 if (door_ucred(&ucr) != 0) { 226 req->nwbda_result = NWAM_ERROR_INTERNAL; 227 (void) door_return((char *)req, arg_size, NULL, 0); 228 } 229 230 /* Check auths */ 231 uid = ucred_getruid(ucr); 232 233 if (req->nwbda_cmd == NWAM_BACKEND_DOOR_CMD_READ_REQ) 234 write = B_FALSE; 235 if ((err = nwam_check_auths(uid, write, req->nwbda_flags)) 236 != NWAM_SUCCESS) { 237 if (write) { 238 nwam_record_audit_event(ucr, 239 req->nwbda_cmd == NWAM_BACKEND_DOOR_CMD_UPDATE_REQ ? 240 ADT_netcfg_update : ADT_netcfg_remove, 241 (char *)req->nwbda_object, 242 (char *)req->nwbda_dbname, ADT_FAILURE, 243 ADT_FAIL_VALUE_AUTH); 244 } 245 req->nwbda_result = err; 246 goto door_return; 247 } 248 249 switch (req->nwbda_cmd) { 250 case NWAM_BACKEND_DOOR_CMD_READ_REQ: 251 if ((req->nwbda_result = nwam_read_object_from_files_backend 252 (strlen(req->nwbda_dbname) > 0 ? req->nwbda_dbname : NULL, 253 strlen(req->nwbda_object) > 0 ? req->nwbda_object : NULL, 254 req->nwbda_flags, &newobj)) != NWAM_SUCCESS) { 255 break; 256 } 257 if (newobj != NULL) { 258 size_t datalen = arg_size - 259 sizeof (nwam_backend_door_arg_t); 260 caddr_t dataptr = (caddr_t)req + 261 sizeof (nwam_backend_door_arg_t); 262 263 if ((req->nwbda_result = nwam_pack_object_list(newobj, 264 (char **)&dataptr, &datalen)) != NWAM_SUCCESS) 265 req->nwbda_datalen = 0; 266 else 267 req->nwbda_datalen = datalen; 268 nwam_free_object_list(newobj); 269 } else { 270 req->nwbda_datalen = 0; 271 } 272 break; 273 274 case NWAM_BACKEND_DOOR_CMD_UPDATE_REQ: 275 if (req->nwbda_datalen == 0) { 276 obj = NULL; 277 } else { 278 if ((req->nwbda_result = 279 nwam_read_object_from_backend_door_arg 280 (req, NULL, NULL, &obj)) != NWAM_SUCCESS) 281 break; 282 } 283 req->nwbda_result = nwam_update_object_in_files_backend( 284 req->nwbda_dbname[0] == 0 ? NULL : req->nwbda_dbname, 285 req->nwbda_object[0] == 0 ? NULL : req->nwbda_object, 286 req->nwbda_flags, obj); 287 nwam_free_object_list(obj); 288 if (req->nwbda_result == NWAM_SUCCESS) { 289 req->nwbda_datalen = 0; 290 nwam_record_audit_event(ucr, ADT_netcfg_update, 291 (char *)req->nwbda_object, 292 (char *)req->nwbda_dbname, ADT_SUCCESS, 293 ADT_SUCCESS); 294 } 295 break; 296 297 case NWAM_BACKEND_DOOR_CMD_REMOVE_REQ: 298 req->nwbda_result = nwam_remove_object_from_files_backend 299 (strlen(req->nwbda_dbname) > 0 ? req->nwbda_dbname : NULL, 300 strlen(req->nwbda_object) > 0 ? req->nwbda_object : NULL, 301 req->nwbda_flags); 302 if (req->nwbda_result == NWAM_SUCCESS) { 303 nwam_record_audit_event(ucr, ADT_netcfg_update, 304 (char *)req->nwbda_object, 305 (char *)req->nwbda_dbname, ADT_SUCCESS, 306 ADT_SUCCESS); 307 } 308 break; 309 310 default: 311 req->nwbda_result = NWAM_INVALID_ARG; 312 break; 313 } 314 315 door_return: 316 ucred_free(ucr); 317 318 (void) door_return((char *)req, arg_size, NULL, 0); 319 } 320 321 static int backend_door_fd = -1; 322 323 void 324 nwam_backend_fini(void) 325 { 326 if (backend_door_fd != -1) { 327 (void) door_revoke(backend_door_fd); 328 backend_door_fd = -1; 329 } 330 (void) unlink(NWAM_BACKEND_DOOR_FILE); 331 } 332 333 nwam_error_t 334 nwam_backend_init(void) 335 { 336 int did; 337 struct stat statbuf; 338 339 /* Create the door directory if it doesn't already exist */ 340 if (stat(NWAM_DOOR_DIR, &statbuf) < 0) { 341 if (mkdir(NWAM_DOOR_DIR, (mode_t)0755) < 0) 342 return (NWAM_ERROR_BACKEND_INIT); 343 } else { 344 if ((statbuf.st_mode & S_IFMT) != S_IFDIR) 345 return (NWAM_ERROR_BACKEND_INIT); 346 } 347 348 if (chmod(NWAM_DOOR_DIR, 0755) < 0 || 349 chown(NWAM_DOOR_DIR, UID_NETADM, GID_NETADM) < 0) 350 return (NWAM_ERROR_BACKEND_INIT); 351 352 /* Do a low-overhead "touch" on the file that will be the door node. */ 353 did = open(NWAM_BACKEND_DOOR_FILE, 354 O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW | O_NONBLOCK, 355 S_IRUSR | S_IRGRP | S_IROTH); 356 357 if (did != -1) 358 (void) close(did); 359 else if (errno != EEXIST) 360 return (NWAM_ERROR_BACKEND_INIT); 361 362 /* Create the door. */ 363 backend_door_fd = door_create(nwam_backend_door_server, NULL, 364 DOOR_REFUSE_DESC); 365 if (backend_door_fd == -1) 366 return (NWAM_ERROR_BACKEND_INIT); 367 368 /* Attach the door to the file. */ 369 (void) fdetach(NWAM_BACKEND_DOOR_FILE); 370 if (fattach(backend_door_fd, NWAM_BACKEND_DOOR_FILE) == -1) { 371 (void) door_revoke(backend_door_fd); 372 return (NWAM_ERROR_BACKEND_INIT); 373 } 374 375 return (NWAM_SUCCESS); 376 } 377 378 static nwam_error_t 379 nwam_backend_door_call(nwam_backend_door_cmd_t cmd, char *dbname, 380 char *objname, uint64_t flags, void *obj) 381 { 382 uchar_t reqbuf[NWAM_BACKEND_DOOR_ARG_SIZE]; 383 /* LINTED: alignment */ 384 nwam_backend_door_arg_t *req = (nwam_backend_door_arg_t *)&reqbuf; 385 nwam_error_t err, reserr; 386 387 if ((err = nwam_create_backend_door_arg(cmd, dbname, objname, flags, 388 obj, req)) != NWAM_SUCCESS) 389 return (err); 390 391 if (nwam_make_door_call(NWAM_BACKEND_DOOR_FILE, &backend_door_client_fd, 392 req, sizeof (reqbuf)) != 0) 393 return (NWAM_ERROR_BIND); 394 395 reserr = req->nwbda_result; 396 397 if (cmd == NWAM_BACKEND_DOOR_CMD_READ_REQ) { 398 err = nwam_read_object_from_backend_door_arg(req, dbname, 399 objname, obj); 400 } 401 402 return (err == NWAM_SUCCESS ? reserr : err); 403 } 404 405 /* 406 * Read object specified by objname from backend dbname, retrieving an object 407 * list representation. 408 * 409 * If dbname is NULL, obj is a list of string arrays consisting of the list 410 * of backend dbnames. 411 * 412 * If objname is NULL, read all objects in the specified dbname and create 413 * an object list containing a string array which represents each object. 414 * 415 * Otherwise obj will point to a list of the properties for the object 416 * specified by objname in the backend dbname. 417 */ 418 /* ARGSUSED2 */ 419 nwam_error_t 420 nwam_read_object_from_backend(char *dbname, char *objname, 421 uint64_t flags, void *obj) 422 { 423 nwam_error_t err = nwam_check_auths(getuid(), B_FALSE, flags); 424 425 if (err != NWAM_SUCCESS) 426 return (err); 427 428 return (nwam_backend_door_call(NWAM_BACKEND_DOOR_CMD_READ_REQ, 429 dbname, objname, flags, obj)); 430 } 431 432 /* 433 * Read in all objects from backend dbname and update object corresponding 434 * to objname with properties recorded in proplist, writing the results to 435 * the backend dbname. 436 */ 437 nwam_error_t 438 nwam_update_object_in_backend(char *dbname, char *objname, 439 uint64_t flags, void *obj) 440 { 441 nwam_error_t err = nwam_check_auths(getuid(), B_TRUE, flags); 442 443 if (err != NWAM_SUCCESS) 444 return (err); 445 446 return (nwam_backend_door_call(NWAM_BACKEND_DOOR_CMD_UPDATE_REQ, 447 dbname, objname, flags, obj)); 448 } 449 450 /* 451 * Remove specified object from backend by reading in the list of objects, 452 * removing objname and writing the remainder. 453 * 454 * If objname is NULL, remove the backend dbname. 455 */ 456 nwam_error_t 457 nwam_remove_object_from_backend(char *dbname, char *objname, uint64_t flags) 458 { 459 nwam_error_t err = nwam_check_auths(getuid(), B_TRUE, flags); 460 461 if (err != NWAM_SUCCESS) 462 return (err); 463 464 return (nwam_backend_door_call(NWAM_BACKEND_DOOR_CMD_REMOVE_REQ, 465 dbname, objname, flags, NULL)); 466 } 467