xref: /titanic_50/usr/src/lib/libnwam/common/libnwam_known_wlan.c (revision 3c112a2b34403220c06c3e2fcac403358cfba168)
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 <ctype.h>
29 #include <sys/param.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <strings.h>
35 #include <unistd.h>
36 #include <libdllink.h>
37 #include <libdlwlan.h>
38 
39 #include "libnwam_impl.h"
40 #include <libnwam_priv.h>
41 #include <libnwam.h>
42 
43 /*
44  * Functions to support creating, modifying and destroying
45  * known WLAN objects. These represent the WiFi connection history,
46  * and are used by nwamd to identify and connect to known WLANs in
47  * scan results.
48  */
49 
50 static nwam_error_t valid_keyname(nwam_value_t);
51 static nwam_error_t valid_keyslot(nwam_value_t);
52 static nwam_error_t valid_secmode(nwam_value_t);
53 
54 struct nwam_prop_table_entry known_wlan_prop_table_entries[] = {
55 	{NWAM_KNOWN_WLAN_PROP_PRIORITY, NWAM_VALUE_TYPE_UINT64, B_FALSE,
56 	    1, 1, nwam_valid_uint64,
57 	    "specifies priority of known WLAN - lower values are prioritized",
58 	    NWAM_TYPE_ANY, NWAM_CLASS_ANY},
59 	{NWAM_KNOWN_WLAN_PROP_BSSIDS, NWAM_VALUE_TYPE_STRING, B_FALSE,
60 	    0, NWAM_MAX_NUM_VALUES, nwam_valid_mac_addr,
61 	    "specifies BSSID(s) (of the form aa:bb:cc:dd:ee:ff) associated "
62 	    "with known WLAN",
63 	    NWAM_TYPE_ANY, NWAM_CLASS_ANY},
64 	{NWAM_KNOWN_WLAN_PROP_KEYNAME, NWAM_VALUE_TYPE_STRING, B_FALSE,
65 	    0, 1, valid_keyname,
66 	    "specifies security key name used with known WLAN",
67 	    NWAM_TYPE_ANY, NWAM_CLASS_ANY},
68 	{NWAM_KNOWN_WLAN_PROP_KEYSLOT, NWAM_VALUE_TYPE_UINT64, B_FALSE,
69 	    0, 1, valid_keyslot,
70 	    "specifies key slot [1-4] for security key used with known WLAN",
71 	    NWAM_TYPE_ANY, NWAM_CLASS_ANY},
72 	{NWAM_KNOWN_WLAN_PROP_SECURITY_MODE, NWAM_VALUE_TYPE_UINT64, B_FALSE,
73 	    0, 1, valid_secmode,
74 	    "specifies security mode used for known WLAN",
75 	    NWAM_TYPE_ANY, NWAM_CLASS_ANY}
76 };
77 
78 #define	NWAM_NUM_KNOWN_WLAN_PROPS	\
79 		(sizeof (known_wlan_prop_table_entries) / \
80 		sizeof (*known_wlan_prop_table_entries))
81 
82 struct nwam_prop_table known_wlan_prop_table =
83 	{ NWAM_NUM_KNOWN_WLAN_PROPS, known_wlan_prop_table_entries };
84 
85 nwam_error_t
86 nwam_known_wlan_read(const char *name, uint64_t flags,
87     nwam_known_wlan_handle_t *kwhp)
88 {
89 	return (nwam_read(NWAM_OBJECT_TYPE_KNOWN_WLAN,
90 	    NWAM_KNOWN_WLAN_CONF_FILE, name, flags, kwhp));
91 }
92 
93 nwam_error_t
94 nwam_known_wlan_create(const char *name, nwam_known_wlan_handle_t *kwhp)
95 {
96 	nwam_error_t err;
97 	nwam_value_t priorityval = NULL;
98 
99 	assert(kwhp != NULL && name != NULL);
100 
101 	if ((err = nwam_create(NWAM_OBJECT_TYPE_KNOWN_WLAN,
102 	    NWAM_KNOWN_WLAN_CONF_FILE, name, kwhp)) != NWAM_SUCCESS)
103 		return (err);
104 
105 	/*
106 	 * Create new object list for known WLAN.  The initial priority is
107 	 * also set.
108 	 */
109 	if ((err = nwam_alloc_object_list(&((*kwhp)->nwh_data)))
110 	    != NWAM_SUCCESS)
111 		goto finish;
112 	if ((err = nwam_value_create_uint64(0, &priorityval)) != NWAM_SUCCESS)
113 		goto finish;
114 	err = nwam_set_prop_value((*kwhp)->nwh_data,
115 	    NWAM_KNOWN_WLAN_PROP_PRIORITY, priorityval);
116 
117 finish:
118 	nwam_value_free(priorityval);
119 	if (err != NWAM_SUCCESS) {
120 		nwam_known_wlan_free(*kwhp);
121 		*kwhp = NULL;
122 	}
123 	return (err);
124 }
125 
126 nwam_error_t
127 nwam_known_wlan_get_name(nwam_known_wlan_handle_t kwh, char **namep)
128 {
129 	return (nwam_get_name(kwh, namep));
130 }
131 
132 nwam_error_t
133 nwam_known_wlan_set_name(nwam_known_wlan_handle_t kwh, const char *name)
134 {
135 	return (nwam_set_name(kwh, name));
136 }
137 
138 boolean_t
139 nwam_known_wlan_can_set_name(nwam_known_wlan_handle_t kwh)
140 {
141 	return (!kwh->nwh_committed);
142 }
143 
144 /*
145  * Used to store wlan names/priorities for prioritized walk.
146  */
147 struct nwam_wlan_info {
148 	char *wlan_name;
149 	uint64_t wlan_priority;
150 	boolean_t wlan_walked;
151 };
152 
153 struct nwam_wlan_info_list {
154 	struct nwam_wlan_info **list;
155 	uint_t num_wlans;
156 };
157 
158 /*
159  * Used to read in each known WLAN name/priority.
160  */
161 static int
162 get_wlans_cb(nwam_known_wlan_handle_t kwh, void *data)
163 {
164 	struct nwam_wlan_info_list *wil = data;
165 	struct nwam_wlan_info **list = wil->list;
166 	struct nwam_wlan_info **newlist = NULL;
167 	nwam_error_t err;
168 	nwam_value_t priorityval = NULL;
169 	uint_t num_wlans = wil->num_wlans;
170 
171 	/* Reallocate WLAN list and allocate new info list element. */
172 	if ((newlist = realloc(list,
173 	    sizeof (struct nwam_wlan_info *) * ++num_wlans)) == NULL ||
174 	    (newlist[num_wlans - 1] = calloc(1,
175 	    sizeof (struct nwam_wlan_info))) == NULL) {
176 		if (newlist != NULL)
177 			free(newlist);
178 		return (NWAM_NO_MEMORY);
179 	}
180 
181 	/* Update list since realloc() may have relocated it */
182 	wil->list = newlist;
183 
184 	/* Retrieve name/priority */
185 	if ((err = nwam_known_wlan_get_name(kwh,
186 	    &((newlist[num_wlans - 1])->wlan_name))) != NWAM_SUCCESS ||
187 	    (err = nwam_known_wlan_get_prop_value(kwh,
188 	    NWAM_KNOWN_WLAN_PROP_PRIORITY, &priorityval)) != NWAM_SUCCESS ||
189 	    (err = nwam_value_get_uint64(priorityval,
190 	    &((newlist[num_wlans - 1])->wlan_priority))) != NWAM_SUCCESS) {
191 		free(newlist[num_wlans - 1]->wlan_name);
192 		nwam_value_free(priorityval);
193 		free(newlist[num_wlans - 1]);
194 		return (err);
195 	}
196 	nwam_value_free(priorityval);
197 
198 	(newlist[num_wlans - 1])->wlan_walked = B_FALSE;
199 
200 	wil->num_wlans = num_wlans;
201 
202 	return (NWAM_SUCCESS);
203 }
204 
205 /*
206  * Some recursion is required here, since if _WALK_PRIORITY_ORDER is specified,
207  * we need to first walk the list of known WLANs to retrieve names
208  * and priorities, then utilize that list to carry out an in-order walk.
209  */
210 nwam_error_t
211 nwam_walk_known_wlans(int(*cb)(nwam_known_wlan_handle_t, void *), void *data,
212     uint64_t flags, int *retp)
213 {
214 	nwam_known_wlan_handle_t kwh;
215 	nwam_error_t err;
216 	int ret = 0;
217 
218 	assert(cb != NULL);
219 
220 	if ((err = nwam_valid_flags(flags,
221 	    NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER | NWAM_FLAG_BLOCKING))
222 	    != NWAM_SUCCESS)
223 		return (err);
224 
225 	if ((flags & NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER) != 0) {
226 		struct nwam_wlan_info_list wil = { NULL, 0};
227 		uint64_t iflags = flags &~
228 		    NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER;
229 		uint64_t minpriority;
230 		int errval, i, j, minindex;
231 
232 		if (nwam_walk_known_wlans(get_wlans_cb, &wil, iflags, &errval)
233 		    != NWAM_SUCCESS) {
234 			err = (nwam_error_t)errval;
235 			goto done;
236 		}
237 
238 		err = NWAM_SUCCESS;
239 
240 		for (i = 0; i < wil.num_wlans; i++) {
241 			/* Find lowest priority value not walked so far. */
242 			minpriority = (uint64_t)-1;
243 			for (j = 0; j < wil.num_wlans; j++) {
244 				if (wil.list[j]->wlan_priority < minpriority &&
245 				    !(wil.list[j]->wlan_walked)) {
246 					minpriority =
247 					    wil.list[j]->wlan_priority;
248 					minindex = j;
249 				}
250 			}
251 			wil.list[minindex]->wlan_walked = B_TRUE;
252 			if ((err = nwam_known_wlan_read
253 			    (wil.list[minindex]->wlan_name,
254 			    iflags, &kwh)) != NWAM_SUCCESS) {
255 				goto done;
256 			}
257 			ret = cb(kwh, data);
258 			if (ret != 0) {
259 				nwam_known_wlan_free(kwh);
260 				err = NWAM_WALK_HALTED;
261 				goto done;
262 			}
263 			nwam_known_wlan_free(kwh);
264 		}
265 done:
266 		if (wil.list != NULL) {
267 			for (j = 0; j < wil.num_wlans; j++) {
268 				free(wil.list[j]->wlan_name);
269 				free(wil.list[j]);
270 			}
271 			free(wil.list);
272 		}
273 		if (retp != NULL)
274 			*retp = ret;
275 		return (err);
276 	}
277 
278 	return (nwam_walk(NWAM_OBJECT_TYPE_KNOWN_WLAN,
279 	    NWAM_KNOWN_WLAN_CONF_FILE, cb, data, flags, retp, NULL));
280 }
281 
282 void
283 nwam_known_wlan_free(nwam_known_wlan_handle_t kwh)
284 {
285 	nwam_free(kwh);
286 }
287 
288 nwam_error_t
289 nwam_known_wlan_copy(nwam_known_wlan_handle_t oldkwh, const char *newname,
290     nwam_known_wlan_handle_t *newkwhp)
291 {
292 	return (nwam_copy(NWAM_KNOWN_WLAN_CONF_FILE, oldkwh, newname, newkwhp));
293 }
294 
295 nwam_error_t
296 nwam_known_wlan_delete_prop(nwam_known_wlan_handle_t kwh, const char *propname)
297 {
298 	nwam_error_t err;
299 	void *olddata;
300 
301 	assert(kwh != NULL && propname != NULL);
302 
303 	/*
304 	 * Duplicate data, remove property and validate. If validation
305 	 * fails, revert to data duplicated prior to remove.
306 	 */
307 	if ((err = nwam_dup_object_list(kwh->nwh_data, &olddata))
308 	    != NWAM_SUCCESS)
309 		return (err);
310 	if ((err = nwam_delete_prop(kwh->nwh_data, propname)) != NWAM_SUCCESS) {
311 		nwam_free_object_list(kwh->nwh_data);
312 		kwh->nwh_data = olddata;
313 		return (err);
314 	}
315 	if ((err = nwam_known_wlan_validate(kwh, NULL)) != NWAM_SUCCESS) {
316 		nwam_free_object_list(kwh->nwh_data);
317 		kwh->nwh_data = olddata;
318 		return (err);
319 	}
320 	nwam_free_object_list(olddata);
321 
322 	return (NWAM_SUCCESS);
323 }
324 
325 nwam_error_t
326 nwam_known_wlan_set_prop_value(nwam_known_wlan_handle_t kwh,
327     const char *propname, nwam_value_t value)
328 {
329 	nwam_error_t err;
330 
331 	assert(kwh != NULL && propname != NULL && value != NULL);
332 
333 	if ((err = nwam_known_wlan_validate_prop(kwh, propname, value))
334 	    != NWAM_SUCCESS)
335 		return (err);
336 
337 	return (nwam_set_prop_value(kwh->nwh_data, propname, value));
338 }
339 
340 nwam_error_t
341 nwam_known_wlan_get_prop_value(nwam_known_wlan_handle_t kwh,
342     const char *propname, nwam_value_t *valuep)
343 {
344 	return (nwam_get_prop_value(kwh->nwh_data, propname, valuep));
345 }
346 
347 nwam_error_t
348 nwam_known_wlan_walk_props(nwam_known_wlan_handle_t kwh,
349     int (*cb)(const char *, nwam_value_t, void *),
350     void *data, uint64_t flags, int *retp)
351 {
352 	return (nwam_walk_props(kwh, cb, data, flags, retp));
353 }
354 
355 struct priority_collision_data {
356 	char *wlan_name;
357 	uint64_t priority;
358 };
359 
360 static int
361 avoid_priority_collisions_cb(nwam_known_wlan_handle_t kwh, void *data)
362 {
363 	nwam_value_t priorityval;
364 	nwam_error_t err;
365 	struct priority_collision_data *pcd = data;
366 	char *name;
367 	uint64_t priority;
368 
369 	err = nwam_known_wlan_get_name(kwh, &name);
370 	if (err != NWAM_SUCCESS)
371 		return (err);
372 	if (strcmp(name, pcd->wlan_name) == 0) {
373 		/* skip to-be-updated wlan */
374 		free(name);
375 		return (NWAM_SUCCESS);
376 	}
377 	free(name);
378 
379 	err = nwam_known_wlan_get_prop_value(kwh, NWAM_KNOWN_WLAN_PROP_PRIORITY,
380 	    &priorityval);
381 	if (err != NWAM_SUCCESS)
382 		return (err);
383 	err = nwam_value_get_uint64(priorityval, &priority);
384 	if (err != NWAM_SUCCESS)
385 		return (err);
386 	nwam_value_free(priorityval);
387 
388 	if (priority < pcd->priority)
389 		return (NWAM_SUCCESS);
390 
391 	if (priority == pcd->priority) {
392 		/* Two priority values collide.  Move this one up. */
393 		err = nwam_value_create_uint64(priority + 1, &priorityval);
394 		if (err != NWAM_SUCCESS)
395 			return (err);
396 		err = nwam_known_wlan_set_prop_value(kwh,
397 		    NWAM_KNOWN_WLAN_PROP_PRIORITY, priorityval);
398 		nwam_value_free(priorityval);
399 		if (err != NWAM_SUCCESS) {
400 			return (err);
401 		}
402 		/*
403 		 * We are doing a walk, and will continue shifting until
404 		 * we find a gap in the priority numbers; thus no need to
405 		 * do collision checking here.
406 		 */
407 		err = nwam_known_wlan_commit(kwh,
408 		    NWAM_FLAG_KNOWN_WLAN_NO_COLLISION_CHECK);
409 		if (err != NWAM_SUCCESS)
410 			return (err);
411 
412 		(pcd->priority)++;
413 		return (NWAM_SUCCESS);
414 	}
415 
416 	/*
417 	 * Only possiblity left at this point is that we're looking
418 	 * at a priority greater than the last one we wrote, so we've
419 	 * found a gap.  We can halt the walk now.
420 	 */
421 	return (NWAM_WALK_HALTED);
422 }
423 
424 nwam_error_t
425 nwam_known_wlan_commit(nwam_known_wlan_handle_t kwh, uint64_t flags)
426 {
427 	nwam_error_t err;
428 	nwam_value_t priorityval;
429 	int ret = 0;
430 	struct priority_collision_data pcd;
431 
432 	assert(kwh != NULL && kwh->nwh_data != NULL);
433 
434 	if ((err = nwam_known_wlan_validate(kwh, NULL)) != NWAM_SUCCESS)
435 		return (err);
436 
437 	/*
438 	 * If the NO_COLLISION_CHECK flag is set, no need to check for
439 	 * collision.
440 	 */
441 	if (flags & NWAM_FLAG_KNOWN_WLAN_NO_COLLISION_CHECK)
442 		return (nwam_commit(NWAM_KNOWN_WLAN_CONF_FILE, kwh,
443 		    (flags & NWAM_FLAG_GLOBAL_MASK) |
444 		    NWAM_FLAG_ENTITY_KNOWN_WLAN));
445 
446 	/*
447 	 * We need to do priority checking.  Walk the list, looking
448 	 * for the first entry with priority greater than or equal
449 	 * to the entry we're adding.  Commit the new one (without
450 	 * doing additional checking), and then increment other
451 	 * entries as needed.
452 	 */
453 	err = nwam_known_wlan_get_prop_value(kwh,
454 	    NWAM_KNOWN_WLAN_PROP_PRIORITY, &priorityval);
455 	if (err != NWAM_SUCCESS)
456 		return (err);
457 	err = nwam_value_get_uint64(priorityval, &(pcd.priority));
458 	nwam_value_free(priorityval);
459 	if (err != NWAM_SUCCESS)
460 		return (err);
461 	err = nwam_known_wlan_get_name(kwh, &(pcd.wlan_name));
462 	if (err != NWAM_SUCCESS)
463 		return (err);
464 	err = nwam_walk_known_wlans(avoid_priority_collisions_cb, &pcd,
465 	    NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER, &ret);
466 	free(pcd.wlan_name);
467 	/*
468 	 * a halted walk is okay, it just means we didn't have
469 	 * to walk the entire list to resolve priorities
470 	 */
471 	if (ret != NWAM_SUCCESS && ret != NWAM_WALK_HALTED)
472 		return (ret);
473 
474 	return (nwam_known_wlan_commit(kwh,
475 	    flags | NWAM_FLAG_KNOWN_WLAN_NO_COLLISION_CHECK));
476 }
477 
478 nwam_error_t
479 nwam_known_wlan_destroy(nwam_known_wlan_handle_t kwh, uint64_t flags)
480 {
481 	return (nwam_destroy(NWAM_KNOWN_WLAN_CONF_FILE, kwh,
482 	    flags | NWAM_FLAG_ENTITY_KNOWN_WLAN));
483 }
484 
485 nwam_error_t
486 nwam_known_wlan_get_prop_description(const char *propname,
487     const char **descriptionp)
488 {
489 	return (nwam_get_prop_description(known_wlan_prop_table, propname,
490 	    descriptionp));
491 }
492 
493 /* Property-specific value validation functions should go here. */
494 
495 static nwam_error_t
496 valid_keyname(nwam_value_t value)
497 {
498 	char *keyname;
499 
500 	if (nwam_value_get_string(value, &keyname) != NWAM_SUCCESS)
501 		return (NWAM_ENTITY_INVALID_VALUE);
502 
503 	if (!dladm_valid_secobj_name(keyname))
504 		return (NWAM_ENTITY_INVALID_VALUE);
505 
506 	return (NWAM_SUCCESS);
507 }
508 
509 static nwam_error_t
510 valid_keyslot(nwam_value_t value)
511 {
512 	uint64_t keyslot;
513 
514 	if (nwam_value_get_uint64(value, &keyslot) != NWAM_SUCCESS)
515 		return (NWAM_ENTITY_INVALID_VALUE);
516 
517 	if (keyslot < 1 || keyslot > 4)
518 		return (NWAM_ENTITY_INVALID_VALUE);
519 
520 	return (NWAM_SUCCESS);
521 }
522 
523 static nwam_error_t
524 valid_secmode(nwam_value_t value)
525 {
526 	uint64_t secmode;
527 
528 	if (nwam_value_get_uint64(value, &secmode) != NWAM_SUCCESS)
529 		return (NWAM_ENTITY_INVALID_VALUE);
530 
531 	if (secmode != DLADM_WLAN_SECMODE_NONE &&
532 	    secmode != DLADM_WLAN_SECMODE_WEP &&
533 	    secmode != DLADM_WLAN_SECMODE_WPA)
534 		return (NWAM_ENTITY_INVALID_VALUE);
535 
536 	return (NWAM_SUCCESS);
537 }
538 
539 nwam_error_t
540 nwam_known_wlan_validate(nwam_known_wlan_handle_t kwh, const char **errpropp)
541 {
542 	return (nwam_validate(known_wlan_prop_table, kwh, errpropp));
543 }
544 
545 nwam_error_t
546 nwam_known_wlan_validate_prop(nwam_known_wlan_handle_t kwh,
547     const char *propname, nwam_value_t value)
548 {
549 	return (nwam_validate_prop(known_wlan_prop_table, kwh, propname,
550 	    value));
551 }
552 
553 /*
554  * Given a property, return expected property data type
555  */
556 nwam_error_t
557 nwam_known_wlan_get_prop_type(const char *propname, nwam_value_type_t *typep)
558 {
559 	return (nwam_get_prop_type(known_wlan_prop_table, propname, typep));
560 }
561 
562 nwam_error_t
563 nwam_known_wlan_prop_multivalued(const char *propname, boolean_t *multip)
564 {
565 	return (nwam_prop_multivalued(known_wlan_prop_table, propname, multip));
566 }
567 
568 nwam_error_t
569 nwam_known_wlan_get_default_proplist(const char ***prop_list,
570     uint_t *numvaluesp)
571 {
572 	return (nwam_get_default_proplist(known_wlan_prop_table,
573 	    NWAM_TYPE_ANY, NWAM_CLASS_ANY, prop_list, numvaluesp));
574 }
575 
576 /*
577  * Add the given ESSID, BSSID, secmode, keyslot and key name to known WLANs.
578  * BSSID and keyname can be NULL.
579  */
580 nwam_error_t
581 nwam_known_wlan_add_to_known_wlans(const char *essid, const char *bssid,
582     uint32_t secmode, uint_t keyslot, const char *keyname)
583 {
584 	nwam_known_wlan_handle_t kwh;
585 	nwam_value_t keynameval = NULL, keyslotval = NULL, bssidsval = NULL;
586 	nwam_value_t secmodeval = NULL, priorityval = NULL;
587 	char **old_bssids = NULL, **new_bssids;
588 	uint_t nelem = 0;
589 	nwam_error_t err;
590 	int i, j;
591 
592 	/*
593 	 * Check if the given ESSID already exists as known WLAN.  If so,
594 	 * add the BSSID to the bssids property.  If not, create one with
595 	 * the given ESSID and add BSSID if given.
596 	 */
597 	err = nwam_known_wlan_read(essid, 0, &kwh);
598 
599 	switch (err) {
600 	case NWAM_ENTITY_NOT_FOUND:
601 		if ((err = nwam_known_wlan_create(essid, &kwh)) != NWAM_SUCCESS)
602 			return (err);
603 		/* New known WLAN - set priority to 0 */
604 		if ((err = nwam_value_create_uint64(0, &priorityval))
605 		    != NWAM_SUCCESS) {
606 			nwam_known_wlan_free(kwh);
607 			return (err);
608 		}
609 		err = nwam_known_wlan_set_prop_value(kwh,
610 		    NWAM_KNOWN_WLAN_PROP_PRIORITY, priorityval);
611 		nwam_value_free(priorityval);
612 		if (err != NWAM_SUCCESS) {
613 			nwam_known_wlan_free(kwh);
614 			return (err);
615 		}
616 		/* If BSSID is NULL, nothing more to do here. */
617 		if (bssid == NULL)
618 			break;
619 		if ((err = nwam_value_create_string((char *)bssid, &bssidsval))
620 		    != NWAM_SUCCESS) {
621 			nwam_known_wlan_free(kwh);
622 			return (err);
623 		}
624 		/* Set the bssids property */
625 		err = nwam_known_wlan_set_prop_value(kwh,
626 		    NWAM_KNOWN_WLAN_PROP_BSSIDS, bssidsval);
627 		nwam_value_free(bssidsval);
628 		if (err != NWAM_SUCCESS) {
629 			nwam_known_wlan_free(kwh);
630 			return (err);
631 		}
632 		break;
633 	case NWAM_SUCCESS:
634 		/* If no bssid is specified, nothing to do */
635 		if (bssid == NULL)
636 			break;
637 
638 		/* known WLAN exists, retrieve the existing bssids property */
639 		err = nwam_known_wlan_get_prop_value(kwh,
640 		    NWAM_KNOWN_WLAN_PROP_BSSIDS, &bssidsval);
641 		if (err != NWAM_SUCCESS && err != NWAM_ENTITY_NOT_FOUND) {
642 			nwam_known_wlan_free(kwh);
643 			return (err);
644 		}
645 		if (err == NWAM_SUCCESS) {
646 			if ((err = nwam_value_get_string_array(bssidsval,
647 			    &old_bssids, &nelem)) != NWAM_SUCCESS) {
648 				nwam_value_free(bssidsval);
649 				nwam_known_wlan_free(kwh);
650 				return (err);
651 			}
652 		}
653 		/* Create a new array to append given BSSID */
654 		new_bssids = calloc(nelem + 1, sizeof (char *));
655 		if (new_bssids == NULL) {
656 			nwam_value_free(bssidsval);
657 			nwam_known_wlan_free(kwh);
658 			return (NWAM_NO_MEMORY);
659 		}
660 
661 		/*
662 		 * Copy over existing BSSIDs to the new array.  Also, check
663 		 * to make sure that the given BSSID doesn't already exist
664 		 * in the known WLAN.  If so, do abort copying and return
665 		 * NWAM_SUCCESS.
666 		 */
667 		for (i = 0; i < nelem; i++) {
668 			if (strcmp(old_bssids[i], bssid) == 0) {
669 				/* nothing to do, so free up everything */
670 				for (j = 0; j < i; j++)
671 					free(new_bssids[j]);
672 				free(new_bssids);
673 				nwam_value_free(bssidsval);
674 				goto set_key_info;
675 			}
676 			new_bssids[i] = strdup(old_bssids[i]);
677 		}
678 		new_bssids[nelem] = strdup(bssid);
679 		nwam_value_free(bssidsval);
680 
681 		err = nwam_value_create_string_array(new_bssids, nelem + 1,
682 		    &bssidsval);
683 		for (i = 0; i < nelem + 1; i++)
684 			free(new_bssids[i]);
685 		free(new_bssids);
686 		if (err != NWAM_SUCCESS) {
687 			nwam_known_wlan_free(kwh);
688 			return (err);
689 		}
690 		/* Set the bssids property */
691 		err = nwam_known_wlan_set_prop_value(kwh,
692 		    NWAM_KNOWN_WLAN_PROP_BSSIDS, bssidsval);
693 		nwam_value_free(bssidsval);
694 		if (err != NWAM_SUCCESS) {
695 			nwam_known_wlan_free(kwh);
696 			return (err);
697 		}
698 		break;
699 	default:
700 		return (err);
701 	}
702 
703 set_key_info:
704 	/* Set the security mode property */
705 	if ((err = nwam_value_create_uint64(secmode, &secmodeval))
706 	    != NWAM_SUCCESS) {
707 		nwam_known_wlan_free(kwh);
708 		return (err);
709 	}
710 	err = nwam_known_wlan_set_prop_value(kwh,
711 	    NWAM_KNOWN_WLAN_PROP_SECURITY_MODE, secmodeval);
712 	nwam_value_free(secmodeval);
713 
714 	if (err != NWAM_SUCCESS) {
715 		nwam_known_wlan_free(kwh);
716 		return (err);
717 	}
718 
719 	if (keyname != NULL) {
720 		if ((err = nwam_value_create_string((char *)keyname,
721 		    &keynameval)) != NWAM_SUCCESS) {
722 			nwam_known_wlan_free(kwh);
723 			return (err);
724 		}
725 		err = nwam_known_wlan_set_prop_value(kwh,
726 		    NWAM_KNOWN_WLAN_PROP_KEYNAME, keynameval);
727 		nwam_value_free(keynameval);
728 		if (err != NWAM_SUCCESS) {
729 			nwam_known_wlan_free(kwh);
730 			return (err);
731 		}
732 		if ((err = nwam_value_create_uint64(keyslot,
733 		    &keyslotval)) != NWAM_SUCCESS) {
734 			nwam_known_wlan_free(kwh);
735 			return (err);
736 		}
737 		err = nwam_known_wlan_set_prop_value(kwh,
738 		    NWAM_KNOWN_WLAN_PROP_KEYSLOT, keyslotval);
739 		nwam_value_free(keyslotval);
740 	}
741 
742 	err = nwam_known_wlan_commit(kwh, 0);
743 	nwam_known_wlan_free(kwh);
744 
745 	return (err);
746 }
747 
748 /*
749  * Remove the given BSSID/keyname from the bssids/keyname property for the
750  * given ESSID.
751  */
752 nwam_error_t
753 nwam_known_wlan_remove_from_known_wlans(const char *essid, const char *bssid,
754     const char *keyname)
755 {
756 	nwam_known_wlan_handle_t kwh;
757 	nwam_value_t bssidsval;
758 	char **old_bssids, **new_bssids;
759 	uint_t nelem;
760 	nwam_error_t err;
761 	int i, found = -1;
762 
763 	/* Retrieve the existing bssids */
764 	if ((err = nwam_known_wlan_read(essid, 0, &kwh)) != NWAM_SUCCESS)
765 		return (err);
766 	if ((err = nwam_known_wlan_get_prop_value(kwh,
767 	    NWAM_KNOWN_WLAN_PROP_BSSIDS, &bssidsval)) != NWAM_SUCCESS) {
768 		nwam_known_wlan_free(kwh);
769 		return (err);
770 	}
771 	if ((err = nwam_value_get_string_array(bssidsval, &old_bssids, &nelem))
772 	    != NWAM_SUCCESS) {
773 		nwam_value_free(bssidsval);
774 		nwam_known_wlan_free(kwh);
775 		return (err);
776 	}
777 
778 	/* Cycle through the BSSIDs array to find the BSSID to remove */
779 	for (i = 0; i < nelem; i++) {
780 		if (strcmp(old_bssids[i], bssid)  == 0) {
781 			found = i;
782 			break;
783 		}
784 	}
785 
786 	/* Given BSSID was not found in the array */
787 	if (found == -1) {
788 		nwam_value_free(bssidsval);
789 		nwam_known_wlan_free(kwh);
790 		return (NWAM_INVALID_ARG);
791 	}
792 
793 	/* If removing the only BSSID entry, remove the bssids property */
794 	if (nelem == 1) {
795 		nwam_value_free(bssidsval);
796 		if ((err = nwam_known_wlan_delete_prop(kwh,
797 		    NWAM_KNOWN_WLAN_PROP_BSSIDS)) != NWAM_SUCCESS) {
798 			nwam_known_wlan_free(kwh);
799 			return (err);
800 		}
801 		err = nwam_known_wlan_commit(kwh, 0);
802 		nwam_known_wlan_free(kwh);
803 		return (err);
804 	}
805 
806 	new_bssids = calloc(nelem - 1, sizeof (char *));
807 	if (new_bssids == NULL) {
808 		nwam_value_free(bssidsval);
809 		nwam_known_wlan_free(kwh);
810 		return (NWAM_NO_MEMORY);
811 	}
812 
813 	/* Copy over other BSSIDs */
814 	for (i = 0; i < found; i++)
815 		new_bssids[i] = strdup(old_bssids[i]);
816 	for (i = found + 1; i < nelem; i++)
817 		new_bssids[i-1] = strdup(old_bssids[i]);
818 	nwam_value_free(bssidsval);
819 
820 	err = nwam_value_create_string_array(new_bssids, nelem - 1, &bssidsval);
821 	for (i = 0; i < nelem - 1; i++)
822 		free(new_bssids[i]);
823 	free(new_bssids);
824 	if (err != NWAM_SUCCESS) {
825 		nwam_known_wlan_free(kwh);
826 		return (err);
827 	}
828 
829 	/* Set the bssids property */
830 	err = nwam_known_wlan_set_prop_value(kwh, NWAM_KNOWN_WLAN_PROP_BSSIDS,
831 	    bssidsval);
832 	nwam_value_free(bssidsval);
833 	if (err != NWAM_SUCCESS) {
834 		nwam_known_wlan_free(kwh);
835 		return (err);
836 	}
837 
838 	if (keyname != NULL) {
839 		if ((err = nwam_known_wlan_delete_prop(kwh,
840 		    NWAM_KNOWN_WLAN_PROP_KEYNAME)) != NWAM_SUCCESS) {
841 			nwam_known_wlan_free(kwh);
842 			return (err);
843 		}
844 		if ((err = nwam_known_wlan_delete_prop(kwh,
845 		    NWAM_KNOWN_WLAN_PROP_KEYSLOT)) != NWAM_SUCCESS) {
846 			nwam_known_wlan_free(kwh);
847 			return (err);
848 		}
849 	}
850 
851 	err = nwam_known_wlan_commit(kwh, 0);
852 	nwam_known_wlan_free(kwh);
853 
854 	return (err);
855 }
856