/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <stdlib.h> #include <errno.h> #include <fcntl.h> #include <string.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/wait.h> #include <netinet/in.h> #include <arpa/inet.h> #include <linux/wireless.h> #include <openssl/evp.h> #include <openssl/sha.h> #define LOG_TAG "SoftapController" #include <cutils/log.h> #include <netutils/ifc.h> #include <private/android_filesystem_config.h> #include "wifi.h" #include "ResponseCode.h" #include "SoftapController.h" static const char HOSTAPD_CONF_FILE[] = "/data/misc/wifi/hostapd.conf"; static const char HOSTAPD_BIN_FILE[] = "/system/bin/hostapd"; SoftapController::SoftapController() : mPid(0) {} SoftapController::~SoftapController() { } int SoftapController::startSoftap() { pid_t pid = 1; if (mPid) { ALOGE("SoftAP is already running"); return ResponseCode::SoftapStatusResult; } if ((pid = fork()) < 0) { ALOGE("fork failed (%s)", strerror(errno)); return ResponseCode::ServiceStartFailed; } if (!pid) { ensure_entropy_file_exists(); if (execl(HOSTAPD_BIN_FILE, HOSTAPD_BIN_FILE, "-e", WIFI_ENTROPY_FILE, HOSTAPD_CONF_FILE, (char *) NULL)) { ALOGE("execl failed (%s)", strerror(errno)); } ALOGE("SoftAP failed to start"); return ResponseCode::ServiceStartFailed; } else { mPid = pid; ALOGD("SoftAP started successfully"); usleep(AP_BSS_START_DELAY); } return ResponseCode::SoftapStatusResult; } int SoftapController::stopSoftap() { if (mPid == 0) { ALOGE("SoftAP is not running"); return ResponseCode::SoftapStatusResult; } ALOGD("Stopping the SoftAP service..."); kill(mPid, SIGTERM); waitpid(mPid, NULL, 0); mPid = 0; ALOGD("SoftAP stopped successfully"); usleep(AP_BSS_STOP_DELAY); return ResponseCode::SoftapStatusResult; } bool SoftapController::isSoftapStarted() { return (mPid != 0); } /* * Arguments: * argv[2] - wlan interface * argv[3] - SSID * argv[4] - Broadcast/Hidden * argv[5] - Channel * argv[6] - Security * argv[7] - Key */ int SoftapController::setSoftap(int argc, char *argv[]) { char psk_str[2*SHA256_DIGEST_LENGTH+1]; int ret = ResponseCode::SoftapStatusResult; int fd; int hidden = 0; int channel = AP_CHANNEL_DEFAULT; char *wbuf = NULL; char *fbuf = NULL; if (argc < 5) { ALOGE("Softap set is missing arguments. Please use:"); ALOGE("softap <wlan iface> <SSID> <hidden/broadcast> <channel> <wpa2?-psk|open> <passphrase>"); return ResponseCode::CommandSyntaxError; } if (!strcasecmp(argv[4], "hidden")) hidden = 1; if (argc >= 5) { channel = atoi(argv[5]); if (channel <= 0) channel = AP_CHANNEL_DEFAULT; } asprintf(&wbuf, "interface=%s\ndriver=nl80211\nctrl_interface=" "/data/misc/wifi/hostapd\nssid=%s\nchannel=%d\nieee80211n=1\n" "hw_mode=g\nignore_broadcast_ssid=%d\nwowlan_triggers=any\n", argv[2], argv[3], channel, hidden); if (argc > 7) { if (!strcmp(argv[6], "wpa-psk")) { generatePsk(argv[3], argv[7], psk_str); asprintf(&fbuf, "%swpa=3\nwpa_pairwise=TKIP CCMP\nwpa_psk=%s\n", wbuf, psk_str); } else if (!strcmp(argv[6], "wpa2-psk")) { generatePsk(argv[3], argv[7], psk_str); asprintf(&fbuf, "%swpa=2\nrsn_pairwise=CCMP\nwpa_psk=%s\n", wbuf, psk_str); } else if (!strcmp(argv[6], "open")) { asprintf(&fbuf, "%s", wbuf); } } else if (argc > 6) { if (!strcmp(argv[6], "open")) { asprintf(&fbuf, "%s", wbuf); } } else { asprintf(&fbuf, "%s", wbuf); } fd = open(HOSTAPD_CONF_FILE, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW, 0660); if (fd < 0) { ALOGE("Cannot update \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno)); free(wbuf); free(fbuf); return ResponseCode::OperationFailed; } if (write(fd, fbuf, strlen(fbuf)) < 0) { ALOGE("Cannot write to \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno)); ret = ResponseCode::OperationFailed; } free(wbuf); free(fbuf); /* Note: apparently open can fail to set permissions correctly at times */ if (fchmod(fd, 0660) < 0) { ALOGE("Error changing permissions of %s to 0660: %s", HOSTAPD_CONF_FILE, strerror(errno)); close(fd); unlink(HOSTAPD_CONF_FILE); return ResponseCode::OperationFailed; } if (fchown(fd, AID_SYSTEM, AID_WIFI) < 0) { ALOGE("Error changing group ownership of %s to %d: %s", HOSTAPD_CONF_FILE, AID_WIFI, strerror(errno)); close(fd); unlink(HOSTAPD_CONF_FILE); return ResponseCode::OperationFailed; } close(fd); return ret; } /* * Arguments: * argv[2] - interface name * argv[3] - AP or P2P or STA */ int SoftapController::fwReloadSoftap(int argc, char *argv[]) { char *fwpath = NULL; if (argc < 4) { ALOGE("SoftAP fwreload is missing arguments. Please use: softap <wlan iface> <AP|P2P|STA>"); return ResponseCode::CommandSyntaxError; } if (strcmp(argv[3], "AP") == 0) { fwpath = (char *)wifi_get_fw_path(WIFI_GET_FW_PATH_AP); } else if (strcmp(argv[3], "P2P") == 0) { fwpath = (char *)wifi_get_fw_path(WIFI_GET_FW_PATH_P2P); } else if (strcmp(argv[3], "STA") == 0) { fwpath = (char *)wifi_get_fw_path(WIFI_GET_FW_PATH_STA); } if (!fwpath) return ResponseCode::CommandParameterError; if (wifi_change_fw_path((const char *)fwpath)) { ALOGE("Softap fwReload failed"); return ResponseCode::OperationFailed; } else { ALOGD("Softap fwReload - Ok"); } return ResponseCode::SoftapStatusResult; } void SoftapController::generatePsk(char *ssid, char *passphrase, char *psk_str) { unsigned char psk[SHA256_DIGEST_LENGTH]; int j; // Use the PKCS#5 PBKDF2 with 4096 iterations PKCS5_PBKDF2_HMAC_SHA1(passphrase, strlen(passphrase), reinterpret_cast<const unsigned char *>(ssid), strlen(ssid), 4096, SHA256_DIGEST_LENGTH, psk); for (j=0; j < SHA256_DIGEST_LENGTH; j++) { sprintf(&psk_str[j*2], "%02x", psk[j]); } }