shill: add support for connecting to WPA-PSK networks

BUG=chromium-os:20897
TEST=unittests, some autotests (see below)

the following autotests now pass, at least in the wifi_vm_config:
- network_WiFiManager.000_SSID_Length_Limit
- network_WiFiSecMat.010CheckWPA_TKIP
- network_WiFiSecMat.011CheckWPA_AES
- network_WiFiSecMat.012CheckWPA_Multi
- network_WiFiSecMat.018CheckWPA_CounterMeasures

Change-Id: Ie7499fd87f661ceef3ef0aae348a08bd43c305f4
Reviewed-on: http://gerrit.chromium.org/gerrit/8586
Tested-by: mukesh agrawal <quiche@google.com>
Reviewed-by: Paul Stewart <pstew@chromium.org>
diff --git a/HACKING b/HACKING
index 3a1f7c2..109de02 100644
--- a/HACKING
+++ b/HACKING
@@ -31,10 +31,12 @@
   - Read data via the appropriate named method, rather than depending on
     implicit conversion. E.g.,
 
+       ::DBus::Variant var;
        int8 data = var.reader().get_byte();
 
     rather than
 
+       ::DBus::Variant var;
        int8 data = var;
 
     RATIONALE: The explicit version is only marginally longer than the
@@ -44,12 +46,52 @@
   - Where there is no named method, call the appropriate cast operator
     explicitly. E.g.
 
+    ::DBus::Variant var;
     vector<unsigned int> data = var.operator vector<unsigned int>();
 
     RATIONALE: Calling the cast operator explicitly avoids conflicts with
     constructors that might also be used to make the conversion. It also
     avoids requiring that the reader understand C++ conversion rules.
 
+  - Write data via the appropriate named method. E.g.,
+
+       ::DBus::Variant var;
+       int16_t data;
+       var.writer().append_int16(data);
+
+    rather than
+
+       ::DBus::Variant var;
+       int16_t data;
+       var.writer() << data;
+
+    RATIONALE: Similarly as for reading, the explicit version is only
+    marginally longer, and does not require the reader to understand
+    overload resolution.
+
+  - Where there is no named method, write by using the stream
+    insertion operator. E.g.
+
+       ::DBus::Variant var;
+       ::DBus::MessageIter writer;
+       map<string, string> data;
+       writer = var.writer();
+       writer << data;
+
+    RATIONALE: This case is somewhat unfortunate, because it's not as
+    clear as its analogue for reading. However, the alternative is to
+    duplicate the code of the stream insertion operator overloads.
+
+    Note that the writer can't be omitted. E.g.
+
+       ::DBus::Variant var;
+       map<string, string> data;
+       var.writer() << data;
+
+    does not work (at least for some types of |data|). Without the
+    intermediate variable, g++ seems to ignore templates during
+    overload resolution.
+
 - When deferring work from a signal handler (e.g. a D-Bus callback) to
   the event loop, name the deferred work function by adding "Task" to
   the name of the function deferring the work. E.g.
diff --git a/Makefile b/Makefile
index c1f4639..38caba0 100644
--- a/Makefile
+++ b/Makefile
@@ -131,7 +131,8 @@
 	technology.o \
 	wifi.o \
 	wifi_endpoint.o \
-	wifi_service.o
+	wifi_service.o \
+	wpa_supplicant.o
 
 SHILL_BIN = shill
 SHILL_MAIN_OBJ = shill_main.o
@@ -199,6 +200,7 @@
 	service_unittest.o \
 	shill_unittest.o \
 	testrunner.o \
+	wifi_endpoint_unittest.o \
 	wifi_service_unittest.o \
 	wifi_unittest.o
 
diff --git a/mock_wifi.h b/mock_wifi.h
index 9dc8430..fb3c428 100644
--- a/mock_wifi.h
+++ b/mock_wifi.h
@@ -36,6 +36,9 @@
   MOCK_METHOD1(Scan, void(Error *error));
   MOCK_METHOD2(GetService,
                WiFiServiceRefPtr(const KeyValueStore &args, Error *error));
+  MOCK_METHOD2(ConnectTo,
+               void(WiFiService *,
+                    const std::map<std::string, ::DBus::Variant> &));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockWiFi);
diff --git a/wifi.cc b/wifi.cc
index f11a517..f6602c3 100644
--- a/wifi.cc
+++ b/wifi.cc
@@ -32,6 +32,7 @@
 #include "shill/supplicant_process_proxy_interface.h"
 #include "shill/wifi_endpoint.h"
 #include "shill/wifi_service.h"
+#include "shill/wpa_supplicant.h"
 
 using std::map;
 using std::string;
@@ -55,17 +56,6 @@
     "service type is unsupported";
 const char WiFi::kManagerErrorUnsupportedServiceMode[] =
     "service mode is unsupported";
