12

《深入理解Android:Wi-Fi,NFC和GPS》章节连载[节选]--第四章 深入理解wpa_supplican...

 3 years ago
source link: https://blog.csdn.net/Innost/article/details/20862875
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

《深入理解Android:Wi-Fi,NFC和GPS》章节连载[节选]--第四章 深入理解wpa_supplicant

首先感谢各位兄弟姐妹们的耐心等待。本书预计在3月中旬上市发售。从今天开始,我将在博客中连载此书的一些内容。注意,此处连载的是未经出版社编辑的原始稿件,所以样子会有些非专业。

注意,如下是本章目录,本文节选4.1-4.3  4.5~4.5.2 及4.6节

为了方便读者深入学习,本系列连载都会将作者研究过

程中所学习的参考文献列出来

                                                     第4章 深入理解wpa_supplicant

本章主要内容

  • 介绍wpa_supplicant的初始化
  • 介绍EAP和EAPOL
  • 介绍wpa_supplicant如何加入无线网络

4.1 概述

wpa_supplicant是一个开源软件项目,它实现了Station[1]对无线网络进行管理和控制的功能。根据官方[2]描述,wpa_supplicant所支持的功能非常多,此处列举其中几个重要的功能点。

支持WPA和IEEE 802.11i所定义的大部分功能:这部分功能集中在安全方面,包括:

  • 支持WPA-PSK(即WPA-Personal)和WPA-Enterprise(即利用RAIDUS认证服务器来完成身份认证的情况)。
  • 数据加密方面支持CCMP、TKIP、WEP104和WEP40。注意,WEP104和WEP40中的数字代表密钥的长度。104表示密钥长度为104个二进制位(如以ASCII字符个数来计算的话,WEP104支持的密钥长度为13个ASCII字符)。
  • 完全支持WPA和WPA2,包括PMKSA缓存,预认证(pre-authentication)等功能。
  • 支持IEEE 802.11r和802.11w,其中802.11r规范定义了快速基础服务转移(Fast Transition)功能,而802.11w则新增了对管理帧的安全保护机制。
  • 支持WFA制定的Wi-Fi Proctected Setup功能、P2P、TDLS等。

支持多种EAP Method:主要和802.1X中Supplicant的功能有关,wpa_supplicant支持多达25种EAP Method,包括:

  • EAP-TLS:TLS(Transport Layer Security)本身是一种传输层安全协议,它利用密钥算法提供端点身份认证与通讯保密,其基础是公钥基础设施(public key infrastructure,简称PKI)。EAP-TLS定义于RFC 5216。
  • EAP-PEAP:PEAP(Protected Extensible Authentication Protocol,可扩展EAP)由微软、思科以及RSA Security三个公司共同开发,是一种利用证书加用户名和密码来进行身份验证的方法。
  • EAP-TTLS:它是TLS的拓展,全名为Tunneled Transport Layer Security(隧道传输层安全协议),相比TLS,它简化了认证过程中客户端的工作。
  • EAP-SIM、EAP-PSK、EAP-GPSK等其他认证方法。

读者可阅读参考资料[1]以了解更多EAP方法的知识。

对各种无线网卡和驱动的支持:

  • 支持nl80211/cfg80211驱动和Linux Wireless Extension驱动[3]
  • 支持Windows平台中的NDIS驱动。

提示:wpa_supplicant虽然支持Windows平台,但笔者相信绝大多数读者使用的都是Windows自带的无线网络管理程序(或者Intel芯片相关软件提供的无线网络管理程序)。从功能角度来说,读者可认为wpa_supplicant是这些私有程序的一种开源实现。

Android做为开源世界的集大成者,它在无线网络管理和控制方面直接使用了wpa_supplicant。Android 4.1中,external目录下有两个和wpa_supplicant相关的目录,分别是wpa_supplicant_6和wpa_supplicant_8。6和8分别代表对应wpa_supplicant的版本号为0.6.10和2.0-devel。

提示:关于wpa_supplicant的发布历史,请读者参考http://hostap.epitest.fi/releases.html。

本书的分析目标是wpa_supplicant_8,它包含三个主要子目录,分别是:

  • hostapd:当手机进入SoftAP模式时,手机将扮演AP的角色,故需要hostapd来提供AP的功能。
  • wpa_supplicant:Station模式,也叫Managed模式。它是本书分析的重点。
  • src:hostapd和wpa_supplicant中都包含了一些通用的数据结构和处理方法,这些内容都放在此src目录中。注意,hostapd/src和wpa_supplicant/src子目录均链接到此src目录。

