• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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