source: TI12-security/trunk/python/Tests/MyProxyClient/m2CryptoMyPxClnt.py @ 1691

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/Tests/MyProxyClient/m2CryptoMyPxClnt.py@1691
Revision 1691, 13.6 KB checked in by pjkersha, 13 years ago (diff)
  • Converted into class.
  • Store works with client authentication - pub/pri key are passed to

SSL Context object.

  • logon has bug getting response. Code needs removal of all use of OpenSSL
  • Property svn:executable set to *
Line 
1#!/usr/bin/env python
2#
3# myproxy client
4#
5# Tom Uram <turam@mcs.anl.gov>
6# 2005/08/04
7#
8
9
10import os
11import socket
12from M2Crypto import X509, RSA, EVP, m2, BIO
13from M2Crypto.SSL.Context import Context
14from M2Crypto.SSL.Connection import Connection
15
16from OpenSSL import crypto, SSL
17import re
18
19class GetException(Exception): pass
20class RetrieveProxyException(Exception): pass
21class StoreCredException(Exception): pass
22
23
24debug = 0
25def debuglevel(level):
26    global debug
27    return debug >= level
28
29class MyProxy(object):
30   
31    __getCmd="""VERSION=MYPROXYv2
32COMMAND=0
33USERNAME=%s
34PASSPHRASE=%s
35LIFETIME=%d\0"""
36   
37    __storeCmd="""VERSION=MYPROXYv2
38COMMAND=5
39USERNAME=%s
40PASSPHRASE=
41LIFETIME=%d\0"""
42
43    def __init__(self):
44        pass
45   
46   
47    #_________________________________________________________________________       
48    def _createCertReq(self,
49                       CN,
50                       O='NDG',
51                       OU='BADC',
52                       bits=1024, 
53                       messageDigest="md5"):
54        """
55        Create certificate request.
56       
57        Returns: certificate request PEM text, private key PEM text
58        """
59                       
60        # Create certificate request
61        req = X509.Request()
62   
63        # Generate keys
64        key = RSA.gen_key(bits, m2.RSA_F4)
65   
66        # Create public key object
67        pubKey = EVP.PKey()
68        pubKey.assign_rsa(key)
69       
70        # Add the public key to the request
71        req.set_version(0)# Seems to default to 0, but we can now set it as well
72        req.set_pubkey(pubKey)
73       
74        # Set DN
75        x509Name = X509.X509_Name()
76        x509Name.CN = CN
77        x509Name.OU = OU
78        x509Name.O = O
79        req.set_subject_name(x509Name)
80       
81        req.sign(pubKey, messageDigest)
82       
83        return (req.as_asn1(), key.as_pem(cipher=None))
84   
85   
86    #_________________________________________________________________________           
87    def _deserializeResponse(self, msg):
88        """
89        Deserialize a MyProxy server response
90       
91        Returns: integer response, errorTxt (if any)
92        """
93       
94        lines = msg.split('\n')
95       
96        # get response value
97        responselines = filter( lambda x: x.startswith('RESPONSE'), lines)
98        responseline = responselines[0]
99        response = int(responseline.split('=')[1])
100       
101        # get error text
102        errorTxt = ""
103        errorlines = filter( lambda x: x.startswith('ERROR'), lines)
104        for e in errorlines:
105            etext = e.split('=')[1]
106            errorTxt += etext
107       
108        return response, errorTxt
109     
110 
111    #_________________________________________________________________________             
112    def _deserializeCerts(self, inp_dat):
113       
114        pemCerts = []
115       
116        dat = inp_dat
117       
118        import base64
119        while dat:
120   
121            # find start of cert, get length       
122            ind = dat.find('\x30\x82')
123            if ind < 0:
124                break
125               
126            len = 256*ord(dat[ind+2]) + ord(dat[ind+3])
127   
128            # extract der-format cert, and convert to pem
129            c = dat[ind:ind+len+4]
130            x509 = crypto.load_certificate(crypto.FILETYPE_ASN1,c)
131            pem_cert = crypto.dump_certificate(crypto.FILETYPE_PEM,x509)
132            pemCerts.append(pem_cert)
133   
134            # trim cert from data
135            dat = dat[ind + len + 4:]
136       
137   
138        return pemCerts
139
140
141    #_________________________________________________________________________   
142    def store(self,
143              hostname, 
144              username, 
145              certFile,
146              keyFile,
147              lifetime=43200,
148              port=7512):
149        """\
150        store credentials in a MyProxy server
151       
152        Exceptions:  GetException, StoreCredException
153        """
154        import pdb; pdb.set_trace()
155        # Set to version 3? - YES! MyProxy requires it.  See
156        # http://grid.ncsa.uiuc.edu/myproxy/protocol/
157        context = Context(protocol='sslv3')
158        context.load_cert('../hostcert.pem', 
159                          keyfile='../hostkey.pem', 
160                          callback=lambda *ar, **kw: None)
161   
162        # disable for compatibility with myproxy server (er, globus)
163        # globus doesn't handle this case, apparently, and instead
164        # chokes in proxy delegation code
165        context.set_options(m2.SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS)#0x00000800L)
166       
167        # connect to myproxy server
168        if debuglevel(1):   print "debug: connect to myproxy server"
169        conn = Connection(context, sock=socket.socket())
170       
171        # Fudge to avoid checking client cert - seems to pick globus
172        # host/<hostname> one
173        conn.clientPostConnectionCheck = None
174        conn.connect((hostname, port))
175       
176        # send globus compatibility stuff
177        if debuglevel(1):   
178            print "debug: send globus compat byte"
179        conn.write('0')
180   
181        # send store command
182        if debuglevel(1): 
183            print "debug: send store command"
184           
185        storeCmd = MyProxy.__storeCmd % (username, lifetime)
186        conn.write(storeCmd)
187   
188        # process server response
189        if debuglevel(1):   
190            print "debug: get server response for store command request"
191           
192        dat = conn.recv(8192)
193        if debuglevel(1):   
194            print dat
195           
196        response, errorTxt = self._deserializeResponse(dat)
197        if response:
198            raise GetException, errorTxt
199        else:
200            if debuglevel(1):   
201                print "debug: server response ok"
202       
203        # Send certificate and private key
204        pat = re.compile(\
205             '-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----', 
206             re.S)
207       
208        #certTxt = pat.findall(open(certFile).read())[0]
209        certTxt = X509.load_cert(certFile).as_pem()
210        keyTxt = open(keyFile).read()
211    #    PwdCB = lambda *ar, **kw: open('../tmp').read().strip()                                         
212    #    keyTxt = EVP.load_key(keyFile, callback=PwdCB).as_pem(callback=PwdCB)
213       
214        conn.send(certTxt + keyTxt)
215   
216   
217        # process server response
218        if debuglevel(1):   
219            print "debug: get server response for store command completed"
220        resp = conn.recv(8192)
221        response, errorTxt = self._deserializeResponse(resp)
222        if response:
223            raise RetrieveProxyException, errorTxt
224        else:
225            if debuglevel(1):
226                print "debug: server response ok"
227       
228       
229    def logon(self,
230              hostname,
231              username,
232              passphrase,
233              outfile,
234              lifetime=43200,
235              port=7512):
236        """
237        Function to retrieve a proxy credential from a MyProxy server
238       
239        Exceptions:  GetException, RetrieveProxyException
240        """
241        import pdb;pdb.set_trace()
242   
243        # Set to version 3?
244        context = Context()
245       
246        # disable for compatibility with myproxy server (er, globus)
247        # globus doesn't handle this case, apparently, and instead
248        # chokes in proxy delegation code
249        context.set_options(m2.SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS)#0x00000800L)
250       
251        # connect to myproxy server
252        if debuglevel(1):   print "debug: connect to myproxy server"
253        conn = Connection(context, sock=socket.socket())
254       
255        # Fudge to avoid checking client cert - seems to pick globus
256        # host/<hostname> one
257        conn.clientPostConnectionCheck = None
258        conn.connect((hostname,port))
259       
260        # send globus compatibility stuff
261        if debuglevel(1):   print "debug: send globus compat byte"
262        conn.write('0')
263   
264        # send get command
265        if debuglevel(1):   print "debug: send get command"
266        getCmd = MyProxy.__getCmd % (username,passphrase,lifetime)
267        conn.write(getCmd)
268   
269        # process server response
270        if debuglevel(1):   print "debug: get server response"
271        dat = conn.recv(8192)
272        if debuglevel(1):   print dat
273        response,errorTxt = self._deserializeResponse(dat)
274        if response:
275            raise GetException(errorTxt)
276        else:
277            if debuglevel(1):   print "debug: server response ok"
278       
279        # generate and send certificate request
280        # - The client will generate a public/private key pair and send a
281        #   NULL-terminated PKCS#10 certificate request to the server.
282        if debuglevel(1):   print "debug: send cert request"
283   
284        certReq, priKey = self._createCertReq(username)
285        conn.send(certReq)
286   
287        # process certificates
288        # - 1 byte , number of certs
289        dat = conn.recv(1)
290        nCerts = ord(dat[0])
291       
292        # - n certs
293        if debuglevel(1):   print "debug: receive certs"
294        dat = conn.recv(8192)
295        if debuglevel(2):
296            print "debug: dumping cert data to myproxy.dump"
297            f = file('myproxy.dump','w')
298            f.write(dat)
299            f.close()
300   
301        # process server response
302        if debuglevel(1):   print "debug: get server response"
303        resp = conn.recv(8192)
304        response, errorTxt = self._deserializeCerts(resp)
305        if response:
306            raise RetrieveProxyException(errorTxt)
307        else:
308            if debuglevel(1):   print "debug: server response ok"
309   
310        # deserialize certs from received cert data
311        pemCerts = self._deserializeCerts(dat)
312        if len(pemCerts) != nCerts:
313            print "Warning: %d certs expected, %d received" % (nCerts,len(pemCerts))
314   
315        # write certs and private key to file
316        # - proxy cert
317        # - private key
318        # - rest of cert chain
319        if debuglevel(1):   print "debug: write proxy and certs to",outfile
320        f = file(outfile,'w')
321        f.write(pemCerts[0])
322        f.write(priKey)
323        for c in pemCerts[1:]:
324            f.write(c)
325        f.close()
326       
327
328    def getDelegation(self, *arg, **kw):
329        """Retrieve proxy cert for user - same as logon"""
330        self.logon(*arg, **kw)
331
332
333#_____________________________________________________________________________   
334def main():
335    import sys
336    import optparse
337    import getpass
338   
339    parser = optparse.OptionParser()
340    parser.add_option("-g", 
341                      "--get-delegation", 
342                      dest="getDelegation", 
343                      default=False,
344                      action="store_true",
345                      help="Get delegation / logon")
346   
347    parser.add_option("-c", "--store-cert", dest="certFile", default=None,
348                       help="Certificate to be stored")
349   
350    parser.add_option("-y", "--store-key", dest="keyFile", default=None,
351                       help="Private key to be stored")
352
353    parser.add_option("-s", "--pshost", dest="host", 
354                       help="The hostname of the MyProxy server to contact")
355    parser.add_option("-p", "--psport", dest="port", default=7512,
356                       help="The port of the MyProxy server to contact")
357    parser.add_option("-l", "--username", dest="username", 
358                       help="The username with which the credential is stored on the MyProxy server")
359    parser.add_option("-o", "--out", dest="outfile", 
360                       help="The username with which the credential is stored on the MyProxy server")
361    parser.add_option("-t", "--proxy-lifetime", dest="lifetime", default=43200,
362                       help="The username with which the credential is stored on the MyProxy server")
363    parser.add_option("-d", "--debug", dest="debug", default=0,
364                       help="Debug mode: 1=print debug info ; 2=print as in (1), and dump data to myproxy.dump")
365
366    (options,args) = parser.parse_args()
367   
368    debug = options.debug
369   
370    # process options
371    host = options.host
372    if not host:
373        print "Error: MyProxy host not specified"
374        sys.exit(1)
375    port = int(options.port)
376   
377    username = options.username
378    if not username:
379        if sys.platform == 'win32':
380            username = os.environ["USERNAME"]
381        else:
382            import pwd
383            username = pwd.getpwuid(os.geteuid())[0]
384    lifetime = int(options.lifetime)
385
386    myProxy = MyProxy()
387   
388    if options.getDelegation:
389               
390        outfile = options.outfile
391        if not outfile:
392            if sys.platform == 'win32':
393                outfile = 'proxy'
394            elif sys.platform in ['linux2','darwin']:
395                outfile = '/tmp/x509up_u%s' % (os.getuid())
396   
397        # Get MyProxy password
398        passphrase = getpass.getpass()
399           
400        # Retrieve proxy cert
401        try:
402            myProxy.logon(host,
403                          username,
404                          passphrase,
405                          outfile,
406                          lifetime=lifetime,
407                          port=port)
408            print "A proxy has been received for user %s in %s." % \
409                (username, outfile)
410           
411        except Exception,e:
412            if debuglevel(1):
413                import traceback
414                traceback.print_exc()
415            else:
416                print "Error:", e
417    else:
418        try:
419            myProxy.store(host, 
420                         username, 
421                         options.certFile,
422                         options.keyFile,
423                         lifetime=lifetime,
424                         port=port)
425           
426        except Exception,e:
427            if debuglevel(1):
428                import traceback
429                traceback.print_exc()
430            else:
431                print "Error:", e
432
433
434if __name__ == '__main__':
435    main()
Note: See TracBrowser for help on using the repository browser.