wpa_supplicant是Android用户空间中无线网络部分的核心模块,所有Framework层中和Wi-Fi相关的操作最终都将借由wpa_supplicant来完成。另外,wpa_supplicant本身对802.11、802.1X和Wi-Fi Alliance定义的一些规范都有极好的支持。所以,分析它将是加深理解802.11及相关理论知识的一个非常重要的途径。

本章拟打算带领读者从两条分析路线来掌握wpa_supplicant和相关的功能模块。

  • 路线一:首先将介绍wpa_supplicant的初始化过程。这条路线将帮助读者了解wpa_supplicant中常见的数据结构及之间的关系。这条路非常难走,请读者做好心理准备。
  • 路线二:我们将通过命令行发送命令的方式触发wpa_supplicant进行相关工作,使得手机加入一个利用WPA-PSK进行认证的无线网络。这条路线将帮助读者了解wpa_supplicant中的命令处理、scan、association、4-way handshake等相关处理流程。

提示:后续章节还将围绕Android中无线网络技术开展更多的讨论:

1)第5章将介绍Android Framework中的WifiService及其相关模块。

2)第6、7章节将继续wpa_supplicant之旅,其内容和WPS、Wi-Fi P2P以及WifiP2pService有关。

为了行文方便,本书将用WPAS来表示wpa_supplicant。另外,后文代码分析中还能见到一种重要的数据结构,它也叫wpa_supplicant。请读者根据上下文信息来理解wpa_supplicant的含义。

正式开始分析之旅前,我们先来简单了解下wpa_supplicant。

4.2 初识wpa_supplicant

本节介绍WPAS一些外围知识,包括软件结构、编译配置、控制命令和对应控制API的用法。其中,控制命令的格式和API的用法将在后续介绍WifiService相关模块时会见到。另外,在研究WPAS时,能熟练掌握用git查询历史版本信息也非常关键。WPAS的故事首先从其软件架构开始。

4.2.1 wpa_supplicant架构介绍

wpa_supplicant是一个比较庞大的开源软件项目,包含500多个文件,20万行代码,其内部模块构成如图4-1所示[2]。

图4-1  wpa_supplicant软件架构

图4-1所示的WPAS软件架构包括如下重要模块:

  • WPAS所有工作都围绕事件(对应图4-1中的event loop模块)展开。即,它是基于事件驱动的。事件驱动和消息驱动类似,主线程等待事件的发生并处理它们。WPAS没有使用多线程编程,所有事件处理都在主线程中完成。从这一点看,WPAS的运行机制倒是很简单。
  • 位于event loop模块下方的driver i/f(i/f代表interface)接口模块用于隔离和底层驱动直接交互的那些driver控制模块(如wext、ndiswrapper等,WPAS中称之为driver wrapper)。这些driver wrapper和平台以及芯片所使用的驱动相关。不过,由于driver i/f的隔离作用,WPAS中其他模块将能最大程度保持平台以及驱动无关性。
  • driver wrapper经常要返回一些信息给上层。WPAS中,这些信息将通过driver events的方式反馈给WPAS其他模块进行处理。
  • 上一章曾介绍过EAP以及EAPOL协议。除了定义消息格式外,RFC4137文档定义了EAP状态机,而802.1X文档中还定义了EAPOL状态机。WPAS根据这两个协议分别实现了EAP和EAPOL状态机。本章后续将详细分析这两个状态机以及背后的协议。除此之外,WPAS还定义了自己的状态机(即WPA/WPA2 State Machine)。
  • WPAS实现了多种EAP方法,如图4-1中EAP method模块所示。另外它还包含了TLS模块和crypto模块用于支持对应的EAP方法。
  • EAPOL以及EAP消息都属于LLC层数据,所以WPAS的l2_packet模块用于收发EAPOL和EAP消息。
  • WPAS支持较多的配置参数,这些参数的处理由configuration模块完成。
  • WPAS是C/S结构中的Server端,它通过ctrl i/f模块向客户端提供通信接口。Linux/Unix平台中,Client端利用Unix域socket与其通信。目前常用的Client端wpa_cli(无界面的命令行程序)和wpa_gui(UI用Qt实现)。

WPAS支持众多功能,使用前往往需根据平台或驱动的特性进行编译配置,下面通过一个实例来介绍如何在Android中编译wpa_supplicant。

