简体   繁体   English

在Linux上通过Python连接到受保护的WiFi

[英]Connecting to a protected WiFi from Python on Linux

I'm creating a software for Ubuntu Linux that needs to connect to a WiFi AP. 我正在为Ubuntu Linux创建一个需要连接到WiFi AP的软件。 The WiFi network is not predefined and can change several times during a single run of the software (the user is the one who orders the change). WiFi网络不是预定义的,在软件的一次运行中可能会更改多次(用户是订购更改的用户)。 The idea is this: given a set of SSIDs and their WPA or WEP passphrases, the software should be able to switch between the networks on the whim, without the need to change any configuration files anywhere in the system. 想法是这样的:给定一组SSID及其WPA或WEP密码,该软件应该能够随心所欲地在网络之间切换,而无需在系统中的任何位置更改任何配置文件。

The huge problem, as it seems, is to pass the passphrase to the connection. 看来,最大的问题是将密码短语传递给连接。 Here's what I've been operating with so far: 到目前为止,这是我一直在进行的操作:

  • Ubuntu 12.10 machine equipped with a WiFi dongle. 配备WiFi加密狗的Ubuntu 12.10机器。
  • Python, which runs the software, and which will be used to request the connections Python,运行该软件,并将用于请求连接
  • connman 0.79 康曼0.79
  • wpa_supplicant v1.0 wpa_supplicant v1.0
  • d-bus 公交车

At first I thought it would be possible to pass the passphrase to connman through d-bus, but neither this version of connman, nor 1.11, seem to expose any method for that. 最初,我认为可以通过d-bus将密码短语传递给connman,但是此版本的connman或1.11似乎都没有公开任何方法。 Then I found out that it's possible to dump a service_<SSID>.conf file to the /var/lib/connman/ directory. 然后我发现可以将service_<SSID>.conf文件转储到/var/lib/connman/目录。 Contents of the file are very simple and look like this: 该文件的内容非常简单,如下所示:

[service_SSID]
Type=wifi
Name=Network-SSID
Passphrase=here-goes-the-passphrase

Once this file is created, connecting to the network requires a simple call to net.connman.Service.Connect() method in appropriate service. 创建此文件后,连接到网络需要在适当的服务中简单地调用net.connman.Service.Connect()方法。 The problem is that connman won't parse the config file unless it's restarted. 问题在于,除非重新启动配置文件,否则connman不会解析该配置文件。 This requires sudo privileges, additional time, and raises risk for all the "what can go wrong now" things to occur. 这需要sudo特权,额外的时间,并增加了发生所有“现在可能出问题的地方”的风险。 Then I figured that passphrase could be somehow passed to the wpa_supplicant d-bus API, but I failed to find anything. 然后我发现可以将密码短语以某种方式传递给wpa_supplicant d-bus API,但是我什么都没找到。

Google searches have failed me too. Google搜索也使我失败了。 It is as if no one ever tried to do this before. 好像以前没有人尝试过这样做。

Command sudo iwconfig wlan0 essid <SSID> key s:<PASSPHRASE> results in a SET failed on device wlan0 ; Invalid argument. 命令sudo iwconfig wlan0 essid <SSID> key s:<PASSPHRASE>导致SET failed on device wlan0 ; Invalid argument.SET failed on device wlan0 ; Invalid argument. SET failed on device wlan0 ; Invalid argument. error. 错误。 Also, it requires sudo which I would like to avoid. 另外,它需要sudo,我想避免。

I tried to figure out how the wpa_gui program does its magic. 我试图弄清楚wpa_gui程序是如何发挥作用的。 First of all I discovered that it also requires sudo, and that it sends a bunch of commands directly to /var/run/wpa_supplicant/wlan0 . 首先,我发现它也需要sudo,并且它直接将一堆命令发送到/var/run/wpa_supplicant/wlan0 Replicating this behavior would be a last resort for me if I don't figure out anything simplier. 如果我不觉得更简单,那么复制这种行为对我来说将是万不得已的方法。

So, the big question is this: How to use Python in order to connect to a WEP/WPA protected WiFi network? 因此,最大的问题是:如何使用Python连接到受WEP / WPA保护的WiFi网络?
I'm also wondering if using connman is a good approach here and if I shouldn't revert to the Network Manager, which is Ubuntu's default. 我也想知道在这里使用connman是否是一个好方法,是否不应该使用网络管理器(这是Ubuntu的默认设置)。

This shows how to do this for WPA. 这显示了如何为WPA执行此操作。

First of all, ditch connman and use NetworkManager. 首先,抛弃connman并使用NetworkManager。 The example script below shows how to enable wireless support, check if network with given SSID is available, connect to that SSID using a WPA passphrase, and then disconnect from the network and disable wireless. 下面的示例脚本显示了如何启用无线支持,检查具有给定SSID的网络是否可用,使用WPA密码连接到该SSID,然后断开与网络的连接并禁用无线功能。 I'm fairly sure that this script can be improved, but the current version serves enough as an example: 我相当确定可以对该脚本进行改进,但是当前版本足以作为示例:

#!/usr/bin/python

# This script shows how to connect to a WPA protected WiFi network
# by communicating through D-Bus to NetworkManager 0.9.
#
# Reference URLs:
# http://projects.gnome.org/NetworkManager/developers/
# http://projects.gnome.org/NetworkManager/developers/settings-spec-08.html

