Changeset 1762

Show
Ignore:
Timestamp:
10/23/06 11:11:21 (5 years ago)
Author:
kelmo
Message:

This patch fixes susceptibility to remote abuse of Channel Switch
Announcement Information Elements by injection of Beacon Frame
packets and improves the reliability of channel switch procedure under
conditions of frequent beacon misses.

Currently, channel switch is performed only after receiving Channel Switch
Announcement with Channel Switch Count <= 1. This ensures proper operation
under normal conditions. However, Beacon misses happen (especially if there
is a reason to leave the current channel). If the last Beacon before channel
switch is lost, then STA will remain on the channel. Such a condition is not
fatal, because STA will eventually recover after some time (by scanning for
the BSS and rejoining). It is not optimal, though. The information that a
channel switch is scheduled could have been already known thanks to prior
announcements. Ignoring them is just losing the opportunity to work fluently
under harsh conditions.

Of course it has a drawback. The last (lost) Beacon before scheduled channel
switch could cancel it. If so, then our STA will change the channel, notice
that there is no one to talk to there and then try recover as usual. However,
if we lose last Beacon it is more probable that the channel switch was not
cancelled and thus assuming that we decrease the probability of missing (or
performing an incorrect) channel switch.

BTW It is desirable to pay more attention to received Channel Switch
Announcements to avoid malicious packet injections that can cause a serious
DoS.

Unfortunately, the "BTW" remark appeared to be true. Using extremely simple
packet injection, an attacker can send his own Beacon Frames with Channel
Switch Announcement Information Elements. If he sets CS Count <= 1, then the
client station (madwifi in STA (Managed) mode) follows it blindly and changes
its operating channel. This completely interrupts communication for a
significant period of time (up to 10 seconds) and therefore current design
can be considered as a serious flaw allowing the attacker to perform DoS attack
easily.

The idea to monitor all incoming CSA IEs gives a possibility to avoid such
attacks or at least to reduce their impact to the minimum. Its implementation
has been applied as r962. Its functionality in brief:

  • IEEE80211_CSA_PROTECTION_PERIOD has been introduced. It defines the minimal
    Channel Switch Count in the initial packet. This means that the period in
    which Channel Switch is announced cannot be too short. This protects the
    client from switching to another channel after receiving malicious CSA IE
    with Count <= 1.
  • Interval measurement between subsequent CSA IEs is checked against the
    actual CSA Count drop and the right multiplicity of Beacon Interval. It
    allows to react to injected packets. The actual action is to cancel the
    switch. It is not the ideal solution because now it is easy to enforce
    cancelling the switch. However, not to switch is better than to switch ;D,
    because the worst impact possible is disruption of communication for a few
    seconds needed to scan the band to find the AP. But this can potentially
    happen only when the switch is really going to happen and cannot be
    triggered by the attacker.
  • CSA parameters (Chan, Mode) are monitored for change.
  • Channel to switch to is looked up after receiving first CSA IE.
  • When any proper CSA IE is received, a timer is set up to make the switch
    proceed even though the terminal CSA IE has not arrived. This addresses the
    main task described in the ticket description.

The applied changes work fine and do much (if not the most) of what was possible
to ensure proper functioning while using available IEEE802.11h facilities.

