source: TI12-security/trunk/MyProxyClient/myproxy/client.py @ 6847

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg-security/TI12-security/trunk/MyProxyClient/myproxy/client.py@6847
Revision 6847, 51.8 KB checked in by pjkersha, 11 years ago (diff)

Fix for SSL verification DN and hostname settings. Re-release as 1.0

Line 
1"""MyProxy Client interface
2
3Developed for the NERC DataGrid Project: http://ndg.nerc.ac.uk/
4
5Major re-write of an original class.   This updated version implements methods
6with SSL calls with PyOpenSSL rather use calls to myproxy client executables as
7in the original.  This version is adapted and extended from an original
8program myproxy_logon by Tom Uram <turam@mcs.anl.gov>
9"""
10__author__ = "P J Kershaw"
11__date__ = "02/06/05"
12__copyright__ = "(C) 2010 Science and Technology Facilities Council"
13__license__ = """BSD - See LICENSE file in top-level directory
14
15For myproxy_logon see Access Grid Toolkit Public License (AGTPL)
16
17This product includes software developed by and/or derived from the Access
18Grid Project (http://www.accessgrid.org) to which the U.S. Government retains
19certain rights."""
20__contact__ = "Philip.Kershaw@stfc.ac.uk"
21__revision__ = '$Id: $'
22import logging
23log = logging.getLogger(__name__)
24
25import sys
26import os
27import socket
28import base64
29import re
30import traceback
31
32from OpenSSL import crypto, SSL
33
34from myproxy.utils.openssl import OpenSSLConfig
35from myproxy.utils import CaseSensitiveConfigParser
36
37
38class MyProxyServerSSLCertVerification(object):
39    """Check MyProxy server identity.  If hostname doesn't match, allow match of
40    host's Distinguished Name against MYPROXY_SERVER_DN setting"""
41    DN_LUT = {
42        'commonName':               'CN',
43        'organisationalUnitName':   'OU',
44        'organisation':             'O',
45        'countryName':              'C',
46        'emailAddress':             'EMAILADDRESS',
47        'localityName':             'L',
48        'stateOrProvinceName':      'ST',
49        'streetAddress':            'STREET',
50        'domainComponent':          'DC',
51        'userid':                   'UID'
52    }
53    PARSER_RE_STR = '/(%s)=' % '|'.join(DN_LUT.keys() + DN_LUT.values())
54    PARSER_RE = re.compile(PARSER_RE_STR)   
55       
56    SERVER_CN_PREFIX = 'host/'
57
58    __slots__ = ('__hostname', '__cnPrefix', '__certDN')
59   
60    def __init__(self, 
61                 certDN=None,
62                 hostname=None,
63                 cnPrefix=SERVER_CN_PREFIX):
64        """Override parent class __init__ to enable setting of certDN
65        setting
66       
67        @type certDN: string
68        @param certDN: Set the expected Distinguished Name of the
69        MyProxy server to avoid errors matching hostnames.  This is useful
70        where the hostname is not fully qualified
71        """
72        self.__cnPrefix = None
73        self.__certDN = None
74        self.__hostname = None
75       
76        if certDN is not None:
77            self.certDN = certDN
78           
79        if hostname is not None:
80            self.hostname = hostname
81           
82        self.cnPrefix = cnPrefix
83       
84    def __call__(self, connection, peerCert, errorStatus, errorDepth, 
85                 successStatus):
86        """Verify MyProxy server certificate
87       
88        @type connection: OpenSSL.SSL.Connection
89        @param connection: SSL connection object
90        @type peerCert: basestring
91        @param peerCert: MyProxy server host certificate as OpenSSL.crypto.X509
92        instance
93        @type errorStatus: int
94        @param errorStatus: error code to return if verification fails
95        @type errorDepth: int
96        @param errorDepth:
97        @type successStatus: int
98        @param successStatus:
99        @rtype: int
100        @return: status code
101        """
102        if peerCert.has_expired():
103            # Any expired certificate in the chain should result in an error
104            log.error('Certificate %r in peer certificate chain has expired', 
105                      peerCert.get_subject())
106               
107            return errorStatus
108           
109        elif errorDepth == 0:
110            # Only interested in DN of last certificate in the chain - this must
111            # match the expected MyProxy Server DN setting
112            peerCertSubj = peerCert.get_subject()
113            peerCertDN = peerCertSubj.get_components()
114            peerCertDN.sort()
115
116            if self.certDN is None:
117                # Check hostname against peer certificate CN field instead:
118                if self.hostname is None:
119                    log.error('No "hostname" or "certDN" set to check peer '
120                              'certificate against')
121                    return errorStatus
122                   
123                cn = self.cnPrefix + self.hostname
124                if peerCertSubj.commonName == cn:
125                    return successStatus
126                else:
127                    log.error('Peer certificate CN %r doesn\'t match the '
128                              'expected CN %r', peerCertSubj.commonName, cn)
129                    return errorStatus
130            else:
131                if peerCertDN == self.certDN:
132                    return successStatus
133                else:
134                    log.error('Peer certificate DN %r doesn\'t match the '
135                              'expected DN %r', peerCertDN, self.certDN)
136                    return errorStatus
137        else:
138            return successStatus
139             
140    def _getCertDN(self):
141        return self.__certDN
142   
143    def _setCertDN(self, val):
144        if isinstance(val, basestring):
145            # Allow for quoted DN
146            certDN = val.strip('"')
147           
148            dnFields = self.__class__.PARSER_RE.split(certDN)
149            if len(dnFields) < 2:
150                raise TypeError('Error parsing DN string: "%s"' % certDN)
151   
152            self.__certDN = zip(dnFields[1::2], dnFields[2::2])
153            self.__certDN.sort()
154           
155        elif not isinstance(val, list):
156            for i in val:
157                if not len(i) == 2:
158                    raise TypeError('Expecting list of two element DN field, '
159                                    'DN field value pairs for "certDN" '
160                                    'attribute')
161            self.__certDN = val
162        else:
163            raise TypeError('Expecting list or string type for "certDN" '
164                            'attribute')
165       
166    certDN = property(fget=_getCertDN,
167                      fset=_setCertDN,
168                      doc="Distinguished Name for MyProxy Server Certificate")
169       
170    # Get/Set Property methods
171    def _getHostname(self):
172        return self.__hostname
173   
174    def _setHostname(self, val):
175        if not isinstance(val, basestring):
176            raise TypeError("Expecting string type for hostname "
177                                 "attribute")
178        self.__hostname = val
179       
180    hostname = property(fget=_getHostname,
181                        fset=_setHostname,
182                        doc="hostname of MyProxy server")
183   
184    def _getCNPrefix(self):
185        """References SSL Certificate verification object property!"""
186        return self.__cnPrefix
187   
188    def _setCNPrefix(self, val):
189        """Sets SSL Certificate verification object property!"""
190        if not isinstance(val, basestring):
191            raise TypeError("Expecting string type for hostname "
192                                 "attribute")
193        self.__cnPrefix = val
194   
195    cnPrefix = property(fget=_getCNPrefix,
196                        fset=_setCNPrefix,
197                        doc="Prefix for MyProxy Server Certificate "
198                            "Distinguished Name CommonName field; usually set "
199                            "to 'host/' for Globus host certificates")
200                   
201   
202class MyProxyClientError(Exception):
203    """Base exception class for MyProxyClient exceptions"""
204
205
206class MyProxyClientConfigError(MyProxyClientError):
207    """Error with configuration"""
208     
209     
210class MyProxyClientGetError(MyProxyClientError):
211    """Exceptions arising from get request to server"""
212   
213   
214class MyProxyClientRetrieveError(MyProxyClientError):
215    """Error recovering a response from MyProxy"""
216
217
218class MyProxyCredentialsAlreadyExist(MyProxyClientError):
219    """Attempting to upload credentials to the server which already exist.  -
220    See MyProxyClient.store
221    """
222   
223   
224class MyProxyClientGetTrustRootsError(MyProxyClientError):
225    """Error retrieving trust roots"""
226           
227       
228class MyProxyClient(object):
229    """MyProxy client interface
230   
231    Based on protocol definitions in:
232   
233    http://grid.ncsa.uiuc.edu/myproxy/protocol/
234
235    @type MYPROXY_SERVER_ENVVARNAME: string
236    @cvar MYPROXY_SERVER_ENVVARNAME: server environment variable name
237   
238    @type MYPROXY_SERVER_PORT_ENVVARNAME: string
239    @cvar MYPROXY_SERVER_PORT_ENVVARNAME: port environment variable name
240   
241    @type MYPROXY_SERVER_DN_ENVVARNAME: string
242    @cvar MYPROXY_SERVER_DN_ENVVARNAME: server certificate Distinguished Name
243    environment variable name
244   
245    @type GET_CMD: string
246    @cvar GET_CMD: get command string
247   
248    @type INFO_CMD: string
249    @cvar INFO_CMD: info command string
250   
251    @type DESTROY_CMD: string
252    @cvar DESTROY_CMD: destroy command string
253   
254    @type CHANGE_PASSPHRASE_CMD: string
255    @cvar CHANGE_PASSPHRASE_CMD: command string to change cred pass-phrase
256   
257    @type STORE_CMD: string
258    @cvar STORE_CMD: store command string
259   
260    @type GET_TRUST_ROOTS_CMD: string
261    @cvar GET_TRUST_ROOTS_CMD: get trust roots command string
262   
263    @type _hostCertSubDirPath: string
264    @cvar _hostCertSubDirPath: sub-directory path host certificate (as tuple)
265   
266    @type _hostKeySubDirPath: string
267    @cvar _hostKeySubDirPath: sub-directory path to host key (as tuple)
268   
269    @type PRIKEY_NBITS: int
270    @cvar PRIKEY_NBITS: default number of bits for private key generated
271   
272    @type MESSAGE_DIGEST_TYPE: string
273    @cvar MESSAGE_DIGEST_TYPE: message digest type is MD5
274   
275    @type SERVER_RESP_BLK_SIZE: int
276    @cvar SERVER_RESP_BLK_SIZE: block size for retrievals from server
277   
278    @type MAX_RECV_TRIES: int
279    @cvar MAX_RECV_TRIES: maximum number of retrievals of size
280    SERVER_RESP_BLK_SIZE before this client gives up
281   
282    @type DEF_PROXY_FILEPATH: string
283    @cvar DEF_PROXY_FILEPATH: default location for proxy file to be written to
284   
285    @type PROXY_FILE_PERMISSIONS: int
286    @cvar PROXY_FILE_PERMISSIONS: file permissions returned proxy file is
287    created with
288   
289    @type PROPERTY_DEFAULTS: tuple
290    @cvar PROPERTY_DEFAULTS: sets permissable element names for MyProxy config
291    file
292    """
293    MYPROXY_SERVER_ENVVARNAME = 'MYPROXY_SERVER'
294    MYPROXY_SERVER_PORT_ENVVARNAME = 'MYPROXY_SERVER_PORT'
295    MYPROXY_SERVER_DN_ENVVARNAME = 'MYPROXY_SERVER_DN'
296     
297    GET_CMD="""VERSION=MYPROXYv2
298COMMAND=0
299USERNAME=%s
300PASSPHRASE=%s
301LIFETIME=%d"""
302
303    PUT_CMD="""VERSION=MYPROXYv2
304COMMAND=1
305USERNAME=%s
306PASSPHRASE=<pass phrase>
307LIFETIME=%d"""
308     
309    INFO_CMD="""VERSION=MYPROXYv2
310COMMAND=2
311USERNAME=%s
312PASSPHRASE=PASSPHRASE
313LIFETIME=0"""
314 
315    DESTROY_CMD="""VERSION=MYPROXYv2
316COMMAND=3
317USERNAME=%s
318PASSPHRASE=PASSPHRASE
319LIFETIME=0"""
320
321    CHANGE_PASSPHRASE_CMD="""VERSION=MYPROXYv2
322 COMMAND=4
323 USERNAME=%s
324 PASSPHRASE=%s
325 NEW_PHRASE=%s
326 LIFETIME=0"""
327   
328    STORE_CMD="""VERSION=MYPROXYv2
329COMMAND=5
330USERNAME=%s
331PASSPHRASE=
332LIFETIME=%d"""
333
334    GET_TRUST_ROOTS_CMD="""VERSION=MYPROXYv2
335COMMAND=7
336USERNAME=%s
337PASSPHRASE=%s
338LIFETIME=0
339TRUSTED_CERTS=1"""
340
341    _hostCertSubDirPath = ('etc', 'hostcert.pem')
342    _hostKeySubDirPath = ('etc', 'hostkey.pem')
343   
344    PROXY_FILE_PERMISSIONS = 0600
345   
346    # Work out default location of proxy file if it exists.  This is set if a
347    # call has been made previously to logon / get-delegation
348    DEF_PROXY_FILEPATH = sys.platform == ('win32' and 'proxy' or 
349                                    sys.platform in ('linux2', 'darwin') and 
350                                    '/tmp/x509up_u%s' % (os.getuid()) 
351                                    or None) 
352   
353    PRIKEY_NBITS = 4096
354    MESSAGE_DIGEST_TYPE = "md5"
355    SERVER_RESP_BLK_SIZE = 8192
356    MAX_RECV_TRIES = 1024
357   
358    # valid configuration property keywords
359    PROPERTY_DEFAULTS = {
360       'hostname':              'localhost',
361       'port':                  7512,
362       'serverDN':              None,
363       'openSSLConfFilePath':   '',
364       'proxyCertMaxLifetime':  43200,
365       'proxyCertLifetime':     43200,
366       'caCertFilePath':        None,
367       'caCertDir':             None
368    }
369   
370    # Restrict attributes to the above properties, their equivalent
371    # protected values + extra OpenSSL config object.
372    __slots__ = tuple(['__' + k for k in PROPERTY_DEFAULTS.keys()])
373    __slots__ += ('__openSSLConfig', '__cfg', '__serverSSLCertVerify')
374
375    def __init__(self, cfgFilePath=None, **prop):
376        """Make any initial settings for client connections to MyProxy
377       
378        Settings are held in a dictionary which can be set from **prop,
379        a call to setProperties() or by passing settings in an XML file
380        given by cfgFilePath
381       
382        @param cfgFilePath: set properties via a configuration file
383        @type cfgFilePath: basestring
384        @param **prop: set properties via keywords - see
385        PROPERTY_DEFAULTS class variable for a list of these
386        @type **prop: dict
387        """       
388        self.__serverSSLCertVerify = MyProxyServerSSLCertVerification()
389        self.__hostname = None
390        self.__port = None
391        self.__serverDN = None
392        self.__openSSLConfFilePath = None
393        self.__proxyCertMaxLifetime = MyProxyClient.PROPERTY_DEFAULTS[
394                                                        'proxyCertMaxLifetime']
395        self.__proxyCertLifetime = MyProxyClient.PROPERTY_DEFAULTS[
396                                                        'proxyCertLifetime']
397        self.__caCertFilePath = None
398        self.__caCertDir = None
399       
400        self.__cfg = None
401
402        # Configuration file used to get default subject when generating a
403        # new proxy certificate request
404        self.__openSSLConfig = OpenSSLConfig()
405
406        # Server host name - take from environment variable if available
407        self.hostname = os.environ.get(MyProxyClient.MYPROXY_SERVER_ENVVARNAME,
408                                    MyProxyClient.PROPERTY_DEFAULTS['hostname'])
409           
410        # ... and port number
411        self.port = int(os.environ.get(
412                                MyProxyClient.MYPROXY_SERVER_PORT_ENVVARNAME, 
413                                MyProxyClient.PROPERTY_DEFAULTS['port']))
414
415        # Server Distinguished Name
416        serverDN = os.environ.get(MyProxyClient.MYPROXY_SERVER_DN_ENVVARNAME,
417                                  MyProxyClient.PROPERTY_DEFAULTS['serverDN'])
418        if serverDN is not None:
419            self.serverDN = serverDN
420       
421        # Any keyword settings override the defaults above
422        for opt, val in prop.items():
423            setattr(self, opt, val)
424       
425        # If properties file is set any parameters settings in file will
426        # override those set by input keyword or the defaults
427        if cfgFilePath is not None:
428            self.parseConfig(cfg=cfgFilePath)
429
430    def _getServerSSLCertVerify(self):
431        return self.__serverSSLCertVerify
432
433    serverSSLCertVerify = property(_getServerSSLCertVerify, 
434                                   doc="Server SSL Certificate Verification "
435                                       "callable")
436
437
438    def _getServerSSLCertVerify(self):
439        return self.__serverSSLCertVerify
440
441    def _setServerSSLCertVerify(self, value):
442        if not isinstance(value, MyProxyServerSSLCertVerification):
443            raise TypeError('Expecting %r derived type for '
444                            '"serverSSLCertVerify" attribute; got %r' %
445                            MyProxyServerSSLCertVerification,
446                            value)
447        self.__serverSSLCertVerify = value
448
449    serverSSLCertVerify = property(_getServerSSLCertVerify, 
450                                   _setServerSSLCertVerify, 
451                                   doc="Class with a __call__ method which is "
452                                       "passed to the SSL context to verify "
453                                       "the peer (MyProxy server) certificate "
454                                       "in the SSL handshake between this "
455                                       "client and the MyProxy server")
456
457    def parseConfig(self, cfg, section='DEFAULT'):
458        '''Extract parameters from _cfg config object'''
459       
460        if isinstance(cfg, basestring):
461            cfgFilePath = os.path.expandvars(cfg)
462            self.__cfg = CaseSensitiveConfigParser()
463            self.__cfg.read(cfgFilePath)
464        else:
465            cfgFilePath = None
466            self.__cfg = cfg
467       
468        for key, val in self.__cfg.items(section):
469            setattr(self, key, val)
470       
471    # Get/Set Property methods
472    def _getHostname(self):
473        return self.__hostname
474   
475    def _setHostname(self, val):
476        """Also sets SSL Certificate verification object property!"""
477        if not isinstance(val, basestring):
478            raise TypeError("Expecting string type for hostname "
479                                 "attribute")
480        self.__hostname = val
481        self.__serverSSLCertVerify.hostname = val
482       
483    hostname = property(fget=_getHostname,
484                        fset=_setHostname,
485                        doc="hostname of MyProxy server")
486   
487    def _getPort(self):
488        return self.__port
489   
490    def _setPort(self, val):
491        if isinstance(val, basestring):
492            self.__port = int(val)
493        elif isinstance(val, int):
494            self.__port = val
495        else:
496            raise TypeError("Expecting int type for port attribute")
497   
498    port = property(fget=_getPort,
499                    fset=_setPort,
500                    doc="Port number for MyProxy server")
501   
502    def _getServerDN(self):
503        return self.__serverDN
504   
505    def _setServerDN(self, val):
506        """Also sets SSL Certificate verification object property!"""
507        if not isinstance(val, basestring):
508            raise TypeError("Expecting string type for serverDN attribute")
509       
510        self.__serverDN = val
511        self.__serverSSLCertVerify.certDN = val
512   
513    serverDN = property(fget=_getServerDN,
514                        fset=_setServerDN,
515                        doc="Distinguished Name for MyProxy Server "
516                            "Certificate")
517   
518    def _getServerCNPrefix(self):
519        """References SSL Certificate verification object property!"""
520        return self.__serverSSLCertVerify.cnPrefix
521   
522    def _setServerCNPrefix(self, val):
523        """Sets SSL Certificate verification object property!"""
524        self.__serverSSLCertVerify.cnPrefix = val
525   
526    serverCNPrefix = property(fget=_getServerCNPrefix,
527                              fset=_setServerCNPrefix,
528                              doc="Prefix for MyProxy Server Certificate "
529                                  "Distinguished Name CoomonName field; "
530                                  "usually set to host/ for Globus host "
531                                  "certificates")
532       
533    def _getOpenSSLConfFilePath(self):
534        return self.__openSSLConfFilePath
535   
536    def _setOpenSSLConfFilePath(self, val):
537        if not isinstance(val, basestring):
538            raise TypeError('Expecting string type for "openSSLConfFilePath" '
539                            'attribute')
540           
541        self.__openSSLConfFilePath = os.path.expandvars(val)
542        self.__openSSLConfig.filePath = self.__openSSLConfFilePath
543        self.__openSSLConfig.read() 
544   
545    openSSLConfFilePath = property(fget=_getOpenSSLConfFilePath,
546                                   fset=_setOpenSSLConfFilePath,
547                                   doc="file path for OpenSSL config file")
548   
549    def _getProxyCertMaxLifetime(self):
550        return self.__proxyCertMaxLifetime
551   
552    def _setProxyCertMaxLifetime(self, val):
553        if isinstance(val, basestring):
554            self.__proxyCertMaxLifetime = int(val)
555           
556        elif isinstance(val, int):
557            self.__proxyCertMaxLifetime = val
558        else:
559            raise TypeError("Expecting int type for proxyCertMaxLifetime "
560                            "attribute")
561   
562    proxyCertMaxLifetime = property(fget=_getProxyCertMaxLifetime,
563                                    fset=_setProxyCertMaxLifetime,
564                                    doc="Default max. lifetime allowed for "
565                                        "Proxy Certificate retrieved - used "
566                                        "by store method")
567   
568    def _getProxyCertLifetime(self):
569        return self.__proxyCertLifetime
570   
571    def _setProxyCertLifetime(self, val):
572        if isinstance(val, basestring):
573            self.__proxyCertLifetime = int(val)
574        elif isinstance(val, int):
575            self.__proxyCertLifetime = val
576        else:
577            raise TypeError("Expecting int type for proxyCertLifetime "
578                            "attribute")
579   
580    proxyCertLifetime = property(fget=_getProxyCertLifetime,
581                                 fset=_setProxyCertLifetime,
582                                 doc="Default proxy cert. lifetime used in "
583                                     "logon request")
584   
585    def _getCACertFilePath(self):
586        return self.__caCertFilePath
587   
588    def _setCACertFilePath(self, val):
589        '''@type val: basestring
590        @param val: file path for CA certificate to be used to verify
591        MyProxy server certificate'''
592       
593        if isinstance(val, basestring):
594            if val == '':
595                self.__caCertFilePath = None
596            else:
597                self.__caCertFilePath = os.path.expandvars(val)
598               
599        elif isinstance(val, None):
600            raise TypeError("Expecting string type for caCertFilePath "
601                                 "attribute")       
602       
603    caCertFilePath = property(fget=_getCACertFilePath,
604                              fset=_setCACertFilePath,
605                              doc="CA certificate file path - MyProxy server "
606                                  "certificate must validate against it and/"
607                                  "or any present in caCertDir")
608
609    def _getCACertDir(self):
610        return self.__caCertDir
611
612    def _setCACertDir(self, val):
613        '''Specify a directory containing PEM encoded CA certs. used for
614        validation of MyProxy server certificate.
615       
616        Set to None to make OpenSSL.SSL.Context.load_verify_locations ignore
617        this parameter
618       
619        @type val: basestring/None
620        @param val: directory path'''
621       
622        if isinstance(val, basestring):
623            if val == '':
624                self.__caCertDir = None
625            else:
626                self.__caCertDir = os.path.expandvars(val)
627               
628        elif isinstance(val, None):
629            self.__caCertDir = val   
630        else:
631            raise TypeError("Expecting string or None type for caCertDir "
632                            "attribute")
633       
634    caCertDir = property(fget=_getCACertDir,
635                         fset=_setCACertDir,
636                         doc="directory containing PEM encoded CA "
637                             "certificates.  Use along with caCertFilePath "
638                             "setting to validate MyProxy server certificate")
639
640
641    def _getOpenSSLConfig(self):
642        "Get OpenSSLConfig object property method"
643        return self.__openSSLConfig
644   
645    openSSLConfig = property(fget=_getOpenSSLConfig, doc="OpenSSLConfig object")
646
647    def _initConnection(self, 
648                        certFile=None, 
649                        keyFile=None,
650                        keyFilePassphrase=None):
651        """Initialise connection setting up SSL context and client and
652        server side identity checks
653       
654        @type sslCertFile: basestring
655        @param sslCertFile: certificate for SSL client authentication.  It may
656        be owner of a credential to be acted on or the concatenated proxy
657        certificate + proxy's signing cert.  SSL client authentication is not
658        necessary for getDelegation / logon calls
659        @type sslKeyFile: basestring
660        @param sslKeyFile: client private key file
661        @type keyFilePassphrase: basestring
662        @param keyFilePassphrase: pass-phrase protecting private key if set
663        """
664        # Must be version 3 for MyProxy
665        context = SSL.Context(SSL.SSLv3_METHOD)
666       
667        if self.caCertFilePath or self.caCertDir:
668            context.load_verify_locations(self.caCertFilePath, self.caCertDir)
669           
670        if certFile:
671            try:
672                context.use_certificate_chain_file(certFile)
673                def pwdCallback(passphraseMaxLen, 
674                                promptPassphraseTwice,
675                                passphrase):
676                    if len(passphrase) > passphraseMaxLen:
677                        log.error('Passphrase length %d is greater than the '
678                                  'maximum length allowed %d',
679                                  len(passphrase), passphraseMaxLen)
680                        return ''
681                       
682                    return passphrase
683                   
684                if keyFilePassphrase is not None:
685                    context.set_passwd_cb(pwdCallback, keyFilePassphrase)
686                   
687                context.use_privatekey_file(keyFile)
688            except Exception:
689                raise MyProxyClientConfigError("Loading certificate "
690                                               "and private key for SSL "
691                                               "connection [also check CA "
692                                               "certificate settings]: %s" % 
693                                               traceback.format_exc()) 
694           
695        # Verify peer's (MyProxy server) certificate
696        context.set_verify(SSL.VERIFY_PEER|SSL.VERIFY_FAIL_IF_NO_PEER_CERT, 
697                           self.__serverSSLCertVerify) 
698       
699           
700        # Disable for compatibility with myproxy server (er, globus)
701        # globus doesn't handle this case, apparently, and instead
702        # chokes in proxy delegation code
703        context.set_options(SSL.OP_DONT_INSERT_EMPTY_FRAGMENTS)
704       
705        # connect to myproxy server
706        conn = SSL.Connection(context, socket.socket())
707       
708        return conn
709       
710    def _createKeyPair(self, nBitsForKey=PRIKEY_NBITS):
711        """Generate key pair and return as PEM encoded string
712        @type nBitsForKey: int
713        @param nBitsForKey: number of bits for private key generation -
714        default is 2048
715        @rtype: string
716        @return: public/private key pair
717        """
718        keyPair = crypto.PKey()
719        keyPair.generate_key(crypto.TYPE_RSA, nBitsForKey)
720       
721        return keyPair
722           
723    def _createCertReq(self, CN, keyPair, messageDigest=MESSAGE_DIGEST_TYPE):
724        """Create a certificate request.
725       
726        @type CN: basestring
727        @param CN: Common Name for certificate - effectively the same as the
728        username for the MyProxy credential
729        @type keyPair: string/None
730        @param keyPair: public/private key pair
731        @type messageDigest: basestring
732        @param messageDigest: message digest type - default is MD5
733        @rtype: tuple
734        @return certificate request PEM text and private key PEM text
735        """
736       
737        # Check all required certifcate request DN parameters are set               
738        # Create certificate request
739        certReq = crypto.X509Req()
740       
741        # Create public key object
742        certReq.set_pubkey(keyPair)
743       
744        # Add the public key to the request
745        certReq.sign(keyPair, messageDigest)
746       
747        derCertReq = crypto.dump_certificate_request(crypto.FILETYPE_ASN1, 
748                                                     certReq)
749
750        return derCertReq
751   
752    def _deserializeResponse(self, msg, *fieldNames):
753        """
754        Deserialize a MyProxy server response
755       
756        @param msg: string response message from MyProxy server
757        @return: tuple of integer response and errorTxt string (if any) and all
758        the fields parsed.  fields is a list of two element, field name, field
759        value tuples.
760        @rtype: tuple
761        """ 
762        lines = msg.split('\n')
763        fields = [tuple(line.split('=', 1)) for line in lines][:-1]
764       
765        # get response value
766        respCode = [int(v) for k, v in fields if k == 'RESPONSE'][0]
767
768        # get error text
769        errorTxt = os.linesep.join([v for k, v in fields if k == 'ERROR'])
770       
771        # Check for custom fields requested by caller to this method
772        if fieldNames:
773            fieldsDict = {}
774            for k, v in fields:
775                names = [name for name in fieldNames if k.startswith(name)]
776                if len(names) == 0:
777                    continue
778                else:
779                    if v.isdigit():
780                        fieldsDict[k] = int(v)
781                    else:
782                        fieldsDict[k] = v
783             
784            # Return additional dict item in tuple 
785            return respCode, errorTxt, fieldsDict
786        else:
787            return respCode, errorTxt   
788 
789    def _deserializeCerts(self, inputDat):
790        """Unpack certificates returned from a get delegation call to the
791        server
792       
793        @param inputDat: string containing the proxy cert and private key
794        and signing cert all in DER format
795       
796        @return list containing the equivalent to the input in PEM format"""
797        pemCerts = []
798        dat = inputDat
799       
800        while dat:
801            # find start of cert, get length       
802            ind = dat.find('\x30\x82')
803            if ind < 0:
804                break
805               
806            len = 256*ord(dat[ind+2]) + ord(dat[ind+3])
807   
808            # extract der-format cert, and convert to pem
809            derCert = dat[ind:ind+len+4]
810            x509Cert = crypto.load_certificate(crypto.FILETYPE_ASN1, derCert)
811            pemCert = crypto.dump_certificate(crypto.FILETYPE_PEM, x509Cert)       
812            pemCerts.append(pemCert)
813   
814            # trim cert from data
815            dat = dat[ind + len + 4:]
816           
817        return pemCerts
818       
819    @classmethod
820    def writeProxyFile(cls, proxyCert, proxyPriKey, userX509Cert, 
821                       filePath=None):
822        """Write out proxy cert to file in the same way as myproxy-logon -
823        proxy cert, private key, user cert.  Nb. output from logon can be
824        passed direct into this method
825       
826        @type proxyCert: string
827        @param proxyCert: proxy certificate
828        @type proxyPriKey: string
829        @param proxyPriKey: private key for proxy
830        @type userX509Cert: string
831        @param userX509Cert: user certificate which issued the proxy
832        @type filePath: string
833        @param filePath: set to override the default filePath"""
834       
835        if filePath is None:
836            filePath = MyProxyClient.DEF_PROXY_FILEPATH
837           
838        if filePath is None:
839            MyProxyClientConfigError("Error setting proxy file path - invalid "
840                                     "platform?")
841       
842        outStr = proxyCert + proxyPriKey + userX509Cert       
843        open(MyProxyClient.DEF_PROXY_FILEPATH, 'w').write(outStr)
844        try:
845            # Make sure permissions are set correctly
846            os.chmod(MyProxyClient.DEF_PROXY_FILEPATH, 
847                     MyProxyClient.PROXY_FILE_PERMISSIONS)
848        except Exception, e:
849            # Don't leave the file lying around if couldn't change it's
850            # permissions
851            os.unlink(MyProxyClient.DEF_PROXY_FILEPATH)
852           
853            log.error('Unable to set %o permissions for proxy file "%s": %s'% 
854                      (MyProxyClient.PROXY_FILE_PERMISSIONS,
855                       MyProxyClient.DEF_PROXY_FILEPATH, e))
856            raise
857
858    @classmethod
859    def readProxyFile(cls, filePath=None):
860        """Read proxy cert file following the format used by myproxy-logon -
861        proxy, cert, private key, user cert.
862       
863        @rtype: tuple
864        @return: tuple containing proxy cert, private key, user cert"""
865        if filePath is None:
866            filePath = MyProxyClient.DEF_PROXY_FILEPATH
867           
868        if filePath is None:
869            MyProxyClientConfigError("Error setting proxy file path - invalid "
870                                     "platform?")
871               
872        proxy = open(MyProxyClient.DEF_PROXY_FILEPATH).read()
873       
874        # Split certs and key into separate tuple items
875        return tuple(['-----BEGIN'+i for i in proxy.split('-----BEGIN')[1:]])
876
877    def put(self,
878            username,
879            passphrase,
880            userCertFile,
881            userKeyFile, 
882            lifetime=None,
883            sslCertFile=None,
884            sslKeyFile=None,
885            sslKeyFilePassphrase=None):
886        """Store a proxy credential on the server
887       
888        Unfortunately this method is not implemented as it requires the creation
889        of a proxy certificate by the client but PyOpenSSL doesn't currently
890        support the required proxyCertInfo X.509 certificate extension
891       
892        @raise NotImplementedError: see above
893       
894        @type username: string
895        @param username: username selected for new credential
896        @type passphrase: string
897        @param passphrase: pass-phrase for new credential.  This will be used
898        by the server to authenticate later requests.  IT must be at least
899        6 characters.  The server may impose other restrictions too depending
900        on its configuration.
901        @type certFile: string
902        @param certFile: user's X.509 proxy certificate in PEM format
903        @type keyFile: string
904        @param keyFile: equivalent private key file in PEM format
905        @type sslCertFile: string
906        @param sslCertFile: certificate used for client authentication with
907        the MyProxy server SSL connection.  If not set,
908        this argument defaults to $GLOBUS_LOCATION/etc/hostcert.pem
909        @type sslKeyFile: string
910        @param sslKeyFile: corresponding private key file.  See explanation
911        for sslCertFile
912        @type sslKeyFilePassphrase: string
913        @param sslKeyFilePassphrase: passphrase for sslKeyFile.  Omit if the
914        private key is not password protected. 
915        @type lifetime: int / None
916        @param lifetime: the maximum lifetime allowed for retrieved proxy
917        credentials in seconds. defaults to proxyCertMaxLifetime attribute value
918        """
919        raise NotImplementedError('put method is not currently implemented.  '
920                                  'It requires the creation of a proxy '
921                                  'certificate by the client but PyOpenSSL '
922                                  'doesn\'t currently support the required '
923                                  'proxyCertInfo X.509 certificate extension.')
924
925    def info(self,
926             username, 
927             sslCertFile=None,
928             sslKeyFile=None,
929             sslKeyFilePassphrase=None):
930        """return True/False whether credentials exist on the server for a
931        given username
932       
933        @raise MyProxyClientGetError:
934        @raise MyProxyClientRetrieveError:
935       
936        @type username: string
937        @param username: username selected for credential
938        @type sslCertFile: string
939        @param sslCertFile: certificate used for client authentication with
940        the MyProxy server SSL connection.  This ID will be set as the owner
941        of the stored credentials.  Only the owner can later remove
942        credentials with myproxy-destroy or the destroy method.  If not set,
943        this argument defaults to $GLOBUS_LOCATION/etc/hostcert.pem
944        @type sslKeyFile: string
945        @param sslKeyFile: corresponding private key file.  See explanation
946        for sslCertFile
947        @type sslKeyFilePassphrase: string
948        @param sslKeyFilePassphrase: passphrase for sslKeyFile.  Omit if the
949        private key is not password protected.
950        """
951        globusLoc = os.environ.get('GLOBUS_LOCATION')
952        if not sslCertFile:
953            if globusLoc:
954                sslCertFile = os.path.join(globusLoc, 
955                                            *MyProxyClient._hostCertSubDirPath)
956                sslKeyFile = os.path.join(globusLoc, 
957                                            *MyProxyClient._hostKeySubDirPath)
958            else:
959                raise MyProxyClientError(
960            "No client authentication cert. and private key file were given")
961
962        # Set-up SSL connection
963        conn = self._initConnection(certFile=sslCertFile,
964                                    keyFile=sslKeyFile,
965                                    keyFilePassphrase=sslKeyFilePassphrase)
966       
967        conn.connect((self.hostname, self.port))
968       
969        # send globus compatibility stuff
970        conn.write('0')
971   
972        # send info command - ensure conversion from unicode before writing
973        cmd = MyProxyClient.INFO_CMD % username
974        conn.write(str(cmd))
975   
976        # process server response
977        dat = conn.recv(MyProxyClient.SERVER_RESP_BLK_SIZE)
978         
979        # Pass in the names of fields to return in the dictionary 'field'
980        respCode, errorTxt, field = self._deserializeResponse(dat, 
981                                                              'CRED_START_TIME', 
982                                                              'CRED_END_TIME', 
983                                                              'CRED_OWNER')
984
985        return not bool(respCode), errorTxt, field
986
987    def changePassphrase(self,
988                         username, 
989                         passphrase,
990                         newPassphrase,
991                         sslCertFile=None,
992                         sslKeyFile=None,
993                         sslKeyFilePassphrase=None):
994        """change pass-phrase protecting the credentials for a given username
995       
996        @raise MyProxyClientGetError:
997        @raise MyProxyClientRetrieveError:
998       
999        @param username: username of credential
1000        @param passphrase: existing pass-phrase for credential
1001        @param newPassphrase: new pass-phrase to replace the existing one.
1002        @param sslCertFile: certificate used for client authentication with
1003        the MyProxy server SSL connection.  This ID will be set as the owner
1004        of the stored credentials.  Only the owner can later remove
1005        credentials with myproxy-destroy or the destroy method.  If not set,
1006        this argument defaults to $GLOBUS_LOCATION/etc/hostcert.pem
1007        @param sslKeyFile: corresponding private key file.  See explanation
1008        for sslCertFile
1009        @param sslKeyFilePassphrase: passphrase for sslKeyFile.  Omit if the
1010        private key is not password protected. 
1011        @return none
1012        """
1013        globusLoc = os.environ.get('GLOBUS_LOCATION')
1014        if not sslCertFile or not sslKeyFile:
1015            if globusLoc:
1016                sslCertFile = os.path.join(globusLoc, 
1017                                         *MyProxyClient._hostCertSubDirPath)
1018                sslKeyFile = os.path.join(globusLoc, 
1019                                         *MyProxyClient._hostKeySubDirPath)
1020            else:
1021                raise MyProxyClientError(
1022            "No client authentication cert. and private key file were given")
1023       
1024        # Set-up SSL connection
1025        conn = self._initConnection(certFile=sslCertFile,
1026                                    keyFile=sslKeyFile,
1027                                    keyFilePassphrase=sslKeyFilePassphrase)
1028
1029        conn.connect((self.hostname, self.port))
1030       
1031        # send globus compatibility stuff
1032        conn.write('0')
1033   
1034        # send command - ensure conversion from unicode before writing
1035        cmd = MyProxyClient.CHANGE_PASSPHRASE_CMD % (username, 
1036                                                     passphrase,
1037                                                     newPassphrase)
1038        conn.write(str(cmd))
1039   
1040        # process server response
1041        dat = conn.recv(MyProxyClient.SERVER_RESP_BLK_SIZE)
1042           
1043        respCode, errorTxt = self._deserializeResponse(dat)
1044        if respCode:
1045            raise MyProxyClientGetError(errorTxt)
1046
1047    def destroy(self,
1048                username, 
1049                sslCertFile=None,
1050                sslKeyFile=None,
1051                sslKeyFilePassphrase=None):
1052        """destroy credentials from the server for a given username
1053       
1054        @raise MyProxyClientGetError:
1055        @raise MyProxyClientRetrieveError:
1056       
1057        @param username: username selected for credential
1058        @param sslCertFile: certificate used for client authentication with
1059        the MyProxy server SSL connection.  This ID will be set as the owner
1060        of the stored credentials.  Only the owner can later remove
1061        credentials with myproxy-destroy or the destroy method.  If not set,
1062        this argument defaults to $GLOBUS_LOCATION/etc/hostcert.pem
1063        @param sslKeyFile: corresponding private key file.  See explanation
1064        for sslCertFile
1065        @param sslKeyFilePassphrase: passphrase for sslKeyFile.  Omit if the
1066        private key is not password protected. 
1067        @return none
1068        """
1069        globusLoc = os.environ.get('GLOBUS_LOCATION')
1070        if not sslCertFile or not sslKeyFile:
1071            if globusLoc:
1072                sslCertFile = os.path.join(globusLoc, 
1073                                         *MyProxyClient._hostCertSubDirPath)
1074                sslKeyFile = os.path.join(globusLoc, 
1075                                         *MyProxyClient._hostKeySubDirPath)
1076            else:
1077                raise MyProxyClientError(
1078            "No client authentication cert. and private key file were given")
1079       
1080        # Set-up SSL connection
1081        conn = self._initConnection(certFile=sslCertFile,
1082                                    keyFile=sslKeyFile,
1083                                    keyFilePassphrase=sslKeyFilePassphrase)
1084
1085        conn.connect((self.hostname, self.port))
1086       
1087        # send globus compatibility stuff
1088        conn.write('0')
1089   
1090        # send destroy command - ensure conversion from unicode before writing
1091        cmd = MyProxyClient.DESTROY_CMD % username
1092        conn.write(str(cmd))
1093   
1094        # process server response
1095        dat = conn.recv(MyProxyClient.SERVER_RESP_BLK_SIZE)
1096           
1097        respCode, errorTxt = self._deserializeResponse(dat)
1098        if respCode:
1099            raise MyProxyClientGetError(errorTxt)
1100
1101    def store(self,
1102              username,
1103              passphrase, 
1104              certFile,
1105              keyFile,
1106              sslCertFile=None,
1107              sslKeyFile=None,
1108              sslKeyFilePassphrase=None,
1109              lifetime=None,
1110              force=True):
1111        """Upload credentials to the server
1112       
1113        @raise MyProxyClientGetError:
1114        @raise MyProxyClientRetrieveError:
1115       
1116        @type username: string
1117        @param username: username selected for new credential
1118        @type passphrase: string
1119        @param passphrase: pass-phrase for new credential.  This is the pass
1120        phrase which protects keyfile.
1121        @type certFile: string
1122        @param certFile: user's X.509 certificate in PEM format
1123        @type keyFile: string
1124        @param keyFile: equivalent private key file in PEM format
1125        @type sslCertFile: string
1126        @param sslCertFile: certificate used for client authentication with
1127        the MyProxy server SSL connection.  This ID will be set as the owner
1128        of the stored credentials.  Only the owner can later remove
1129        credentials with myproxy-destroy or the destroy method.  If not set,
1130        this argument defaults to $GLOBUS_LOCATION/etc/hostcert.pem or if this
1131        is not set, certFile
1132        @type sslKeyFile: string
1133        @param sslKeyFile: corresponding private key file.  See explanation
1134        for sslCertFile
1135        @type sslKeyFilePassphrase: string
1136        @param sslKeyFilePassphrase: passphrase for sslKeyFile.  Omit if the
1137        private key is not password protected.  Nb. keyFile is expected to
1138        be passphrase protected as this will be the passphrase used for
1139        logon / getDelegation.
1140        @type Force: bool
1141        @param force: set to True to overwrite any existing creds with the
1142        same username.  If, force=False a check is made with a call to info.
1143        If creds already, exist exit without proceeding
1144        """
1145       
1146        lifetime = lifetime or self.proxyCertMaxLifetime
1147
1148        # Inputs must be string type otherwise server will reject the request
1149        if isinstance(username, unicode):
1150            username = str(username)
1151           
1152        if isinstance(passphrase, unicode):
1153            passphrase = str(passphrase)
1154       
1155        globusLoc = os.environ.get('GLOBUS_LOCATION')
1156        if not sslCertFile or not sslKeyFile:
1157            if globusLoc:
1158                sslCertFile = os.path.join(globusLoc, 
1159                                         *MyProxyClient._hostCertSubDirPath)
1160                sslKeyFile = os.path.join(globusLoc, 
1161                                         *MyProxyClient._hostKeySubDirPath)
1162            else:
1163                # Default so that the owner is the same as the ID of the
1164                # credentials to be uploaded.
1165                sslCertFile = certFile
1166                sslKeyFile = keyFile
1167                sslKeyFilePassphrase = passphrase
1168               
1169        if not force:
1170            # Check credentials don't already exist
1171            if self.info(username,
1172                         sslCertFile=sslCertFile,
1173                         sslKeyFile=sslKeyFile,
1174                         sslKeyFilePassphrase=sslKeyFilePassphrase)[0]:
1175                raise MyProxyCredentialsAlreadyExist(
1176                        "Credentials already exist for user: %s" % username)
1177
1178        # Set up SSL connection
1179        conn = self._initConnection(certFile=sslCertFile,
1180                                    keyFile=sslKeyFile,
1181                                    keyFilePassphrase=sslKeyFilePassphrase)
1182       
1183        conn.connect((self.hostname, self.port))
1184       
1185        # send globus compatibility stuff
1186        conn.write('0')
1187   
1188        # send store command - ensure conversion from unicode before writing
1189        cmd = MyProxyClient.STORE_CMD % (username, lifetime)
1190        conn.write(str(cmd))
1191   
1192        # process server response
1193        dat = conn.recv(MyProxyClient.SERVER_RESP_BLK_SIZE)
1194           
1195        respCode, errorTxt = self._deserializeResponse(dat)
1196        if respCode:
1197            raise MyProxyClientGetError(errorTxt)
1198       
1199        # Send certificate and private key
1200        certTxt = open(certFile).read()
1201        keyTxt = open(keyFile).read()
1202       
1203        conn.send(certTxt + keyTxt)
1204   
1205   
1206        # process server response
1207        resp = conn.recv(MyProxyClient.SERVER_RESP_BLK_SIZE)
1208        respCode, errorTxt = self._deserializeResponse(resp)
1209        if respCode:
1210            raise MyProxyClientRetrieveError(errorTxt)
1211       
1212    def logon(self, username, passphrase, lifetime=None, keyPair=None, 
1213              certReq=None, nBitsForKey=PRIKEY_NBITS):
1214        """Retrieve a proxy credential from a MyProxy server
1215       
1216        Exceptions:  MyProxyClientGetError, MyProxyClientRetrieveError
1217       
1218        @type username: basestring
1219        @param username: username of credential
1220       
1221        @type passphrase: basestring
1222        @param passphrase: pass-phrase for private key of credential held on
1223        server
1224       
1225        @type lifetime: int
1226        @param lifetime: lifetime for generated certificate
1227       
1228        @rtype: tuple
1229        @return credentials as strings in PEM format: the
1230        user certificate, it's private key and the issuing certificate.  The
1231        issuing certificate is only set if the user certificate is a proxy
1232        """
1233       
1234        lifetime = lifetime or self.proxyCertLifetime
1235
1236        # Certificate request may be passed as an input but if not generate it
1237        # here request here
1238        if certReq is None:
1239            # If no key pair was passed, generate here
1240            if keyPair is None:
1241                keyPair = self._createKeyPair(nBitsForKey=nBitsForKey)
1242               
1243            certReq = self._createCertReq(username, keyPair)
1244
1245        if keyPair is not None: 
1246            pemKeyPair = crypto.dump_privatekey(crypto.FILETYPE_PEM, keyPair)
1247       
1248        # Set-up SSL connection
1249        conn = self._initConnection()
1250        conn.connect((self.hostname, self.port))
1251       
1252        # send globus compatibility stuff
1253        conn.write('0')
1254   
1255        # send get command - ensure conversion from unicode before writing
1256        cmd = MyProxyClient.GET_CMD % (username, passphrase, lifetime)
1257        conn.write(str(cmd))
1258   
1259        # process server response
1260        dat = conn.recv(MyProxyClient.SERVER_RESP_BLK_SIZE)
1261        respCode, errorTxt = self._deserializeResponse(dat)
1262        if respCode:
1263            raise MyProxyClientGetError(errorTxt)
1264       
1265        # Send certificate request
1266        conn.send(certReq)
1267   
1268        # process certificates
1269        # - 1st byte , number of certs
1270        dat = conn.recv(1)
1271        nCerts = ord(dat[0])
1272       
1273        # - n certs
1274        dat = conn.recv(MyProxyClient.SERVER_RESP_BLK_SIZE)
1275   
1276        # process server response
1277        resp = conn.recv(MyProxyClient.SERVER_RESP_BLK_SIZE)
1278        respCode, errorTxt = self._deserializeResponse(resp)
1279        if respCode:
1280            raise MyProxyClientRetrieveError(errorTxt)
1281   
1282        # deserialize certs from received cert data
1283        pemCerts = self._deserializeCerts(dat)
1284        if len(pemCerts) != nCerts:
1285            MyProxyClientRetrieveError("%d certs expected, %d received" % 
1286                                       (nCerts, len(pemCerts)))
1287   
1288        if keyPair is not None:
1289            # Return certs and private key
1290            # - proxy or dynamically issued certificate (MyProxy CA mode)
1291            # - private key
1292            # - rest of cert chain if proxy cert issued
1293            creds = [pemCerts[0], pemKeyPair]
1294            creds.extend(pemCerts[1:])
1295        else:
1296            # Key generated externally - return certificate chain only
1297            creds = pemCerts
1298
1299       
1300        return tuple(creds)
1301
1302    def getDelegation(self, *arg, **kw):
1303        """Retrieve proxy cert for user - same as logon"""
1304        return self.logon(*arg, **kw)
1305   
1306    def getTrustRoots(self, username='', passphrase=''):
1307        """Get trust roots for the given MyProxy server
1308       
1309        @type username: basestring
1310        @param username: username (optional)
1311       
1312        @type passphrase: basestring
1313        @param passphrase: pass-phrase (optional)
1314        server
1315       
1316        @return: trust root files as a dictionary keyed by file name with each
1317        item value set to the file contents
1318        @rtype: dict
1319        """
1320        # Set-up SSL connection
1321        conn = self._initConnection()
1322        conn.connect((self.hostname, self.port))
1323       
1324        # send globus compatibility stuff
1325        conn.write('0')
1326   
1327        # send get command - ensure conversion from unicode before writing
1328        cmd = MyProxyClient.GET_TRUST_ROOTS_CMD % (username, passphrase)
1329        conn.write(str(cmd))
1330   
1331        # process server response chunks until all consumed
1332        dat = ''
1333        try:
1334            for tries in range(MyProxyClient.MAX_RECV_TRIES):
1335                dat += conn.recv(MyProxyClient.SERVER_RESP_BLK_SIZE)
1336        except SSL.SysCallError:
1337            # Expect this exception when response content exhausted
1338            pass
1339       
1340        # Precaution
1341        if tries == MyProxyClient.MAX_RECV_TRIES:
1342            log.warning('Maximum %d tries reached for getTrustRoots response '
1343                        'block retrieval with block size %d', 
1344                        MyProxyClient.MAX_RECV_TRIES,
1345                        MyProxyClient.SERVER_RESP_BLK_SIZE)
1346           
1347        respCode, errorTxt, fileData = self._deserializeResponse(dat, 
1348                                                                'TRUSTED_CERTS',
1349                                                                'FILEDATA_')
1350        if respCode:
1351            raise MyProxyClientGetTrustRootsError(errorTxt)
1352       
1353        filesDict = dict([(k, base64.b64decode(v)) 
1354                          for k, v in fileData.items() if k != 'TRUSTED_CERTS'])
1355       
1356        return filesDict
1357       
Note: See TracBrowser for help on using the repository browser.