aboutsummaryrefslogtreecommitdiff
path: root/potpourri/format-application-x-rpki.py
blob: 184103f9317a8655b383b25b34ca2082610427ff (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# $Id$
# 
# Copyright (C) 2014  Dragon Research Labs ("DRL")
# Portions copyright (C) 2010--2012  Internet Systems Consortium ("ISC")
# 
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notices and this permission notice appear in all copies.
# 
# THE SOFTWARE IS PROVIDED "AS IS" AND DRL AND ISC DISCLAIM ALL
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL DRL OR
# ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
# OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.

"""
Take the basic application/x-rpki messages that rpkid and friends
log and translate them into a text version that's easier to search,
without losing any of the original data.  We use MH for the output
format because nmh makes a handy viewer.
"""

import email.mime
import email.mime.application
import email.mime.text
import email.mime.multipart
import email.utils
import email.encoders
import mailbox
import rpki.POW
import lxml.etree
import argparse
import sys
import base64

parser = argparse.ArgumentParser(description = __doc__)
parser.add_argument("-i", "--input", required = True,
                    help = "input Maildir")
parser.add_argument("-m", "--mark", action = "store_true",
                    help = "mark seen messages")
parser.add_argument("-k", "--kill", action = "store_true",
                    help = "kill seen messages")
parser.add_argument("-o", "--output", required = True,
                    help = "output MH folder")
parser.add_argument("-t", "--tag",
                    default = "{http://www.apnic.net/specs/rescerts/up-down/}message",
                    help = "XML namespace tag for an input message")
parser.add_argument("-u", "--unseen", action = "store_true",
                    help = "only process unseen messages")
args = parser.parse_args()

def pprint_cert(b64):
  return rpki.POW.X509.derRead(base64.b64decode(b64)).pprint()
  
def up_down():
  msg["X-RPKI-Up-Down-Type"] = xml.get("type")
  msg["X-RPKI-Up-Down-Sender"] = xml.get("sender")
  msg["X-RPKI-Up-Down-Recipient"] = xml.get("recipient")
  msg["Subject"] = "Up-down %s %s => %s" % (xml.get("type"), xml.get("sender"), xml.get("recipient"))
  for x in xml:
    if x.tag.endswith("class"):
      for y in x:
        if y.tag.endswith("certificate") or y.tag.endswith("issuer"):
          msg.attach(email.mime.text.MIMEText(pprint_cert(y.text)))

def left_right():
  msg["X-RPKI-Left-Right-Type"] = xml.get("type")
  msg["Subject"] = "Left-right %s" % xml.get("type")

def publication():
  msg["X-RPKI-Left-Right-Type"] = xml.get("type")
  msg["Subject"] = "Publication %s" % xml.get("type")

dispatch = { "{http://www.apnic.net/specs/rescerts/up-down/}message" : up_down,
             "{http://www.hactrn.net/uris/rpki/left-right-spec/}msg" : left_right,
             "{http://www.hactrn.net/uris/rpki/publication-spec/}msg" : publication }

def fix_headers():
  if "X-RPKI-PID" in srcmsg or "X-RPKI-Object" in srcmsg:
    msg["X-RPKI-PID"] = srcmsg["X-RPKI-PID"]
    msg["X-RPKI-Object"] = srcmsg["X-RPKI-Object"]
  else:
    words = srcmsg["Subject"].split()
    msg["X-RPKI-PID"] = words[1]
    msg["X-RPKI-Object"] = " ".join(words[4:])
  
destination = None
source = None
try:
  destination = mailbox.MH(args.output, factory = None, create = True)
  source = mailbox.Maildir(args.input, factory = None)

  for srckey, srcmsg in source.iteritems():
    if args.unseen and "S" in srcmsg.get_flags():
      continue
    assert not srcmsg.is_multipart() and srcmsg.get_content_type() == "application/x-rpki"
    payload = srcmsg.get_payload(decode = True)
    cms = rpki.POW.CMS.derRead(payload)
    txt = cms.verify(rpki.POW.X509Store(), None, rpki.POW.CMS_NOCRL | rpki.POW.CMS_NO_SIGNER_CERT_VERIFY | rpki.POW.CMS_NO_ATTR_VERIFY | rpki.POW.CMS_NO_CONTENT_VERIFY)
    xml = lxml.etree.fromstring(txt)
    tag = xml.tag
    if args.tag and tag != args.tag:
      continue
    msg = email.mime.multipart.MIMEMultipart("related")
    msg["X-RPKI-Tag"] = tag
    for i in ("Date", "Message-ID", "X-RPKI-Timestamp"):
      msg[i] = srcmsg[i]
    fix_headers()
    if tag in dispatch:
      dispatch[tag]()
    if "Subject" not in msg:
      msg["Subject"] = srcmsg["Subject"]
    msg.attach(email.mime.text.MIMEText(txt))
    msg.attach(email.mime.application.MIMEApplication(payload, "x-rpki"))
    msg.epilogue = "\n"                 # Force trailing newline
    key = destination.add(msg)
    print "Added", key
    if args.kill:
      del source[srckey]
    elif args.mark:
      srcmsg.set_subdir("cur")
      srcmsg.add_flag("S")
      source[srckey] = srcmsg

finally:
  if destination:
    destination.close()
  if source:
    source.close()