import dbus
import time

SEEKED_SSID = "skynet"
SEEKED_PASSPHRASE = "qwertyuiop"

if __name__ == "__main__":
    bus = dbus.SystemBus()
    # Obtain handles to manager objects.
    manager_bus_object = bus.get_object("org.freedesktop.NetworkManager",
                                        "/org/freedesktop/NetworkManager")
    manager = dbus.Interface(manager_bus_object,
                             "org.freedesktop.NetworkManager")
    manager_props = dbus.Interface(manager_bus_object,
                                   "org.freedesktop.DBus.Properties")

    # Enable Wireless. If Wireless is already enabled, this does nothing.
    was_wifi_enabled = manager_props.Get("org.freedesktop.NetworkManager",
                                         "WirelessEnabled")
    if not was_wifi_enabled:
        print "Enabling WiFi and sleeping for 10 seconds ..."
        manager_props.Set("org.freedesktop.NetworkManager", "WirelessEnabled",
                          True)
        # Give the WiFi adapter some time to scan for APs. This is absolutely
        # the wrong way to do it, and the program should listen for
        # AccessPointAdded() signals, but it will do.
        time.sleep(10)

    # Get path to the 'wlan0' device. If you're uncertain whether your WiFi
    # device is wlan0 or something else, you may utilize manager.GetDevices()
    # method to obtain a list of all devices, and then iterate over these
    # devices to check if DeviceType property equals NM_DEVICE_TYPE_WIFI (2).
    device_path = manager.GetDeviceByIpIface("wlan0")
    print "wlan0 path: ", device_path

    # Connect to the device's Wireless interface and obtain list of access
    # points.
    device = dbus.Interface(bus.get_object("org.freedesktop.NetworkManager",
                                           device_path),
                            "org.freedesktop.NetworkManager.Device.Wireless")
    accesspoints_paths_list = device.GetAccessPoints()

    # Identify our access point. We do this by comparing our desired SSID
    # to the SSID reported by the AP.
    our_ap_path = None
    for ap_path in accesspoints_paths_list:
        ap_props = dbus.Interface(
            bus.get_object("org.freedesktop.NetworkManager", ap_path),
            "org.freedesktop.DBus.Properties")
        ap_ssid = ap_props.Get("org.freedesktop.NetworkManager.AccessPoint",
                               "Ssid")
        # Returned SSID is a list of ASCII values. Let's convert it to a proper
        # string.
        str_ap_ssid = "".join(chr(i) for i in ap_ssid)
        print ap_path, ": SSID =", str_ap_ssid
        if str_ap_ssid == SEEKED_SSID:
            our_ap_path = ap_path
            break

    if not our_ap_path:
        print "AP not found :("
        exit(2)
    print "Our AP: ", our_ap_path

    # At this point we have all the data we need. Let's prepare our connection
    # parameters so that we can tell the NetworkManager what is the passphrase.
    connection_params = {
        "802-11-wireless": {
            "security": "802-11-wireless-security",
        },
        "802-11-wireless-security": {
            "key-mgmt": "wpa-psk",
            "psk": SEEKED_PASSPHRASE
        },
    }

    # Establish the connection.
    settings_path, connection_path = manager.AddAndActivateConnection(
        connection_params, device_path, our_ap_path)
    print "settings_path =", settings_path
    print "connection_path =", connection_path

    # Wait until connection is established. This may take a few seconds.
    NM_ACTIVE_CONNECTION_STATE_ACTIVATED = 2
    print """Waiting for connection to reach """ \
          """NM_ACTIVE_CONNECTION_STATE_ACTIVATED state ..."""
    connection_props = dbus.Interface(
        bus.get_object("org.freedesktop.NetworkManager", connection_path),
        "org.freedesktop.DBus.Properties")
    state = 0
    while True:
        # Loop forever until desired state is detected.
        #
        # A timeout should be implemented here, otherwise the program will
        # get stuck if connection fails.
        #
        # IF PASSWORD IS BAD, NETWORK MANAGER WILL DISPLAY A QUERY DIALOG!
        # This is something that should be avoided, but I don't know how, yet.
        #
        # Also, if connection is disconnected at this point, the Get()
        # method will raise an org.freedesktop.DBus.Error.UnknownMethod
        # exception. This should also be anticipated.
        state = connection_props.Get(
            "org.freedesktop.NetworkManager.Connection.Active", "State")
        if state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED:
            break
        time.sleep(0.001)
    print "Connection established!"

    #
    # Connection is established. Do whatever is necessary.
    # ...
    #
    print "Sleeping for 5 seconds ..."
    time.sleep(5)
    print "Disconnecting ..."

    # Clean up: disconnect and delete connection settings. If program crashes
    # before this point is reached then connection settings will be stored
    # forever.
    # Some pre-init cleanup feature should be devised to deal with this problem,
    # but this is an issue for another topic.
    manager.DeactivateConnection(connection_path)
    settings = dbus.Interface(
        bus.get_object("org.freedesktop.NetworkManager", settings_path),
        "org.freedesktop.NetworkManager.Settings.Connection")
    settings.Delete()

    # Disable Wireless (optional step)
    if not was_wifi_enabled:
        manager_props.Set("org.freedesktop.NetworkManager", "WirelessEnabled",
                          False)
    print "DONE!"
    exit(0)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM