2008年5月6日 星期二

從 Information Elements 解析無線網路的加密細節

Standard
無線網路的訊號是公開發送的,雖然無法攔截,但只要你有無線裝置,基本上都能『監聽』空氣中的無線網路封包。也因為無線通訊的本身是不安全的,就如同傳統網路可能被 ISP 或連線路徑中的 Route Node 所監聽一樣,所以無論是哪一種通訊,目前的解決方式都是採用封包或資料加密,以達成安全連線的目的。在一般網路通訊中,常聽到的方法有 Secure Sockets Layer(SSL) 這一類,而對於無線網路通訊來說,就有所謂的 WEP、WPA、WPA2 等等眾多規格。嚴格來說,SSL 和無線網路的加密並不算同一層面的實作,但是他們有相同的目的和類似的原理。

最早的無線加密非常單純,可以用 WEP 一種加密方式走遍天下,只要判斷出遠端無線基地台或存取點需要加密金鑰,我們就可以假設該加密是 WEP 。不過由於 WEP 並不安全,有心人經過封包收集後,還是能很快破出金鑰,所以,日後就出現了 WPA、WPA2 種種加密方法的進階補強版。

想要知道該無線訊號是哪一種加密方法,可以從無線裝置所提供的『Information Elements』去得知,從事件 IWEVGENIE 中,就可取得加密的細節 ,這部份可參考 iwlist 或 LXPanel netstat plugin 內部的實作。

Informations Elements(IEs) 的結構定義,分為兩種:

  1. 當加密方式是 WPA or Others,這也是最 IEs 原始的定義,結構如下:



  2. 當加密方式是 WPA2 時,則沒有 OUI 欄位,定義如下:


  • Type 欄位:
    • 0xdd: WPA or Others
    • 0x30: WPA2
  • OUI 定義(3 Bytes)
    • WPA: 0x00, 0x50, 0xf2
    • WPA2: 0x00, 0x0f, 0xac
* 若有 OUI 欄位(4 Bytes),最後 1 Byte 為 0x01
  • Data 欄位

* 所有數值皆採用 Little Endian

因為 WPA 和 WPA2 是後來擴充上去的定義,並不在原始的 IEs 標準裡,所以許多欄位可能依遵循的標準而有所增減,不過,除非是發射端過於老舊,理論上都應該會遵循後來 WPA/WPA2 擴充的標準。在程式實作解析WPA/WPA2 時,原則上只要是無法辨別的 Type,一律視為 TKIP 就可以向下相容了,可以從 『Wireless Tools』的 Source Code 中找到這樣的處理原則。

關於 Cipher Type 和 Authentication Suite(key_mgmt) 的定義可參考:
static const char * iw_ie_cypher_name[] = {
"none",
"WEP-40",
"TKIP",
"WRAP",
"CCMP",
"WEP-104",
};

static const char * iw_ie_key_mgmt_name[] = {
"none",
"802.1x",
"PSK",
};


