SUNRPC: Add support for privacy to generic gss-api code.
[safe/jmp/linux-2.6] / net / ieee80211 / ieee80211_wx.c
1 /******************************************************************************
2
3   Copyright(c) 2004 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   <jkmaline@cc.hut.fi>
9   Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.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   James P. Ketrenos <ipw2100-admin@linux.intel.com>
29   Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30
31 ******************************************************************************/
32
33 #include <linux/kmod.h>
34 #include <linux/module.h>
35
36 #include <net/ieee80211.h>
37 #include <linux/wireless.h>
38
39 static const char *ieee80211_modes[] = {
40         "?", "a", "b", "ab", "g", "ag", "bg", "abg"
41 };
42
43 #define MAX_CUSTOM_LEN 64
44 static inline char *ipw2100_translate_scan(struct ieee80211_device *ieee,
45                                            char *start, char *stop,
46                                            struct ieee80211_network *network)
47 {
48         char custom[MAX_CUSTOM_LEN];
49         char *p;
50         struct iw_event iwe;
51         int i, j;
52         u8 max_rate, rate;
53
54         /* First entry *MUST* be the AP MAC address */
55         iwe.cmd = SIOCGIWAP;
56         iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
57         memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN);
58         start = iwe_stream_add_event(start, stop, &iwe, IW_EV_ADDR_LEN);
59
60         /* Remaining entries will be displayed in the order we provide them */
61
62         /* Add the ESSID */
63         iwe.cmd = SIOCGIWESSID;
64         iwe.u.data.flags = 1;
65         if (network->flags & NETWORK_EMPTY_ESSID) {
66                 iwe.u.data.length = sizeof("<hidden>");
67                 start = iwe_stream_add_point(start, stop, &iwe, "<hidden>");
68         } else {
69                 iwe.u.data.length = min(network->ssid_len, (u8) 32);
70                 start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
71         }
72
73         /* Add the protocol name */
74         iwe.cmd = SIOCGIWNAME;
75         snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s",
76                  ieee80211_modes[network->mode]);
77         start = iwe_stream_add_event(start, stop, &iwe, IW_EV_CHAR_LEN);
78
79         /* Add mode */
80         iwe.cmd = SIOCGIWMODE;
81         if (network->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
82                 if (network->capability & WLAN_CAPABILITY_ESS)
83                         iwe.u.mode = IW_MODE_MASTER;
84                 else
85                         iwe.u.mode = IW_MODE_ADHOC;
86
87                 start = iwe_stream_add_event(start, stop, &iwe, IW_EV_UINT_LEN);
88         }
89
90         /* Add frequency/channel */
91         iwe.cmd = SIOCGIWFREQ;
92 /*      iwe.u.freq.m = ieee80211_frequency(network->channel, network->mode);
93         iwe.u.freq.e = 3; */
94         iwe.u.freq.m = network->channel;
95         iwe.u.freq.e = 0;
96         iwe.u.freq.i = 0;
97         start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN);
98
99         /* Add encryption capability */
100         iwe.cmd = SIOCGIWENCODE;
101         if (network->capability & WLAN_CAPABILITY_PRIVACY)
102                 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
103         else
104                 iwe.u.data.flags = IW_ENCODE_DISABLED;
105         iwe.u.data.length = 0;
106         start = iwe_stream_add_point(start, stop, &iwe, network->ssid);
107
108         /* Add basic and extended rates */
109         max_rate = 0;
110         p = custom;
111         p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Rates (Mb/s): ");
112         for (i = 0, j = 0; i < network->rates_len;) {
113                 if (j < network->rates_ex_len &&
114                     ((network->rates_ex[j] & 0x7F) <
115                      (network->rates[i] & 0x7F)))
116                         rate = network->rates_ex[j++] & 0x7F;
117                 else
118                         rate = network->rates[i++] & 0x7F;
119                 if (rate > max_rate)
120                         max_rate = rate;
121                 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
122                               "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
123         }
124         for (; j < network->rates_ex_len; j++) {
125                 rate = network->rates_ex[j] & 0x7F;
126                 p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
127                               "%d%s ", rate >> 1, (rate & 1) ? ".5" : "");
128                 if (rate > max_rate)
129                         max_rate = rate;
130         }
131
132         iwe.cmd = SIOCGIWRATE;
133         iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
134         iwe.u.bitrate.value = max_rate * 500000;
135         start = iwe_stream_add_event(start, stop, &iwe, IW_EV_PARAM_LEN);
136
137         iwe.cmd = IWEVCUSTOM;
138         iwe.u.data.length = p - custom;
139         if (iwe.u.data.length)
140                 start = iwe_stream_add_point(start, stop, &iwe, custom);
141
142         /* Add quality statistics */
143         /* TODO: Fix these values... */
144         iwe.cmd = IWEVQUAL;
145         iwe.u.qual.qual = network->stats.signal;
146         iwe.u.qual.level = network->stats.rssi;
147         iwe.u.qual.noise = network->stats.noise;
148         iwe.u.qual.updated = network->stats.mask & IEEE80211_STATMASK_WEMASK;
149         if (!(network->stats.mask & IEEE80211_STATMASK_RSSI))
150                 iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID;
151         if (!(network->stats.mask & IEEE80211_STATMASK_NOISE))
152                 iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID;
153         if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL))
154                 iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID;
155
156         start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN);
157
158         iwe.cmd = IWEVCUSTOM;
159         p = custom;
160
161         iwe.u.data.length = p - custom;
162         if (iwe.u.data.length)
163                 start = iwe_stream_add_point(start, stop, &iwe, custom);
164
165         if (ieee->wpa_enabled && network->wpa_ie_len) {
166                 char buf[MAX_WPA_IE_LEN * 2 + 30];
167
168                 u8 *p = buf;
169                 p += sprintf(p, "wpa_ie=");
170                 for (i = 0; i < network->wpa_ie_len; i++) {
171                         p += sprintf(p, "%02x", network->wpa_ie[i]);
172                 }
173
174                 memset(&iwe, 0, sizeof(iwe));
175                 iwe.cmd = IWEVCUSTOM;
176                 iwe.u.data.length = strlen(buf);
177                 start = iwe_stream_add_point(start, stop, &iwe, buf);
178         }
179
180         if (ieee->wpa_enabled && network->rsn_ie_len) {
181                 char buf[MAX_WPA_IE_LEN * 2 + 30];
182
183                 u8 *p = buf;
184                 p += sprintf(p, "rsn_ie=");
185                 for (i = 0; i < network->rsn_ie_len; i++) {
186                         p += sprintf(p, "%02x", network->rsn_ie[i]);
187                 }
188
189                 memset(&iwe, 0, sizeof(iwe));
190                 iwe.cmd = IWEVCUSTOM;
191                 iwe.u.data.length = strlen(buf);
192                 start = iwe_stream_add_point(start, stop, &iwe, buf);
193         }
194
195         /* Add EXTRA: Age to display seconds since last beacon/probe response
196          * for given network. */
197         iwe.cmd = IWEVCUSTOM;
198         p = custom;
199         p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
200                       " Last beacon: %lums ago",
201                       (jiffies - network->last_scanned) / (HZ / 100));
202         iwe.u.data.length = p - custom;
203         if (iwe.u.data.length)
204                 start = iwe_stream_add_point(start, stop, &iwe, custom);
205
206         return start;
207 }
208
209 int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
210                           struct iw_request_info *info,
211                           union iwreq_data *wrqu, char *extra)
212 {
213         struct ieee80211_network *network;
214         unsigned long flags;
215
216         char *ev = extra;
217         char *stop = ev + IW_SCAN_MAX_DATA;
218         int i = 0;
219
220         IEEE80211_DEBUG_WX("Getting scan\n");
221
222         spin_lock_irqsave(&ieee->lock, flags);
223
224         list_for_each_entry(network, &ieee->network_list, list) {
225                 i++;
226                 if (ieee->scan_age == 0 ||
227                     time_after(network->last_scanned + ieee->scan_age, jiffies))
228                         ev = ipw2100_translate_scan(ieee, ev, stop, network);
229                 else
230                         IEEE80211_DEBUG_SCAN("Not showing network '%s ("
231                                              MAC_FMT ")' due to age (%lums).\n",
232                                              escape_essid(network->ssid,
233                                                           network->ssid_len),
234                                              MAC_ARG(network->bssid),
235                                              (jiffies -
236                                               network->last_scanned) / (HZ /
237                                                                         100));
238         }
239
240         spin_unlock_irqrestore(&ieee->lock, flags);
241
242         wrqu->data.length = ev - extra;
243         wrqu->data.flags = 0;
244
245         IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i);
246
247         return 0;
248 }
249
250 int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
251                             struct iw_request_info *info,
252                             union iwreq_data *wrqu, char *keybuf)
253 {
254         struct iw_point *erq = &(wrqu->encoding);
255         struct net_device *dev = ieee->dev;
256         struct ieee80211_security sec = {
257                 .flags = 0
258         };
259         int i, key, key_provided, len;
260         struct ieee80211_crypt_data **crypt;
261
262         IEEE80211_DEBUG_WX("SET_ENCODE\n");
263
264         key = erq->flags & IW_ENCODE_INDEX;
265         if (key) {
266                 if (key > WEP_KEYS)
267                         return -EINVAL;
268                 key--;
269                 key_provided = 1;
270         } else {
271                 key_provided = 0;
272                 key = ieee->tx_keyidx;
273         }
274
275         IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ?
276                            "provided" : "default");
277
278         crypt = &ieee->crypt[key];
279
280         if (erq->flags & IW_ENCODE_DISABLED) {
281                 if (key_provided && *crypt) {
282                         IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n",
283                                            key);
284                         ieee80211_crypt_delayed_deinit(ieee, crypt);
285                 } else
286                         IEEE80211_DEBUG_WX("Disabling encryption.\n");
287
288                 /* Check all the keys to see if any are still configured,
289                  * and if no key index was provided, de-init them all */
290                 for (i = 0; i < WEP_KEYS; i++) {
291                         if (ieee->crypt[i] != NULL) {
292                                 if (key_provided)
293                                         break;
294                                 ieee80211_crypt_delayed_deinit(ieee,
295                                                                &ieee->crypt[i]);
296                         }
297                 }
298
299                 if (i == WEP_KEYS) {
300                         sec.enabled = 0;
301                         sec.level = SEC_LEVEL_0;
302                         sec.flags |= SEC_ENABLED | SEC_LEVEL;
303                 }
304
305                 goto done;
306         }
307
308         sec.enabled = 1;
309         sec.flags |= SEC_ENABLED;
310
311         if (*crypt != NULL && (*crypt)->ops != NULL &&
312             strcmp((*crypt)->ops->name, "WEP") != 0) {
313                 /* changing to use WEP; deinit previously used algorithm
314                  * on this key */
315                 ieee80211_crypt_delayed_deinit(ieee, crypt);
316         }
317
318         if (*crypt == NULL) {
319                 struct ieee80211_crypt_data *new_crypt;
320
321                 /* take WEP into use */
322                 new_crypt = kmalloc(sizeof(struct ieee80211_crypt_data),
323                                     GFP_KERNEL);
324                 if (new_crypt == NULL)
325                         return -ENOMEM;
326                 memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data));
327                 new_crypt->ops = ieee80211_get_crypto_ops("WEP");
328                 if (!new_crypt->ops) {
329                         request_module("ieee80211_crypt_wep");
330                         new_crypt->ops = ieee80211_get_crypto_ops("WEP");
331                 }
332
333                 if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
334                         new_crypt->priv = new_crypt->ops->init(key);
335
336                 if (!new_crypt->ops || !new_crypt->priv) {
337                         kfree(new_crypt);
338                         new_crypt = NULL;
339
340                         printk(KERN_WARNING "%s: could not initialize WEP: "
341                                "load module ieee80211_crypt_wep\n", dev->name);
342                         return -EOPNOTSUPP;
343                 }
344                 *crypt = new_crypt;
345         }
346
347         /* If a new key was provided, set it up */
348         if (erq->length > 0) {
349                 len = erq->length <= 5 ? 5 : 13;
350                 memcpy(sec.keys[key], keybuf, erq->length);
351                 if (len > erq->length)
352                         memset(sec.keys[key] + erq->length, 0,
353                                len - erq->length);
354                 IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n",
355                                    key, escape_essid(sec.keys[key], len),
356                                    erq->length, len);
357                 sec.key_sizes[key] = len;
358                 (*crypt)->ops->set_key(sec.keys[key], len, NULL,
359                                        (*crypt)->priv);
360                 sec.flags |= (1 << key);
361                 /* This ensures a key will be activated if no key is
362                  * explicitely set */
363                 if (key == sec.active_key)
364                         sec.flags |= SEC_ACTIVE_KEY;
365         } else {
366                 len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN,
367                                              NULL, (*crypt)->priv);
368                 if (len == 0) {
369                         /* Set a default key of all 0 */
370                         IEEE80211_DEBUG_WX("Setting key %d to all zero.\n",
371                                            key);
372                         memset(sec.keys[key], 0, 13);
373                         (*crypt)->ops->set_key(sec.keys[key], 13, NULL,
374                                                (*crypt)->priv);
375                         sec.key_sizes[key] = 13;
376                         sec.flags |= (1 << key);
377                 }
378
379                 /* No key data - just set the default TX key index */
380                 if (key_provided) {
381                         IEEE80211_DEBUG_WX
382                             ("Setting key %d to default Tx key.\n", key);
383                         ieee->tx_keyidx = key;
384                         sec.active_key = key;
385                         sec.flags |= SEC_ACTIVE_KEY;
386                 }
387         }
388
389       done:
390         ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED);
391         sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : WLAN_AUTH_SHARED_KEY;
392         sec.flags |= SEC_AUTH_MODE;
393         IEEE80211_DEBUG_WX("Auth: %s\n", sec.auth_mode == WLAN_AUTH_OPEN ?
394                            "OPEN" : "SHARED KEY");
395
396         /* For now we just support WEP, so only set that security level...
397          * TODO: When WPA is added this is one place that needs to change */
398         sec.flags |= SEC_LEVEL;
399         sec.level = SEC_LEVEL_1;        /* 40 and 104 bit WEP */
400
401         if (ieee->set_security)
402                 ieee->set_security(dev, &sec);
403
404         /* Do not reset port if card is in Managed mode since resetting will
405          * generate new IEEE 802.11 authentication which may end up in looping
406          * with IEEE 802.1X.  If your hardware requires a reset after WEP
407          * configuration (for example... Prism2), implement the reset_port in
408          * the callbacks structures used to initialize the 802.11 stack. */
409         if (ieee->reset_on_keychange &&
410             ieee->iw_mode != IW_MODE_INFRA &&
411             ieee->reset_port && ieee->reset_port(dev)) {
412                 printk(KERN_DEBUG "%s: reset_port failed\n", dev->name);
413                 return -EINVAL;
414         }
415         return 0;
416 }
417
418 int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
419                             struct iw_request_info *info,
420                             union iwreq_data *wrqu, char *keybuf)
421 {
422         struct iw_point *erq = &(wrqu->encoding);
423         int len, key;
424         struct ieee80211_crypt_data *crypt;
425
426         IEEE80211_DEBUG_WX("GET_ENCODE\n");
427
428         key = erq->flags & IW_ENCODE_INDEX;
429         if (key) {
430                 if (key > WEP_KEYS)
431                         return -EINVAL;
432                 key--;
433         } else
434                 key = ieee->tx_keyidx;
435
436         crypt = ieee->crypt[key];
437         erq->flags = key + 1;
438
439         if (crypt == NULL || crypt->ops == NULL) {
440                 erq->length = 0;
441                 erq->flags |= IW_ENCODE_DISABLED;
442                 return 0;
443         }
444
445         if (strcmp(crypt->ops->name, "WEP") != 0) {
446                 /* only WEP is supported with wireless extensions, so just
447                  * report that encryption is used */
448                 erq->length = 0;
449                 erq->flags |= IW_ENCODE_ENABLED;
450                 return 0;
451         }
452
453         len = crypt->ops->get_key(keybuf, WEP_KEY_LEN, NULL, crypt->priv);
454         erq->length = (len >= 0 ? len : 0);
455
456         erq->flags |= IW_ENCODE_ENABLED;
457
458         if (ieee->open_wep)
459                 erq->flags |= IW_ENCODE_OPEN;
460         else
461                 erq->flags |= IW_ENCODE_RESTRICTED;
462
463         return 0;
464 }
465
466 EXPORT_SYMBOL(ieee80211_wx_get_scan);
467 EXPORT_SYMBOL(ieee80211_wx_set_encode);
468 EXPORT_SYMBOL(ieee80211_wx_get_encode);