summaryrefslogtreecommitdiff
path: root/tools/check-sth.py
blob: 708a4389be8182bf5e8d6497e8e1f63c523b34fd (plain)
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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2015, NORDUnet A/S.
# See LICENSE for licensing information.

import sys
import argparse
import json
import errno
import shutil
from datetime import datetime, timedelta, tzinfo
from certtools import get_sth

NAGIOS_OK = 0
NAGIOS_WARN = 1
NAGIOS_CRIT = 2
NAGIOS_UNKNOWN = 3

DEFAULT_CUR_FILE = 'cur-sth.json'
DEFAULT_PREV_FILE = 'prev-sth.json'

parser = argparse.ArgumentParser(description="")
parser.add_argument('--cur-sth',
                    default=DEFAULT_CUR_FILE,
                    help="File containing current STH (default=%s)" % DEFAULT_CUR_FILE)
parser.add_argument('--prev-sth',
                    default=DEFAULT_PREV_FILE,
                    help="File containing previous STH (default=%s" % DEFAULT_PREV_FILE)
parser.add_argument('baseurl', help="Base URL for CT server")

def print_sth(sth):
    if sth is None:
        print "NONE"
    else:
        print sth['timestamp']
        print sth['sha256_root_hash']
        print sth['tree_size']
        print sth['tree_head_signature']

def get_new_sth(baseurl):
    try:
        sth = get_sth(baseurl)
    except Exception, e:
        print e
        sys.exit(NAGIOS_UNKNOWN)
    return sth
        
def read_sth(fn):
    try:
        f = open(fn)
    except IOError, errno.ENOENT:
        return None
    return json.loads(f.read())

def mv_file(fromfn, tofn):
    shutil.move(fromfn, tofn)

def write_file(fn, sth):
    open(fn, 'w').write(json.dumps(sth))

class UTC(tzinfo):
    def utcoffset(self, dt):
      return timedelta(hours=0)
    def dst(self, dt):
        return timedelta(0)

def check_age(sth):
    now = datetime.now(UTC())
    sth_time = datetime.fromtimestamp(sth['timestamp'] / 1000, UTC())
    if now > sth_time + timedelta(0, 6 * 3600):
        print "CRITICAL: %s is older than 6h: %s" % \
          (sth['sha256_root_hash'], sth_time)
        sys.exit(NAGIOS_CRIT)
    if now > sth_time + timedelta(0, 2 * 3600):
        print "WARNING: %s is older than 2h: %s" % \
          (sth['sha256_root_hash'], sth_time)
        sys.exit(NAGIOS_WARN)

def check_treesize(cur, prev):
    if prev is not None:
        if cur['tree_size'] < prev['tree_size']:
            print "CRITICAL: new tree smaller than previous tree (%d < %d)" % \
              (cur['tree_size'], prev['tree_size'])
            sys.exit(NAGIOS_CRIT)

def main(args):
    if args.cur_sth is None:
        args.cur_sth = "cur-sth.json"
    if args.prev_sth is None:
        args.prev_sth = "prev-sth.json"

    new = get_new_sth(args.baseurl)
    cur = read_sth(args.cur_sth)
    if cur is None or new['sha256_root_hash'] != cur['sha256_root_hash']:
        if cur is not None:
            mv_file(args.cur_sth, args.prev_sth)
        write_file(args.cur_sth, new)
        cur = new
    prev = read_sth(args.prev_sth)

    #print_sth(cur)
    #print_sth(prev)

    check_age(cur)
    check_treesize(cur, prev)
    # TODO: verify signature
    # TODO: get and verify consistency proof

    sys.exit(NAGIOS_OK)

if __name__ == '__main__':
    main(parser.parse_args())