add ca.verify tests
[stack/cam.git] / cam / main.py
1 #!/usr/bin/python
2
3 import logging
4 import optparse
5 import sys
6 import time
7 from cam import config
8
9
10 USAGE = '''cam [<OPTIONS>] <COMMAND> [<ARG>...]
11 CAM v%(version)s - (c)2012-2014 by <ale@incal.net>
12 Minimal X509 Certification Authority management tool.
13
14 Known commands:
15
16   init [<RSA_CRT>]
17     Initialize the environment and create a new CA certificate
18     (you can also import an existing certificate)
19
20   gen <TAG>...
21     Create (or re-create) the certificates corresponding
22     to TAG
23
24   gencrl
25     Update the CRL
26
27   list
28     List all known certificates
29
30   verify FILES...
31     Verify the certificates found in FILES against the CA
32
33   fp [<TAG>...]
34     Print SHA1/MD5 fingerprints of certificates
35
36   files <TAG>...
37     Dump all the certificate-related files of this TAG
38
39   check
40     Should be run weekly from a cron job to warn you if some
41     certificates are about to expire (controlled by the 'warning_days'
42     parameter in the 'global' section of the configuration)
43
44 The configuration file consists of a ini-style file, with a 'ca'
45 section that specifies global CA parameters, and more sections for
46 each tag with certificate-specific information. See the documentation
47 for more details on how to write your own configuration.
48
49 Run `cam --help' to get a list of available command-line options.
50
51 ''' % {'version': '2.1'}
52
53
54 def find_cert(certs, name):
55     for c in certs:
56         if c.name == name:
57             return c
58     raise Exception('Certificate "%s" not found' % name)
59
60
61 def cmd_init(global_config, ca, certs, args):
62     ca.create()
63
64
65 def cmd_gen(global_config, ca, certs, args):
66     if len(args) < 1:
67         print 'Nothing to do.'
68     for tag in args:
69         ca.generate(find_cert(certs, tag))
70
71
72 def cmd_gencrl(global_config, ca, certs, args):
73     ca.gencrl()
74
75
76 def cmd_files(global_config, ca, certs, args):
77     if len(args) < 1:
78         print 'Nothing to do.'
79     for tag in args:
80         c = find_cert(certs, tag)
81         print c.public_key_file
82         print c.private_key_file
83
84
85 def cmd_list(global_config, ca, certs, args):
86     now = time.time()
87     for cert in sorted(certs, key=lambda x: x.name):
88         expiry = cert.get_expiration_date()
89         state = 'OK'
90         expiry_str = ''
91         if not expiry:
92             state = 'MISSING'
93         else:
94             if expiry < now:
95                 state = 'EXPIRED'
96             expiry_str = time.strftime('%Y/%m/%d', time.gmtime(expiry))
97         print cert.name, cert.cn, state, expiry_str
98
99
100 def cmd_verify(global_config, ca, certs, args):
101     if len(args) < 1:
102         print 'Nothing to do.'
103     failed = False
104     for path in args:
105         if not ca.verify(path):
106             print '%s: FAIL' % path
107             failed = True
108         else:
109             print '%s: OK' % path
110     return failed
111
112
113 def cmd_fingerprint(global_config, ca, certs, args):
114     if len(args) > 0:
115         certs = [find_cert(certs, x) for x in args]
116     for cert in certs:
117         print cert.name, cert.cn
118         print '  SHA1:', cert.get_fingerprint('sha1')
119         print '  MD5:', cert.get_fingerprint('md5')
120
121
122 def cmd_check(global_config, ca, certs, args):
123     now = time.time()
124     warning_time = 86400 * int(global_config.get('warning_days', 15))
125     retval = 0
126     for cert in certs:
127         exp = cert.get_expiration_date()
128         if exp and (exp - now) < warning_time:
129             print '%s (%s) is about to expire.' % (cert.name, cert.cn)
130             retval = 1
131     return retval
132
133
134 cmd_table = {
135     'init': cmd_init,
136     'gen': cmd_gen,
137     'gencrl': cmd_gencrl,
138     'files': cmd_files,
139     'list': cmd_list,
140     'verify': cmd_verify,
141     'fp': cmd_fingerprint,
142     'fingerprint': cmd_fingerprint,
143     'check': cmd_check,
144 }
145
146
147 def main():
148     parser = optparse.OptionParser(usage=USAGE)
149     parser.add_option('-d', '--debug', dest='debug', help='Be verbose',
150                       action='store_true')
151     parser.add_option('-c', '--config', dest='config', help='Config file')
152     opts, args = parser.parse_args()
153
154     if len(args) > 0 and args[0] == 'help':
155         parser.print_help()
156         return 0
157     if not opts.config:
158         parser.error('Must specify --config')
159     if len(args) < 1:
160         parser.error('Must specify a command')
161
162     logging.basicConfig(
163         format='cam: %(levelname)s: %(message)s',
164         level=logging.DEBUG if opts.debug else logging.INFO)
165
166     try:
167         global_config, ca, certs = config.read_config(opts.config)
168         try:
169             cmd, args = args[0], args[1:]
170             if cmd not in cmd_table:
171                 parser.error('unknown command "%s"' % cmd)
172             cmdfn = cmd_table[cmd]
173             return cmdfn(global_config, ca, certs, args)
174         finally:
175             ca.close()
176     except Exception as e:
177         if opts.debug:
178             logging.exception(e)
179         else:
180             logging.error(e)
181         return 1
182
183
184 def main_wrapper():
185     try:
186         return main()
187     except Exception:
188         logging.exception('uncaught exception')
189         return 1
190
191
192 if __name__ == '__main__':
193     sys.exit(main_wrapper())