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