4.2.2 wpa_supplicant编译配置介绍

本实例的背景情况如下:

笔者有一台三星Galaxy Note2手机,其OS为Android 4.1.2。现在,笔者打算编译一个AOSP(Android Open Source Project)的wpa_supplicant程序以替换Note2中原有的wpa_supplicant。

提示:AOSP即Google公版Android源码。几乎所有手机厂商都会根据芯片、硬件以及厂商自定义的特性去修改它。由于Note 2源码不公开,所以笔者只能编译AOSP版的wpa_supplicant。

假设读者已经按第1章要求部署完毕Android 4.1源码和开发环境,那么接下来要做的是:

cd  4.1source #首先进入4.1源码根目录

source build/envsetup #建立Android源码编译环境

lunch  #选择要编译的设备和版本,笔者选择了1,代表full-eng。eng代表工程版,该选项对应的目标设备类型

        #(TARGET_PRODUCT)为generic,其编译出来的镜像文件可由模拟器加载并运行

由上述配置可知,笔者将使用generic的版本来编译一个wpa_supplicant以运行在真实的机器上。

提醒:通过执行lunch命令可知,不同的设备应有对应的编译配置项。由于笔者没有Note 2的源码,所以只能尝试编译generic版本。

接下来要为generic平台定制所使用的wpa_supplicant版本,这是通过修改BoardConfig.mk来完成的。

[-->BoardConfig.mk]

#在此文件最后添加如下内容:

WPA_SUPPLICANT_VERSION := VER_0_8_X  #表明使用wpa_supplicant_8

BOARD_WPA_SUPPLICANT_DRIVER := NL80211  #表明驱动使用Nl80211

BOARD_WLAN_DEVICE := bcmdhd  #表明kernel中的wifi设备为博通公司的bcmdhd

#编译博通公司驱动相关的静态库,该库对应的代码也在AOSP源码中,位置是,

#hardware/broadcom/wlan/bcmdhd/wpa_supplicant_8_lib/

BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_bcmdhd 

巧合的是,Note 2使用的wlan芯片刚好为bcmdhd。

除了修改BoardConfig.mk外,WPAS也定义了自己的编译配置文件android.config,其内容如下:

......#该文件主要定义了编译时生成的宏,各平台根据自己的硬件情况去设置需要编译的内容

# Driver interface for generic Linux wireless extensions

CONFIG_DRIVER_WEXT=y  #可注释这一条以取消编译WEXT相关代码

# Driver interface for Linux drivers using the nl80211 kernel interface

CONFIG_DRIVER_NL80211=y  #可去掉此行的注释符号以增加对Nl80211的支持

CONFIG_LIBNL20=y

......#其他很多编译配置项都可在此文件中修改

#注意,此文件中对CONFIG_DRIVER_NL80211的修改和BoardConfig.mk中的BOARD_WPA_SUPPLICANT_DRIVER

#相重合。BoardConfig.mk的优先级较高,所以请读者先修改它。

配置完毕后,开始编译:

#首先要编译wpa_supplicant依赖的静态库lib_driver_cmd_bcmdhd

mmm hardware/broadcom/wlan/bcmdhd/wpa_supplicant_8_lib/

mmm external/wpa_supplicant_8 #生成wpa_supplicant,同时也会生成wpa_cli

然后将编译后wpa_supplicant替换Note 2的/system/bin/wpa_supplicant并设置其为可运行(通过chmod命令设置其权限位0755)。同时,笔者也把wpa_cli push到/system/bin下为后续测试做准备。

经过测试,笔者发现AOSP的wpa_supplicant以及wpa_cli均能正常工作在Note 2上。这也间接表明Note 2并未对wpa_supplicant以及博通芯片相关的代码做较大改动。

注意:严格来说,android.cfg应该是唯一的编译控制文件。但由于底层wlan芯片不同,WPAS可能还依赖其他模块。所以,在具体实施时,BoardConfig.mk(或其他文件,视具体情况而定)也需要做修改。

4.2.3 命令和控制API介绍

1.  命令介绍