-const char WiFi::kSupplicantPath[]        = "/fi/w1/wpa_supplicant1";
-const char WiFi::kSupplicantDBusAddr[]    = "fi.w1.wpa_supplicant1";
-const char WiFi::kSupplicantWiFiDriver[]  = "nl80211";
-const char WiFi::kSupplicantErrorInterfaceExists[] =
-    "fi.w1.wpa_supplicant1.InterfaceExists";
-const char WiFi::kSupplicantPropertySSID[]        = "ssid";
-const char WiFi::kSupplicantPropertyNetworkMode[] = "mode";
-const char WiFi::kSupplicantPropertyKeyMode[]     = "key_mgmt";
-const char WiFi::kSupplicantPropertyScanType[]    = "Type";
-const char WiFi::kSupplicantKeyModeNone[] = "NONE";
-const char WiFi::kSupplicantScanTypeActive[] = "active";
 
 // NB: we assume supplicant is already running. [quiche.20110518]
 WiFi::WiFi(ControlInterface *control_interface,
@@ -109,19 +99,19 @@
 
   supplicant_process_proxy_.reset(
       ProxyFactory::factory()->CreateSupplicantProcessProxy(
-          kSupplicantPath, kSupplicantDBusAddr));
+          wpa_supplicant::kDBusPath, wpa_supplicant::kDBusAddr));
   try {
     std::map<string, DBus::Variant> create_interface_args;
     create_interface_args["Ifname"].writer().
         append_string(link_name().c_str());
     create_interface_args["Driver"].writer().
-        append_string(kSupplicantWiFiDriver);
+        append_string(wpa_supplicant::kDriverNL80211);
     // TODO(quiche) create_interface_args["ConfigFile"].writer().append_string
     // (file with pkcs config info)
     interface_path =
         supplicant_process_proxy_->CreateInterface(create_interface_args);
   } catch (const DBus::Error e) {  // NOLINT
-    if (!strcmp(e.name(), kSupplicantErrorInterfaceExists)) {
+    if (!strcmp(e.name(), wpa_supplicant::kErrorInterfaceExists)) {
       interface_path =
           supplicant_process_proxy_->GetInterface(link_name());
       // XXX crash here, if device missing?
@@ -132,7 +122,7 @@
 
   supplicant_interface_proxy_.reset(
       ProxyFactory::factory()->CreateSupplicantInterfaceProxy(
-          this, interface_path, kSupplicantDBusAddr));
+          this, interface_path, wpa_supplicant::kDBusAddr));
 
   // TODO(quiche) set ApScan=1 and BSSExpireAge=190, like flimflam does?
 
@@ -239,25 +229,21 @@
       task_factory_.NewRunnableMethod(&WiFi::ScanDoneTask));
 }
 
-void WiFi::ConnectTo(WiFiService *service) {
-  std::map<string, DBus::Variant> add_network_args;
-  DBus::MessageIter writer;
+void WiFi::ConnectTo(WiFiService *service,
+                     const map<string, DBus::Variant> &service_params) {
   DBus::Path network_path;
 
   // TODO(quiche): handle cases where already connected
 
-  add_network_args[kSupplicantPropertyNetworkMode].writer().
-      append_uint32(WiFiEndpoint::ModeStringToUint(service->mode()));
-  add_network_args[kSupplicantPropertyKeyMode].writer().
-      append_string(service->key_management().c_str());
-  // TODO(quiche): figure out why we can't use operator<< without the
-  // temporary variable.
-  writer = add_network_args[kSupplicantPropertySSID].writer();
-  writer << service->ssid();
-  // TODO(quiche): set scan_ssid=1, like flimflam does?
+  // TODO(quiche): set scan_ssid=1 in service_params, like flimflam does?
+  try {
+    network_path =
+        supplicant_interface_proxy_->AddNetwork(service_params);
+  } catch (const DBus::Error e) {  // NOLINT
+    LOG(ERROR) << "exception while adding network: " << e.what();
+    return;
+  }
 
-  network_path =
-      supplicant_interface_proxy_->AddNetwork(add_network_args);
   supplicant_interface_proxy_->SelectNetwork(network_path);
   // XXX add to favorite networks list?
 
@@ -289,10 +275,9 @@
       LOG(INFO) << "found new endpoint. "
                 << "ssid: " << endpoint.ssid_string() << ", "
                 << "bssid: " << endpoint.bssid_string() << ", "
-                << "signal: " << endpoint.signal_strength();
+                << "signal: " << endpoint.signal_strength() << ", "
+                << "security: " << endpoint.security_mode();
 
-      // XXX key mode should reflect endpoint params (not always use
-      // kSupplicantKeyModeNone)
       WiFiServiceRefPtr service(
           new WiFiService(control_interface(),
                           dispatcher(),
@@ -300,7 +285,7 @@
                           this,
                           endpoint.ssid(),
                           endpoint.network_mode(),
-                          kSupplicantKeyModeNone));
+                          endpoint.security_mode()));
       services()->push_back(service);
       service_by_private_id_[service_id_private] = service;
       manager()->RegisterService(service);
