1#!/usr/bin/python 2# 3# Copyright 2017 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17"""Partial implementation of the PFKEYv2 interface.""" 18 19# pylint: disable=g-bad-todo,bad-whitespace 20 21import os 22from socket import * # pylint: disable=wildcard-import 23import sys 24 25import cstruct 26import net_test 27 28 29# AF_KEY socket type. See include/linux/socket.h. 30AF_KEY = 15 31 32# PFKEYv2 constants. See include/uapi/linux/pfkeyv2.h. 33PF_KEY_V2 = 2 34 35# IPsec constants. See include/uapi/linux/ipsec.h. 36IPSEC_MODE_ANY = 0 37IPSEC_MODE_TRANSPORT = 1 38IPSEC_MODE_TUNNEL = 2 39IPSEC_MODE_BEET = 3 40 41# Operation types. 42SADB_ADD = 3 43SADB_DELETE = 4 44SADB_DUMP = 10 45 46# SA types. 47SADB_TYPE_UNSPEC = 0 48SADB_TYPE_AH = 2 49SADB_TYPE_ESP = 3 50 51# SA states. 52SADB_SASTATE_LARVAL = 0 53SADB_SASTATE_MATURE = 1 54SADB_SASTATE_DYING = 2 55SADB_SASTATE_DEAD = 3 56 57# Authentication algorithms. 58SADB_AALG_NONE = 0 59SADB_AALG_MD5HMAC = 2 60SADB_AALG_SHA1HMAC = 3 61SADB_X_AALG_SHA2_256HMAC = 5 62SADB_X_AALG_SHA2_384HMAC = 6 63SADB_X_AALG_SHA2_512HMAC = 7 64SADB_X_AALG_RIPEMD160HMAC = 8 65SADB_X_AALG_AES_XCBC_MAC = 9 66SADB_X_AALG_NULL = 251 67 68# Encryption algorithms. 69SADB_EALG_NONE = 0 70SADB_EALG_DESCBC = 2 71SADB_EALG_3DESCBC = 3 72SADB_X_EALG_CASTCBC = 6 73SADB_X_EALG_BLOWFISHCBC = 7 74SADB_EALG_NULL = 11 75SADB_X_EALG_AESCBC = 12 76SADB_X_EALG_AESCTR = 13 77SADB_X_EALG_AES_CCM_ICV8 = 14 78SADB_X_EALG_AES_CCM_ICV12 = 15 79SADB_X_EALG_AES_CCM_ICV16 = 16 80SADB_X_EALG_AES_GCM_ICV8 = 18 81SADB_X_EALG_AES_GCM_ICV12 = 19 82SADB_X_EALG_AES_GCM_ICV16 = 20 83SADB_X_EALG_CAMELLIACBC = 22 84SADB_X_EALG_NULL_AES_GMAC = 23 85SADB_X_EALG_SERPENTCBC = 252 86SADB_X_EALG_TWOFISHCBC = 253 87 88# Extension Header values. 89SADB_EXT_RESERVED = 0 90SADB_EXT_SA = 1 91SADB_EXT_LIFETIME_CURRENT = 2 92SADB_EXT_LIFETIME_HARD = 3 93SADB_EXT_LIFETIME_SOFT = 4 94SADB_EXT_ADDRESS_SRC = 5 95SADB_EXT_ADDRESS_DST = 6 96SADB_EXT_ADDRESS_PROXY = 7 97SADB_EXT_KEY_AUTH = 8 98SADB_EXT_KEY_ENCRYPT = 9 99SADB_EXT_IDENTITY_SRC = 10 100SADB_EXT_IDENTITY_DST = 11 101SADB_EXT_SENSITIVITY = 12 102SADB_EXT_PROPOSAL = 13 103SADB_EXT_SUPPORTED_AUTH = 14 104SADB_EXT_SUPPORTED_ENCRYPT = 15 105SADB_EXT_SPIRANGE = 16 106SADB_X_EXT_KMPRIVATE = 17 107SADB_X_EXT_POLICY = 18 108SADB_X_EXT_SA2 = 19 109SADB_X_EXT_NAT_T_TYPE = 20 110SADB_X_EXT_NAT_T_SPORT = 21 111SADB_X_EXT_NAT_T_DPORT = 22 112SADB_X_EXT_NAT_T_OA = 23 113SADB_X_EXT_SEC_CTX = 24 114SADB_X_EXT_KMADDRESS = 25 115SADB_X_EXT_FILTER = 26 116 117# Data structure formats. 118# These aren't constants, they're classes. So, pylint: disable=invalid-name 119SadbMsg = cstruct.Struct( 120 "SadbMsg", "=BBBBHHII", "version type errno satype len reserved seq pid") 121 122# Fake struct containing the common beginning of all extension structs. 123SadbExt = cstruct.Struct("SadbExt", "=HH", "len exttype") 124 125SadbSa = cstruct.Struct( 126 "SadbSa", "=IBBBBI", "spi replay state auth encrypt flags") 127 128SadbLifetime = cstruct.Struct( 129 "SadbLifetime", "=IQQQ", "allocations bytes addtime usetime") 130 131SadbAddress = cstruct.Struct("SadbAddress", "=BB2x", "proto prefixlen") 132 133SadbKey = cstruct.Struct("SadbKey", "=H2x", "bits") 134 135SadbXSa2 = cstruct.Struct("SadbXSa2", "=B3xII", "mode sequence reqid") 136 137SadbXNatTType = cstruct.Struct("SadbXNatTType", "=B3x", "type") 138 139SadbXNatTPort = cstruct.Struct("SadbXNatTPort", "!H2x", "port") 140 141 142def _GetConstantName(value, prefix): 143 """Translates a number to a constant of the same value in this file.""" 144 thismodule = sys.modules[__name__] 145 # Match shorter constant names first. This allows us to match SADB_DUMP and 146 # instead of, say, SADB_EXT_LIFETIME_HARD if we pass in a prefix of "SADB_" 147 # and a value of 3, and match SADB_EXT_LIFETIME_HARD just by specifying 148 # a longer prefix. 149 for name in sorted(dir(thismodule), key=len): 150 if (name.startswith(prefix) and 151 name.isupper() and getattr(thismodule, name) == value): 152 return name 153 return value 154 155 156def _GetMultiConstantName(value, prefixes): 157 for prefix in prefixes: 158 name = _GetConstantName(value, prefix) 159 try: 160 int(name) 161 continue 162 except ValueError: 163 return name 164 165 166# Converts extension blobs to a (name, struct, attrs) tuple. 167def ParseExtension(exttype, data): 168 struct_type = None 169 if exttype == SADB_EXT_SA: 170 struct_type = SadbSa 171 elif exttype in [SADB_EXT_LIFETIME_CURRENT, SADB_EXT_LIFETIME_HARD, 172 SADB_EXT_LIFETIME_SOFT]: 173 struct_type = SadbLifetime 174 elif exttype in [SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST, 175 SADB_EXT_ADDRESS_PROXY]: 176 struct_type = SadbAddress 177 elif exttype in [SADB_EXT_KEY_AUTH, SADB_EXT_KEY_ENCRYPT]: 178 struct_type = SadbKey 179 elif exttype == SADB_X_EXT_SA2: 180 struct_type = SadbXSa2 181 elif exttype == SADB_X_EXT_NAT_T_TYPE: 182 struct_type = SadbXNatTType 183 elif exttype in [SADB_X_EXT_NAT_T_SPORT, SADB_X_EXT_NAT_T_DPORT]: 184 struct_type = SadbXNatTPort 185 186 if struct_type: 187 ext, attrs = cstruct.Read(data, struct_type) 188 else: 189 ext, attrs, = data, "" 190 191 return exttype, ext, attrs 192 193 194class PfKey(object): 195 196 """PF_KEY interface to kernel IPsec implementation.""" 197 198 def __init__(self): 199 self.sock = socket(AF_KEY, SOCK_RAW, PF_KEY_V2) 200 net_test.SetNonBlocking(self.sock) 201 self.seq = 0 202 203 def Recv(self): 204 reply = self.sock.recv(4096) 205 msg = SadbMsg(reply) 206 # print("RECV: " + self.DecodeSadbMsg(msg)) 207 if msg.errno != 0: 208 raise OSError(msg.errno, os.strerror(msg.errno)) 209 return reply 210 211 def SendAndRecv(self, msg, extensions): 212 self.seq += 1 213 msg.seq = self.seq 214 msg.pid = os.getpid() 215 msg.len = (len(SadbMsg) + len(extensions)) / 8 216 self.sock.send(msg.Pack() + extensions) 217 # print("SEND: " + self.DecodeSadbMsg(msg)) 218 return self.Recv() 219 220 def PackPfKeyExtensions(self, extlist): 221 extensions = "" 222 for exttype, extstruct, attrs in extlist: 223 extdata = extstruct.Pack() 224 ext = SadbExt(((len(extdata) + len(SadbExt) + len(attrs)) / 8, exttype)) 225 extensions += ext.Pack() + extdata + attrs 226 return extensions 227 228 def MakeSadbMsg(self, msgtype, satype): 229 # errno is 0. seq, pid and len are filled in by SendAndRecv(). 230 return SadbMsg((PF_KEY_V2, msgtype, 0, satype, 0, 0, 0, 0)) 231 232 def MakeSadbExtAddr(self, exttype, addr): 233 prefixlen = {AF_INET: 32, AF_INET6: 128}[addr.family] 234 packed = addr.Pack() 235 padbytes = (len(SadbExt) + len(SadbAddress) + len(packed)) % 8 236 packed += "\x00" * padbytes 237 return (exttype, SadbAddress((0, prefixlen)), packed) 238 239 def AddSa(self, src, dst, spi, satype, mode, reqid, encryption, 240 encryption_key, auth, auth_key): 241 """Adds a security association.""" 242 msg = self.MakeSadbMsg(SADB_ADD, satype) 243 replay = 4 244 extlist = [ 245 (SADB_EXT_SA, SadbSa((htonl(spi), replay, SADB_SASTATE_MATURE, 246 auth, encryption, 0)), ""), 247 self.MakeSadbExtAddr(SADB_EXT_ADDRESS_SRC, src), 248 self.MakeSadbExtAddr(SADB_EXT_ADDRESS_DST, dst), 249 (SADB_X_EXT_SA2, SadbXSa2((mode, 0, reqid)), ""), 250 (SADB_EXT_KEY_AUTH, SadbKey((len(auth_key) * 8,)), auth_key), 251 (SADB_EXT_KEY_ENCRYPT, SadbKey((len(encryption_key) * 8,)), 252 encryption_key) 253 ] 254 self.SendAndRecv(msg, self.PackPfKeyExtensions(extlist)) 255 256 def DelSa(self, src, dst, spi, satype): 257 """Deletes a security association.""" 258 msg = self.MakeSadbMsg(SADB_DELETE, satype) 259 extlist = [ 260 (SADB_EXT_SA, SadbSa((htonl(spi), 4, SADB_SASTATE_MATURE, 261 0, 0, 0)), ""), 262 self.MakeSadbExtAddr(SADB_EXT_ADDRESS_SRC, src), 263 self.MakeSadbExtAddr(SADB_EXT_ADDRESS_DST, dst), 264 ] 265 self.SendAndRecv(msg, self.PackPfKeyExtensions(extlist)) 266 267 @staticmethod 268 def DecodeSadbMsg(msg): 269 msgtype = _GetConstantName(msg.type, "SADB_") 270 satype = _GetConstantName(msg.satype, "SADB_TYPE_") 271 return ("SadbMsg(version=%d, type=%s, errno=%d, satype=%s, " 272 "len=%d, reserved=%d, seq=%d, pid=%d)" % ( 273 msg.version, msgtype, msg.errno, satype, msg.len, 274 msg.reserved, msg.seq, msg.pid)) 275 276 @staticmethod 277 def DecodeSadbSa(sa): 278 state = _GetConstantName(sa.state, "SADB_SASTATE_") 279 auth = _GetMultiConstantName(sa.auth, ["SADB_AALG_", "SADB_X_AALG"]) 280 encrypt = _GetMultiConstantName(sa.encrypt, ["SADB_EALG_", 281 "SADB_X_EALG_"]) 282 return ("SadbSa(spi=%x, replay=%d, state=%s, " 283 "auth=%s, encrypt=%s, flags=%x)" % ( 284 sa.spi, sa.replay, state, auth, encrypt, sa.flags)) 285 286 @staticmethod 287 def ExtensionsLength(msg, struct_type): 288 return (msg.len * 8) - len(struct_type) 289 290 @staticmethod 291 def ParseExtensions(data): 292 """Parses the extensions in a SADB message.""" 293 extensions = [] 294 while data: 295 ext, data = cstruct.Read(data, SadbExt) 296 datalen = PfKey.ExtensionsLength(ext, SadbExt) 297 extdata, data = data[:datalen], data[datalen:] 298 extensions.append(ParseExtension(ext.exttype, extdata)) 299 return extensions 300 301 def DumpSaInfo(self): 302 """Returns a list of (SadbMsg, [(extension, attr), ...], ...) tuples.""" 303 dump = [] 304 msg = self.MakeSadbMsg(SADB_DUMP, SADB_TYPE_UNSPEC) 305 received = self.SendAndRecv(msg, "") 306 while received: 307 msg, data = cstruct.Read(received, SadbMsg) 308 extlen = self.ExtensionsLength(msg, SadbMsg) 309 extensions, data = data[:extlen], data[extlen:] 310 dump.append((msg, self.ParseExtensions(extensions))) 311 if msg.seq == 0: # End of dump. 312 break 313 received = self.Recv() 314 return dump 315 316 def PrintSaInfos(self, dump): 317 for msg, extensions in dump: 318 print(self.DecodeSadbMsg(msg)) 319 for exttype, ext, attrs in extensions: 320 exttype = _GetMultiConstantName(exttype, ["SADB_EXT", "SADB_X_EXT"]) 321 if exttype == SADB_EXT_SA: 322 print(" %s %s %s" % 323 (exttype, self.DecodeSadbSa(ext), attrs.encode("hex"))) 324 print(" %s %s %s" % (exttype, ext, attrs.encode("hex"))) 325 print("") 326 327 328if __name__ == "__main__": 329 p = PfKey() 330 p.DumpSaInfo() 331