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
nwam_check_auths(uid_t uid,boolean_t write,uint64_t flags)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
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)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
nwam_read_object_from_backend_door_arg(nwam_backend_door_arg_t * arg,char * dbname,char * name,void * objp)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
nwam_backend_door_server(void * cookie,char * arg,size_t arg_size,door_desc_t * dp,uint_t ndesc)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
nwam_backend_fini(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
nwam_backend_init(void)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
nwam_backend_door_call(nwam_backend_door_cmd_t cmd,char * dbname,char * objname,uint64_t flags,void * obj)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
nwam_read_object_from_backend(char * dbname,char * objname,uint64_t flags,void * obj)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
nwam_update_object_in_backend(char * dbname,char * objname,uint64_t flags,void * obj)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
nwam_remove_object_from_backend(char * dbname,char * objname,uint64_t flags)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