@@ -315,8 +300,8 @@
 void WiFi::ScanTask() {
   VLOG(2) << "WiFi " << link_name() << " scan requested.";
   std::map<string, DBus::Variant> scan_args;
-  scan_args[kSupplicantPropertyScanType].writer().
-      append_string(kSupplicantScanTypeActive);
+  scan_args[wpa_supplicant::kPropertyScanType].writer().
+      append_string(wpa_supplicant::kScanTypeActive);
   // TODO(quiche) indicate scanning in UI
   supplicant_interface_proxy_->Scan(scan_args);
   scan_pending_ = true;
diff --git a/wifi.h b/wifi.h
index 7687215..919891f 100644
--- a/wifi.h
+++ b/wifi.h
@@ -47,7 +47,9 @@
   void ScanDone();
 
   // called by WiFiService
-  void ConnectTo(WiFiService *service);
+  virtual void ConnectTo(
+      WiFiService *service,
+      const std::map<std::string, ::DBus::Variant> &service_params);
 
   // called by Manager
   virtual WiFiServiceRefPtr GetService(const KeyValueStore &args, Error *error);
@@ -64,16 +66,6 @@
   static const char kManagerErrorUnsupportedSecurityMode[];
   static const char kManagerErrorUnsupportedServiceType[];
   static const char kManagerErrorUnsupportedServiceMode[];
-  static const char kSupplicantPath[];
-  static const char kSupplicantDBusAddr[];
-  static const char kSupplicantWiFiDriver[];
-  static const char kSupplicantErrorInterfaceExists[];
-  static const char kSupplicantPropertySSID[];
-  static const char kSupplicantPropertyNetworkMode[];
-  static const char kSupplicantPropertyKeyMode[];
-  static const char kSupplicantPropertyScanType[];
-  static const char kSupplicantKeyModeNone[];
-  static const char kSupplicantScanTypeActive[];
 
   void ScanDoneTask();
   void ScanTask();
diff --git a/wifi_endpoint.cc b/wifi_endpoint.cc
index 300c258..3791bdc 100644
--- a/wifi_endpoint.cc
+++ b/wifi_endpoint.cc
@@ -5,41 +5,35 @@
 #include "shill/wifi_endpoint.h"
 
 #include <base/logging.h>
+#include <base/stl_util-inl.h>
 #include <base/stringprintf.h>
 #include <base/string_number_conversions.h>
+#include <base/string_util.h>
 #include <chromeos/dbus/service_constants.h>
 
+#include "shill/wpa_supplicant.h"
+
+using std::map;
+using std::set;
 using std::string;
+using std::vector;
 
 namespace shill {
 
-const char WiFiEndpoint::kSupplicantPropertySSID[]   = "SSID";
-const char WiFiEndpoint::kSupplicantPropertyBSSID[]  = "BSSID";
-const char WiFiEndpoint::kSupplicantPropertySignal[] = "Signal";
-const char WiFiEndpoint::kSupplicantPropertyMode[]   = "Mode";
-
-const char WiFiEndpoint::kSupplicantNetworkModeInfrastructure[] =
-    "infrastructure";
-const char WiFiEndpoint::kSupplicantNetworkModeAdHoc[]       = "ad-hoc";
-const char WiFiEndpoint::kSupplicantNetworkModeAccessPoint[] = "ap";
-
-const uint32_t WiFiEndpoint::kSupplicantNetworkModeInfrastructureInt = 0;
-const uint32_t WiFiEndpoint::kSupplicantNetworkModeAdHocInt          = 1;
-const uint32_t WiFiEndpoint::kSupplicantNetworkModeAccessPointInt    = 2;
-
 WiFiEndpoint::WiFiEndpoint(
-    const std::map<string, ::DBus::Variant> &properties) {
+    const map<string, ::DBus::Variant> &properties) {
   // XXX will segfault on missing properties
   ssid_ =
-      properties.find(kSupplicantPropertySSID)->second.
+      properties.find(wpa_supplicant::kBSSPropertySSID)->second.
       operator std::vector<uint8_t>();
   bssid_ =
-      properties.find(kSupplicantPropertyBSSID)->second.
+      properties.find(wpa_supplicant::kBSSPropertyBSSID)->second.
       operator std::vector<uint8_t>();
   signal_strength_ =
-      properties.find(kSupplicantPropertySignal)->second;
+      properties.find(wpa_supplicant::kBSSPropertySignal)->second;
   network_mode_ = ParseMode(
-      properties.find(kSupplicantPropertyMode)->second);
+      properties.find(wpa_supplicant::kBSSPropertyMode)->second);
+  security_mode_ = ParseSecurity(properties);
 
   if (network_mode_.empty()) {
     // XXX log error?
@@ -58,16 +52,16 @@
 // static
 uint32_t WiFiEndpoint::ModeStringToUint(const std::string &mode_string) {
   if (mode_string == flimflam::kModeManaged)
-    return kSupplicantNetworkModeInfrastructureInt;
+    return wpa_supplicant::kNetworkModeInfrastructureInt;
   else if (mode_string == flimflam::kModeAdhoc)
-    return kSupplicantNetworkModeAdHocInt;
+    return wpa_supplicant::kNetworkModeAdHocInt;
   else
     NOTIMPLEMENTED() << "Shill dos not support " << mode_string
                      << " mode at this time.";
   return 0;
 }
 
-const std::vector<uint8_t> &WiFiEndpoint::ssid() const {
+const vector<uint8_t> &WiFiEndpoint::ssid() const {
   return ssid_;
 }
 
@@ -92,16 +86,20 @@
 }
 
 const string &WiFiEndpoint::network_mode() const {
-    return network_mode_;
+  return network_mode_;
+}
+
+const string &WiFiEndpoint::security_mode() const {
+  return security_mode_;
 }
 
 // static
-const char *WiFiEndpoint::ParseMode(const std::string &mode_string) {
-  if (mode_string == kSupplicantNetworkModeInfrastructure) {
+const char *WiFiEndpoint::ParseMode(const string &mode_string) {
+  if (mode_string == wpa_supplicant::kNetworkModeInfrastructure) {
     return flimflam::kModeManaged;
-  } else if (mode_string == kSupplicantNetworkModeAdHoc) {
+  } else if (mode_string == wpa_supplicant::kNetworkModeAdHoc) {
     return flimflam::kModeAdhoc;
-  } else if (mode_string == kSupplicantNetworkModeAccessPoint) {
+  } else if (mode_string == wpa_supplicant::kNetworkModeAccessPoint) {
     NOTREACHED() << "Shill does not support AP mode at this time.";
     return NULL;
   } else {
@@ -110,4 +108,71 @@
   }
 }
 
+// static
+const char *WiFiEndpoint::ParseSecurity(
+    const map<string, ::DBus::Variant> &properties) {
+  set<KeyManagement> rsn_key_management_methods;
+  if (ContainsKey(properties, wpa_supplicant::kPropertyRSN)) {
+    // TODO(quiche): check type before casting
+    const map<string, ::DBus::Variant> rsn_properties(
+        properties.find(wpa_supplicant::kPropertyRSN)->second.
+        operator map<string, ::DBus::Variant>());
+    ParseKeyManagementMethods(rsn_properties, &rsn_key_management_methods);
+  }
+
+  set<KeyManagement> wpa_key_management_methods;
+  if (ContainsKey(properties, wpa_supplicant::kPropertyWPA)) {
+    // TODO(qucihe): check type before casting
+    const map<string, ::DBus::Variant> rsn_properties(
+        properties.find(wpa_supplicant::kPropertyWPA)->second.
+        operator map<string, ::DBus::Variant>());
+    ParseKeyManagementMethods(rsn_properties, &wpa_key_management_methods);
+  }
+
+  bool wep_privacy = false;
+  if (ContainsKey(properties, wpa_supplicant::kPropertyPrivacy)) {
+    wep_privacy = properties.find(wpa_supplicant::kPropertyPrivacy)->second.
+        reader().get_bool();
+  }
+
+  if (ContainsKey(rsn_key_management_methods, kKeyManagement802_1x) ||
+      ContainsKey(wpa_key_management_methods, kKeyManagement802_1x)) {
+    return flimflam::kSecurity8021x;
+  } else if (ContainsKey(rsn_key_management_methods, kKeyManagementPSK)) {
+    return flimflam::kSecurityRsn;
+  } else if (ContainsKey(wpa_key_management_methods, kKeyManagementPSK)) {
+    return flimflam::kSecurityWpa;
+  } else if (wep_privacy) {
+    return flimflam::kSecurityWep;
+  } else {
+    return flimflam::kSecurityNone;
+  }
+}
+
+// static
+void WiFiEndpoint::ParseKeyManagementMethods(
+    const map<string, ::DBus::Variant> &security_method_properties,
+    set<KeyManagement> *key_management_methods) {
+  if (!ContainsKey(security_method_properties,
+                   wpa_supplicant::kSecurityMethodPropertyKeyManagement)) {
+    return;
+  }
+
+  // TODO(quiche): check type before cast
+  const vector<string> key_management_vec =
+      security_method_properties.
+      find(wpa_supplicant::kSecurityMethodPropertyKeyManagement)->second.
+      operator vector<string>();
+  for (vector<string>::const_iterator it = key_management_vec.begin();
+       it != key_management_vec.end();
+       ++it) {
+    if (EndsWith(*it, wpa_supplicant::kKeyManagementMethodSuffixEAP, true)) {
+      key_management_methods->insert(kKeyManagement802_1x);
+    } else if (
+        EndsWith(*it, wpa_supplicant::kKeyManagementMethodSuffixPSK, true)) {
+      key_management_methods->insert(kKeyManagementPSK);
+    }
+  }
+}
+
 }  // namespace shill
diff --git a/wifi_endpoint.h b/wifi_endpoint.h
index 17bf60b..c7b161c 100644
--- a/wifi_endpoint.h
+++ b/wifi_endpoint.h
@@ -6,11 +6,13 @@
 #define SHILL_WIFI_ENDPOINT_
 
 #include <map>
+#include <set>
 #include <string>
 #include <vector>
 
 #include <base/memory/ref_counted.h>
 #include <dbus-c++/dbus.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
 #include "shill/endpoint.h"
 #include "shill/shill_event.h"
@@ -33,24 +35,34 @@
   const std::string &bssid_hex() const;
   int16_t signal_strength() const;
   const std::string &network_mode() const;
+  const std::string &security_mode() const;
 
  private:
-  static const uint32_t kSupplicantNetworkModeInfrastructureInt;
-  static const uint32_t kSupplicantNetworkModeAdHocInt;
-  static const uint32_t kSupplicantNetworkModeAccessPointInt;
+  friend class WiFiEndpointTest;
+  // these test cases need access to the KeyManagement enum
+  FRIEND_TEST(WiFiEndpointTest, ParseKeyManagementMethodsEAP);
+  FRIEND_TEST(WiFiEndpointTest, ParseKeyManagementMethodsPSK);
+  FRIEND_TEST(WiFiEndpointTest, ParseKeyManagementMethodsEAPAndPSK);
 
-  static const char kSupplicantPropertySSID[];
-  static const char kSupplicantPropertyBSSID[];
-  static const char kSupplicantPropertySignal[];
-  static const char kSupplicantPropertyMode[];
-  static const char kSupplicantNetworkModeInfrastructure[];
-  static const char kSupplicantNetworkModeAdHoc[];
-  static const char kSupplicantNetworkModeAccessPoint[];
+  enum KeyManagement {
+    kKeyManagement802_1x,
+    kKeyManagementPSK
+  };
 
   // Maps mode strings from supplicant into flimflam's nomenclature, as defined
   // in chromeos/dbus/service_constants.h
   static const char *ParseMode(const std::string &mode_string);
+  // Parses an Endpoint's properties to identify approprirate flimflam
+  // security property value, as defined in chromeos/dbus/service_constants.h
+  static const char *ParseSecurity(
+      const std::map<std::string, ::DBus::Variant> &properties);
+  // Parses and Endpoint's properties' "RSN" or "WPA" sub-dictionary, to
+  // identify supported key management methods (802.1x or PSK).
+  static void ParseKeyManagementMethods(
+      const std::map<std::string, ::DBus::Variant> &security_method_properties,
+      std::set<KeyManagement> *key_management_methods);
 
+  // TODO(quiche): make const?
   std::vector<uint8_t> ssid_;
   std::vector<uint8_t> bssid_;
   std::string ssid_string_;
@@ -58,7 +70,10 @@
   std::string bssid_string_;
   std::string bssid_hex_;
   int16_t signal_strength_;
+  // network_mode_ and security_mode_ are represented as flimflam names
+  // (not necessarily the same as wpa_supplicant names)
   std::string network_mode_;
+  std::string security_mode_;
 
   DISALLOW_COPY_AND_ASSIGN(WiFiEndpoint);
 };
diff --git a/wifi_endpoint_unittest.cc b/wifi_endpoint_unittest.cc
new file mode 100644
index 0000000..cb02824
--- /dev/null
+++ b/wifi_endpoint_unittest.cc
@@ -0,0 +1,138 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shill/wifi_endpoint.h"
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <base/stl_util-inl.h>
+#include <chromeos/dbus/service_constants.h>
+#include <gtest/gtest.h>
+
+#include "shill/wpa_supplicant.h"
+
+using std::map;
+using std::set;
+using std::string;
+using std::vector;
+
+using ::testing::Test;
+
+namespace shill {
+
+class WiFiEndpointTest : public Test {
+ public:
+  WiFiEndpointTest() {}
+  virtual ~WiFiEndpointTest() {}
+
+ protected:
+  vector<string> make_string_vector1(const string &str1) {
+    vector<string> strvec;
+    strvec.push_back(str1);
+    return strvec;
+  }
+
+  vector<string> make_string_vector2(const string &str1, const string &str2) {
+    vector<string> strvec;
+    strvec.push_back(str1);
+    strvec.push_back(str2);
+    return strvec;
+  }
+
+  map<string, ::DBus::Variant> make_key_management_args(
+      vector<string> key_management_method_strings) {
+    map<string, ::DBus::Variant> args;
+    ::DBus::MessageIter writer;
+    writer =
+        args[wpa_supplicant::kSecurityMethodPropertyKeyManagement].writer();
+    writer << key_management_method_strings;
+    return args;
+  }
+
+  map<string, ::DBus::Variant> make_security_args(
+      const string &security_protocol,
+      const string &key_management_method) {
+    map<string, ::DBus::Variant> args;
+    ::DBus::MessageIter writer;
+    writer = args[security_protocol].writer();
+    writer <<
+        make_key_management_args(make_string_vector1(key_management_method));
+    return args;
+  }
+
+  const char *ParseSecurity(
+    const map<string, ::DBus::Variant> &properties) {
+    return WiFiEndpoint::ParseSecurity(properties);
+  }
+};
+
+TEST_F(WiFiEndpointTest, ParseKeyManagementMethodsEAP) {
+  set<WiFiEndpoint::KeyManagement> parsed_methods;
+  WiFiEndpoint::ParseKeyManagementMethods(
+      make_key_management_args(make_string_vector1("something-eap")),
+      &parsed_methods);
+  EXPECT_TRUE(
+      ContainsKey(parsed_methods, WiFiEndpoint::kKeyManagement802_1x));
+  EXPECT_FALSE(
+      ContainsKey(parsed_methods, WiFiEndpoint::kKeyManagementPSK));
+}
+
+TEST_F(WiFiEndpointTest, ParseKeyManagementMethodsPSK) {
+  set<WiFiEndpoint::KeyManagement> parsed_methods;
+  WiFiEndpoint::ParseKeyManagementMethods(
+      make_key_management_args(make_string_vector1("something-psk")),
+      &parsed_methods);
+  EXPECT_TRUE(
+      ContainsKey(parsed_methods, WiFiEndpoint::kKeyManagementPSK));
+  EXPECT_FALSE(
+      ContainsKey(parsed_methods, WiFiEndpoint::kKeyManagement802_1x));
+}
+
+TEST_F(WiFiEndpointTest, ParseKeyManagementMethodsEAPAndPSK) {
+  set<WiFiEndpoint::KeyManagement> parsed_methods;
+  WiFiEndpoint::ParseKeyManagementMethods(
+      make_key_management_args(
+          make_string_vector2("something-eap", "something-psk")),
+      &parsed_methods);
+  EXPECT_TRUE(
+      ContainsKey(parsed_methods, WiFiEndpoint::kKeyManagement802_1x));
+  EXPECT_TRUE(
+      ContainsKey(parsed_methods, WiFiEndpoint::kKeyManagementPSK));
+}
+
+TEST_F(WiFiEndpointTest, ParseSecurityRSN802_1x) {
+  EXPECT_STREQ(flimflam::kSecurity8021x,
+               ParseSecurity(make_security_args("RSN", "something-eap")));
+}
+
+TEST_F(WiFiEndpointTest, ParseSecurityWPA802_1x) {
+  EXPECT_STREQ(flimflam::kSecurity8021x,
+               ParseSecurity(make_security_args("WPA", "something-eap")));
+}
+
+TEST_F(WiFiEndpointTest, ParseSecurityRSNPSK) {
+  EXPECT_STREQ(flimflam::kSecurityRsn,
+               ParseSecurity(make_security_args("RSN", "something-psk")));
+}
+
+TEST_F(WiFiEndpointTest, ParseSecurityWPAPSK) {
+  EXPECT_STREQ(flimflam::kSecurityWpa,
+               ParseSecurity(make_security_args("WPA", "something-psk")));
+}
+
+TEST_F(WiFiEndpointTest, ParseSecurityWEP) {
+  map<string, ::DBus::Variant> top_params;
+  top_params[wpa_supplicant::kPropertyPrivacy].writer().append_bool(true);
+  EXPECT_STREQ(flimflam::kSecurityWep, ParseSecurity(top_params));
+}
+
+TEST_F(WiFiEndpointTest, ParseSecurityNone) {
+  map<string, ::DBus::Variant> top_params;
+  EXPECT_STREQ(flimflam::kSecurityNone, ParseSecurity(top_params));
+}
+
+}  // namespace shill
diff --git a/wifi_service.cc b/wifi_service.cc
index 881bf1d..861e969 100644
--- a/wifi_service.cc
+++ b/wifi_service.cc
@@ -11,11 +11,14 @@
 #include <base/string_number_conversions.h>
 #include <base/string_util.h>
 #include <chromeos/dbus/service_constants.h>
+#include <dbus/dbus.h>
 
 #include "shill/control_interface.h"
 #include "shill/device.h"
 #include "shill/shill_event.h"
 #include "shill/wifi.h"
+#include "shill/wifi_endpoint.h"
+#include "shill/wpa_supplicant.h"
 
 using std::string;
 
@@ -27,15 +30,13 @@
                          const WiFiRefPtr &device,
                          const std::vector<uint8_t> ssid,
                          const std::string &mode,
-                         const std::string &key_management)
+                         const std::string &security)
     : Service(control_interface, dispatcher, manager, flimflam::kTypeWifi),
-      security_(flimflam::kSecurityNone),
+      security_(security),
       mode_(mode),
       task_factory_(this),
       wifi_(device),
       ssid_(ssid) {
-  SetEAPKeyManagement(key_management);
-
   PropertyStore *store = this->mutable_store();
   store->RegisterConstString(flimflam::kModeProperty, &mode_);
   store->RegisterString(flimflam::kPassphraseProperty, &passphrase_);
@@ -54,8 +55,30 @@
            "_" +
            string(reinterpret_cast<const char*>(ssid_.data()), ssid_.size()));
 
-  // TODO(quiche): set based on security properties
-  need_passphrase_ = false;
+  // TODO(quiche): determine if it is okay to set EAP.KeyManagement for
+  // a service that is not 802.1x.
+  if (security_ == flimflam::kSecurity8021x) {
+    NOTIMPLEMENTED();
+    // XXX needs_passpharse_ = false ?
+  } else if (security_ == flimflam::kSecurityPsk) {
+    SetEAPKeyManagement("WPA-PSK");
+    need_passphrase_ = true;
+  } else if (security_ == flimflam::kSecurityRsn) {
+    SetEAPKeyManagement("WPA-PSK");
+    need_passphrase_ = true;
+  } else if (security_ == flimflam::kSecurityWpa) {
+    SetEAPKeyManagement("WPA-PSK");
+    need_passphrase_ = true;
+  } else if (security_ == flimflam::kSecurityWep) {
+    SetEAPKeyManagement("NONE");
+    need_passphrase_ = true;
+  } else if (security_ == flimflam::kSecurityNone) {
+    SetEAPKeyManagement("NONE");
+    need_passphrase_ = false;
+  } else {
+    LOG(ERROR) << "unsupported security method " << security_;
+  }
+
   // TODO(quiche): figure out when to set true
   hidden_ssid_ = false;
 }
