1 /******************************************************************************
2 
3   Copyright(c) 2004-2005 Intel Corporation. All rights reserved.
4 
5   Portions of this file are based on the WEP enablement code provided by the
6   Host AP project hostap-drivers v0.1.3
7   Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
8   <j@w1.fi>
9   Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
10 
11   This program is free software; you can redistribute it and/or modify it
12   under the terms of version 2 of the GNU General Public License as
13   published by the Free Software Foundation.
14 
15   This program is distributed in the hope that it will be useful, but WITHOUT
16   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
18   more details.
19 
20   You should have received a copy of the GNU General Public License along with
21   this program; if not, write to the Free Software Foundation, Inc., 59
22   Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23 
24   The full GNU General Public License is included in this distribution in the
25   file called LICENSE.
26 
27   Contact Information:
28   Intel Linux Wireless <ilw@linux.intel.com>
29   Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30 
31 ******************************************************************************/
32 
33 #include <linux/hardirq.h>
34 #include <linux/kmod.h>
35 #include <linux/slab.h>
36 #include <linux/module.h>
37 #include <linux/jiffies.h>
38 
39 #include <net/lib80211.h>
40 #include <linux/wireless.h>
41 
42 #include "libipw.h"
43 
44 static const char *libipw_modes[] = {
45 	"?", "a", "b", "ab", "g", "ag", "bg", "abg"
46 };
47 
elapsed_jiffies_msecs(unsigned long start)48 static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
49 {
50 	unsigned long end = jiffies;
51 
52 	if (end >= start)
53 		return jiffies_to_msecs(end - start);
54 
55 	return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
56 }
57 
58 #define MAX_CUSTOM_LEN 64
libipw_translate_scan(struct libipw_device * ieee,char * start,char * stop,struct libipw_network * network,struct iw_request_info * info)59 static char *libipw_translate_scan(struct libipw_device *ieee,
60 				      char *start, char *stop,
61 				      struct libipw_network *network,
62 				      struct iw_request_info *info)
63 {
64 	char custom[MAX_CUSTOM_LEN];
65 	char *p;
66 	struct iw_event iwe;
67 	int i, j;
68 	char *current_val;	/* For rates */
69 	u8 rate;
70 
71 	/* First entry *MUST* be the AP MAC address */
72 	iwe.cmd = SIOCGIWAP;
73 	iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
74 	memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
75 	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN);
76 
77 	/* Remaining entries will be displayed in the order we provide them */
78 
79 	/* Add the ESSID */
80 	iwe.cmd = SIOCGIWESSID;
81 	iwe.u.data.flags = 1;
82 	iwe.u.data.length = min(network->ssid_len, (u8) 32);
83 	start = iwe_stream_add_point(info, start, stop,
84 				     &iwe, network->ssid);
85 
86 	/* Add the protocol name */
87 	iwe.cmd = SIOCGIWNAME;
88 	snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
89 		 libipw_modes[network->mode]);
90 	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN);
91 
92 	/* Add mode */
93 	iwe.cmd = SIOCGIWMODE;
94 	if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
95 		if (network->capability & WLAN_CAPABILITY_ESS)
96 			iwe.u.mode = IW_MODE_MASTER;
97 		else
98 			iwe.u.mode = IW_MODE_ADHOC;
99 
100 		start = iwe_stream_add_event(info, start, stop,
101 					     &iwe, IW_EV_UINT_LEN);
102 	}
103 
104 	/* Add channel and frequency */
105 	/* Note : userspace automatically computes channel using iwrange */
106 	iwe.cmd = SIOCGIWFREQ;
107 	iwe.u.freq.m = libipw_channel_to_freq(ieee, network->channel);
108 	iwe.u.freq.e = 6;
109 	iwe.u.freq.i = 0;
110 	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN);
111 
112 	/* Add encryption capability */
113 	iwe.cmd = SIOCGIWENCODE;
114 	if (network->capability & WLAN_CAPABILITY_PRIVACY)
115 		iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
116 	else
117 		iwe.u.data.flags = IW_ENCODE_DISABLED;
118 	iwe.u.data.length = 0;
119 	start = iwe_stream_add_point(info, start, stop,
120 				     &iwe, network->ssid);
121 
122 	/* Add basic and extended rates */
123 	/* Rate : stuffing multiple values in a single event require a bit
124 	 * more of magic - Jean II */
125 	current_val = start + iwe_stream_lcp_len(info);
126 	iwe.cmd = SIOCGIWRATE;
127 	/* Those two flags are ignored... */
128 	iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
129 
130 	for (i = 0, j = 0; i < network->rates_len;) {
131 		if (j < network->rates_ex_len &&
132 		    ((network->rates_ex[j] & 0x7F) <
133 		     (network->rates[i] & 0x7F)))
134 			rate = network->rates_ex[j++] & 0x7F;
135 		else
136 			rate = network->rates[i++] & 0x7F;
137 		/* Bit rate given in 500 kb/s units (+ 0x80) */
138 		iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
139 		/* Add new value to event */
140 		current_val = iwe_stream_add_value(info, start, current_val,
141 						   stop, &iwe, IW_EV_PARAM_LEN);
142 	}
143 	for (; j < network->rates_ex_len; j++) {
144 		rate = network->rates_ex[j] & 0x7F;
145 		/* Bit rate given in 500 kb/s units (+ 0x80) */
146 		iwe.u.bitrate.value = ((rate & 0x7f) * 500000);
147 		/* Add new value to event */
148 		current_val = iwe_stream_add_value(info, start, current_val,
149 						   stop, &iwe, IW_EV_PARAM_LEN);
150 	}
151 	/* Check if we added any rate */
152 	if ((current_val - start) > iwe_stream_lcp_len(info))
153 		start = current_val;
154 
155 	/* Add quality statistics */
156 	iwe.cmd = IWEVQUAL;
157 	iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
158 	    IW_QUAL_NOISE_UPDATED;
159 
160 	if (!(network->stats.mask & LIBIPW_STATMASK_RSSI)) {
161 		iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID |
162 		    IW_QUAL_LEVEL_INVALID;
163 		iwe.u.qual.qual = 0;
164 	} else {
165 		if (ieee->perfect_rssi == ieee->worst_rssi)
166 			iwe.u.qual.qual = 100;
167 		else
168 			iwe.u.qual.qual =
169 			    (100 *
170 			     (ieee->perfect_rssi - ieee->worst_rssi) *
171 			     (ieee->perfect_rssi - ieee->worst_rssi) -
172 			     (ieee->perfect_rssi - network->stats.rssi) *
173 			     (15 * (ieee->perfect_rssi - ieee->worst_rssi) +
174 			      62 * (ieee->perfect_rssi -
175 				    network->stats.rssi))) /
176 			    ((ieee->perfect_rssi -
177 			      ieee->worst_rssi) * (ieee->perfect_rssi -
178 						   ieee->worst_rssi));
179 		if (iwe.u.qual.qual > 100)
180 			iwe.u.qual.qual = 100;
181 		else if (iwe.u.qual.qual < 1)
182 			iwe.u.qual.qual = 0;
183 	}
184 
185 	if (!(network->stats.mask & LIBIPW_STATMASK_NOISE)) {
186 		iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
187 		iwe.u.qual.noise = 0;
188 	} else {
189 		iwe.u.qual.noise = network->stats.noise;
190 	}
191 
192 	if (!(network->stats.mask & LIBIPW_STATMASK_SIGNAL)) {
193 		iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
194 		iwe.u.qual.level = 0;
195 	} else {
196 		iwe.u.qual.level = network->stats.signal;
197 	}
198 
199 	start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN);
200 
201 	iwe.cmd = IWEVCUSTOM;
202 	p = custom;
203 
204 	iwe.u.data.length = p - custom;
205 	if (iwe.u.data.length)
206 		start = iwe_stream_add_point(info, start, stop, &iwe, custom);
207 
208 	memset(&iwe, 0, sizeof(iwe));
209 	if (network->wpa_ie_len) {
210 		char buf[MAX_WPA_IE_LEN];
211 		memcpy(buf, network->wpa_ie, network->wpa_ie_len);
212 		iwe.cmd = IWEVGENIE;
213 		iwe.u.data.length = network->wpa_ie_len;
214 		start = iwe_stream_add_point(info, start, stop, &iwe, buf);
215 	}
216 
217 	memset(&iwe, 0, sizeof(iwe));
218 	if (network->rsn_ie_len) {
219 		char buf[MAX_WPA_IE_LEN];
220 		memcpy(buf, network->rsn_ie, network->rsn_ie_len);
221 		iwe.cmd = IWEVGENIE;
222 		iwe.u.data.length = network->rsn_ie_len;
223 		start = iwe_stream_add_point(info, start, stop, &iwe, buf);
224 	}
225 
226 	/* Add EXTRA: Age to display seconds since last beacon/probe response
227 	 * for given network. */
228 	iwe.cmd = IWEVCUSTOM;
229 	p = custom;
230 	p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
231 		      " Last beacon: %ums ago",
232 		      elapsed_jiffies_msecs(network->last_scanned));
233 	iwe.u.data.length = p - custom;
234 	if (iwe.u.data.length)
235 		start = iwe_stream_add_point(info, start, stop, &iwe, custom);
236 
237 	/* Add spectrum management information */
238 	iwe.cmd = -1;
239 	p = custom;
240 	p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: ");
241 
242 	if (libipw_get_channel_flags(ieee, network->channel) &
243 	    LIBIPW_CH_INVALID) {
244 		iwe.cmd = IWEVCUSTOM;
245 		p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID ");
246 	}
247 
248 	if (libipw_get_channel_flags(ieee, network->channel) &
249 	    LIBIPW_CH_RADAR_DETECT) {
250 		iwe.cmd = IWEVCUSTOM;
251 		p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS ");
252 	}
253 
254 	if (iwe.cmd == IWEVCUSTOM) {
255 		iwe.u.data.length = p - custom;
256 		start = iwe_stream_add_point(info, start, stop, &iwe, custom);
257 	}
258 
259 	return start;
260 }
261 
262 #define SCAN_ITEM_SIZE 128
263 
libipw_wx_get_scan(struct libipw_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)264 int libipw_wx_get_scan(struct libipw_device *ieee,
265 			  struct iw_request_info *info,
266 			  union iwreq_data *wrqu, char *extra)
267 {
268 	struct libipw_network *network;
269 	unsigned long flags;
270 	int err = 0;
271 
272 	char *ev = extra;
273 	char *stop = ev + wrqu->data.length;
274 	int i = 0;
275 	DECLARE_SSID_BUF(ssid);
276 
277 	LIBIPW_DEBUG_WX("Getting scan\n");
278 
279 	spin_lock_irqsave(&ieee->lock, flags);
280 
281 	list_for_each_entry(network, &ieee->network_list, list) {
282 		i++;
283 		if (stop - ev < SCAN_ITEM_SIZE) {
284 			err = -E2BIG;
285 			break;
286 		}
287 
288 		if (ieee->scan_age == 0 ||
289 		    time_after(network->last_scanned + ieee->scan_age, jiffies))
290 			ev = libipw_translate_scan(ieee, ev, stop, network,
291 						      info);
292 		else {
293 			LIBIPW_DEBUG_SCAN("Not showing network '%s ("
294 					     "%pM)' due to age (%ums).\n",
295 					     print_ssid(ssid, network->ssid,
296 							 network->ssid_len),
297 					     network->bssid,
298 					     elapsed_jiffies_msecs(
299 					               network->last_scanned));
300 		}
301 	}
302 
303 	spin_unlock_irqrestore(&ieee->lock, flags);
304 
305 	wrqu->data.length = ev - extra;
306 	wrqu->data.flags = 0;
307 
308 	LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i);
309 
310 	return err;
311 }
312 
libipw_wx_set_encode(struct libipw_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * keybuf)313 int libipw_wx_set_encode(struct libipw_device *ieee,
314 			    struct iw_request_info *info,
315 			    union iwreq_data *wrqu, char *keybuf)
316 {
317 	struct iw_point *erq = &(wrqu->encoding);
318 	struct net_device *dev = ieee->dev;
319 	struct libipw_security sec = {
320 		.flags = 0
321 	};
322 	int i, key, key_provided, len;
323 	struct lib80211_crypt_data **crypt;
324 	int host_crypto = ieee->host_encrypt || ieee->host_decrypt;
325 	DECLARE_SSID_BUF(ssid);
326 
327 	LIBIPW_DEBUG_WX("SET_ENCODE\n");
328 
329 	key = erq->flags & IW_ENCODE_INDEX;
330 	if (key) {
331 		if (key > WEP_KEYS)
332 			return -EINVAL;
333 		key--;
334 		key_provided = 1;
335 	} else {
336 		key_provided = 0;
337 		key = ieee->crypt_info.tx_keyidx;
338 	}
339 
340 	LIBIPW_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
341 			   "provided" : "default");
342 
343 	crypt = &ieee->crypt_info.crypt[key];
344 
345 	if (erq->flags & IW_ENCODE_DISABLED) {
346 		if (key_provided && *crypt) {
347 			LIBIPW_DEBUG_WX("Disabling encryption on key %d.\n",
348 					   key);
349 			lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
350 		} else
351 			LIBIPW_DEBUG_WX("Disabling encryption.\n");
352 
353 		/* Check all the keys to see if any are still configured,
354 		 * and if no key index was provided, de-init them all */
355 		for (i = 0; i < WEP_KEYS; i++) {
356 			if (ieee->crypt_info.crypt[i] != NULL) {
357 				if (key_provided)
358 					break;
359 				lib80211_crypt_delayed_deinit(&ieee->crypt_info,
360 							       &ieee->crypt_info.crypt[i]);
361 			}
362 		}
363 
364 		if (i == WEP_KEYS) {
365 			sec.enabled = 0;
366 			sec.encrypt = 0;
367 			sec.level = SEC_LEVEL_0;
368 			sec.flags |= SEC_ENABLED | SEC_LEVEL | SEC_ENCRYPT;
369 		}
370 
371 		goto done;
372 	}
373 
374 	sec.enabled = 1;
375 	sec.encrypt = 1;
376 	sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
377 
378 	if (*crypt != NULL && (*crypt)->ops != NULL &&
379 	    strcmp((*crypt)->ops->name, "WEP") != 0) {
380 		/* changing to use WEP; deinit previously used algorithm
381 		 * on this key */
382 		lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
383 	}
384 
385 	if (*crypt == NULL && host_crypto) {
386 		struct lib80211_crypt_data *new_crypt;
387 
388 		/* take WEP into use */
389 		new_crypt = kzalloc(sizeof(struct lib80211_crypt_data),
390 				    GFP_KERNEL);
391 		if (new_crypt == NULL)
392 			return -ENOMEM;
393 		new_crypt->ops = lib80211_get_crypto_ops("WEP");
394 		if (!new_crypt->ops) {
395 			request_module("lib80211_crypt_wep");
396 			new_crypt->ops = lib80211_get_crypto_ops("WEP");
397 		}
398 
399 		if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
400 			new_crypt->priv = new_crypt->ops->init(key);
401 
402 		if (!new_crypt->ops || !new_crypt->priv) {
403 			kfree(new_crypt);
404 			new_crypt = NULL;
405 
406 			printk(KERN_WARNING "%s: could not initialize WEP: "
407 			       "load module lib80211_crypt_wep\n", dev->name);
408 			return -EOPNOTSUPP;
409 		}
410 		*crypt = new_crypt;
411 	}
412 
413 	/* If a new key was provided, set it up */
414 	if (erq->length > 0) {
415 		len = erq->length <= 5 ? 5 : 13;
416 		memcpy(sec.keys[key], keybuf, erq->length);
417 		if (len > erq->length)
418 			memset(sec.keys[key] + erq->length, 0,
419 			       len - erq->length);
420 		LIBIPW_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
421 				   key, print_ssid(ssid, sec.keys[key], len),
422 				   erq->length, len);
423 		sec.key_sizes[key] = len;
424 		if (*crypt)
425 			(*crypt)->ops->set_key(sec.keys[key], len, NULL,
426 					       (*crypt)->priv);
427 		sec.flags |= (1 << key);
428 		/* This ensures a key will be activated if no key is
429 		 * explicitly set */
430 		if (key == sec.active_key)
431 			sec.flags |= SEC_ACTIVE_KEY;
432 
433 	} else {
434 		if (host_crypto) {
435 			len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
436 						     NULL, (*crypt)->priv);
437 			if (len == 0) {
438 				/* Set a default key of all 0 */
439 				LIBIPW_DEBUG_WX("Setting key %d to all "
440 						   "zero.\n", key);
441 				memset(sec.keys[key], 0, 13);
442 				(*crypt)->ops->set_key(sec.keys[key], 13, NULL,
443 						       (*crypt)->priv);
444 				sec.key_sizes[key] = 13;
445 				sec.flags |= (1 << key);
446 			}
447 		}
448 		/* No key data - just set the default TX key index */
449 		if (key_provided) {
450 			LIBIPW_DEBUG_WX("Setting key %d to default Tx "
451 					   "key.\n", key);
452 			ieee->crypt_info.tx_keyidx = key;
453 			sec.active_key = key;
454 			sec.flags |= SEC_ACTIVE_KEY;
455 		}
456 	}
457 	if (erq->flags & (IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED)) {
458 		ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
459 		sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN :
460 		    WLAN_AUTH_SHARED_KEY;
461 		sec.flags |= SEC_AUTH_MODE;
462 		LIBIPW_DEBUG_WX("Auth: %s\n",
463 				   sec.auth_mode == WLAN_AUTH_OPEN ?
464 				   "OPEN" : "SHARED KEY");
465 	}
466 
467 	/* For now we just support WEP, so only set that security level...
468 	 * TODO: When WPA is added this is one place that needs to change */
469 	sec.flags |= SEC_LEVEL;
470 	sec.level = SEC_LEVEL_1;	/* 40 and 104 bit WEP */
471 	sec.encode_alg[key] = SEC_ALG_WEP;
472 
473       done:
474 	if (ieee->set_security)
475 		ieee->set_security(dev, &sec);
476 
477 	return 0;
478 }
479 
libipw_wx_get_encode(struct libipw_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * keybuf)480 int libipw_wx_get_encode(struct libipw_device *ieee,
481 			    struct iw_request_info *info,
482 			    union iwreq_data *wrqu, char *keybuf)
483 {
484 	struct iw_point *erq = &(wrqu->encoding);
485 	int len, key;
486 	struct lib80211_crypt_data *crypt;
487 	struct libipw_security *sec = &ieee->sec;
488 
489 	LIBIPW_DEBUG_WX("GET_ENCODE\n");
490 
491 	key = erq->flags & IW_ENCODE_INDEX;
492 	if (key) {
493 		if (key > WEP_KEYS)
494 			return -EINVAL;
495 		key--;
496 	} else
497 		key = ieee->crypt_info.tx_keyidx;
498 
499 	crypt = ieee->crypt_info.crypt[key];
500 	erq->flags = key + 1;
501 
502 	if (!sec->enabled) {
503 		erq->length = 0;
504 		erq->flags |= IW_ENCODE_DISABLED;
505 		return 0;
506 	}
507 
508 	len = sec->key_sizes[key];
509 	memcpy(keybuf, sec->keys[key], len);
510 
511 	erq->length = len;
512 	erq->flags |= IW_ENCODE_ENABLED;
513 
514 	if (ieee->open_wep)
515 		erq->flags |= IW_ENCODE_OPEN;
516 	else
517 		erq->flags |= IW_ENCODE_RESTRICTED;
518 
519 	return 0;
520 }
521 
libipw_wx_set_encodeext(struct libipw_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)522 int libipw_wx_set_encodeext(struct libipw_device *ieee,
523 			       struct iw_request_info *info,
524 			       union iwreq_data *wrqu, char *extra)
525 {
526 	struct net_device *dev = ieee->dev;
527 	struct iw_point *encoding = &wrqu->encoding;
528 	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
529 	int i, idx, ret = 0;
530 	int group_key = 0;
531 	const char *alg, *module;
532 	struct lib80211_crypto_ops *ops;
533 	struct lib80211_crypt_data **crypt;
534 
535 	struct libipw_security sec = {
536 		.flags = 0,
537 	};
538 
539 	idx = encoding->flags & IW_ENCODE_INDEX;
540 	if (idx) {
541 		if (idx < 1 || idx > WEP_KEYS)
542 			return -EINVAL;
543 		idx--;
544 	} else
545 		idx = ieee->crypt_info.tx_keyidx;
546 
547 	if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
548 		crypt = &ieee->crypt_info.crypt[idx];
549 		group_key = 1;
550 	} else {
551 		/* some Cisco APs use idx>0 for unicast in dynamic WEP */
552 		if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP)
553 			return -EINVAL;
554 		if (ieee->iw_mode == IW_MODE_INFRA)
555 			crypt = &ieee->crypt_info.crypt[idx];
556 		else
557 			return -EINVAL;
558 	}
559 
560 	sec.flags |= SEC_ENABLED | SEC_ENCRYPT;
561 	if ((encoding->flags & IW_ENCODE_DISABLED) ||
562 	    ext->alg == IW_ENCODE_ALG_NONE) {
563 		if (*crypt)
564 			lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
565 
566 		for (i = 0; i < WEP_KEYS; i++)
567 			if (ieee->crypt_info.crypt[i] != NULL)
568 				break;
569 
570 		if (i == WEP_KEYS) {
571 			sec.enabled = 0;
572 			sec.encrypt = 0;
573 			sec.level = SEC_LEVEL_0;
574 			sec.flags |= SEC_LEVEL;
575 		}
576 		goto done;
577 	}
578 
579 	sec.enabled = 1;
580 	sec.encrypt = 1;
581 
582 	if (group_key ? !ieee->host_mc_decrypt :
583 	    !(ieee->host_encrypt || ieee->host_decrypt ||
584 	      ieee->host_encrypt_msdu))
585 		goto skip_host_crypt;
586 
587 	switch (ext->alg) {
588 	case IW_ENCODE_ALG_WEP:
589 		alg = "WEP";
590 		module = "lib80211_crypt_wep";
591 		break;
592 	case IW_ENCODE_ALG_TKIP:
593 		alg = "TKIP";
594 		module = "lib80211_crypt_tkip";
595 		break;
596 	case IW_ENCODE_ALG_CCMP:
597 		alg = "CCMP";
598 		module = "lib80211_crypt_ccmp";
599 		break;
600 	default:
601 		LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
602 				   dev->name, ext->alg);
603 		ret = -EINVAL;
604 		goto done;
605 	}
606 
607 	ops = lib80211_get_crypto_ops(alg);
608 	if (ops == NULL) {
609 		request_module(module);
610 		ops = lib80211_get_crypto_ops(alg);
611 	}
612 	if (ops == NULL) {
613 		LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n",
614 				   dev->name, ext->alg);
615 		ret = -EINVAL;
616 		goto done;
617 	}
618 
619 	if (*crypt == NULL || (*crypt)->ops != ops) {
620 		struct lib80211_crypt_data *new_crypt;
621 
622 		lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
623 
624 		new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL);
625 		if (new_crypt == NULL) {
626 			ret = -ENOMEM;
627 			goto done;
628 		}
629 		new_crypt->ops = ops;
630 		if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
631 			new_crypt->priv = new_crypt->ops->init(idx);
632 		if (new_crypt->priv == NULL) {
633 			kfree(new_crypt);
634 			ret = -EINVAL;
635 			goto done;
636 		}
637 		*crypt = new_crypt;
638 	}
639 
640 	if (ext->key_len > 0 && (*crypt)->ops->set_key &&
641 	    (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq,
642 				   (*crypt)->priv) < 0) {
643 		LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name);
644 		ret = -EINVAL;
645 		goto done;
646 	}
647 
648       skip_host_crypt:
649 	if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
650 		ieee->crypt_info.tx_keyidx = idx;
651 		sec.active_key = idx;
652 		sec.flags |= SEC_ACTIVE_KEY;
653 	}
654 
655 	if (ext->alg != IW_ENCODE_ALG_NONE) {
656 		memcpy(sec.keys[idx], ext->key, ext->key_len);
657 		sec.key_sizes[idx] = ext->key_len;
658 		sec.flags |= (1 << idx);
659 		if (ext->alg == IW_ENCODE_ALG_WEP) {
660 			sec.encode_alg[idx] = SEC_ALG_WEP;
661 			sec.flags |= SEC_LEVEL;
662 			sec.level = SEC_LEVEL_1;
663 		} else if (ext->alg == IW_ENCODE_ALG_TKIP) {
664 			sec.encode_alg[idx] = SEC_ALG_TKIP;
665 			sec.flags |= SEC_LEVEL;
666 			sec.level = SEC_LEVEL_2;
667 		} else if (ext->alg == IW_ENCODE_ALG_CCMP) {
668 			sec.encode_alg[idx] = SEC_ALG_CCMP;
669 			sec.flags |= SEC_LEVEL;
670 			sec.level = SEC_LEVEL_3;
671 		}
672 		/* Don't set sec level for group keys. */
673 		if (group_key)
674 			sec.flags &= ~SEC_LEVEL;
675 	}
676       done:
677 	if (ieee->set_security)
678 		ieee->set_security(ieee->dev, &sec);
679 
680 	return ret;
681 }
682 
libipw_wx_get_encodeext(struct libipw_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)683 int libipw_wx_get_encodeext(struct libipw_device *ieee,
684 			       struct iw_request_info *info,
685 			       union iwreq_data *wrqu, char *extra)
686 {
687 	struct iw_point *encoding = &wrqu->encoding;
688 	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
689 	struct libipw_security *sec = &ieee->sec;
690 	int idx, max_key_len;
691 
692 	max_key_len = encoding->length - sizeof(*ext);
693 	if (max_key_len < 0)
694 		return -EINVAL;
695 
696 	idx = encoding->flags & IW_ENCODE_INDEX;
697 	if (idx) {
698 		if (idx < 1 || idx > WEP_KEYS)
699 			return -EINVAL;
700 		idx--;
701 	} else
702 		idx = ieee->crypt_info.tx_keyidx;
703 
704 	if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
705 	    ext->alg != IW_ENCODE_ALG_WEP)
706 		if (idx != 0 || ieee->iw_mode != IW_MODE_INFRA)
707 			return -EINVAL;
708 
709 	encoding->flags = idx + 1;
710 	memset(ext, 0, sizeof(*ext));
711 
712 	if (!sec->enabled) {
713 		ext->alg = IW_ENCODE_ALG_NONE;
714 		ext->key_len = 0;
715 		encoding->flags |= IW_ENCODE_DISABLED;
716 	} else {
717 		if (sec->encode_alg[idx] == SEC_ALG_WEP)
718 			ext->alg = IW_ENCODE_ALG_WEP;
719 		else if (sec->encode_alg[idx] == SEC_ALG_TKIP)
720 			ext->alg = IW_ENCODE_ALG_TKIP;
721 		else if (sec->encode_alg[idx] == SEC_ALG_CCMP)
722 			ext->alg = IW_ENCODE_ALG_CCMP;
723 		else
724 			return -EINVAL;
725 
726 		ext->key_len = sec->key_sizes[idx];
727 		memcpy(ext->key, sec->keys[idx], ext->key_len);
728 		encoding->flags |= IW_ENCODE_ENABLED;
729 		if (ext->key_len &&
730 		    (ext->alg == IW_ENCODE_ALG_TKIP ||
731 		     ext->alg == IW_ENCODE_ALG_CCMP))
732 			ext->ext_flags |= IW_ENCODE_EXT_TX_SEQ_VALID;
733 
734 	}
735 
736 	return 0;
737 }
738 
739 EXPORT_SYMBOL(libipw_wx_set_encodeext);
740 EXPORT_SYMBOL(libipw_wx_get_encodeext);
741 
742 EXPORT_SYMBOL(libipw_wx_get_scan);
743 EXPORT_SYMBOL(libipw_wx_set_encode);
744 EXPORT_SYMBOL(libipw_wx_get_encode);
745