Signed-off-by: Michal Wrobel <xmxwx@asn.pl>

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/net80211/ieee80211.h

    r1756 r1762  
    373373} __packed; 
    374374 
     375/* 
     376 * Channel Switch Announcement information element. 
     377 */ 
     378struct ieee80211_ie_csa { 
     379        u_int8_t csa_id;        /* IEEE80211_ELEMID_CHANSWITCHANN */ 
     380        u_int8_t csa_len;       /* == 3 */ 
     381        u_int8_t csa_mode;      /* Channel Switch Mode: 1 == stop transmission until CS */ 
     382        u_int8_t csa_chan;      /* New Channel Number */ 
     383        u_int8_t csa_count;     /* TBTTs until Channel Switch happens */ 
     384} __packed; 
     385 
     386/* minimal Channel Switch Count in the initial announcement */ 
     387#define IEEE80211_CSA_PROTECTION_PERIOD 3 
     388 
     389/* maximum allowed deviance of measurement of intervals between CSA in Beacons */ 
     390#define IEEE80211_CSA_SANITY_THRESHOLD 100     
     391 
     392 
    375393/* does frame have QoS sequence control data */ 
    376394#define IEEE80211_QOS_HAS_SEQ(wh) \ 
  • trunk/net80211/ieee80211_input.c

    r1756 r1762  
    22912291} 
    22922292 
     2293static void 
     2294ieee80211_doth_cancel_cs(struct ieee80211vap *vap) 
     2295{ 
     2296        del_timer(&vap->iv_csa_timer); 
     2297        if (vap->iv_csa_jiffies)  
     2298                IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH, 
     2299                                "channel switch cancelled (was: to %u in %u " 
     2300                                "tbtt, mode %u)\n", vap->iv_csa_chan->ic_ieee, 
     2301                                vap->iv_csa_count, vap->iv_csa_mode); 
     2302        vap->iv_csa_jiffies = 0; 
     2303} 
     2304 
     2305static void 
     2306ieee80211_doth_switch_channel(struct ieee80211vap *vap) 
     2307{ 
     2308        struct ieee80211com *ic = vap->iv_ic; 
     2309 
     2310        IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH, 
     2311                        "%s: Channel switch to %d NOW!\n", 
     2312                        __func__, vap->iv_csa_chan->ic_ieee); 
     2313#if 0 
     2314        /* XXX does not belong here? */ 
     2315        /* XXX doesn't stop management frames */ 
     2316        /* XXX who restarts the queue? */ 
     2317        /* NB: for now, error here is non-catastrophic. 
     2318         *     in the future we may need to ensure we 
     2319         *     stop xmit on this channel. 
     2320         */ 
     2321        netif_stop_queue(ic->ic_dev); 
     2322#endif 
     2323 
     2324        vap->iv_csa_jiffies = 0; /* supress "cancel" msg */ 
     2325        ieee80211_doth_cancel_cs(vap); 
     2326 
     2327        ic->ic_prevchan = ic->ic_curchan; 
     2328        ic->ic_curchan = ic->ic_bsschan = vap->iv_csa_chan; 
     2329        ic->ic_set_channel(ic); 
     2330} 
     2331 
     2332static void 
     2333ieee80211_doth_switch_channel_tmr(unsigned long arg) 
     2334{ 
     2335        struct ieee80211vap *vap = (struct ieee80211vap *)arg; 
     2336        ieee80211_doth_switch_channel(vap); 
     2337} 
     2338 
    22932339static int 
    2294 ieee80211_parse_dothparams(struct ieee80211vap *vap, u_int8_t *frm, 
     2340ieee80211_parse_csaie(struct ieee80211_node *ni, u_int8_t *frm, 
    22952341        const struct ieee80211_frame *wh) 
    22962342{ 
     2343        struct ieee80211vap *vap = ni->ni_vap; 
    22972344        struct ieee80211com *ic = vap->iv_ic; 
    2298         u_int len = frm[1]; 
    2299         u_int8_t chan, tbtt; 
    2300  
    2301         if (len < 4 - 2) {              /* XXX ie struct definition */ 
     2345        struct ieee80211_channel *c; 
     2346        struct ieee80211_ie_csa *csa_ie = (struct ieee80211_ie_csa *)frm; 
     2347 
     2348        if (!frm) { 
     2349                /* we had CS underway but now we got Beacon without CSA IE */ 
     2350                /* XXX abuse? */ 
     2351 
     2352                IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH, 
     2353                                "%s: channel switch is scheduled, but we got " 
     2354                                "Beacon without CSA IE!\n", __func__); 
     2355 
     2356                ieee80211_doth_cancel_cs(vap); 
     2357                return 0; 
     2358        } 
     2359 
     2360        if (csa_ie->csa_len != 3) { 
    23022361                IEEE80211_DISCARD_IE(vap, 
    23032362                        IEEE80211_MSG_ELEMID | IEEE80211_MSG_DOTH, 
    2304                         wh, "channel switch", "too short, len %u", len); 
     2363                        wh, "channel switch", "invalid length %u", 
     2364                        csa_ie->csa_len); 
    23052365                return -1; 
    23062366        } 
    2307         chan = frm[3]; 
    2308         if (isclr(ic->ic_chan_avail, chan)) { 
     2367 
     2368        if (isclr(ic->ic_chan_avail, csa_ie->csa_chan)) { 
    23092369                IEEE80211_DISCARD_IE(vap, 
    23102370                        IEEE80211_MSG_ELEMID | IEEE80211_MSG_DOTH, 
    2311                         wh, "channel switch", "invalid channel %u", chan); 
     2371                        wh, "channel switch", "invalid channel %u",  
     2372                        csa_ie->csa_chan); 
    23122373                return -1; 
    23132374        } 
    2314         tbtt = frm[4]; 
    2315         IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH, 
    2316                 "%s: channel switch to %d in %d tbtt\n", __func__, chan, tbtt); 
    2317         if (tbtt <= 1) { 
    2318                 struct ieee80211_channel *c; 
    2319  
    2320                 IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH, 
    2321                         "%s: Channel switch to %d NOW!\n", __func__, chan); 
    2322 #if 0 
    2323                 /* XXX does not belong here? */ 
    2324                 /* XXX doesn't stop management frames */ 
    2325                 /* XXX who restarts the queue? */ 
    2326                 /* NB: for now, error here is non-catastrophic. 
    2327                  *     in the future we may need to ensure we 
    2328                  *     stop xmit on this channel. 
    2329                  */ 
    2330                 netif_stop_queue(ic->ic_dev); 
    2331 #endif 
    2332                 if ((c = ieee80211_doth_findchan(vap, chan)) == NULL) { 
    2333                         /* XXX something wrong */ 
    2334                         IEEE80211_DISCARD_IE(vap, 
     2375 
     2376        if ((c = ieee80211_doth_findchan(vap, csa_ie->csa_chan)) == NULL) { 
     2377                /* XXX something wrong */ 
     2378                IEEE80211_DISCARD_IE(vap, 
    23352379                                IEEE80211_MSG_ELEMID | IEEE80211_MSG_DOTH, 
    23362380                                wh, "channel switch", 
    2337                                 "channel %u lookup failed", chan); 
     2381                                "channel %u lookup failed", csa_ie->csa_chan); 
     2382                return -1; 
     2383        } 
     2384 
     2385        IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH, 
     2386                "%s: channel switch to %u in %u tbtt (mode %u) announced\n", 
     2387                __func__, csa_ie->csa_chan, csa_ie->csa_count, 
     2388                csa_ie->csa_mode); 
     2389 
     2390        if (vap->iv_csa_jiffies) { 
     2391                /* CSA was received recently */ 
     2392                if (c != vap->iv_csa_chan) { 
     2393                        /* XXX abuse? */ 
     2394                        IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH, 
     2395                                        "%s: channel switch channel " 
     2396                                        "changed from %u to %u!\n", __func__, 
     2397                                        vap->iv_csa_chan->ic_ieee, 
     2398                                        csa_ie->csa_chan); 
     2399 
     2400                        if (vap->iv_csa_count > IEEE80211_CSA_PROTECTION_PERIOD) 
     2401                                ieee80211_doth_cancel_cs(vap); 
    23382402                        return 0; 
    23392403                } 
    2340                 ic->ic_prevchan = ic->ic_curchan; 
    2341                 ic->ic_curchan = ic->ic_bsschan = c; 
    2342                 ic->ic_set_channel(ic); 
    2343                 return 1; 
    2344         } 
     2404 
     2405                if (csa_ie->csa_mode != vap->iv_csa_mode) { 
     2406                        /* Can be abused, but with no (to little) impact. */ 
     2407 
     2408                        /* CS mode change has no influence on our actions since 
     2409                         * we don't respect cs modes at all (yet). Complain and 
     2410                         * forget. */ 
     2411                        IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH, 
     2412                                        "%s: channel switch mode changed from " 
     2413                                        "%u to %u!\n", __func__,  
     2414                                        vap->iv_csa_mode, csa_ie->csa_mode); 
     2415                } 
     2416 
     2417                if (csa_ie->csa_count >= vap->iv_csa_count) { 
     2418                        /* XXX abuse? what for? */ 
     2419                        IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH, 
     2420                                        "%s: channel switch count didn't " 
     2421                                        "decrease (%u -> %u)!\n", __func__, 
     2422                                        vap->iv_csa_count, csa_ie->csa_count); 
     2423                        return 0; 
     2424                } 
     2425 
     2426                { 
     2427                        u_int32_t elapsed = IEEE80211_JIFFIES_TO_TU( 
     2428                            jiffies - vap->iv_csa_jiffies); 
     2429                        u_int32_t cnt_diff = vap->iv_csa_count - 
     2430                          csa_ie->csa_count; 
     2431                        u_int32_t expected = ni->ni_intval * cnt_diff; 
     2432                        int32_t delta = elapsed - expected; 
     2433                        if (delta < 0) 
     2434                                delta = -delta; 
     2435                        if (delta > IEEE80211_CSA_SANITY_THRESHOLD) { 
     2436                                /* XXX abuse? for now, it's safer to cancel CS 
     2437                                 * than to follow it blindly */ 
     2438                                IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH, 
     2439                                                "%s: %u.%02u bintvals elapsed, " 
     2440                                                "but count dropped by %u (delta" 
     2441                                                " = %u TUs)\n", __func__, 
     2442                                                elapsed / ni->ni_intval, 
     2443                                                elapsed * 100 / ni->ni_intval  
     2444                                                % 100, cnt_diff, delta); 
     2445 
     2446                                ieee80211_doth_cancel_cs(vap); 
     2447                                return 0; 
     2448                        } 
     2449                } 
     2450 
     2451                vap->iv_csa_count = csa_ie->csa_count; 
     2452                mod_timer(&vap->iv_csa_timer, jiffies +  
     2453                                IEEE80211_TU_TO_JIFFIES(vap->iv_csa_count 
     2454                                  * ni->ni_intval + 10)); 
     2455        } else { 
     2456                /* CSA wasn't received recently, so this is the first one in 
     2457                 * the sequence. */ 
     2458 
     2459                if (csa_ie->csa_count < IEEE80211_CSA_PROTECTION_PERIOD) { 
     2460                        /* XXX abuse? */ 
     2461                        IEEE80211_DISCARD_IE(vap, 
     2462                                        IEEE80211_MSG_ELEMID |  
     2463                                        IEEE80211_MSG_DOTH, 
     2464                                        wh, "channel switch", 
     2465                                        "initial announcement: channel switch" 
     2466                                        " would occur too soon (in %u tbtt)", 
     2467                                        csa_ie->csa_count); 
     2468                        return 0; 
     2469                } 
     2470 
     2471                vap->iv_csa_mode = csa_ie->csa_mode; 
     2472                vap->iv_csa_count = csa_ie->csa_count; 
     2473                vap->iv_csa_chan = c; 
     2474 
     2475                vap->iv_csa_timer.function = ieee80211_doth_switch_channel_tmr; 
     2476                vap->iv_csa_timer.data = (unsigned long)vap; 
     2477                vap->iv_csa_timer.expires = jiffies + IEEE80211_TU_TO_JIFFIES( 
     2478                    vap->iv_csa_count * ni->ni_intval + 10); 
     2479                add_timer(&vap->iv_csa_timer); 
     2480        } 
     2481 
     2482        vap->iv_csa_jiffies = jiffies; 
     2483 
     2484        if (vap->iv_csa_count <= 1) 
     2485                ieee80211_doth_switch_channel(vap); 
     2486 
    23452487        return 0; 
    23462488} 
     
    25512693                        case IEEE80211_ELEMID_CHANSWITCHANN: 
    25522694                                if (ic->ic_flags & IEEE80211_F_DOTH) 
    2553                                         scan.doth = frm; 
     2695                                        scan.csa = frm; 
    25542696                                break; 
    25552697                        default: 
     
    26532795                        if (scan.ath != NULL) 
    26542796                                ieee80211_parse_athParams(ni, scan.ath); 
    2655                         if (scan.doth != NULL
    2656                                 ieee80211_parse_dothparams(vap, scan.doth, wh); 
     2797                        if (scan.csa != NULL || vap->iv_csa_jiffies
     2798                                ieee80211_parse_csaie(ni, scan.csa, wh); 
    26572799                        if (scan.tim != NULL) { 
    26582800                                /* 
  • trunk/net80211/ieee80211_proto.c

    r1759 r1762  
    128128        init_timer(&vap->iv_xrvapstart); 
    129129        init_timer(&vap->iv_swbmiss); 
     130        init_timer(&vap->iv_csa_timer); 
    130131        vap->iv_mgtsend.function = ieee80211_tx_timeout; 
    131132        vap->iv_mgtsend.data = (unsigned long) vap; 
  • trunk/net80211/ieee80211_scan.h

    r1698 r1762  
    139139        u_int8_t *rates; 
    140140        u_int8_t *xrates; 
    141         u_int8_t *doth
     141        u_int8_t *csa
    142142        u_int8_t *wpa; 
    143143        u_int8_t *rsn; 
  • trunk/net80211/ieee80211_var.h

    r1756 r1762  
    8888#define IEEE80211_TU_TO_MS(x)   (((x) * 1024) / 1000) 
    8989#define IEEE80211_TU_TO_JIFFIES(x) ((IEEE80211_TU_TO_MS(x) * HZ) / 1000) 
     90#define IEEE80211_JIFFIES_TO_TU(x) IEEE80211_MS_TO_TU((x) * 1000 / HZ) 
    9091 
    9192#define IEEE80211_APPIE_MAX     1024 
     
    349350        struct ieee80211_roam iv_roam;          /* sta-mode roaming state */ 
    350351 
     352        u_int32_t iv_csa_jiffies;               /* last csa recv jiffies */ 
     353        u_int8_t iv_csa_count;                  /* last csa count */ 
     354        struct ieee80211_channel *iv_csa_chan;  /* last csa channel */ 
     355        u_int8_t iv_csa_mode;                   /* last csa mode */ 
     356        struct timer_list iv_csa_timer;         /* csa timer */ 
    351357        u_int32_t *iv_aid_bitmap;               /* association id map */ 
    352358        u_int16_t iv_max_aid;