| /* |
| * |
| * Copyright (C) 2017 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 <string> |
| #include <vector> |
| |
| #include <ctype.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <getopt.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <inttypes.h> |
| |
| #include <arpa/inet.h> |
| #include <netinet/in.h> |
| |
| #include <sys/socket.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| |
| #include <linux/in.h> |
| #include <linux/netlink.h> |
| #include <linux/xfrm.h> |
| |
| #include "android-base/stringprintf.h" |
| #include "android-base/strings.h" |
| #include "android-base/unique_fd.h" |
| #define LOG_TAG "XfrmController" |
| #include "NetdConstants.h" |
| #include "NetlinkCommands.h" |
| #include "ResponseCode.h" |
| #include "XfrmController.h" |
| #include <cutils/log.h> |
| #include <cutils/properties.h> |
| #include <logwrap/logwrap.h> |
| |
| #define VDBG 1 // STOPSHIP if true |
| |
| namespace android { |
| namespace net { |
| |
| namespace { |
| |
| constexpr uint32_t ALGO_MASK_AUTH_ALL = ~0; |
| constexpr uint32_t ALGO_MASK_CRYPT_ALL = ~0; |
| |
| constexpr uint8_t REPLAY_WINDOW_SIZE = 4; |
| |
| constexpr uint32_t RAND_SPI_MIN = 1; |
| constexpr uint32_t RAND_SPI_MAX = 0xFFFFFFFE; |
| |
| constexpr uint32_t INVALID_SPI = 0; |
| |
| #define XFRM_MSG_TRANS(x) \ |
| case x: \ |
| return #x; |
| |
| const char* xfrmMsgTypeToString(uint16_t msg) { |
| switch (msg) { |
| XFRM_MSG_TRANS(XFRM_MSG_NEWSA) |
| XFRM_MSG_TRANS(XFRM_MSG_DELSA) |
| XFRM_MSG_TRANS(XFRM_MSG_GETSA) |
| XFRM_MSG_TRANS(XFRM_MSG_NEWPOLICY) |
| XFRM_MSG_TRANS(XFRM_MSG_DELPOLICY) |
| XFRM_MSG_TRANS(XFRM_MSG_GETPOLICY) |
| XFRM_MSG_TRANS(XFRM_MSG_ALLOCSPI) |
| XFRM_MSG_TRANS(XFRM_MSG_ACQUIRE) |
| XFRM_MSG_TRANS(XFRM_MSG_EXPIRE) |
| XFRM_MSG_TRANS(XFRM_MSG_UPDPOLICY) |
| XFRM_MSG_TRANS(XFRM_MSG_UPDSA) |
| XFRM_MSG_TRANS(XFRM_MSG_POLEXPIRE) |
| XFRM_MSG_TRANS(XFRM_MSG_FLUSHSA) |
| XFRM_MSG_TRANS(XFRM_MSG_FLUSHPOLICY) |
| XFRM_MSG_TRANS(XFRM_MSG_NEWAE) |
| XFRM_MSG_TRANS(XFRM_MSG_GETAE) |
| XFRM_MSG_TRANS(XFRM_MSG_REPORT) |
| XFRM_MSG_TRANS(XFRM_MSG_MIGRATE) |
| XFRM_MSG_TRANS(XFRM_MSG_NEWSADINFO) |
| XFRM_MSG_TRANS(XFRM_MSG_GETSADINFO) |
| XFRM_MSG_TRANS(XFRM_MSG_GETSPDINFO) |
| XFRM_MSG_TRANS(XFRM_MSG_NEWSPDINFO) |
| XFRM_MSG_TRANS(XFRM_MSG_MAPPING) |
| default: |
| return "XFRM_MSG UNKNOWN"; |
| } |
| } |
| |
| // actually const but cannot be declared as such for reasons |
| uint8_t kPadBytesArray[] = {0, 0, 0}; |
| void* kPadBytes = static_cast<void*>(kPadBytesArray); |
| |
| #if VDBG |
| #define LOG_HEX(__desc16__, __buf__, __len__) \ |
| do{ logHex(__desc16__, __buf__, __len__); }while(0) |
| #define LOG_IOV(__iov__, __iov_len__) \ |
| do{ logIov(__iov__, __iov_len__); }while(0) |
| |
| void logHex(const char* desc16, const char* buf, size_t len) { |
| char* printBuf = new char[len * 2 + 1 + 26]; // len->ascii, +newline, +prefix strlen |
| int offset = 0; |
| if (desc16) { |
| sprintf(printBuf, "{%-16s}", desc16); |
| offset += 18; // prefix string length |
| } |
| sprintf(printBuf + offset, "[%4.4u]: ", (len > 9999) ? 9999 : (unsigned)len); |
| offset += 8; |
| |
| for (uint32_t j = 0; j < (uint32_t)len; j++) { |
| sprintf(&printBuf[j * 2 + offset], "%0.2x", buf[j]); |
| } |
| ALOGD("%s", printBuf); |
| delete[] printBuf; |
| } |
| |
| void logIov(const iovec* iov, size_t iovLen) { |
| for (uint32_t i = 0; i < (uint32_t)iovLen; i++) { |
| const iovec* row = &iov[i]; |
| logHex(0, reinterpret_cast<char*>(row->iov_base), row->iov_len); |
| } |
| } |
| |
| #else |
| #define LOG_HEX(__desc16__, __buf__, __len__) |
| #define LOG_IOV(__iov__, __iov_len__) |
| #endif |
| |
| class XfrmSocketImpl : public XfrmSocket { |
| private: |
| static constexpr int NLMSG_DEFAULTSIZE = 8192; |
| |
| union NetlinkResponse { |
| nlmsghdr hdr; |
| struct _err_ { |
| nlmsghdr hdr; |
| nlmsgerr err; |
| } err; |
| |
| struct _buf_ { |
| nlmsghdr hdr; |
| char buf[NLMSG_DEFAULTSIZE]; |
| } buf; |
| }; |
| |
| public: |
| virtual bool open() { |
| mSock = openNetlinkSocket(NETLINK_XFRM); |
| if (mSock <= 0) { |
| ALOGW("Could not get a new socket, line=%d", __LINE__); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static int validateResponse(NetlinkResponse response, size_t len) { |
| if (len < sizeof(nlmsghdr)) { |
| ALOGW("Invalid response message received over netlink"); |
| return -EBADMSG; |
| } |
| |
| switch (response.hdr.nlmsg_type) { |
| case NLMSG_NOOP: |
| case NLMSG_DONE: |
| return 0; |
| case NLMSG_OVERRUN: |
| ALOGD("Netlink request overran kernel buffer"); |
| return -EBADMSG; |
| case NLMSG_ERROR: |
| if (len < sizeof(NetlinkResponse::_err_)) { |
| ALOGD("Netlink message received malformed error response"); |
| return -EBADMSG; |
| } |
| return response.err.err.error; // Netlink errors are negative errno. |
| case XFRM_MSG_NEWSA: |
| break; |
| } |
| |
| if (response.hdr.nlmsg_type < XFRM_MSG_BASE /*== NLMSG_MIN_TYPE*/ || |
| response.hdr.nlmsg_type > XFRM_MSG_MAX) { |
| ALOGD("Netlink message responded with an out-of-range message ID"); |
| return -EBADMSG; |
| } |
| |
| // TODO Add more message validation here |
| return 0; |
| } |
| |
| virtual int sendMessage(uint16_t nlMsgType, uint16_t nlMsgFlags, uint16_t nlMsgSeqNum, |
| iovec* iov, int iovLen) const { |
| nlmsghdr nlMsg = { |
| .nlmsg_type = nlMsgType, .nlmsg_flags = nlMsgFlags, .nlmsg_seq = nlMsgSeqNum, |
| }; |
| |
| iov[0].iov_base = &nlMsg; |
| iov[0].iov_len = NLMSG_HDRLEN; |
| for (int i = 0; i < iovLen; ++i) { |
| nlMsg.nlmsg_len += iov[i].iov_len; |
| } |
| |
| ALOGD("Sending Netlink XFRM Message: %s", xfrmMsgTypeToString(nlMsgType)); |
| if (VDBG) |
| LOG_IOV(iov, iovLen); |
| |
| int ret; |
| |
| if (writev(mSock, iov, iovLen) < 0) { |
| ALOGE("netlink socket writev failed (%s)", strerror(errno)); |
| return -errno; |
| } |
| |
| NetlinkResponse* response = new NetlinkResponse{}; |
| |
| if ((ret = recv(mSock, response, sizeof(*response), 0)) < 0) { |
| ALOGE("netlink response contains error (%s)", strerror(errno)); |
| delete response; |
| return -errno; |
| } |
| |
| LOG_HEX("netlink msg resp", reinterpret_cast<char*>(response), ret); |
| |
| ret = validateResponse(*response, ret); |
| delete response; |
| if (ret < 0) |
| ALOGE("netlink response contains error (%s)", strerror(-ret)); |
| return ret; |
| } |
| }; |
| |
| int convertToXfrmAddr(const std::string& strAddr, xfrm_address_t* xfrmAddr) { |
| if (strAddr.length() == 0) { |
| memset(xfrmAddr, 0, sizeof(*xfrmAddr)); |
| return AF_UNSPEC; |
| } |
| |
| if (inet_pton(AF_INET6, strAddr.c_str(), reinterpret_cast<void*>(xfrmAddr))) { |
| return AF_INET6; |
| } else if (inet_pton(AF_INET, strAddr.c_str(), reinterpret_cast<void*>(xfrmAddr))) { |
| return AF_INET; |
| } else { |
| return -EAFNOSUPPORT; |
| } |
| } |
| |
| void fillXfrmNlaHdr(nlattr* hdr, uint16_t type, uint16_t len) { |
| hdr->nla_type = type; |
| hdr->nla_len = len; |
| } |
| |
| void fillXfrmCurLifetimeDefaults(xfrm_lifetime_cur* cur) { |
| memset(reinterpret_cast<char*>(cur), 0, sizeof(*cur)); |
| } |
| void fillXfrmLifetimeDefaults(xfrm_lifetime_cfg* cfg) { |
| cfg->soft_byte_limit = XFRM_INF; |
| cfg->hard_byte_limit = XFRM_INF; |
| cfg->soft_packet_limit = XFRM_INF; |
| cfg->hard_packet_limit = XFRM_INF; |
| } |
| |
| /* |
| * Allocate SPIs within an (inclusive) range of min-max. |
| * returns 0 (INVALID_SPI) once the entire range has been parsed. |
| */ |
| class RandomSpi { |
| public: |
| RandomSpi(int min, int max) : mMin(min) { |
| time_t t; |
| srand((unsigned int)time(&t)); |
| // TODO: more random random |
| mNext = rand(); |
| mSize = max - min + 1; |
| mCount = mSize; |
| } |
| |
| uint32_t next() { |
| if (!mCount) |
| return 0; |
| mCount--; |
| return (mNext++ % mSize) + mMin; |
| } |
| |
| private: |
| uint32_t mNext; |
| uint32_t mSize; |
| uint32_t mMin; |
| uint32_t mCount; |
| }; |
| |
| } // namespace |
| |
| // |
| // Begin XfrmController Impl |
| // |
| // |
| XfrmController::XfrmController(void) {} |
| |
| int XfrmController::ipSecAllocateSpi(int32_t transformId, int32_t direction, |
| const std::string& localAddress, |
| const std::string& remoteAddress, int32_t inSpi, |
| int32_t* outSpi) { |
| |
| ALOGD("XfrmController:%s, line=%d", __FUNCTION__, __LINE__); |
| ALOGD("transformId=%d", transformId); |
| ALOGD("direction=%d", direction); |
| ALOGD("localAddress=%s", localAddress.c_str()); |
| ALOGD("remoteAddress=%s", remoteAddress.c_str()); |
| ALOGD("inSpi=%0.8x", inSpi); |
| |
| XfrmSaInfo saInfo{}; |
| int ret; |
| |
| if ((ret = fillXfrmSaId(direction, localAddress, remoteAddress, INVALID_SPI, &saInfo)) < 0) { |
| return ret; |
| } |
| |
| XfrmSocketImpl sock; |
| if (!sock.open()) { |
| ALOGD("Sock open failed for XFRM, line=%d", __LINE__); |
| return -1; // TODO: return right error; for whatever reason the sock |
| // failed to open |
| } |
| |
| int minSpi = RAND_SPI_MIN, maxSpi = RAND_SPI_MAX; |
| |
| if (inSpi) |
| minSpi = maxSpi = inSpi; |
| ret = allocateSpi(saInfo, minSpi, maxSpi, reinterpret_cast<uint32_t*>(outSpi), sock); |
| if (ret < 0) { |
| ALOGD("Failed to Allocate an SPI, line=%d", __LINE__); |
| *outSpi = INVALID_SPI; |
| } |
| |
| return ret; |
| } |
| |
| int XfrmController::ipSecAddSecurityAssociation( |
| int32_t transformId, int32_t mode, int32_t direction, const std::string& localAddress, |
| const std::string& remoteAddress, int64_t /* underlyingNetworkHandle */, int32_t spi, |
| const std::string& authAlgo, const std::vector<uint8_t>& authKey, int32_t authTruncBits, |
| const std::string& cryptAlgo, const std::vector<uint8_t>& cryptKey, int32_t cryptTruncBits, |
| int32_t encapType, int32_t encapLocalPort, int32_t encapRemotePort, int32_t* allocatedSpi) { |
| android::RWLock::AutoWLock lock(mLock); |
| |
| ALOGD("XfrmController::%s, line=%d", __FUNCTION__, __LINE__); |
| ALOGD("transformId=%d", transformId); |
| ALOGD("mode=%d", mode); |
| ALOGD("direction=%d", direction); |
| ALOGD("localAddress=%s", localAddress.c_str()); |
| ALOGD("remoteAddress=%s", remoteAddress.c_str()); |
| ALOGD("spi=%0.8x", spi); |
| ALOGD("authAlgo=%s", authAlgo.c_str()); |
| ALOGD("authTruncBits=%d", authTruncBits); |
| ALOGD("cryptAlgo=%s", cryptAlgo.c_str()); |
| ALOGD("cryptTruncBits=%d,", cryptTruncBits); |
| ALOGD("encapType=%d", encapType); |
| ALOGD("encapLocalPort=%d", encapLocalPort); |
| ALOGD("encapRemotePort=%d", encapRemotePort); |
| |
| XfrmSaInfo saInfo{}; |
| int ret; |
| |
| if ((ret = fillXfrmSaId(direction, localAddress, remoteAddress, spi, &saInfo)) < 0) { |
| return ret; |
| } |
| |
| saInfo.transformId = transformId; |
| |
| // STOPSHIP : range check the key lengths to prevent puncturing and overflow |
| saInfo.auth = XfrmAlgo{ |
| .name = authAlgo, .key = authKey, .truncLenBits = static_cast<uint16_t>(authTruncBits)}; |
| |
| saInfo.crypt = XfrmAlgo{ |
| .name = cryptAlgo, .key = cryptKey, .truncLenBits = static_cast<uint16_t>(cryptTruncBits)}; |
| |
| saInfo.direction = static_cast<XfrmDirection>(direction); |
| |
| switch (static_cast<XfrmMode>(mode)) { |
| case XfrmMode::TRANSPORT: |
| case XfrmMode::TUNNEL: |
| saInfo.mode = static_cast<XfrmMode>(mode); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| XfrmSocketImpl sock; |
| if (!sock.open()) { |
| ALOGD("Sock open failed for XFRM, line=%d", __LINE__); |
| return -1; // TODO: return right error; for whatever reason the sock |
| // failed to open |
| } |
| |
| ret = createTransportModeSecurityAssociation(saInfo, sock); |
| if (ret < 0) { |
| ALOGD("Failed creating a Security Association, line=%d", __LINE__); |
| return ret; // something went wrong creating the SA |
| } |
| |
| *allocatedSpi = spi; |
| return 0; |
| } |
| |
| int XfrmController::ipSecDeleteSecurityAssociation(int32_t transformId, int32_t direction, |
| const std::string& localAddress, |
| const std::string& remoteAddress, int32_t spi) { |
| |
| ALOGD("XfrmController:%s, line=%d", __FUNCTION__, __LINE__); |
| ALOGD("transformId=%d", transformId); |
| ALOGD("direction=%d", direction); |
| ALOGD("localAddress=%s", localAddress.c_str()); |
| ALOGD("remoteAddress=%s", remoteAddress.c_str()); |
| ALOGD("spi=%0.8x", spi); |
| |
| XfrmSaId saId; |
| int ret; |
| |
| if ((ret = fillXfrmSaId(direction, localAddress, remoteAddress, spi, &saId)) < 0) { |
| return ret; |
| } |
| |
| XfrmSocketImpl sock; |
| if (!sock.open()) { |
| ALOGD("Sock open failed for XFRM, line=%d", __LINE__); |
| return -1; // TODO: return right error; for whatever reason the sock |
| // failed to open |
| } |
| |
| ret = deleteSecurityAssociation(saId, sock); |
| if (ret < 0) { |
| ALOGD("Failed to delete Security Association, line=%d", __LINE__); |
| return ret; // something went wrong deleting the SA |
| } |
| |
| return ret; |
| } |
| |
| int XfrmController::fillXfrmSaId(int32_t direction, const std::string& localAddress, |
| const std::string& remoteAddress, int32_t spi, XfrmSaId* xfrmId) { |
| xfrm_address_t localXfrmAddr{}, remoteXfrmAddr{}; |
| |
| int addrFamilyLocal, addrFamilyRemote; |
| addrFamilyRemote = convertToXfrmAddr(remoteAddress, &remoteXfrmAddr); |
| addrFamilyLocal = convertToXfrmAddr(localAddress, &localXfrmAddr); |
| if (addrFamilyRemote < 0 || addrFamilyLocal < 0) { |
| return -EINVAL; |
| } |
| |
| if (addrFamilyRemote == AF_UNSPEC || |
| (addrFamilyLocal != AF_UNSPEC && addrFamilyLocal != addrFamilyRemote)) { |
| ALOGD("Invalid or Mismatched Address Families, %d != %d, line=%d", addrFamilyLocal, |
| addrFamilyRemote, __LINE__); |
| return -EINVAL; |
| } |
| |
| xfrmId->addrFamily = addrFamilyRemote; |
| |
| xfrmId->spi = htonl(spi); |
| |
| switch (static_cast<XfrmDirection>(direction)) { |
| case XfrmDirection::IN: |
| xfrmId->dstAddr = localXfrmAddr; |
| xfrmId->srcAddr = remoteXfrmAddr; |
| break; |
| |
| case XfrmDirection::OUT: |
| xfrmId->dstAddr = remoteXfrmAddr; |
| xfrmId->srcAddr = localXfrmAddr; |
| break; |
| |
| default: |
| ALOGD("Invalid XFRM direction, line=%d", __LINE__); |
| // Invalid direction for Transport mode transform: time to bail |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| int XfrmController::ipSecApplyTransportModeTransform(const android::base::unique_fd& socket, |
| int32_t transformId, int32_t direction, |
| const std::string& localAddress, |
| const std::string& remoteAddress, |
| int32_t spi) { |
| ALOGD("XfrmController::%s, line=%d", __FUNCTION__, __LINE__); |
| ALOGD("transformId=%d", transformId); |
| ALOGD("direction=%d", direction); |
| ALOGD("localAddress=%s", localAddress.c_str()); |
| ALOGD("remoteAddress=%s", remoteAddress.c_str()); |
| ALOGD("spi=%0.8x", spi); |
| |
| struct sockaddr_storage saddr; |
| |
| socklen_t len = sizeof(saddr); |
| int err; |
| int userSocket = socket.get(); |
| |
| if ((err = getsockname(userSocket, reinterpret_cast<struct sockaddr*>(&saddr), &len)) < 0) { |
| ALOGE("Failed to get socket info in %s", __FUNCTION__); |
| return -err; |
| } |
| |
| XfrmSaInfo saInfo{}; |
| saInfo.transformId = transformId; |
| saInfo.direction = static_cast<XfrmDirection>(direction); |
| saInfo.spi = spi; |
| |
| if ((err = fillXfrmSaId(direction, localAddress, remoteAddress, spi, &saInfo)) < 0) { |
| ALOGE("Couldn't build SA ID %s", __FUNCTION__); |
| return -err; |
| } |
| |
| if (saInfo.addrFamily != saddr.ss_family) { |
| ALOGE("Transform address family(%d) differs from socket address " |
| "family(%d)!", |
| saInfo.addrFamily, saddr.ss_family); |
| return -EINVAL; |
| } |
| |
| struct { |
| xfrm_userpolicy_info info; |
| xfrm_user_tmpl tmpl; |
| } policy{}; |
| |
| fillTransportModeUserSpInfo(saInfo, &policy.info); |
| fillUserTemplate(saInfo, &policy.tmpl); |
| |
| LOG_HEX("XfrmUserPolicy", reinterpret_cast<char*>(&policy), sizeof(policy)); |
| |
| int sockOpt, sockLayer; |
| switch (saInfo.addrFamily) { |
| case AF_INET: |
| sockOpt = IP_XFRM_POLICY; |
| sockLayer = SOL_IP; |
| break; |
| case AF_INET6: |
| sockOpt = IPV6_XFRM_POLICY; |
| sockLayer = SOL_IPV6; |
| break; |
| default: |
| return -EAFNOSUPPORT; |
| } |
| |
| err = setsockopt(userSocket, sockLayer, sockOpt, &policy, sizeof(policy)); |
| if (err < 0) { |
| err = errno; |
| ALOGE("Error setting socket option for XFRM! (%s)", strerror(err)); |
| } |
| |
| return -err; |
| } |
| |
| int XfrmController::ipSecRemoveTransportModeTransform(const android::base::unique_fd& socket) { |
| (void)socket; |
| return 0; |
| } |
| |
| void XfrmController::fillTransportModeSelector(const XfrmSaInfo& record, xfrm_selector* selector) { |
| selector->family = record.addrFamily; |
| selector->proto = AF_UNSPEC; // TODO: do we need to match the protocol? it's |
| // possible via the socket |
| selector->ifindex = record.netId; // TODO : still need to sort this out |
| } |
| |
| int XfrmController::createTransportModeSecurityAssociation(const XfrmSaInfo& record, |
| const XfrmSocket& sock) { |
| xfrm_usersa_info usersa{}; |
| nlattr_algo_crypt crypt{}; |
| nlattr_algo_auth auth{}; |
| |
| enum { NLMSG_HDR, USERSA, USERSA_PAD, CRYPT, CRYPT_PAD, AUTH, AUTH_PAD, iovLen }; |
| |
| iovec iov[] = { |
| {NULL, 0}, // reserved for the eventual addition of a NLMSG_HDR |
| {&usersa, 0}, // main usersa_info struct |
| {kPadBytes, 0}, // up to NLMSG_ALIGNTO pad bytes of padding |
| {&crypt, 0}, // adjust size if crypt algo is present |
| {kPadBytes, 0}, // up to NLATTR_ALIGNTO pad bytes |
| {&auth, 0}, // adjust size if auth algo is present |
| {kPadBytes, 0}, // up to NLATTR_ALIGNTO pad bytes |
| }; |
| |
| int len; |
| len = iov[USERSA].iov_len = fillUserSaInfo(record, &usersa); |
| iov[USERSA_PAD].iov_len = NLMSG_ALIGN(len) - len; |
| |
| len = iov[CRYPT].iov_len = fillNlAttrXfrmAlgoEnc(record.crypt, &crypt); |
| iov[CRYPT_PAD].iov_len = NLA_ALIGN(len) - len; |
| |
| len = iov[AUTH].iov_len = fillNlAttrXfrmAlgoAuth(record.auth, &auth); |
| iov[AUTH_PAD].iov_len = NLA_ALIGN(len) - len; |
| |
| return sock.sendMessage(XFRM_MSG_UPDSA, NETLINK_REQUEST_FLAGS, 0, iov, iovLen); |
| } |
| |
| int XfrmController::fillNlAttrXfrmAlgoEnc(const XfrmAlgo& inAlgo, nlattr_algo_crypt* algo) { |
| int len = NLA_HDRLEN + sizeof(xfrm_algo); |
| strncpy(algo->crypt.alg_name, inAlgo.name.c_str(), sizeof(algo->crypt.alg_name)); |
| algo->crypt.alg_key_len = inAlgo.key.size() * 8; // bits |
| memcpy(algo->key, &inAlgo.key[0], inAlgo.key.size()); // FIXME :safety checks |
| len += inAlgo.key.size(); |
| fillXfrmNlaHdr(&algo->hdr, XFRMA_ALG_CRYPT, len); |
| return len; |
| } |
| |
| int XfrmController::fillNlAttrXfrmAlgoAuth(const XfrmAlgo& inAlgo, nlattr_algo_auth* algo) { |
| int len = NLA_HDRLEN + sizeof(xfrm_algo_auth); |
| strncpy(algo->auth.alg_name, inAlgo.name.c_str(), sizeof(algo->auth.alg_name)); |
| algo->auth.alg_key_len = inAlgo.key.size() * 8; // bits |
| |
| // This is the extra field for ALG_AUTH_TRUNC |
| algo->auth.alg_trunc_len = inAlgo.truncLenBits; |
| |
| memcpy(algo->key, &inAlgo.key[0], inAlgo.key.size()); // FIXME :safety checks |
| len += inAlgo.key.size(); |
| |
| fillXfrmNlaHdr(&algo->hdr, XFRMA_ALG_AUTH_TRUNC, len); |
| return len; |
| } |
| |
| int XfrmController::fillUserSaInfo(const XfrmSaInfo& record, xfrm_usersa_info* usersa) { |
| fillTransportModeSelector(record, &usersa->sel); |
| |
| usersa->id.proto = IPPROTO_ESP; |
| usersa->id.spi = record.spi; |
| usersa->id.daddr = record.dstAddr; |
| |
| usersa->saddr = record.srcAddr; |
| |
| fillXfrmLifetimeDefaults(&usersa->lft); |
| fillXfrmCurLifetimeDefaults(&usersa->curlft); |
| memset(&usersa->stats, 0, sizeof(usersa->stats)); // leave stats zeroed out |
| usersa->reqid = record.transformId; |
| usersa->family = record.addrFamily; |
| usersa->mode = static_cast<uint8_t>(record.mode); |
| usersa->replay_window = REPLAY_WINDOW_SIZE; |
| usersa->flags = 0; // TODO: should we actually set flags, XFRM_SA_XFLAG_DONT_ENCAP_DSCP? |
| return sizeof(*usersa); |
| } |
| |
| int XfrmController::fillUserSaId(const XfrmSaId& record, xfrm_usersa_id* said) { |
| said->daddr = record.dstAddr; |
| said->spi = record.spi; |
| said->family = record.addrFamily; |
| said->proto = IPPROTO_ESP; |
| |
| return sizeof(*said); |
| } |
| |
| int XfrmController::deleteSecurityAssociation(const XfrmSaId& record, const XfrmSocket& sock) { |
| xfrm_usersa_id said{}; |
| |
| enum { NLMSG_HDR, USERSAID, USERSAID_PAD, iovLen }; |
| |
| iovec iov[] = { |
| {NULL, 0}, // reserved for the eventual addition of a NLMSG_HDR |
| {&said, 0}, // main usersa_info struct |
| {kPadBytes, 0}, // up to NLMSG_ALIGNTO pad bytes of padding |
| }; |
| |
| int len; |
| len = iov[USERSAID].iov_len = fillUserSaId(record, &said); |
| iov[USERSAID_PAD].iov_len = NLMSG_ALIGN(len) - len; |
| |
| return sock.sendMessage(XFRM_MSG_DELSA, NETLINK_REQUEST_FLAGS, 0, iov, iovLen); |
| } |
| |
| int XfrmController::allocateSpi(const XfrmSaInfo& record, uint32_t minSpi, uint32_t maxSpi, |
| uint32_t* outSpi, const XfrmSocket& sock) { |
| xfrm_userspi_info spiInfo{}; |
| |
| enum { NLMSG_HDR, USERSAID, USERSAID_PAD, iovLen }; |
| |
| iovec iov[] = { |
| {NULL, 0}, // reserved for the eventual addition of a NLMSG_HDR |
| {&spiInfo, 0}, // main userspi_info struct |
| {kPadBytes, 0}, // up to NLMSG_ALIGNTO pad bytes of padding |
| }; |
| |
| int len; |
| if (fillUserSaInfo(record, &spiInfo.info) == 0) { |
| ALOGE("Failed to fill transport SA Info"); |
| } |
| |
| len = iov[USERSAID].iov_len = sizeof(spiInfo); |
| iov[USERSAID_PAD].iov_len = NLMSG_ALIGN(len) - len; |
| |
| RandomSpi spiGen = RandomSpi(minSpi, maxSpi); |
| int spi, ret; |
| while ((spi = spiGen.next()) != INVALID_SPI) { |
| spiInfo.min = spi; |
| spiInfo.max = spi; |
| ret = sock.sendMessage(XFRM_MSG_ALLOCSPI, NETLINK_REQUEST_FLAGS, 0, iov, iovLen); |
| |
| /* If the SPI is in use, we'll get ENOENT */ |
| if (ret == -ENOENT) |
| continue; |
| |
| if (ret == 0) { |
| *outSpi = spi; |
| ALOGD("Allocated an SPI: %d", *outSpi); |
| } else { |
| *outSpi = INVALID_SPI; |
| ALOGE("SPI Allocation Failed with error %d", ret); |
| } |
| |
| return ret; |
| } |
| |
| // Should always be -ENOENT if we get here |
| return ret; |
| } |
| |
| int XfrmController::fillTransportModeUserSpInfo(const XfrmSaInfo& record, |
| xfrm_userpolicy_info* usersp) { |
| fillTransportModeSelector(record, &usersp->sel); |
| fillXfrmLifetimeDefaults(&usersp->lft); |
| fillXfrmCurLifetimeDefaults(&usersp->curlft); |
| /* if (index) index & 0x3 == dir -- must be true xfrm_user.c:verify_newpolicy_info() */ |
| usersp->index = 0; |
| usersp->dir = static_cast<uint8_t>(record.direction); |
| usersp->action = XFRM_POLICY_ALLOW; |
| usersp->flags = XFRM_POLICY_LOCALOK; |
| usersp->share = XFRM_SHARE_UNIQUE; |
| return sizeof(*usersp); |
| } |
| |
| int XfrmController::fillUserTemplate(const XfrmSaInfo& record, xfrm_user_tmpl* tmpl) { |
| tmpl->id.daddr = record.dstAddr; |
| tmpl->id.spi = record.spi; |
| tmpl->id.proto = IPPROTO_ESP; |
| |
| tmpl->family = record.addrFamily; |
| tmpl->saddr = record.srcAddr; |
| tmpl->reqid = record.transformId; |
| tmpl->mode = static_cast<uint8_t>(record.mode); |
| tmpl->share = XFRM_SHARE_UNIQUE; |
| tmpl->optional = 0; // if this is true, then a failed state lookup will be considered OK: |
| // http://lxr.free-electrons.com/source/net/xfrm/xfrm_policy.c#L1492 |
| tmpl->aalgos = ALGO_MASK_AUTH_ALL; // TODO: if there's a bitmask somewhere of |
| // algos, we should find it and apply it. |
| // I can't find one. |
| tmpl->ealgos = ALGO_MASK_CRYPT_ALL; // TODO: if there's a bitmask somewhere... |
| return 0; |
| } |
| |
| } // namespace net |
| } // namespace android |