@@ -105,7 +128,39 @@
 
 // private methods
 void WiFiService::ConnectTask() {
-  wifi_->ConnectTo(this);
+  std::map<string, DBus::Variant> params;
+  DBus::MessageIter writer;
+
+  params[wpa_supplicant::kNetworkPropertyMode].writer().
+      append_uint32(WiFiEndpoint::ModeStringToUint(mode_));
+
+  if (security_ == flimflam::kSecurity8021x) {
+    NOTIMPLEMENTED();
+  } else if (security_ == flimflam::kSecurityPsk) {
+    NOTIMPLEMENTED();
+  } else if (security_ == flimflam::kSecurityRsn) {
+    NOTIMPLEMENTED();
+  } else if (security_ == flimflam::kSecurityWpa) {
+    params[wpa_supplicant::kPropertySecurityProtocol].writer().
+        append_string(wpa_supplicant::kSecurityModeWPA);
+    params[wpa_supplicant::kPropertyPreSharedKey].writer().
+        append_string(passphrase_.c_str());
+  } else if (security_ == flimflam::kSecurityWep) {
+    NOTIMPLEMENTED();
+  } else if (security_ == flimflam::kSecurityNone) {
+    // nothing special to do here
+  } else {
+    LOG(ERROR) << "can't connect. unsupported security method " << security_;
+  }
+
+  params[wpa_supplicant::kPropertyKeyManagement].writer().
+      append_string(key_management().c_str());
+  // TODO(quiche): figure out why we can't use operator<< without the
+  // temporary variable.
+  writer = params[wpa_supplicant::kNetworkPropertySSID].writer();
+  writer << ssid_;
+
+  wifi_->ConnectTo(this, params);
 }
 
 string WiFiService::GetDeviceRpcId() {
diff --git a/wifi_service.h b/wifi_service.h
index 66c0630..a6e7aa6 100644
--- a/wifi_service.h
+++ b/wifi_service.h
@@ -27,7 +27,7 @@
               const WiFiRefPtr &device,
               const std::vector<uint8_t> ssid,
               const std::string &mode,
-              const std::string &key_management);
+              const std::string &security);
   ~WiFiService();
 
   // Inherited from Service.