由图4-1的介绍可知,WPAS对外通过控制接口模块与客户端通信。在Android平台中,WPAS的客户端是位于Framework中的WifiService。用户在Settings界面进行Wi-Fi相关的操作最终都会经由WifiService通过发送命令的方式转交给wpa_supplicant去执行。WPAS定义了许多命令,常见的:

  • PING:心跳检测命令。客户端用它判断WPAS是否工作正常。WPAS收到”PING”命令后需要回复“PONG”。
  • MIB:客户端用该命令获取设备的MIB信息。
  • STATUS:客户端用该命令来获取WPAS的工作状态。
  • ADD_NETWORK:为WPAS添加一个新的无线网络。它将返回此新无线网络的id(从0开始)。注意:此network id非常重要,客户端后续将通过它来指明自己想操作的无线网络。
  • SET_NETWORK <network id> <variable> <value>:network id是无线网络的id。此命令用于设置指定无线网络的信息。其中variable为参数名,value为参数的值。
  • ENABLE_NETWORK <network id>:使能某个无线网络。此命令最终将促使WPAS发起一系列操作以加入该无线网络。

除了接收来自Client的命令外,WPAS也会主动给Client发送命令。例如,WPAS需用户为某个无线网络输入密码。这类命令称之为Interactive Request,其格式如下。

  • WPAS向客户端发送的命令遵循CTRL-REQ-<field name>-<network id>-<human readable text>格式。如CTRL-REQ-PASSWORD-0-Passwork needed for SSID test-network。这条命令表示需要用户为0号网络输入密码。
  • 客户端处理完后,需回复CTRL-RSP-<field name>-<network id>-<value>。目前支持的field包括PASSWORD、IDENTITY(EAP中的identity或者用户名)、PIN等。
  • 最后,WPAS还可通过形如“CTRL-EVENT-<field>-<text>”的命令向客户端通知一些事情。

提示:除了“CTRL-EVENT-XXX”之外,WPAS还支持形如“WPA:XXX”和“WPS-XXX”的通知事件。这些事件和WPA和WPS有关。下一章分析WifiService时我们还能见到它们。

图4-2所示为笔者利用wpa_cli测试status命令得到的结果。

图4-2  wpa_cli命令测试示意

图4-2所示为status命令的结果。图中最后几行显示WPAS向wpa_cli返回了两个CTRL-EVENT信息。

2.  控制API介绍

Android平台中WifiService是WPAS的客户端,它和WPAS交互时必须使用wpa_supplicant提供的API。这些API声明于wpa_ctrl.h中,其用法如下:

//必须包含此头文件,链接时需包含libwpa_client.so动态库

#include “wpa_ctrl.h”

客户端使用wpa_ctrl时首先要分配控制对象。下面两个API用于创建和销毁控制对象wpa_ctrl:

//创建一个wpa控制端对象wpa_ctrl。Android平台中,参数ctrl_path代表unix域socket的位置

struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path);

void wpa_ctrl_close(struct wpa_ctrl *ctrl);//注销wpa_ctrl控制对象

下面这个函数用于发送命令给WPAS。

//客户端发送命令给wpa_supplicant,回复的消息保存在reply中

int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,

             char *reply, size_t *reply_len,void (*msg_cb)(char *msg, size_t len));

msg_cb是一个回调函数,该参数的设置和WPAS中C/S通信机制的设计有关:

从Client角度来看,它发送给WPAS的命令所对应的回复属于solicited event(意为有请求的事件),而前面所提到的CTRL-EVENT事件(用于通知事件)对应为unsolicited event(意为未请求的事件)。当Client在等待某个命令的回复时,WPAS同时可能有些通知事件要发送给客户端,这些通知事件不是该命令的回复,所以不能通过wpa_ctrl_request的reply参数返回。

为了防止丢失这些通知事件,wpa_cli设计了一个msg_cb回调用于客户端在等待命令回复的时候处理那些unsolicited event。

这种一个函数完成两样完全不同的功能的设计实在有些特别,所以wpa_supplicant规定只有打开通知事件监听功能的wpa_ctrl对象才能在wpa_ctrl_request中通过msg_cb获取通知事件。而打开通知事件监听功能相关的API如下所示。

//打开通知事件监听功能

int wpa_ctrl_attach(struct wpa_ctrl *ctrl);

//打开通知事件监听功能的wpa_ctrl对象能直接调用下面的函数来接收unsolicited event

int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len);

如果客户端并不发送命令,而只是想接收Unsolicited event的话,可通过wpa_ctrl_recv函数来达到此目的。