LXPanel netstat plugin 在這部份的實作摘錄:
void
wireless_gen_ie(ap_info *info, unsigned char *buffer, int ielen)
{
int offset = 2;
int count;
int i;
unsigned char wpa1_oui[3] = {0x00, 0x50, 0xf2};
unsigned char wpa2_oui[3] = {0x00, 0x0f, 0xac};
unsigned char *wpa_oui;

/* check IE type */
switch(buffer[0]) {
case 0xdd: /* WPA or else */
wpa_oui = wpa1_oui;

if((ielen < 8)
|| (memcmp(&buffer[offset], wpa_oui, 3) != 0)
|| (buffer[offset + 3] != 0x01)) {
if (info->haskey)
info->en_method = NS_WIRELESS_AUTH_WEP;
else
info->en_method = NS_WIRELESS_AUTH_OFF;

info->key_mgmt = NS_IW_IE_KEY_MGMT_NONE;
info->group = NS_IW_IE_CIPHER_NONE;
info->pairwise = NS_IW_IE_CIPHER_NONE;

return;
}

/* OUI and 0x01 */
offset += 4;
break;

case 0x30: /* IEEE 802.11i/WPA2 */
wpa_oui = wpa2_oui;
break;

default: /* Unknown */
if (info->haskey)
info->en_method = NS_WIRELESS_AUTH_WEP;
else
info->en_method = NS_WIRELESS_AUTH_OFF;

info->key_mgmt = NS_IW_IE_KEY_MGMT_NONE;
info->group = NS_IW_IE_CIPHER_NONE;
info->pairwise = NS_IW_IE_CIPHER_NONE;
return;
}

/* assume TKIP */
info->en_method = NS_WIRELESS_AUTH_WPA;
info->key_mgmt = NS_IW_IE_KEY_MGMT_NONE;
info->group = NS_IW_IE_CIPHER_TKIP;
info->pairwise = NS_IW_IE_CIPHER_TKIP;

/* 2 bytes for version number (little endian) */
offset += 2;

/* check group cipher for short IE */
if ((offset+4) > ielen) {
/* this is a short IE, we can assume TKIP/TKIP. */
info->group = NS_IW_IE_CIPHER_TKIP;
info->pairwise = NS_IW_IE_CIPHER_TKIP;
return;
}

/* 4 Bytes for group cipher information [3 bytes][1 Byte] */
if(memcmp(&buffer[offset], wpa_oui, 3)!=0) {
/* the group cipher is proprietary */
info->group = NS_IW_IE_CIPHER_NONE;
} else {
/* pick a byte for type of group cipher */
info->group = buffer[offset+3];
}
offset += 4;

/* check pairwise cipher for short IE */
if ((offset+2) > ielen) {
/* this is a short IE, we can assume TKIP. */
info->pairwise = NS_IW_IE_CIPHER_TKIP;
return;
}

/* 2 bytes for number of pairwise ciphers (little endian) */
count = buffer[offset] | (buffer[offset + 1] << 8);
offset += 2;

/* if we are done */
if ((offset+4*count) > ielen) {
return;
}

/* choose first cipher of pairwise ciphers to use,
* FIXME: Let user decide the cipher is the best way. */
for(i=0;i<count;i++) {
if(memcmp(&buffer[offset], wpa_oui, 3)==0) {
/* pick a byte for type of group cipher */
info->pairwise = buffer[offset+3];
}
offset += 4;
}

/* check authentication suites */
if ((offset+2) > ielen) {
/* this is a short IE, we can assume TKIP. */
info->key_mgmt = NS_IW_IE_KEY_MGMT_NONE;
return;
}

/* 2 bytes for number of authentication suites (little endian) */
count = buffer[offset] | (buffer[offset + 1] << 8);
offset += 2;

/* if we are done */
if ((offset+4*count) > ielen) {
return;

/* choose first key_mgmt of authentication suites to use,
* FIXME: Let user decide the key_mgmt is the best way. */
for(i=0;i<count;i++) {
if(memcmp(&buffer[offset], wpa_oui, 3)==0) {
/* pick a byte for type of key_mgmt */
info-&gtkey_mgmt = buffer[offset+3];
}
offset += 4;
}
}


在之前,LXPanel netstat plugin 對 WPA/WPA2 IEs 支援一直有問題,在最近改寫後的 SVN 最新版已經可以很好的處理 IEs,當然這部份是參考 iwlib 的 iwlist.c 寫出來的,其目的是判斷加密的方式,以通知 LXNM 做相對應的處理。如果想要使用這功能,請使用最新 SVN 版本的 LXPanel 以及LXNM,因為改寫過後的 netstat plugin 和舊版 LXNM 完全不相容。

後記

本文的示意圖表是大略畫出來的,其中可能會用到些縮寫或簡化的名詞表示。