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 193class PfKey(object): 194 195 """PF_KEY interface to kernel IPsec implementation.""" 196 197 def __init__(self): 198 self.sock = socket(AF_KEY, SOCK_RAW, PF_KEY_V2) 199 net_test.SetNonBlocking(self.sock) 200 self.seq = 0 201 202 def Recv(self): 203 reply = self.sock.recv(4096) 204 msg = SadbMsg(reply) 205 # print "RECV:", self.DecodeSadbMsg(msg) 206 if msg.errno != 0: 207 raise OSError(msg.errno, os.strerror(msg.errno)) 208 return reply 209 210 def SendAndRecv(self, msg, extensions): 211 self.seq += 1 212 msg.seq = self.seq 213 msg.pid = os.getpid() 214 msg.len = (len(SadbMsg) + len(extensions)) / 8 215 self.sock.send(msg.Pack() + extensions) 216 # print "SEND:", self.DecodeSadbMsg(msg) 217 return self.Recv() 218 219 def PackPfKeyExtensions(self, extlist): 220 extensions = "" 221 for exttype, extstruct, attrs in extlist: 222 extdata = extstruct.Pack() 223 ext = SadbExt(((len(extdata) + len(SadbExt) + len(attrs)) / 8, exttype)) 224 extensions += ext.Pack() + extdata + attrs 225 return extensions 226 227 def MakeSadbMsg(self, msgtype, satype): 228 # errno is 0. seq, pid and len are filled in by SendAndRecv(). 229 return SadbMsg((PF_KEY_V2, msgtype, 0, satype, 0, 0, 0, 0)) 230 231 def MakeSadbExtAddr(self, exttype, addr): 232 prefixlen = {AF_INET: 32, AF_INET6: 128}[addr.family] 233 packed = addr.Pack() 234 padbytes = (len(SadbExt) + len(SadbAddress) + len(packed)) % 8 235 packed += "\x00" * padbytes 236 return (exttype, SadbAddress((0, prefixlen)), packed) 237 238 def AddSa(self, src, dst, spi, satype, mode, reqid, encryption, 239 encryption_key, auth, auth_key): 240 """Adds a security association.""" 241 msg = self.MakeSadbMsg(SADB_ADD, satype) 242 replay = 4 243 extlist = [ 244 (SADB_EXT_SA, SadbSa((htonl(spi), replay, SADB_SASTATE_MATURE, 245 auth, encryption, 0)), ""), 246 self.MakeSadbExtAddr(SADB_EXT_ADDRESS_SRC, src), 247 self.MakeSadbExtAddr(SADB_EXT_ADDRESS_DST, dst), 248 (SADB_X_EXT_SA2, SadbXSa2((mode, 0, reqid)), ""), 249 (SADB_EXT_KEY_AUTH, SadbKey((len(auth_key) * 8,)), auth_key), 250 (SADB_EXT_KEY_ENCRYPT, SadbKey((len(encryption_key) * 8,)), 251 encryption_key) 252 ] 253 self.SendAndRecv(msg, self.PackPfKeyExtensions(extlist)) 254 255 def DelSa(self, src, dst, spi, satype): 256 """Deletes a security association.""" 257 msg = self.MakeSadbMsg(SADB_DELETE, satype) 258 extlist = [ 259 (SADB_EXT_SA, SadbSa((htonl(spi), 4, SADB_SASTATE_MATURE, 260 0, 0, 0)), ""), 261 self.MakeSadbExtAddr(SADB_EXT_ADDRESS_SRC, src), 262 self.MakeSadbExtAddr(SADB_EXT_ADDRESS_DST, dst), 263 ] 264 self.SendAndRecv(msg, self.PackPfKeyExtensions(extlist)) 265 266 @staticmethod 267 def DecodeSadbMsg(msg): 268 msgtype = _GetConstantName(msg.type, "SADB_") 269 satype = _GetConstantName(msg.satype, "SADB_TYPE_") 270 return ("SadbMsg(version=%d, type=%s, errno=%d, satype=%s, " 271 "len=%d, reserved=%d, seq=%d, pid=%d)" % ( 272 msg.version, msgtype, msg.errno, satype, msg.len, 273 msg.reserved, msg.seq, msg.pid)) 274 275 @staticmethod 276 def DecodeSadbSa(sa): 277 state = _GetConstantName(sa.state, "SADB_SASTATE_") 278 auth = _GetMultiConstantName(sa.auth, ["SADB_AALG_", "SADB_X_AALG"]) 279 encrypt = _GetMultiConstantName(sa.encrypt, ["SADB_EALG_", 280 "SADB_X_EALG_"]) 281 return ("SadbSa(spi=%x, replay=%d, state=%s, " 282 "auth=%s, encrypt=%s, flags=%x)" % ( 283 sa.spi, sa.replay, state, auth, encrypt, sa.flags)) 284 285 @staticmethod 286 def ExtensionsLength(msg, struct_type): 287 return (msg.len * 8) - len(struct_type) 288 289 @staticmethod 290 def ParseExtensions(data): 291 """Parses the extensions in a SADB message.""" 292 extensions = [] 293 while data: 294 ext, data = cstruct.Read(data, SadbExt) 295 datalen = PfKey.ExtensionsLength(ext, SadbExt) 296 extdata, data = data[:datalen], data[datalen:] 297 extensions.append(ParseExtension(ext.exttype, extdata)) 298 return extensions 299 300 def DumpSaInfo(self): 301 """Returns a list of (SadbMsg, [(extension, attr), ...], ...) tuples.""" 302 dump = [] 303 msg = self.MakeSadbMsg(SADB_DUMP, SADB_TYPE_UNSPEC) 304 received = self.SendAndRecv(msg, "") 305 while received: 306 msg, data = cstruct.Read(received, SadbMsg) 307 extlen = self.ExtensionsLength(msg, SadbMsg) 308 extensions, data = data[:extlen], data[extlen:] 309 dump.append((msg, self.ParseExtensions(extensions))) 310 if msg.seq == 0: # End of dump. 311 break 312 received = self.Recv() 313 return dump 314 315 def PrintSaInfos(self, dump): 316 for msg, extensions in dump: 317 print self.DecodeSadbMsg(msg) 318 for exttype, ext, attrs in extensions: 319 exttype = _GetMultiConstantName(exttype, ["SADB_EXT", "SADB_X_EXT"]) 320 if exttype == SADB_EXT_SA: 321 print " ", exttype, self.DecodeSadbSa(ext), attrs.encode("hex") 322 print " ", exttype, ext, attrs.encode("hex") 323 print 324 325 326if __name__ == "__main__": 327 p = PfKey() 328 p.DumpSaInfo() 329