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