added -t --type option
[sfa.git] / sfa / server / sfa-ca.py
1 #!/usr/bin/python
2
3 #
4 # SFA Certificate Signing and management. Root authorities can use this script 
5 # to sign  the certificate of another authority and become its parent. Sub 
6 # authorities (authorities that have had their cert signed by another authority) 
7 # can use this script to update their registry hierarchy with the new cert    
8
9 # Example usage: 
10 #
11 ## sign a peer cert
12 # sfa-ca.py --sign PEER_CERT_FILENAME -o OUTPUT_FILENAME 
13 #
14 ## import a cert and update the registry hierarchy
15 # sfa-ca.py --import CERT_FILENAME   
16 #
17 ## display a cert
18 # sfa-ca.py --display CERT_FILENAME
19
20
21 import os
22 import sys
23 from optparse import OptionParser
24 from sfa.trust.certificate import Keypair, Certificate
25 from sfa.trust.gid import GID, create_uuid
26 from sfa.trust.hierarchy import Hierarchy
27 from sfa.util.config import Config
28 from collections import defaultdict
29
30 def main():
31     args = sys.argv
32     script_name = args[0]
33     parser = OptionParser(usage="%(script_name)s [options]" % locals())
34     parser.add_option("-d", "--display", dest="display", default=None,
35                       help="print contents of specified gid")           
36     parser.add_option("-s", "--sign", dest="sign", default=None, 
37                       help="gid to sign" )
38     parser.add_option("-k", "--key", dest="key", default=None, 
39                       help="keyfile to use for signing")
40     parser.add_option("-a", "--authority", dest="authority", default=None, 
41                       help="sign the gid using the specified authority ")
42     parser.add_option("-i", "--import", dest="importgid", default=None,
43                       help="gid file to import into the registry")
44     parser.add_option("-e", "--export", dest="export", 
45                       help="name of gid to export from registry")
46     parser.add_option("-t", "--type", dest="type",
47                       help="record type", default=None)
48     parser.add_option("-o", "--outfile", dest="outfile",
49                       help="where to write the exprted gid") 
50     parser.add_option("-v", "--verbose", dest="verbose", default=False, 
51                       action="store_true", help="be verbose")           
52                 
53     (options, args) = parser.parse_args()
54
55
56     if options.display:
57         display(options)
58     elif options.sign:
59         sign(options)
60     elif options.importgid:
61         import_gid(options) 
62     elif options.export:
63         export_gid(options)  
64     else:
65         parser.print_help()
66         sys.exit(1)        
67
68
69 def display(options):
70     """
71     Display the sepcified GID
72     """
73     gidfile = os.path.abspath(options.display)
74     if not gidfile or not os.path.isfile(gidfile):
75         print "No such gid: %s" % gidfile
76         sys.exit(1)
77     gid = GID(filename=gidfile)
78     gid.dump(dump_parents=True)
79
80 def sign_gid(gid, parent_key, parent_gid):
81     gid.set_issuer(parent_key, parent_gid.get_hrn())
82     gid.set_parent(parent_gid)
83     gid.set_intermediate_ca(True)
84     gid.set_pubkey(gid.get_pubkey())
85     gid.sign()
86     return gid 
87
88 def sign(options):
89     """
90     Sign the specified gid
91     """
92     hierarchy = Hierarchy()
93     config = Config()
94     default_authority = config.SFA_INTERFACE_HRN
95     auth_info = hierarchy.get_auth_info(default_authority)
96
97     # load the gid
98     gidfile = os.path.abspath(options.sign)
99     if not os.path.isfile(gidfile):
100         print "no such gid: %s" % gidfile
101         sys.exit(1)
102     gid = GID(filename=gidfile)
103
104     # remove previous parent
105     gid = GID(string=gid.save_to_string(save_parents=False))
106
107     # load the parent private info
108     authority = options.authority    
109     # if no pkey was specified, then use the this authority's key
110     if not authority:
111         authority = default_authority 
112     
113     if not hierarchy.auth_exists(authority):
114         print "no such authority: %s" % authority    
115
116     # load the parent gid and key 
117     auth_info = hierarchy.get_auth_info(authority)
118     pkeyfile = auth_info.privkey_filename
119     parent_key = Keypair(filename=pkeyfile)
120     parent_gid = auth_info.gid_object
121
122     # get the outfile
123     outfile = options.outfile
124     if not outfile:
125         outfile = os.path.abspath('./signed-%s.gid' % gid.get_hrn())
126    
127     # check if gid already has a parent
128  
129     # sign the gid
130     if options.verbose:
131         print "Signing %s gid with parent %s" % \
132               (gid.get_hrn(), parent_gid.get_hrn())
133     gid = sign_gid(gid, parent_key, parent_gid)
134     # save the signed gid
135     if options.verbose:
136         print "Writing signed gid %s" % outfile  
137     gid.save_to_file(outfile, save_parents=True)
138     
139
140 def export_gid(options):
141     from sfa.util.table import SfaTable
142     # lookup the record for the specified hrn 
143     hrn = options.export
144     type = options.type
145     # check sfa table first
146     filter = {'hrn': hrn}
147     if type:
148         filter['type'] = type                    
149     table = SfaTable()
150     records = table.find(filter)
151     if not records:
152         # check the authorities hierarchy 
153         hierarchy = Hierarchy()
154         try:
155             auth_info = hierarchy.get_auth_info()
156             gid = auth_info.gid_object 
157         except:
158             print "Record: %s not found" % hrn
159             sys.exit(1)
160     else:
161         record = records[0]
162         gid = GID(string=record['gid'])
163         
164     # get the outfile
165     outfile = options.outfile
166     if not outfile:
167         outfile = os.path.abspath('./%s.gid' % gid.get_hrn())
168
169     # save it
170     if options.verbose:
171         print "Writing %s gid to %s" % (gid.get_hrn(), outfile)
172     gid.save_to_file(outfile, save_parents=True)
173
174 def import_gid(options):
175     """
176     Import the specified gid into the registry (db and authorities 
177     hierarchy) overwriting any previous gid.
178     """
179     from sfa.util.table import SfaTable
180     from sfa.util.record import SfaRecord
181     # load the gid
182     gidfile = os.path.abspath(options.importgid)
183     if not gidfile or not os.path.isfile(gidfile):
184         print "No such gid: %s" % gidfile
185         sys.exit(1)
186     gid = GID(filename=gidfile)
187     
188     # check if it exists within the hierarchy
189     hierarchy = Hierarchy()
190     if not hierarchy.auth_exists(gid.get_hrn()):
191         print "%s not found in hierarchy" % gid.get_hrn()
192         sys.exit(1)
193
194     # check if record exists in db
195     table = SfaTable()
196     records = table.find({'hrn': gid.get_hrn(), 'type': 'authority'})
197     if not records:
198         print "%s not found in record database" % get.get_hrn()  
199         sys.exit(1)
200
201     # update the database record
202     record = records[0]
203     record['gid'] = gid.save_to_string(save_parents=True)
204     table.update(record)
205     if options.verbose:
206         print "Imported %s gid into db" % record['hrn']
207
208     # update the hierarchy
209     auth_info = hierarchy.get_auth_info(gid.get_hrn())  
210     filename = auth_info.gid_filename
211     gid.save_to_file(filename, save_parents=True)
212     if options.verbose:
213         print "Writing %s gid to %s" % (gid.get_hrn(), filename)
214
215     # re-sign all existing gids signed by this authority  
216     # create a dictionary of records keyed on the record's authority
217     record_dict = defaultdict(list)
218     # only get regords that belong to this authority 
219     # or any of its sub authorities   
220     child_records = table.find({'hrn': '%s*' % gid.get_hrn()})
221     if not child_records:
222         return
223   
224     for record in child_records:
225         record_dict[record['authority']].append(record) 
226
227     # start with the authority we just imported       
228     authorities = [gid.get_hrn()]
229     while authorities:
230         next_authorities = []
231         for authority in authorities:
232             # create a new signed gid for each record at this authority 
233             # and update the registry
234             auth_info = hierarchy.get_auth_info(authority)
235             records = record_dict[authority]
236             for record in records:
237                 record_gid = GID(string=record['gid'])
238                 parent_pkey = Keypair(filename=auth_info.privkey_filename)
239                 parent_gid = GID(filename=auth_info.gid_filename)
240                 if options.verbose:
241                     print "re-signing %s gid with parent %s" % \
242                            (record['hrn'], parent_gid.get_hrn())  
243                 signed_gid = sign_gid(record_gid, parent_pkey, parent_gid)
244                 record['gid'] = signed_gid.save_to_string(save_parents=True)
245                 table.update(record)
246                 
247                 # if this is an authority then update the hierarchy
248                 if record['type'] == 'authority':
249                     record_info = hierarchy.get_auth_info(record['hrn'])
250                     if options.verbose:
251                         print "Writing %s gid to %s" % (record['hrn'], record_info.gid_filename) 
252                     signed_gid.save_to_file(filename=record_info.gid_filename, save_parents=True)
253
254              # update list of next authorities
255             tmp_authorities = set([record['hrn'] for record in records \
256                                    if record['type'] == 'authority'])
257             next_authorities.extend(tmp_authorities)
258
259         # move on to next set of authorities
260         authorities = next_authorities     
261
262 if __name__ == '__main__':
263     main()