@@ -44,6 +44,8 @@
   const std::vector<uint8_t> &ssid() const;
 
  private:
+  FRIEND_TEST(WiFiServiceTest, ConnectTask);
+
   void ConnectTask();
 
   std::string GetDeviceRpcId();
diff --git a/wifi_service_unittest.cc b/wifi_service_unittest.cc
index e142d86..cd68eca 100644
--- a/wifi_service_unittest.cc
+++ b/wifi_service_unittest.cc
@@ -17,36 +17,53 @@
 #include "shill/mock_control.h"
 #include "shill/mock_service.h"
 #include "shill/mock_store.h"
+#include "shill/mock_wifi.h"
 #include "shill/property_store_unittest.h"
 #include "shill/shill_event.h"
-#include "shill/wifi.h"
+#include "shill/wpa_supplicant.h"
 
 using std::string;
 using std::vector;
 
 namespace shill {
+using ::testing::NiceMock;
 
 class WiFiServiceTest : public PropertyStoreTest {
  public:
-  WiFiServiceTest() {}
+  WiFiServiceTest() : wifi_(
+      new NiceMock<MockWiFi>(
+          control_interface(),
+          dispatcher(),
+          manager(),
+          "wifi",
+          fake_mac,
+          0)) {}
   virtual ~WiFiServiceTest() {}
+
+ protected:
+  static const char fake_mac[];
+  scoped_refptr<MockWiFi> wifi() { return wifi_; }
+
+ private:
+  scoped_refptr<MockWiFi> wifi_;
 };
 
+// static
+const char WiFiServiceTest::fake_mac[] = "AaBBcCDDeeFF";
+
+MATCHER(WPASecurityArgs, "") {
+  return ContainsKey(arg, wpa_supplicant::kPropertySecurityProtocol) &&
+      ContainsKey(arg, wpa_supplicant::kPropertyPreSharedKey);
+}
+
 TEST_F(WiFiServiceTest, StorageId) {
   vector<uint8_t> ssid(5, 0);
   ssid.push_back(0xff);
-  static const char fake_mac[] = "AaBBcCDDeeFF";
 
-  WiFiRefPtr wifi = new WiFi(control_interface(),
-                             dispatcher(),
-                             manager(),
-                             "wifi",
-                             fake_mac,
-                             0);
   WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(),
                                                    dispatcher(),
                                                    manager(),
-                                                   wifi,
+                                                   wifi(),
                                                    ssid,
                                                    flimflam::kModeManaged,
                                                    "none");
@@ -62,4 +79,18 @@
   EXPECT_NE(id.find(string(flimflam::kModeManaged), mac_pos), string::npos);
 }
 