综上所述,单独使用wpa_ctrl_recv和wpa_ctrl_request都不方便。所以,一种常见的用法是:客户端创建两个wpa_ctrl对象来简化自己的逻辑处理:

  • 一个打开了通知事件监听功能的wpa_ctrl对象将只通过wpa_ctrl_recv来接收通知事件。
  • 另外一个wpa_ctrl专职用于发送命令和接收回复。由于没有调用wpa_ctrl_attach,故它不会收到通知事件。

提示:下一章分析WifiService时将见到这种创建两个wpa_ctrl对象的做法。

4.2.4  git的使用

WPAS难度较大的一个重要原因是其注释较少,很多变量的含义没有任何解释。笔者也为此大伤脑筋。不得以,只能通过查看WPAS代码的历史版本来寻根溯源。经过实践,笔者总结了利用git来查询WPAS历史版本信息的一些步骤,分别如下。

用git clone命令下载WPAS官方代码。

git clone git://w1.fi/srv/git/hostap.git  

以下命令的含义是查询use_monitor在driver_nl80211.c中的变化情况。

git blame src/drivers/driver_nl80211.c | grep use_monitor 

因为use_monitor定义于该文件中,所以用git blame去查看它。得到的结果如图4-3所示。

图4-3  git blame结果

图4-3中的第一行显示了use_monitor最早出现的那个patch的情况,其对应的commit id是a11241fa。接着,再通过命令git log a11241fa可查看当时的commit信息,结果如图4-4所示。

图4-4  git log结果

图4-4展示了a11241fa对应的commit消息。由于提交者一般会在该消息中添加注释性内容。所以可通过研究这些内容来了解代码中某些变量的含义。

下面正式开始WPAS的代码分析之旅。首先是WPAS的初始化流程分析。


[1]注意,wpa_supplicant项目中还包含一个名为hostapd程序的代码,它实现了AP的功能。本书不拟讨论hostpad的代码。

[2] wpa_supplicant项目的官方地址为http://hostap.epitest.fi/

[3]根据审稿专家的反馈,wpa_supplicant仅支持Linux Wireless Extension V19以后的版本。

==========略略略略略略略略略略略略略略略===============

4.5  wpa_supplicant连接无线网络分析

本节将介绍本章第二条分析路线,即通过命令行发送命令的方式触发wpa_supplicant进行相关工作,使手机加入一个利用WPA-PSK进行认证的无线网络。

以笔者的Note 2为例,整个过程用到的命令如下所示。

[命令示例]

adb root #获取手机root用户权限。只有root被破解的手机才能成功

adb shell #登录手机shell

#笔者事先已编译wpa_cli并将其放到/system/bin目录中。这个命令用于启动wpa_cli,-i参数指明unix域控制

#socket文件名,它应该和wpa_supplicant启动时设置的控制接口文件名一致

wpa_cli -iwlan0 #该命令执行后,将进入wpa_cli进程,后续操作都在此进程中开展

#发送ADD_NETWORK命令给wpa_supplicant,它将返回一个新网络配置项的编号。请参考4.3.3.1"wpas_ssid结构

#体介绍"一节

ADD_NETWORK #假设wpa_supplicant返回的新网络配置项编号为0

SET_NETWORK 0 ssid "Test" #设置0号网络的ssid为“Test”

SET_NETWORK 0 key_mgmt WPA-PSK #设置0号网络的key_mgmt为“WPA-PSK”

SET_NETWORK 0 psk "12345Test" #设置0号网络的psk为“12345Test”

ENABLE_NETWORK 0 #使能0号网络,它将触发wpa_supplicant扫描、关联等一系列操作直到加入无线网络“Test”

CTRL+C #退出wpa_cli

dhcpcd wlan0 #启动dhcpd,wlan0为无线接口设备名。dhcpcd可为手机从AP那获取一个IP地址

dhcpcd成功执行后,手机将从AP那分配到一个IP地址。至此,手机就可以使用“Test”无线网络了。

注意:上述命令执行前有几个注意事项:

1)先要在Settings中开启无线网络。这个操作完成了wlan驱动及相应固件加载的工作。该工作实际上由netd来完成,而wpa_cli无法完成它。

2)开启无线网络后,WifiService和wpa_supplicant都开始工作了。为了避免WifiService的干扰,可以把Settings中的那些已知的无线网络信息都清除。

3)由于wpa_supplicant支持多个客户端,所以wpa_cli可以和WifiService共同工作。只要不操作Settings中无线网络相关的选项,WifiService就不会干扰wpa_cli。

