xref: /illumos-gate/usr/src/lib/libnwam/common/libnwam_util.c (revision fb2a9bae0030340ad72b9c26ba1ffee2ee3cafec)
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 <arpa/inet.h>
28 #include <assert.h>
29 #include <atomic.h>
30 #include <ctype.h>
31 #include <errno.h>
32 #include <inet/ip.h>
33 #include <libintl.h>
34 #include <libproc.h>
35 #include <libscf.h>
36 #include <net/if_dl.h>
37 #include <netinet/in.h>
38 #include <pthread.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <strings.h>
42 #include <sys/mman.h>
43 #include <sys/socket.h>
44 #include <sys/types.h>
45 #include <unistd.h>
46 
47 #include "libnwam_impl.h"
48 #include <libnwam_priv.h>
49 #include <libnwam.h>
50 
51 /*
52  * Utility functions for door access, common validation functions etc.
53  */
54 
55 pthread_mutex_t door_mutex = PTHREAD_MUTEX_INITIALIZER;
56 int nwam_door_fd = -1;
57 
58 static int
59 open_door(const char *door_name, int *door_fdp)
60 {
61 	struct door_info dinfo;
62 	int err = 0;
63 
64 	(void) pthread_mutex_lock(&door_mutex);
65 
66 	if (*door_fdp != -1) {
67 		/* Check door fd is not old (from previous nwamd). */
68 		if (door_info(*door_fdp, &dinfo) != 0 ||
69 		    (dinfo.di_attributes & DOOR_REVOKED) != 0) {
70 			(void) close(*door_fdp);
71 			*door_fdp = -1;
72 		}
73 	}
74 	if (*door_fdp == -1) {
75 		*door_fdp = open(door_name, 0);
76 		if (*door_fdp == -1)
77 			err = errno;
78 	}
79 
80 	(void) pthread_mutex_unlock(&door_mutex);
81 
82 	return (err);
83 }
84 
85 int
86 nwam_make_door_call(const char *door_name, int *door_fdp,
87     void *request, size_t request_size)
88 {
89 	int err;
90 	door_arg_t door_args;
91 
92 	door_args.data_ptr = (void *)request;
93 	door_args.data_size = request_size;
94 	door_args.desc_ptr = NULL;
95 	door_args.desc_num = 0;
96 	door_args.rbuf = (void *)request;
97 	door_args.rsize = request_size;
98 
99 	if ((err = open_door(door_name, door_fdp)) != 0)
100 		return (err);
101 
102 	if (door_call(*door_fdp, &door_args) == -1)
103 		return (errno);
104 
105 	return (0);
106 }
107 
108 static nwam_error_t
109 send_msg_to_nwam(nwamd_door_arg_t *request)
110 {
111 	int err;
112 
113 	if ((err = nwam_make_door_call(NWAM_DOOR, &nwam_door_fd,
114 	    request, sizeof (nwamd_door_arg_t))) != 0) {
115 		if (err == ENOENT)
116 			return (NWAM_ERROR_BIND);
117 		return (nwam_errno_to_nwam_error(err));
118 	}
119 
120 	switch (request->nwda_status) {
121 	case NWAM_REQUEST_STATUS_OK:
122 		return (NWAM_SUCCESS);
123 	case NWAM_REQUEST_STATUS_UNKNOWN:
124 		return (NWAM_INVALID_ARG);
125 	case NWAM_REQUEST_STATUS_ALREADY:
126 		return (NWAM_ENTITY_IN_USE);
127 	case NWAM_REQUEST_STATUS_FAILED:
128 		return (request->nwda_error);
129 	default:
130 		return (NWAM_ERROR_INTERNAL);
131 	}
132 }
133 
134 nwam_error_t
135 nwam_request_register_unregister(nwam_request_type_t type,
136     const char *event_msg_file)
137 {
138 	nwamd_door_arg_t req;
139 
140 	req.nwda_type = type;
141 
142 	(void) strlcpy(req.nwda_data.nwdad_register_info.nwdad_name,
143 	    event_msg_file,
144 	    sizeof (req.nwda_data.nwdad_register_info.nwdad_name));
145 
146 	return (send_msg_to_nwam(&req));
147 }
148 
149 nwam_error_t
150 nwam_request_action(nwam_object_type_t object_type,
151     const char *name, const char *parent, nwam_action_t action)
152 {
153 	nwamd_door_arg_t req;
154 
155 	assert(name != NULL);
156 
157 	req.nwda_type = NWAM_REQUEST_TYPE_ACTION;
158 	req.nwda_data.nwdad_object_action.nwdad_object_type = object_type;
159 	req.nwda_data.nwdad_object_action.nwdad_action = action;
160 	(void) strlcpy(req.nwda_data.nwdad_object_action.nwdad_name, name,
161 	    sizeof (req.nwda_data.nwdad_object_action.nwdad_name));
162 	if (parent != NULL) {
163 		(void) strlcpy(req.nwda_data.nwdad_object_action.nwdad_parent,
164 		    parent,
165 		    sizeof (req.nwda_data.nwdad_object_action.nwdad_parent));
166 	} else {
167 		req.nwda_data.nwdad_object_action.nwdad_parent[0] = '\0';
168 	}
169 
170 	return (send_msg_to_nwam(&req));
171 }
172 
173 nwam_error_t
174 nwam_request_state(nwam_object_type_t object_type, const char *name,
175     const char *parent, nwam_state_t *statep, nwam_aux_state_t *auxp)
176 {
177 	nwamd_door_arg_t req;
178 	nwam_error_t err;
179 
180 	assert(name != NULL && statep != NULL && auxp != NULL);
181 
182 	req.nwda_type = NWAM_REQUEST_TYPE_STATE;
183 
184 	req.nwda_data.nwdad_object_state.nwdad_object_type = object_type;
185 
186 	(void) strlcpy(req.nwda_data.nwdad_object_state.nwdad_name, name,
187 	    sizeof (req.nwda_data.nwdad_object_state.nwdad_name));
188 	if (parent != NULL) {
189 		(void) strlcpy(req.nwda_data.nwdad_object_state.nwdad_parent,
190 		    parent,
191 		    sizeof (req.nwda_data.nwdad_object_state.nwdad_parent));
192 	}
193 
194 	err = send_msg_to_nwam(&req);
195 
196 	if (err == NWAM_SUCCESS) {
197 		*statep = req.nwda_data.nwdad_object_state.nwdad_state;
198 		*auxp = req.nwda_data.nwdad_object_state.nwdad_aux_state;
199 	}
200 
201 	return (err);
202 }
203 
204 nwam_error_t
205 nwam_request_wlan(nwam_request_type_t type, const char *name,
206     const char *essid, const char *bssid, uint32_t security_mode,
207     uint_t keyslot, const char *key, boolean_t add_to_known_wlans)
208 {
209 	nwamd_door_arg_t req;
210 
211 	assert(name != NULL);
212 
213 	req.nwda_type = type;
214 
215 	(void) strlcpy(req.nwda_data.nwdad_wlan_info.nwdad_name, name,
216 	    sizeof (req.nwda_data.nwdad_wlan_info));
217 	if (essid != NULL) {
218 		(void) strlcpy(req.nwda_data.nwdad_wlan_info.nwdad_essid, essid,
219 		    sizeof (req.nwda_data.nwdad_wlan_info.nwdad_essid));
220 	} else {
221 		req.nwda_data.nwdad_wlan_info.nwdad_essid[0] = '\0';
222 	}
223 	if (bssid != NULL) {
224 		(void) strlcpy(req.nwda_data.nwdad_wlan_info.nwdad_bssid, bssid,
225 		    sizeof (req.nwda_data.nwdad_wlan_info.nwdad_bssid));
226 	} else {
227 		req.nwda_data.nwdad_wlan_info.nwdad_bssid[0] = '\0';
228 	}
229 	if (key != NULL) {
230 		(void) strlcpy(req.nwda_data.nwdad_wlan_info.nwdad_key, key,
231 		    sizeof (req.nwda_data.nwdad_wlan_info.nwdad_key));
232 		req.nwda_data.nwdad_wlan_info.nwdad_keyslot = keyslot;
233 	} else {
234 		req.nwda_data.nwdad_wlan_info.nwdad_key[0] = '\0';
235 	}
236 
237 	req.nwda_data.nwdad_wlan_info.nwdad_security_mode = security_mode;
238 	req.nwda_data.nwdad_wlan_info.nwdad_add_to_known_wlans =
239 	    add_to_known_wlans;
240 
241 	return (send_msg_to_nwam(&req));
242 }
243 
244 nwam_error_t
245 nwam_request_wlan_scan_results(const char *name, uint_t *num_wlansp,
246     nwam_wlan_t **wlansp)
247 {
248 	nwamd_door_arg_t req;
249 	nwam_error_t err;
250 
251 	assert(name != NULL && num_wlansp != NULL && wlansp != NULL);
252 
253 	req.nwda_type = NWAM_REQUEST_TYPE_WLAN_SCAN_RESULTS;
254 
255 	(void) strlcpy(req.nwda_data.nwdad_wlan_info.nwdad_name, name,
256 	    sizeof (req.nwda_data.nwdad_wlan_info.nwdad_name));
257 
258 	if ((err = send_msg_to_nwam(&req)) != NWAM_SUCCESS)
259 		return (err);
260 
261 	*num_wlansp = req.nwda_data.nwdad_wlan_info.nwdad_num_wlans;
262 
263 	*wlansp = calloc(*num_wlansp, sizeof (nwam_wlan_t));
264 	if (*wlansp == NULL)
265 		return (NWAM_NO_MEMORY);
266 
267 	(void) memcpy(*wlansp, req.nwda_data.nwdad_wlan_info.nwdad_wlans,
268 	    *num_wlansp * sizeof (nwam_wlan_t));
269 
270 	return (NWAM_SUCCESS);
271 }
272 
273 nwam_error_t
274 nwam_request_active_priority_group(int64_t *priorityp)
275 {
276 	nwamd_door_arg_t req;
277 	nwam_error_t err;
278 
279 	assert(priorityp != NULL);
280 
281 	req.nwda_type = NWAM_REQUEST_TYPE_PRIORITY_GROUP;
282 	err = send_msg_to_nwam(&req);
283 
284 	if (err == NWAM_SUCCESS)
285 		*priorityp =
286 		    req.nwda_data.nwdad_priority_group_info.nwdad_priority;
287 
288 	return (err);
289 }
290 
291 /* String conversion functions */
292 
293 const char *
294 nwam_value_type_to_string(nwam_value_type_t type)
295 {
296 	switch (type) {
297 	case NWAM_VALUE_TYPE_BOOLEAN:
298 		return ("boolean");
299 	case NWAM_VALUE_TYPE_INT64:
300 		return ("int64");
301 	case NWAM_VALUE_TYPE_UINT64:
302 		return ("uint64");
303 	case NWAM_VALUE_TYPE_STRING:
304 		return ("string");
305 	default:
306 		return ("unknown");
307 	}
308 }
309 
310 nwam_value_type_t
311 nwam_string_to_value_type(const char *typestr)
312 {
313 	if (strncmp(typestr, nwam_value_type_to_string(NWAM_VALUE_TYPE_BOOLEAN),
314 	    strlen(typestr)) == 0)
315 		return (NWAM_VALUE_TYPE_BOOLEAN);
316 	if (strncmp(typestr, nwam_value_type_to_string(NWAM_VALUE_TYPE_INT64),
317 	    strlen(typestr)) == 0)
318 		return (NWAM_VALUE_TYPE_INT64);
319 	if (strncmp(typestr, nwam_value_type_to_string(NWAM_VALUE_TYPE_UINT64),
320 	    strlen(typestr)) == 0)
321 		return (NWAM_VALUE_TYPE_UINT64);
322 	if (strncmp(typestr, nwam_value_type_to_string(NWAM_VALUE_TYPE_STRING),
323 	    strlen(typestr)) == 0)
324 		return (NWAM_VALUE_TYPE_STRING);
325 	return (NWAM_VALUE_TYPE_UNKNOWN);
326 }
327 
328 const char *
329 nwam_action_to_string(nwam_action_t action)
330 {
331 	switch (action) {
332 	case NWAM_ACTION_ADD:
333 		return ("add");
334 	case NWAM_ACTION_REMOVE:
335 		return ("remove");
336 	case NWAM_ACTION_REFRESH:
337 		return ("refresh");
338 	case NWAM_ACTION_ENABLE:
339 		return ("enable");
340 	case NWAM_ACTION_DISABLE:
341 		return ("disable");
342 	case NWAM_ACTION_DESTROY:
343 		return ("destroy");
344 	default:
345 		return ("unknown");
346 	}
347 }
348 
349 const char *
350 nwam_event_type_to_string(int event_type)
351 {
352 	switch (event_type) {
353 	case NWAM_EVENT_TYPE_NOOP:
354 		return ("NOOP");
355 	case NWAM_EVENT_TYPE_INIT:
356 		return ("INIT");
357 	case NWAM_EVENT_TYPE_SHUTDOWN:
358 		return ("SHUTDOWN");
359 	case NWAM_EVENT_TYPE_OBJECT_ACTION:
360 		return ("OBJECT_ACTION");
361 	case NWAM_EVENT_TYPE_OBJECT_STATE:
362 		return ("OBJECT_STATE");
363 	case NWAM_EVENT_TYPE_PRIORITY_GROUP:
364 		return ("PRIORITY_GROUP");
365 	case NWAM_EVENT_TYPE_INFO:
366 		return ("INFO");
367 	case NWAM_EVENT_TYPE_WLAN_SCAN_REPORT:
368 		return ("WLAN_SCAN_REPORT");
369 	case NWAM_EVENT_TYPE_WLAN_NEED_CHOICE:
370 		return ("WLAN_NEED_CHOICE");
371 	case NWAM_EVENT_TYPE_WLAN_NEED_KEY:
372 		return ("WLAN_NEED_KEY");
373 	case NWAM_EVENT_TYPE_WLAN_CONNECTION_REPORT:
374 		return ("WLAN_CONNECTION_REPORT");
375 	case NWAM_EVENT_TYPE_IF_ACTION:
376 		return ("IF_ACTION");
377 	case NWAM_EVENT_TYPE_IF_STATE:
378 		return ("IF_STATE");
379 	case NWAM_EVENT_TYPE_LINK_ACTION:
380 		return ("LINK_ACTION");
381 	case NWAM_EVENT_TYPE_LINK_STATE:
382 		return ("LINK_STATE");
383 	default:
384 		return ("UNKNOWN");
385 	}
386 }
387 
388 const char *
389 nwam_state_to_string(nwam_state_t state)
390 {
391 	switch (state) {
392 	case NWAM_STATE_UNINITIALIZED:
393 		return ("uninitialized");
394 	case NWAM_STATE_INITIALIZED:
395 		return ("initialized");
396 	case NWAM_STATE_OFFLINE:
397 		return ("offline");
398 	case NWAM_STATE_OFFLINE_TO_ONLINE:
399 		return ("offline*");
400 	case NWAM_STATE_ONLINE_TO_OFFLINE:
401 		return ("online*");
402 	case NWAM_STATE_ONLINE:
403 		return ("online");
404 	case NWAM_STATE_MAINTENANCE:
405 		return ("maintenance");
406 	case NWAM_STATE_DEGRADED:
407 		return ("degraded");
408 	case NWAM_STATE_DISABLED:
409 		return ("disabled");
410 	default:
411 		return ("unknown");
412 	}
413 }
414 
415 const char *
416 nwam_aux_state_to_string(nwam_aux_state_t aux_state)
417 {
418 	switch (aux_state) {
419 	case NWAM_AUX_STATE_UNINITIALIZED:
420 		return ("uninitialized");
421 	case NWAM_AUX_STATE_INITIALIZED:
422 		return ("(re)initialized but not configured");
423 	case NWAM_AUX_STATE_CONDITIONS_NOT_MET:
424 		return ("conditions for activation are unmet");
425 	case NWAM_AUX_STATE_MANUAL_DISABLE:
426 		return ("disabled by administrator");
427 	case NWAM_AUX_STATE_METHOD_FAILED:
428 		return ("method/service failed");
429 	case NWAM_AUX_STATE_METHOD_MISSING:
430 		return ("method or FMRI not specified");
431 	case NWAM_AUX_STATE_INVALID_CONFIG:
432 		return ("invalid configuration values");
433 	case NWAM_AUX_STATE_METHOD_RUNNING:
434 		return ("method/service executing");
435 	case NWAM_AUX_STATE_ACTIVE:
436 		return ("active");
437 	case NWAM_AUX_STATE_LINK_WIFI_SCANNING:
438 		return ("scanning for WiFi networks");
439 	case NWAM_AUX_STATE_LINK_WIFI_NEED_SELECTION:
440 		return ("need WiFi network selection");
441 	case NWAM_AUX_STATE_LINK_WIFI_NEED_KEY:
442 		return ("need WiFi security key");
443 	case NWAM_AUX_STATE_LINK_WIFI_CONNECTING:
444 		return ("connecting to WiFi network");
445 	case NWAM_AUX_STATE_IF_WAITING_FOR_ADDR:
446 		return ("waiting for IP address to be set");
447 	case NWAM_AUX_STATE_IF_DHCP_TIMED_OUT:
448 		return ("DHCP wait timeout, still trying...");
449 	case NWAM_AUX_STATE_IF_DUPLICATE_ADDR:
450 		return ("duplicate address detected");
451 	case NWAM_AUX_STATE_UP:
452 		return ("interface/link is up");
453 	case NWAM_AUX_STATE_DOWN:
454 		return ("interface/link is down");
455 	case NWAM_AUX_STATE_NOT_FOUND:
456 		return ("interface/link not found");
457 	default:
458 		return ("unknown");
459 	}
460 }
461 
462 const char *
463 nwam_object_type_to_string(nwam_object_type_t type)
464 {
465 	switch (type) {
466 	case NWAM_OBJECT_TYPE_NCP:
467 		return ("ncp");
468 	case NWAM_OBJECT_TYPE_NCU:
469 		return ("ncu");
470 	case NWAM_OBJECT_TYPE_LOC:
471 		return ("loc");
472 	case NWAM_OBJECT_TYPE_ENM:
473 		return ("enm");
474 	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
475 		return ("known wlan");
476 	default:
477 		return ("unknown");
478 	}
479 }
480 
481 nwam_object_type_t
482 nwam_string_to_object_type(const char *typestr)
483 {
484 	if (strcasecmp(typestr,
485 	    nwam_object_type_to_string(NWAM_OBJECT_TYPE_NCP)) == 0)
486 		return (NWAM_OBJECT_TYPE_NCP);
487 	if (strcasecmp(typestr,
488 	    nwam_object_type_to_string(NWAM_OBJECT_TYPE_NCU)) == 0)
489 		return (NWAM_OBJECT_TYPE_NCU);
490 	if (strcasecmp(typestr,
491 	    nwam_object_type_to_string(NWAM_OBJECT_TYPE_LOC)) == 0)
492 		return (NWAM_OBJECT_TYPE_LOC);
493 	if (strcasecmp(typestr,
494 	    nwam_object_type_to_string(NWAM_OBJECT_TYPE_ENM)) == 0)
495 		return (NWAM_OBJECT_TYPE_ENM);
496 	if (strcasecmp(typestr,
497 	    nwam_object_type_to_string(NWAM_OBJECT_TYPE_KNOWN_WLAN)) == 0)
498 		return (NWAM_OBJECT_TYPE_KNOWN_WLAN);
499 	return (NWAM_OBJECT_TYPE_UNKNOWN);
500 }
501 
502 nwam_error_t
503 nwam_errno_to_nwam_error(int errnum)
504 {
505 	switch (errnum) {
506 	case 0:
507 		return (NWAM_SUCCESS);
508 	case EBADF:
509 		return (NWAM_ERROR_BIND);
510 	case EPERM:
511 	case EACCES:
512 		return (NWAM_PERMISSION_DENIED);
513 	case ENOENT:
514 		return (NWAM_ENTITY_NOT_FOUND);
515 	case EIDRM:
516 		return (NWAM_ENTITY_INVALID);
517 	case EEXIST:
518 		return (NWAM_ENTITY_EXISTS);
519 	case EAGAIN:
520 	case EBUSY:
521 		return (NWAM_ENTITY_IN_USE);
522 	case ENOMEM:
523 	case ENOSPC:
524 		return (NWAM_NO_MEMORY);
525 	case EINVAL:
526 	case E2BIG:
527 		return (NWAM_INVALID_ARG);
528 	default:
529 		return (NWAM_ERROR_INTERNAL);
530 	}
531 }
532 
533 /* Common validation functions */
534 
535 /*
536  * Do the flags represent a subset of valid_flags?
537  */
538 nwam_error_t
539 nwam_valid_flags(uint64_t flags, uint64_t valid_flags)
540 {
541 
542 	if ((flags | valid_flags) != valid_flags)
543 		return (NWAM_INVALID_ARG);
544 	return (NWAM_SUCCESS);
545 }
546 
547 nwam_error_t
548 nwam_valid_condition(nwam_value_t value)
549 {
550 	char **conditions;
551 	uint_t i, numvalues;
552 	nwam_condition_object_type_t object_type;
553 	nwam_condition_t condition;
554 
555 	if (nwam_value_get_string_array(value, &conditions, &numvalues)
556 	    != NWAM_SUCCESS)
557 		return (NWAM_ENTITY_INVALID_VALUE);
558 
559 	for (i = 0; i < numvalues; i++) {
560 		char *object_name = NULL;
561 
562 		if (nwam_condition_string_to_condition(conditions[i],
563 		    &object_type, &condition, &object_name) != NWAM_SUCCESS)
564 			return (NWAM_ENTITY_INVALID_VALUE);
565 		if (object_name != NULL)
566 			free(object_name);
567 	}
568 	return (NWAM_SUCCESS);
569 }
570 
571 /* check if boolean values are correct, generalize for array of booleans */
572 nwam_error_t
573 nwam_valid_boolean(nwam_value_t value)
574 {
575 	boolean_t *val;
576 	uint_t i, numvalues;
577 
578 	if (nwam_value_get_boolean_array(value, &val, &numvalues)
579 	    != NWAM_SUCCESS)
580 		return (NWAM_ENTITY_INVALID_VALUE);
581 
582 	for (i = 0; i < numvalues; i++) {
583 		if (val[i] != B_TRUE && val[i] != B_FALSE)
584 			return (NWAM_ENTITY_INVALID_VALUE);
585 	}
586 	return (NWAM_SUCCESS);
587 }
588 
589 /* check if uint64 values are correct, generalize for array of ints */
590 nwam_error_t
591 nwam_valid_uint64(nwam_value_t value)
592 {
593 	int64_t *val;
594 	uint_t i, numvalues;
595 
596 	if (nwam_value_get_int64_array(value, &val, &numvalues)
597 	    != NWAM_SUCCESS)
598 		return (NWAM_ENTITY_INVALID_VALUE);
599 
600 	for (i = 0; i < numvalues; i++) {
601 		if (val[i] < 0)
602 			return (NWAM_ENTITY_INVALID_VALUE);
603 	}
604 	return (NWAM_SUCCESS);
605 }
606 
607 /* check if domain names are correct, generalize for array of domains */
608 nwam_error_t
609 nwam_valid_domain(nwam_value_t value)
610 {
611 	char **domainvalues, *domain;
612 	uint_t i, numvalues;
613 	int len, j;
614 
615 	if (nwam_value_get_string_array(value, &domainvalues, &numvalues)
616 	    != NWAM_SUCCESS)
617 		return (NWAM_ENTITY_INVALID_VALUE);
618 
619 	for (i = 0; i < numvalues; i++) {
620 		/*
621 		 * First and last character must be alphanumeric.
622 		 * Only '.' and '-' are allowed.
623 		 */
624 		domain = domainvalues[i];
625 		len = strlen(domain);
626 		if (!isalnum(domain[0]) || !isalnum(domain[len-1]))
627 			return (NWAM_ENTITY_INVALID_VALUE);
628 		for (j = 0; j < len; j++) {
629 			if (!isalnum(domain[j]) &&
630 			    domain[j] != '.' && domain[j] != '-')
631 				return (NWAM_ENTITY_INVALID_VALUE);
632 		}
633 	}
634 	return (NWAM_SUCCESS);
635 }
636 
637 /* check if address prefix is valid */
638 static nwam_error_t
639 nwam_valid_prefix(char *addr, int max_plen)
640 {
641 	char *prefix, *end;
642 	int prefixlen;
643 
644 	if ((prefix = strchr(addr, '/')) != NULL) {
645 		prefix++;
646 		prefixlen = strtol(prefix, &end, 10);
647 		if (prefix == end || prefixlen < 0 || prefixlen > max_plen)
648 			return (NWAM_ENTITY_INVALID_VALUE);
649 	}
650 	return (NWAM_SUCCESS);
651 }
652 
653 /* check if IPv4 addresses are correct, generalize for array of addresses */
654 nwam_error_t
655 nwam_valid_host_v4(nwam_value_t value)
656 {
657 	char **addrvalues, *addr;
658 	uint_t i, numvalues;
659 	struct sockaddr_in sa;
660 
661 	if (nwam_value_get_string_array(value, &addrvalues, &numvalues)
662 	    != NWAM_SUCCESS)
663 		return (NWAM_ENTITY_INVALID_VALUE);
664 
665 	for (i = 0; i < numvalues; i++) {
666 		addr = strdup(addrvalues[i]);
667 		if (nwam_valid_prefix(addr, IP_ABITS) != NWAM_SUCCESS) {
668 			free(addr);
669 			return (NWAM_ENTITY_INVALID_VALUE);
670 		}
671 		/* replace '/' with '\0' */
672 		addr = strsep(&addr, "/");
673 		if (inet_pton(AF_INET, addr, &(sa.sin_addr)) != 1) {
674 			free(addr);
675 			return (NWAM_ENTITY_INVALID_VALUE);
676 		}
677 		free(addr);
678 	}
679 	return (NWAM_SUCCESS);
680 }
681 
682 /* Check if IPv4 address for default route is valid */
683 nwam_error_t
684 nwam_valid_route_v4(nwam_value_t value)
685 {
686 	char *addrvalue;
687 	struct sockaddr_in sa;
688 
689 	if (nwam_value_get_string(value, &addrvalue) != NWAM_SUCCESS)
690 		return (NWAM_ENTITY_INVALID_VALUE);
691 
692 	if (inet_pton(AF_INET, addrvalue, &(sa.sin_addr)) != 1)
693 		return (NWAM_ENTITY_INVALID_VALUE);
694 
695 	return (NWAM_SUCCESS);
696 }
697 
698 /* check if IPv6 addresses are correct, generalize for array of addresses */
699 nwam_error_t
700 nwam_valid_host_v6(nwam_value_t value)
701 {
702 	char **addrvalues, *addr;
703 	uint_t i, numvalues;
704 	struct sockaddr_in6 sa;
705 
706 	if (nwam_value_get_string_array(value, &addrvalues, &numvalues)
707 	    != NWAM_SUCCESS)
708 		return (NWAM_ENTITY_INVALID_VALUE);
709 
710 	for (i = 0; i < numvalues; i++) {
711 		addr = strdup(addrvalues[i]);
712 		if (nwam_valid_prefix(addr, IPV6_ABITS) != NWAM_SUCCESS) {
713 			free(addr);
714 			return (NWAM_ENTITY_INVALID_VALUE);
715 		}
716 		/* replace '/' with '\0' */
717 		addr = strsep(&addr, "/");
718 		if (inet_pton(AF_INET6, addr, &(sa.sin6_addr)) != 1) {
719 			free(addr);
720 			return (NWAM_ENTITY_INVALID_VALUE);
721 		}
722 		free(addr);
723 	}
724 	return (NWAM_SUCCESS);
725 }
726 
727 /* Check if IPv4 address for default route is valid */
728 nwam_error_t
729 nwam_valid_route_v6(nwam_value_t value)
730 {
731 	char *addrvalue;
732 	struct sockaddr_in6 sa;
733 
734 	if (nwam_value_get_string(value, &addrvalue) != NWAM_SUCCESS)
735 		return (NWAM_ENTITY_INVALID_VALUE);
736 
737 	if (inet_pton(AF_INET6, addrvalue, &(sa.sin6_addr)) != 1)
738 		return (NWAM_ENTITY_INVALID_VALUE);
739 
740 	return (NWAM_SUCCESS);
741 }
742 
743 nwam_error_t
744 nwam_valid_host_any(nwam_value_t value)
745 {
746 	if (nwam_valid_host_v4(value) != NWAM_SUCCESS &&
747 	    nwam_valid_host_v6(value) != NWAM_SUCCESS)
748 		return (NWAM_ENTITY_INVALID_VALUE);
749 	return (NWAM_SUCCESS);
750 }
751 
752 nwam_error_t
753 nwam_valid_host_or_domain(nwam_value_t value)
754 {
755 	if (nwam_valid_host_any(value) != NWAM_SUCCESS &&
756 	    nwam_valid_domain(value) != NWAM_SUCCESS)
757 		return (NWAM_ENTITY_INVALID_VALUE);
758 	return (NWAM_SUCCESS);
759 }
760 
761 /* We do not validate file existence, merely that it is an absolute path. */
762 nwam_error_t
763 nwam_valid_file(nwam_value_t value)
764 {
765 	char **files;
766 	uint_t i, numvalues;
767 
768 	if (nwam_value_get_string_array(value, &files, &numvalues)
769 	    != NWAM_SUCCESS)
770 		return (NWAM_ENTITY_INVALID_VALUE);
771 
772 	for (i = 0; i < numvalues; i++) {
773 		int j = 0;
774 		while (isspace(files[i][j]))
775 			j++;
776 		if (files[i][j] != '/')
777 			return (NWAM_ENTITY_INVALID_VALUE);
778 	}
779 	return (NWAM_SUCCESS);
780 }
781 
782 /*
783  * We do not validate existence of the object pointed to by the FMRI
784  * but merely ensure that it is a valid FMRI.  We do this by
785  * using scf_handle_decode_fmri(), but ignore all errors bar
786  * SCF_ERROR_INVALID_ARGUMENT (which indicates the FMRI is invalid).
787  */
788 nwam_error_t
789 nwam_valid_fmri(nwam_value_t value)
790 {
791 	char **valstr;
792 	scf_handle_t *h = NULL;
793 	scf_service_t *svc = NULL;
794 	uint_t i, numvalues;
795 	nwam_error_t err = NWAM_SUCCESS;
796 
797 	if ((err = nwam_value_get_string_array(value, &valstr, &numvalues))
798 	    != NWAM_SUCCESS)
799 		return (err);
800 
801 	h = scf_handle_create(SCF_VERSION);
802 	if (h == NULL)
803 		return (NWAM_ERROR_INTERNAL);
804 
805 	if (scf_handle_bind(h) != 0) {
806 		err = NWAM_ERROR_INTERNAL;
807 		goto out;
808 	}
809 
810 	if ((svc = scf_service_create(h)) == NULL) {
811 		err = NWAM_ERROR_INTERNAL;
812 		goto out;
813 	}
814 
815 
816 	for (i = 0; i < numvalues; i++) {
817 		if (scf_handle_decode_fmri(h, valstr[i], NULL, svc,
818 		    NULL, NULL, NULL, SCF_DECODE_FMRI_TRUNCATE) == 0 ||
819 		    scf_error() != SCF_ERROR_INVALID_ARGUMENT) {
820 			err = NWAM_SUCCESS;
821 			continue;
822 		}
823 		err = NWAM_ENTITY_INVALID_VALUE;
824 		break;
825 	}
826 out:
827 	scf_service_destroy(svc);
828 	scf_handle_destroy(h);
829 	return (err);
830 }
831 
832 /* verifies mac-address and bssids */
833 nwam_error_t
834 nwam_valid_mac_addr(nwam_value_t value)
835 {
836 	char **mac_addrs, *addr;
837 	uchar_t	*hwaddr;
838 	int hwaddrlen, j;
839 	uint_t i, numvalues;
840 
841 	if (nwam_value_get_string_array(value, &mac_addrs, &numvalues)
842 	    != NWAM_SUCCESS)
843 		return (NWAM_ENTITY_INVALID_VALUE);
844 
845 	for (i = 0; i < numvalues; i++) {
846 		addr = mac_addrs[i];
847 		j = 0;
848 
849 		/* validate that a-fA-F0-9 and ':' only */
850 		while (addr[j] != 0) {
851 			if (!isxdigit(addr[j]) && addr[j] != ':')
852 				return (NWAM_ENTITY_INVALID_VALUE);
853 			j++;
854 		}
855 
856 		if ((hwaddr = _link_aton(addr, &hwaddrlen)) == NULL)
857 			return (NWAM_ENTITY_INVALID_VALUE);
858 		free(hwaddr);
859 	}
860 
861 	return (NWAM_SUCCESS);
862 }
863 
864 boolean_t
865 nwam_uid_is_special(void)
866 {
867 	uid_t uid = getuid();
868 	return (uid == UID_NETADM || uid == 0);
869 }
870 
871 nwam_error_t
872 nwam_get_smf_string_property(const char *fmri, const char *pgname,
873     const char *propname, char **valuep)
874 {
875 	scf_handle_t *h = NULL;
876 	scf_snapshot_t *snap = NULL;
877 	scf_instance_t *inst = NULL;
878 	scf_propertygroup_t *pg = NULL;
879 	scf_property_t *prop = NULL;
880 	scf_value_t *val = NULL;
881 	nwam_error_t err = NWAM_SUCCESS;
882 
883 	if ((*valuep = malloc(NWAM_MAX_NAME_LEN)) == NULL)
884 		return (NWAM_NO_MEMORY);
885 
886 	if ((h = scf_handle_create(SCF_VERSION)) == NULL ||
887 	    scf_handle_bind(h) != 0 ||
888 	    (inst = scf_instance_create(h)) == NULL ||
889 	    (snap = scf_snapshot_create(h)) == NULL ||
890 	    (pg = scf_pg_create(h)) == NULL ||
891 	    (prop = scf_property_create(h)) == NULL ||
892 	    (val = scf_value_create(h)) == NULL) {
893 		err = NWAM_ERROR_INTERNAL;
894 		goto out;
895 	}
896 	if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst,
897 	    NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
898 		err = NWAM_ENTITY_NOT_FOUND;
899 		goto out;
900 	}
901 	/* Retrieve value from running snapshot (if present) */
902 	if (scf_instance_get_snapshot(inst, "running", snap) != 0) {
903 		scf_snapshot_destroy(snap);
904 		snap = NULL;
905 	}
906 	if (scf_instance_get_pg_composed(inst, snap, pgname, pg) != 0 ||
907 	    scf_pg_get_property(pg, propname, prop) != 0 ||
908 	    scf_property_get_value(prop, val) != 0 ||
909 	    scf_value_get_astring(val, *valuep, NWAM_MAX_NAME_LEN) == -1) {
910 		err = NWAM_ENTITY_NOT_FOUND;
911 	}
912 out:
913 	if (err != NWAM_SUCCESS)
914 		free(*valuep);
915 
916 	scf_value_destroy(val);
917 	scf_property_destroy(prop);
918 	scf_pg_destroy(pg);
919 	if (snap != NULL)
920 		scf_snapshot_destroy(snap);
921 	scf_instance_destroy(inst);
922 	scf_handle_destroy(h);
923 
924 	return (err);
925 }
926 
927 nwam_error_t
928 nwam_set_smf_string_property(const char *fmri, const char *pgname,
929     const char *propname, const char *propval)
930 {
931 	scf_handle_t *h = NULL;
932 	scf_instance_t *inst = NULL;
933 	scf_propertygroup_t *pg = NULL;
934 	scf_property_t *prop = NULL;
935 	scf_value_t *val = NULL;
936 	scf_transaction_t *tx = NULL;
937 	scf_transaction_entry_t *ent = NULL;
938 	nwam_error_t err = NWAM_SUCCESS;
939 	int result;
940 
941 	if ((h = scf_handle_create(SCF_VERSION)) == NULL ||
942 	    scf_handle_bind(h) != 0 ||
943 	    (inst = scf_instance_create(h)) == NULL ||
944 	    (pg = scf_pg_create(h)) == NULL ||
945 	    (prop = scf_property_create(h)) == NULL ||
946 	    (val = scf_value_create(h)) == NULL ||
947 	    scf_value_set_astring(val, propval) != 0 ||
948 	    (tx = scf_transaction_create(h)) == NULL ||
949 	    (ent = scf_entry_create(h)) == NULL) {
950 		err = NWAM_ERROR_INTERNAL;
951 		goto out;
952 	}
953 	if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst,
954 	    NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0 ||
955 	    scf_instance_get_pg_composed(inst, NULL, pgname, pg) != 0) {
956 		err = NWAM_ENTITY_NOT_FOUND;
957 		goto out;
958 	}
959 
960 retry:
961 	if (scf_transaction_start(tx, pg) == -1 ||
962 	    scf_transaction_property_change(tx, ent, propname, SCF_TYPE_ASTRING)
963 	    == -1 || scf_entry_add_value(ent, val) != 0) {
964 		err = NWAM_ERROR_INTERNAL;
965 		goto out;
966 	}
967 
968 	result = scf_transaction_commit(tx);
969 	switch (result) {
970 	case 1:
971 		(void) smf_refresh_instance(fmri);
972 		break;
973 	case 0:
974 		scf_transaction_reset(tx);
975 		if (scf_pg_update(pg) == -1) {
976 			err = NWAM_ERROR_INTERNAL;
977 			goto out;
978 		}
979 		goto retry;
980 	default:
981 		err = NWAM_ERROR_INTERNAL;
982 		break;
983 	}
984 out:
985 	scf_value_destroy(val);
986 	scf_property_destroy(prop);
987 	scf_pg_destroy(pg);
988 	scf_instance_destroy(inst);
989 	scf_handle_destroy(h);
990 
991 	return (err);
992 }
993