Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[linux-drm-fsl-dcu.git] / net / ieee80211 / softmac / ieee80211softmac_auth.c
1 /*
2  * This file contains the softmac's authentication logic.
3  *
4  * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
5  *                          Joseph Jezak <josejx@gentoo.org>
6  *                          Larry Finger <Larry.Finger@lwfinger.net>
7  *                          Danny van Dyk <kugelfang@gentoo.org>
8  *                          Michael Buesch <mbuesch@freenet.de>
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of version 2 of the GNU General Public License as
12  * published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
17  * more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
22  *
23  * The full GNU General Public License is included in this distribution in the
24  * file called COPYING.
25  */
26
27 #include "ieee80211softmac_priv.h"
28
29 static void ieee80211softmac_auth_queue(struct work_struct *work);
30
31 /* Queues an auth request to the desired AP */
32 int
33 ieee80211softmac_auth_req(struct ieee80211softmac_device *mac, 
34         struct ieee80211softmac_network *net)
35 {
36         struct ieee80211softmac_auth_queue_item *auth;
37         unsigned long flags;
38         
39         if (net->authenticating || net->authenticated)
40                 return 0;
41         net->authenticating = 1;
42
43         /* Add the network if it's not already added */
44         ieee80211softmac_add_network(mac, net);
45
46         dprintk(KERN_NOTICE PFX "Queueing Authentication Request to "MAC_FMT"\n", MAC_ARG(net->bssid));
47         /* Queue the auth request */
48         auth = (struct ieee80211softmac_auth_queue_item *)
49                 kmalloc(sizeof(struct ieee80211softmac_auth_queue_item), GFP_KERNEL);
50         if(auth == NULL)
51                 return -ENOMEM;
52
53         auth->net = net;
54         auth->mac = mac;
55         auth->retry = IEEE80211SOFTMAC_AUTH_RETRY_LIMIT;
56         auth->state = IEEE80211SOFTMAC_AUTH_OPEN_REQUEST;
57         INIT_DELAYED_WORK(&auth->work, ieee80211softmac_auth_queue);
58         
59         /* Lock (for list) */
60         spin_lock_irqsave(&mac->lock, flags);
61
62         /* add to list */
63         list_add_tail(&auth->list, &mac->auth_queue);
64         schedule_delayed_work(&auth->work, 0);
65         spin_unlock_irqrestore(&mac->lock, flags);
66         
67         return 0;
68 }
69
70
71 /* Sends an auth request to the desired AP and handles timeouts */
72 static void
73 ieee80211softmac_auth_queue(struct work_struct *work)
74 {
75         struct ieee80211softmac_device *mac;
76         struct ieee80211softmac_auth_queue_item *auth;
77         struct ieee80211softmac_network *net;
78         unsigned long flags;
79
80         auth = container_of(work, struct ieee80211softmac_auth_queue_item,
81                             work.work);
82         net = auth->net;
83         mac = auth->mac;
84
85         if(auth->retry > 0) {
86                 /* Switch to correct channel for this network */
87                 mac->set_channel(mac->dev, net->channel);
88                 
89                 /* Lock and set flags */
90                 spin_lock_irqsave(&mac->lock, flags);
91                 if (unlikely(!mac->running)) {
92                         /* Prevent reschedule on workqueue flush */
93                         spin_unlock_irqrestore(&mac->lock, flags);
94                         return;
95                 }
96                 net->authenticated = 0;
97                 /* add a timeout call so we eventually give up waiting for an auth reply */
98                 schedule_delayed_work(&auth->work, IEEE80211SOFTMAC_AUTH_TIMEOUT);
99                 auth->retry--;
100                 spin_unlock_irqrestore(&mac->lock, flags);
101                 if (ieee80211softmac_send_mgt_frame(mac, auth->net, IEEE80211_STYPE_AUTH, auth->state))
102                         dprintk(KERN_NOTICE PFX "Sending Authentication Request to "MAC_FMT" failed (this shouldn't happen, wait for the timeout).\n", MAC_ARG(net->bssid));
103                 else
104                         dprintk(KERN_NOTICE PFX "Sent Authentication Request to "MAC_FMT".\n", MAC_ARG(net->bssid));
105                 return;
106         }
107
108         printkl(KERN_WARNING PFX "Authentication timed out with "MAC_FMT"\n", MAC_ARG(net->bssid));
109         /* Remove this item from the queue */
110         spin_lock_irqsave(&mac->lock, flags);
111         net->authenticating = 0;
112         ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_AUTH_TIMEOUT, net);
113         cancel_delayed_work(&auth->work); /* just to make sure... */
114         list_del(&auth->list);
115         spin_unlock_irqrestore(&mac->lock, flags);
116         /* Free it */
117         kfree(auth);
118 }
119
120 /* Sends a response to an auth challenge (for shared key auth). */
121 static void
122 ieee80211softmac_auth_challenge_response(struct work_struct *work)
123 {
124         struct ieee80211softmac_auth_queue_item *aq =
125                 container_of(work, struct ieee80211softmac_auth_queue_item,
126                              work.work);
127
128         /* Send our response */
129         ieee80211softmac_send_mgt_frame(aq->mac, aq->net, IEEE80211_STYPE_AUTH, aq->state);
130 }
131
132 /* Handle the auth response from the AP
133  * This should be registered with ieee80211 as handle_auth 
134  */
135 int 
136 ieee80211softmac_auth_resp(struct net_device *dev, struct ieee80211_auth *auth)
137 {       
138
139         struct list_head *list_ptr;
140         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
141         struct ieee80211softmac_auth_queue_item *aq = NULL;
142         struct ieee80211softmac_network *net = NULL;
143         unsigned long flags;
144         u8 * data;
145         
146         if (unlikely(!mac->running))
147                 return -ENODEV;
148
149         /* Find correct auth queue item */
150         spin_lock_irqsave(&mac->lock, flags);
151         list_for_each(list_ptr, &mac->auth_queue) {
152                 aq = list_entry(list_ptr, struct ieee80211softmac_auth_queue_item, list);
153                 net = aq->net;
154                 if (!memcmp(net->bssid, auth->header.addr2, ETH_ALEN))
155                         break;
156                 else
157                         aq = NULL;
158         }
159         spin_unlock_irqrestore(&mac->lock, flags);
160         
161         /* Make sure that we've got an auth queue item for this request */
162         if(aq == NULL)
163         {
164                 dprintkl(KERN_DEBUG PFX "Authentication response received from "MAC_FMT" but no queue item exists.\n", MAC_ARG(auth->header.addr2));
165                 /* Error #? */
166                 return -1;
167         }                       
168         
169         /* Check for out of order authentication */
170         if(!net->authenticating)
171         {
172                 dprintkl(KERN_DEBUG PFX "Authentication response received from "MAC_FMT" but did not request authentication.\n",MAC_ARG(auth->header.addr2));
173                 return -1;
174         }
175
176         /* Parse the auth packet */
177         switch(auth->algorithm) {
178         case WLAN_AUTH_OPEN:
179                 /* Check the status code of the response */
180
181                 switch(auth->status) {
182                 case WLAN_STATUS_SUCCESS:
183                         /* Update the status to Authenticated */
184                         spin_lock_irqsave(&mac->lock, flags);
185                         net->authenticating = 0;
186                         net->authenticated = 1;
187                         spin_unlock_irqrestore(&mac->lock, flags);
188                         
189                         /* Send event */
190                         printkl(KERN_NOTICE PFX "Open Authentication completed with "MAC_FMT"\n", MAC_ARG(net->bssid));
191                         ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_AUTHENTICATED, net);
192                         break;
193                 default:
194                         /* Lock and reset flags */
195                         spin_lock_irqsave(&mac->lock, flags);
196                         net->authenticated = 0;
197                         net->authenticating = 0;
198                         spin_unlock_irqrestore(&mac->lock, flags);
199                         
200                         printkl(KERN_NOTICE PFX "Open Authentication with "MAC_FMT" failed, error code: %i\n", 
201                                 MAC_ARG(net->bssid), le16_to_cpup(&auth->status));
202                         /* Count the error? */
203                         break;
204                 }
205                 goto free_aq;
206                 break;
207         case WLAN_AUTH_SHARED_KEY:
208                 /* Figure out where we are in the process */
209                 switch(auth->transaction) {
210                 case IEEE80211SOFTMAC_AUTH_SHARED_CHALLENGE:
211                         /* Check to make sure we have a challenge IE */
212                         data = (u8 *)auth->info_element;
213                         if (*data++ != MFIE_TYPE_CHALLENGE) {
214                                 printkl(KERN_NOTICE PFX "Shared Key Authentication failed due to a missing challenge.\n");
215                                 break;  
216                         }
217                         /* Save the challenge */
218                         spin_lock_irqsave(&mac->lock, flags);
219                         net->challenge_len = *data++;   
220                         if (net->challenge_len > WLAN_AUTH_CHALLENGE_LEN)
221                                 net->challenge_len = WLAN_AUTH_CHALLENGE_LEN;
222                         kfree(net->challenge);
223                         net->challenge = kmemdup(data, net->challenge_len,
224                                                  GFP_ATOMIC);
225                         if (net->challenge == NULL) {
226                                 printkl(KERN_NOTICE PFX "Shared Key "
227                                         "Authentication failed due to "
228                                         "memory shortage.\n");
229                                 spin_unlock_irqrestore(&mac->lock, flags);
230                                 break;
231                         }
232                         aq->state = IEEE80211SOFTMAC_AUTH_SHARED_RESPONSE; 
233
234                         /* We reuse the work struct from the auth request here.
235                          * It is safe to do so as each one is per-request, and
236                          * at this point (dealing with authentication response)
237                          * we have obviously already sent the initial auth
238                          * request. */
239                         cancel_delayed_work(&aq->work);
240                         INIT_DELAYED_WORK(&aq->work, &ieee80211softmac_auth_challenge_response);
241                         schedule_delayed_work(&aq->work, 0);
242                         spin_unlock_irqrestore(&mac->lock, flags);
243                         return 0;
244                 case IEEE80211SOFTMAC_AUTH_SHARED_PASS:
245                         kfree(net->challenge);
246                         net->challenge = NULL;
247                         net->challenge_len = 0;
248                         /* Check the status code of the response */
249                         switch(auth->status) {
250                         case WLAN_STATUS_SUCCESS:
251                                 /* Update the status to Authenticated */        
252                                 spin_lock_irqsave(&mac->lock, flags);
253                                 net->authenticating = 0;
254                                 net->authenticated = 1;
255                                 spin_unlock_irqrestore(&mac->lock, flags);
256                                 printkl(KERN_NOTICE PFX "Shared Key Authentication completed with "MAC_FMT"\n", 
257                                         MAC_ARG(net->bssid));
258                                 ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_AUTHENTICATED, net);
259                                 break;
260                         default:
261                                 printkl(KERN_NOTICE PFX "Shared Key Authentication with "MAC_FMT" failed, error code: %i\n", 
262                                         MAC_ARG(net->bssid), le16_to_cpup(&auth->status));
263                                 /* Lock and reset flags */
264                                 spin_lock_irqsave(&mac->lock, flags);
265                                 net->authenticating = 0;
266                                 net->authenticated = 0;
267                                 spin_unlock_irqrestore(&mac->lock, flags);
268                                 /* Count the error? */
269                                 break;
270                         }
271                         goto free_aq;
272                         break;
273                 default:
274                         printkl(KERN_WARNING PFX "Unhandled Authentication Step: %i\n", auth->transaction);
275                         break;
276                 }
277                 goto free_aq;
278                 break;
279         default:
280                 /* ERROR */     
281                 goto free_aq;
282                 break;
283         }
284         return 0;
285 free_aq:
286         /* Cancel the timeout */
287         spin_lock_irqsave(&mac->lock, flags);
288         cancel_delayed_work(&aq->work);
289         /* Remove this item from the queue */
290         list_del(&aq->list);
291         spin_unlock_irqrestore(&mac->lock, flags);
292
293         /* Free it */
294         kfree(aq);
295         return 0;
296 }
297
298 /*
299  * Handle deauthorization
300  */
301 static void
302 ieee80211softmac_deauth_from_net(struct ieee80211softmac_device *mac,
303         struct ieee80211softmac_network *net)
304 {
305         struct ieee80211softmac_auth_queue_item *aq = NULL;
306         struct list_head *list_ptr;
307         unsigned long flags;
308
309         /* deauthentication implies disassociation */
310         ieee80211softmac_disassoc(mac);
311
312         /* Lock and reset status flags */
313         spin_lock_irqsave(&mac->lock, flags);
314         net->authenticating = 0;
315         net->authenticated = 0;
316         
317         /* Find correct auth queue item, if it exists */
318         list_for_each(list_ptr, &mac->auth_queue) {
319                 aq = list_entry(list_ptr, struct ieee80211softmac_auth_queue_item, list);
320                 if (!memcmp(net->bssid, aq->net->bssid, ETH_ALEN))
321                         break;
322                 else
323                         aq = NULL;
324         }
325         
326         /* Cancel pending work */
327         if(aq != NULL)
328                 /* Not entirely safe?  What about running work? */
329                 cancel_delayed_work(&aq->work);
330
331         /* Free our network ref */
332         ieee80211softmac_del_network_locked(mac, net);
333         if(net->challenge != NULL)
334                 kfree(net->challenge);
335         kfree(net);
336         
337         /* can't transmit data right now... */
338         netif_carrier_off(mac->dev);
339         spin_unlock_irqrestore(&mac->lock, flags);
340 }
341
342 /* 
343  * Sends a deauth request to the desired AP
344  */
345 int 
346 ieee80211softmac_deauth_req(struct ieee80211softmac_device *mac, 
347         struct ieee80211softmac_network *net, int reason)
348 {
349         int ret;
350         
351         /* Make sure the network is authenticated */
352         if (!net->authenticated)
353         {
354                 dprintkl(KERN_DEBUG PFX "Can't send deauthentication packet, network is not authenticated.\n");
355                 /* Error okay? */
356                 return -EPERM;
357         }
358         
359         /* Send the de-auth packet */
360         if((ret = ieee80211softmac_send_mgt_frame(mac, net, IEEE80211_STYPE_DEAUTH, reason)))
361                 return ret;
362         
363         ieee80211softmac_deauth_from_net(mac, net);
364         return 0;
365 }
366  
367 /*
368  * This should be registered with ieee80211 as handle_deauth
369  */
370 int 
371 ieee80211softmac_deauth_resp(struct net_device *dev, struct ieee80211_deauth *deauth)
372 {
373         
374         struct ieee80211softmac_network *net = NULL;
375         struct ieee80211softmac_device *mac = ieee80211_priv(dev);
376         
377         if (unlikely(!mac->running))
378                 return -ENODEV;
379
380         if (!deauth) {
381                 dprintk("deauth without deauth packet. eek!\n");
382                 return 0;
383         }
384
385         net = ieee80211softmac_get_network_by_bssid(mac, deauth->header.addr2);
386         
387         if (net == NULL) {
388                 dprintkl(KERN_DEBUG PFX "Received deauthentication packet from "MAC_FMT", but that network is unknown.\n",
389                         MAC_ARG(deauth->header.addr2));
390                 return 0;
391         }
392
393         /* Make sure the network is authenticated */
394         if(!net->authenticated)
395         {
396                 dprintkl(KERN_DEBUG PFX "Can't perform deauthentication, network is not authenticated.\n");
397                 /* Error okay? */
398                 return -EPERM;
399         }
400
401         ieee80211softmac_deauth_from_net(mac, net);
402
403         /* let's try to re-associate */
404         schedule_delayed_work(&mac->associnfo.work, 0);
405         return 0;
406 }