4)然后按上述步骤执行wpa_cli。

根据前文所述,所有来自客户端的命令都由wpa_supplicant_ctrl_iface_receive函数处理(参考4.3.4中“wpa_supplicant_ctrl_iface_init介绍”一节)。该函数代码非常简单,就是根据客户端发送的命令进行对应处理。

[-->ctrl_iface_unix.c::wpa_supplicant_ctrl_iface_receive]

static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,

                          void *sock_ctx)

    struct wpa_supplicant *wpa_s = eloop_ctx;

    struct ctrl_iface_priv *priv = sock_ctx;

    char buf[4096]; int res; struct sockaddr_un from;

    socklen_t fromlen = sizeof(from);

    char *reply = NULL; size_t reply_len = 0; int new_attached = 0;

    res = recvfrom(sock, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &from, &fromlen);

     .....

    buf[res] = '\0';

    //客户端第一次和WPAS连接时,需要发送"ATTACH"命令

    if (os_strcmp(buf, "ATTACH") == 0) {

        ......//略过相关处理

    } .....//"DETACH"和"LEVEL"命令处理

else {

#if defined(CONFIG_P2P) && defined(ANDROID_P2P)

     ......//P2P处理。虽然WPAS编译时打开了CONFIG_P2P和ANDROID_P2P,但本章不讨论P2P相关的内容

#endif

        //大部分的命令处理都在wpa_supplicant_ctrl_iface_process函数中

        reply = wpa_supplicant_ctrl_iface_process(wpa_s, buf,&reply_len);

    if (reply) {//回复客户端

        sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,fromlen);

        os_free(reply);

    } ......

    Client成功ATTACH后,将通知EAPOL模块。因为有些认证流程需要用户的参与(例如输入密码之类的),

    所以当客户端连接上后,EAPOL模块将判断是否需要和客户端交互。读者可阅读

     eapol_sm_notify_ctrl_attached函数。

   if (new_attached)

        eapol_sm_notify_ctrl_attached(wpa_s->eapol);

如上述代码所示,绝大部分命令都由wpa_supplicant_ctrl_iface_process函数处理。下面将按顺序来分析其处理ADD_NETWORK、SET_NETWORK以及ENABLE_NETWORK的代码。

4.5.1  ADD_NETWORK命令处理

[-->ctrl_iface.c::wpa_supplicant_ctrl_iface_process]

