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
55pthread_mutex_t door_mutex = PTHREAD_MUTEX_INITIALIZER;
56int nwam_door_fd = -1;
57
58static int
59open_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
85int
86nwam_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
108static nwam_error_t
109send_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
134nwam_error_t
135nwam_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
149nwam_error_t
150nwam_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
173nwam_error_t
174nwam_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
204nwam_error_t
205nwam_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
244nwam_error_t
245nwam_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
273nwam_error_t
274nwam_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
293const char *
294nwam_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
310nwam_value_type_t
311nwam_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
328const char *
329nwam_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
349const char *
350nwam_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
388const char *
389nwam_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
415const char *
416nwam_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
462const char *
463nwam_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
481nwam_object_type_t
482nwam_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
502nwam_error_t
503nwam_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 */
538nwam_error_t
539nwam_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
547nwam_error_t
548nwam_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 */
572nwam_error_t
573nwam_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 */
590nwam_error_t
591nwam_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 */
608nwam_error_t
609nwam_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 */
638static nwam_error_t
639nwam_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 */
654nwam_error_t
655nwam_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 */
683nwam_error_t
684nwam_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 */
699nwam_error_t
700nwam_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 */
728nwam_error_t
729nwam_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
743nwam_error_t
744nwam_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
752nwam_error_t
753nwam_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. */
762nwam_error_t
763nwam_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 */
788nwam_error_t
789nwam_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	}
826out:
827	scf_service_destroy(svc);
828	scf_handle_destroy(h);
829	return (err);
830}
831
832/* verifies mac-address and bssids */
833nwam_error_t
834nwam_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
864boolean_t
865nwam_uid_is_special(void)
866{
867	uid_t uid = getuid();
868	return (uid == UID_NETADM || uid == 0);
869}
870
871nwam_error_t
872nwam_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	}
912out:
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
927nwam_error_t
928nwam_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
960retry:
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	}
984out:
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