+TEST_F(WiFiServiceTest, ConnectTask) {
+  vector<uint8_t> ssid(5, 0);
+  WiFiServiceRefPtr wifi_service = new WiFiService(control_interface(),
+                                                   dispatcher(),
+                                                   manager(),
+                                                   wifi(),
+                                                   ssid,
+                                                   flimflam::kModeManaged,
+                                                   flimflam::kSecurityWpa);
+  EXPECT_CALL(*wifi(),
+              ConnectTo(wifi_service.get(), WPASecurityArgs()));
+  wifi_service->ConnectTask();
+}
+
 }  // namespace shill
diff --git a/wifi_unittest.cc b/wifi_unittest.cc
index dae7dad..f732e16 100644
--- a/wifi_unittest.cc
+++ b/wifi_unittest.cc
@@ -176,7 +176,8 @@
     return wifi_->supplicant_interface_proxy_.get();
   }
   void InitiateConnect(WiFiService *service) {
-    wifi_->ConnectTo(service);
+    map<string, ::DBus::Variant> params;
+    wifi_->ConnectTo(service, params);
   }
   bool IsLinkUp() {
     return wifi_->link_up_;
diff --git a/wpa_supplicant.cc b/wpa_supplicant.cc
new file mode 100644
index 0000000..1740220
--- /dev/null
+++ b/wpa_supplicant.cc
@@ -0,0 +1,43 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shill/wpa_supplicant.h"
+
+namespace shill {
+
+namespace wpa_supplicant {
+const char kBSSPropertyBSSID[]      = "BSSID";
+const char kBSSPropertySSID[]       = "SSID";
+const char kBSSPropertyMode[]       = "Mode";
+const char kBSSPropertySignal[]     = "Signal";
+const char kDBusAddr[]              = "fi.w1.wpa_supplicant1";
+const char kDBusPath[]              = "/fi/w1/wpa_supplicant1";
+const char kDriverNL80211[]         = "nl80211";
+const char kErrorInterfaceExists[]  = "fi.w1.wpa_supplicant1.InterfaceExists";
+const char kKeyManagementMethodSuffixEAP[] = "-eap";
+const char kKeyManagementMethodSuffixPSK[] = "-psk";
+const char kKeyModeNone[]           = "NONE";
+const char kNetworkModeInfrastructure[] = "infrastructure";
+const char kNetworkModeAdHoc[]       = "ad-hoc";
+const char kNetworkModeAccessPoint[] = "ap";
+const char kNetworkPropertyMode[]   = "mode";
+const char kNetworkPropertySSID[]   = "ssid";
+const char kPropertyKeyManagement[] = "key_mgmt";
+const char kPropertyPreSharedKey[]  = "psk";
+const char kPropertyPrivacy[]       = "Privacy";
+const char kPropertyRSN[]           = "RSN";
+const char kPropertyScanType[]      = "Type";
+const char kPropertySecurityProtocol[] = "proto";
+const char kPropertyWPA[]           = "WPA";
+const char kScanTypeActive[]        = "active";
+const char kSecurityMethodPropertyKeyManagement[] = "KeyMgmt";
+const char kSecurityModeRSN[]       = "RSN";
+const char kSecurityModeWPA[]       = "WPA";
+
+const uint32_t kNetworkModeInfrastructureInt = 0;
+const uint32_t kNetworkModeAdHocInt          = 1;
+const uint32_t kNetworkModeAccessPointInt    = 2;
+};
+
+}  // namespace shill
diff --git a/wpa_supplicant.h b/wpa_supplicant.h
new file mode 100644
index 0000000..1152ded
--- /dev/null
+++ b/wpa_supplicant.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHILL_WPA_SUPPLICANT_H
+#define SHILL_WPA_SUPPLICANT_H
+
+#include <base/basictypes.h>
+
+namespace shill {
+
+namespace wpa_supplicant {
+extern const char kBSSPropertyBSSID[];
+extern const char kBSSPropertySSID[];
+extern const char kBSSPropertyMode[];
+extern const char kBSSPropertySignal[];
+extern const char kDBusAddr[];
+extern const char kDBusPath[];
+extern const char kDriverNL80211[];
+extern const char kErrorInterfaceExists[];
+extern const char kKeyManagementMethodSuffixEAP[];
+extern const char kKeyManagementMethodSuffixPSK[];
+extern const char kKeyModeNone[];
+extern const char kNetworkModeInfrastructure[];
+extern const char kNetworkModeAdHoc[];
+extern const char kNetworkModeAccessPoint[];
+extern const char kNetworkPropertyMode[];
+extern const char kNetworkPropertySSID[];
+extern const char kPropertyBSSID[];
+extern const char kPropertyKeyManagement[];
+extern const char kPropertyMode[];
+extern const char kPropertyPreSharedKey[];
+extern const char kPropertyPrivacy[];
+extern const char kPropertyRSN[];
+extern const char kPropertyScanType[];
+extern const char kPropertySecurityProtocol[];
+extern const char kPropertySignal[];
+extern const char kPropertyWPA[];
+extern const char kScanTypeActive[];
+extern const char kSecurityMethodPropertyKeyManagement[];
+extern const char kSecurityModeRSN[];
+extern const char kSecurityModeWPA[];
+
+extern const uint32_t kNetworkModeInfrastructureInt;
+extern const uint32_t kNetworkModeAdHocInt;
+extern const uint32_t kNetworkModeAccessPointInt;
+};
+
+}  // namespace shill
+
+#endif  // SHILL_WPA_SUPPLICANT_H