char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,char *buf,

                   size_t *resp_len)

    char *reply;

    const int reply_size = 4096;

    int ctrl_rsp = 0;

    int reply_len;

    ......

    reply = os_malloc(reply_size);

   .....

    //开始命令处理

    ......

    else if (os_strcmp(buf, "ADD_NETWORK") == 0) {

        reply_len = wpa_supplicant_ctrl_iface_add_network( wpa_s, reply, reply_size);

    }else if

    ......//其他命令处理

    if (reply_len < 0) {//命令处理出错

        os_memcpy(reply, "FAIL\n", 5);

        reply_len = 5;

    ......

    *resp_len = reply_len;

    return reply;

ADD_NETWORK的真正处理在wpa_supplicant_ctrl_iface_add_network函数中,其代码如下所示。

[-->ctrl_iface.c::wpa_supplicant_ctrl_iface_add_network]

static int wpa_supplicant_ctrl_iface_add_network(struct wpa_supplicant *wpa_s,

                  char *buf, size_t buflen)

    struct wpa_ssid *ssid;

    int ret;

    //wpa_config_add_network返回一个wpa_ssid对象,读者还记得它吗?wpa_ssid是无线网络配置项在

   //WPAS中的反映(请参考4.3.3中“wpa_ssid结构体介绍”一节)。wpa_config_add_network内部就是

   //分配一个wpa_ssid对象,然后将其保存到一个链表中。注意,wpa_config是wpa_supplicant.conf

   //在代码中的代表。所以,此处添加的无线网络信息将会保存到配置文件中,以备下次使用。

    ssid = wpa_config_add_network(wpa_s->conf);

   ......

    wpas_notify_network_added(wpa_s, ssid);

    ssid->disabled = 1; //disabled为1表示该无线网络未启用,需要通过ENABLE_NETWORK来启动它

    //设置该无线网络的默认配置项

    wpa_config_set_network_defaults(ssid);

   //返回该网络的编号(由wpa_ssid的id变量表示。它在wpa_config_add_network函数中被赋值)

    ret = os_snprintf(buf, buflen, "%d\n", ssid->id);

    ......

    return ret;

上述代码比较简单,无非就是分配一个wpa_ssid对象,然后设置它的一些默认属性。整个函数返回该wpa_ssid对象的id,即它在链表中的顺序。

wpa_ssid的默认属性对后续流程有一些影响,那么默认属性都是什么呢?不妨来看看wpa_config_set_network_defaults函数,代码如下所示。

[-->config.c::wpa_config_set_network_defaults]

void wpa_config_set_network_defaults(struct wpa_ssid *ssid)

   //设置proto、pairwise_cipher、group_cipher以及key_mgmt的信息,读者还记得这些变量的含义吗?

   //请参考4.3.3中“安全相关成员变量及背景知识介绍”一节

    ssid->proto = DEFAULT_PROTO;

    ssid->pairwise_cipher = DEFAULT_PAIRWISE;

    ssid->group_cipher = DEFAULT_GROUP;

    ssid->key_mgmt = DEFAULT_KEY_MGMT;

#ifdef IEEE8021X_EAPOL

    ssid->eapol_flags = DEFAULT_EAPOL_FLAGS;//EAP相关变量,见下文解释

    ssid->eap_workaround = DEFAULT_EAP_WORKAROUND;

    ssid->eap.fragment_size = DEFAULT_FRAGMENT_SIZE;

#endif /* IEEE8021X_EAPOL */

#ifdef CONFIG_HT_OVERRIDES

    ......//和802.11n有关,本书不涉及

#endif /* CONFIG_HT_OVERRIDES */

上述代码中出现了三个和EAPOL相关的变量,此处简单介绍一下:

  • eapol_flags:它和动态WEP key有关。只适用于非WPA安全环境中,可取值有三个,分别是1(代码中定义为BIT(0),表示需要为单播数据传输使用动态WEP Key,对应宏为EAPOL_FLAG_REQUIRE_KEY_UNICAST)、2(代码中定义为BIT(1),表示需要为组播数据传输使用动态WEP Key,对应宏为EAPOL_FLAG_REQUIRE_KEY_BROADCAST)以及3(单播和组播都使用动态WEP Key,对应宏为DEFAULT_EAPOL_FLAGS)
  • eap_workaround:身份认证方法多种多样,而有些Authenticator服务器(缩写为AS)并不严格遵守规范。该变量表示碰到这种情况时,WPAS是否可以采取“绕”(workaround本意是“变通”)过去的方式来对待这些AS。由于这种不严格的情况非常普遍,所以该值默认是1,
  • fragment_size:该变量和EAPOL消息分片大小有关。默认的DEFAULT_FRAGMENT_SIZE大小为1398,表示EAPOL消息只要不超过这个大小,就不用对其进行分片。

“ADD_NETWORK”命令比较简单,它最终将返回给客户端对应的无线网络配置的编号。在本例中,它是0。

下面来看客户端通过“SET_NETWORK”为该无线网络配置项设置参数的处理过程。

============================略略略略略===================

4.6  本章总结和参考资料一览

4.6.1  本章总结

本章对wpa_supplicant进行了一番剖析,涉及如下几个重点内容:

  • WPAS的启动:在这条分析路线中,读者能感受到WPAS中多种多样的数据结构以及之间较为复杂的关系。同时,我们对那些极具背景含义的成员变量也进行了深入介绍。
  • EAPOL/EAP模块:这一节中,我们先向读者介绍了理论知识,然后结合代码分析了WPAS如何将理论知识转化成代码。从笔者角度来看,状态机的代码比看状态切换图要复杂得多。读者不妨自己亲身体验一下。
  • 扫描、关联及4-Way Handshake分析:我们通过一个实例讲解了这一流程涉及的重要函数和相关知识点。该流程实际上包含的代码远比书中给出来的多,原因是WPAS支持的许多其他功能(如WPS、P2P、802.11R、802.11W等)都在这一流程中有涉及。根据笔者自己的学习经验,只有先搞清楚一个最简单的流程,后面才能循序渐进地把其他功能的分析加进来。

提示:本章编撰时,市面上搭载Android 4.2的手机较少。所以此处的分析目标是Android 4.1中的wpa_supplicant。不过笔者比较了它和Android 4.2中的wpa_supplicant。虽然4.2中的WPAS变化较大,但主要体现在一些新增功能和Bug修改上。读者如果搞清楚了本章所介绍的内容,再转去研究4.2中的WPAS所需要的学习成本不会太大。

4.6.2  参考资料一览

[1]  http://en.wikipedia.org/wiki/Extensible_Authentication_Protocol

维基百科关于EAP各种方法的一个简单介绍。

[2]  http://hostap.epitest.fi/wpa_supplicant/devel/

wpa_supplicant官方开发文档。读者可以简单浏览一下。

wpa_ssid结构体介绍

[3]  802.11-2012 附录M.4 “Suggested pass-phrase-to-PSK mapping”

该节介绍了passphrase转换成PSK的方法,甚至还有伪代码实现。感兴趣的读者不妨结合WPAS中的代码来研究它。

[4]  802.11-2012 第8.4.2.27.2节“Cipher suites”

[5]  802.11-2012 第8.4.2.27.3节“AKM suites”

上述两小节分别介绍了Cipher和AKM suites的情况。注意,其中定义的取值定义是指在RSN IE中的取值,和代码中定义的宏不是一回事。

[6]  802.11-2012 第12章“Fast BSS Transition”

官方文档。不过难度较大,建议读者阅读“Secure Roaming in 802.11 Networks”一书后再去看它。提醒,此书是笔者目前阅读到的关于Wi-Fi Roaming相关知识介绍最完整的一本。

[7]  Real 802.11 Security:Wi-Fi Protected Access and 802.11i 第6章“How IEEE 802.11 WEP Works and Why It Doesn't”

关于WEP介绍的章节。另外,对安全感兴趣的读者请仔细阅读此书。

[8]  http://www.codealias.info/technotes/opportunistic_pmk_pre-caching

关于Opportunistic PMK Caching的简单介绍。

[9]  Secure Roaming in 802.11 Networks第8章“Opportunistic Key Caching”一节

相比[8]而言,这一节对OKC有更为详尽的介绍。

wpa_supplicant结构体介绍

[10]  802.11无线网络权威指南(第二版)第七章“802.11:RSN、TKIP与CCMP”,P171-P172

[11]  802.11-2012 第11.4.2.4节“TKIP countermeasures procedures”

上述两个参考资料介绍了TKIP countermeasures的处理方式。请读者先阅读[10]。

[12]  http://www.cisco.com/en/US/docs/solutions/Enterprise/Mobility/vowlan/41dg/vowlan_ch5.html

[13]  Secure Roaming in 802.11 Networks第5.2.5节“Background Scanning”

Background Scan技术的介绍。

[14]  http://network.chinabyte.com/359/12453859.shtml

[15]  http://www.docin.com/p-365323002.html

和GAS以及802.11u相关的一些介绍。

wpa_supplicant_init_iface分析之三

[16]  http://www.mjmwired.net/kernel/Documentation/rfkill.txt

[17]  http://lwn.net/Articles/335382/

这两篇资料介绍了rfkill相关的信息。感兴趣的读者不妨仔细阅读它们。

[18]  http://wenku.baidu.com/view/c74758d280eb6294dd886c53.html

RFC2863 3.1.13“IfAdminStatus and IfOperStatus”一节描述了IfOperStatus的取值情况及相关说明。

[19]  http://wireless.kernel.org/en/developers/Documentation/nl80211/kerneldoc

linux wireless kernel官方网站中nl80211内核部分的一些解释。

EAP模块分析

[20]  http://tools.ietf.org/pdf/rfc4137.pdf

RFC4137文档的PDF版。相比TXT版而言,它用图来描述状态机的状态切换。

EAPOL模块分析

[21]  802.1X 2004版

WPAS中的802.1X实现是基于802.1X 2004版。相比2010版而言,笔者觉得2004版的内容更具条理性。尤其是其关于EAPOL各状态机的描述非常清晰。

EAPOL-Key交换流程分析

[22]  Real 802.11 Security:Wi-Fi Protected Access and 802.11i 第10章“WPA and RSN Key Hierarchy”

[23]  802.11-2012 第11.6“Keys and key distribution”

这两篇参考资料对Pairwise Key和Group Key以及4-Way Handshake、Group Key Handshake都有详细的介绍。

[24]  wireless.kernel.org/en/users/Documentation/WoWLAN

[25]  msdn.microsoft.com/en-us/library/windows/hardware/ff571052(v=vs.85).aspx

这两篇文章对WoWLAN有一番介绍。读者可简单阅读它们。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK