예제 #1
0
파일: client.py 프로젝트: stevencox/grayson
    def __init__(self, cfgFilePath=None, **prop):
        """Make any initial settings for client connections to MyProxy
        
        Settings are held in a dictionary which can be set from **prop,
        a call to setProperties() or by passing settings in an XML file
        given by cfgFilePath
        
        @param cfgFilePath:   set properties via a configuration file
        @param **prop:         set properties via keywords - see 
        propertyDefaults class variable for a list of these
        """
        
        # Default settings.  Nb. '_' - override property methods in order to 
        # set defaults
        for opt, val in MyProxyClient.propertyDefaults.items():
            setattr(self, '_'+opt, val)

        # Configuration file used to get default subject when generating a
        # new proxy certificate request
        self._openSSLConfig = OpenSSLConfig()
        
        # Server host name - take from environment variable if available
        self.hostname = os.environ.get('MYPROXY_SERVER',
                                    MyProxyClient.propertyDefaults['hostname'])
            
        # ... and port number
        self.port = int(os.environ.get('MYPROXY_SERVER_PORT', 
                                       MyProxyClient.propertyDefaults['port']))

        # Server Distinguished Name
        self.serverDN = os.environ.get('MYPROXY_SERVER_DN',
                                    MyProxyClient.propertyDefaults['serverDN'])
            
        # keyword settings
        for opt, val in prop.items():
            setattr(self, opt, val)
        
        # If properties file is set any parameters settings in file will
        # override those set by input keyword
        if cfgFilePath is not None:
            self.parseConfig(cfg=cfgFilePath)
예제 #2
0
파일: client.py 프로젝트: stevencox/grayson
class MyProxyClient(object):
    """MyProxy client interface 
    
    Based on protocol definitions in: 
    
    http://grid.ncsa.uiuc.edu/myproxy/protocol/
    
    @type getCmd: string
    @cvar getCmd: get command string
    
    @type putCmd: string
    @cvar putCmd: put command string
    
    @type infoCmd: string
    @cvar infoCmd: info command string
    
    @type destroyCmd: string
    @cvar destroyCmd: destroy command string
    
    @type changePassphraseCmd: string
    @cvar changePassphraseCmd: command string to change cred pass-phrase
    
    @type storeCmd: string
    @cvar storeCmd: store command string
    
    @type _hostCertSubDirPath: string
    @cvar _hostCertSubDirPath: sub-directory path host certificate (as tuple)
    
    @type _hostKeySubDirPath: string
    @cvar _hostKeySubDirPath: sub-directory path to host key (as tuple)
    
    @type propertyDefaults: tuple
    @cvar propertyDefaults: sets permissable element names for MyProxy config 
    file
    """
      
    getCmd="""VERSION=MYPROXYv2
COMMAND=0
USERNAME=%s
PASSPHRASE=%s
LIFETIME=%d"""

    putCmd = """VERSION=MYPROXYv2
COMMAND=1
USERNAME=%s
PASSPHRASE=%s
LIFETIME=%s"""

    infoCmd="""VERSION=MYPROXYv2
COMMAND=2
USERNAME=%s
PASSPHRASE=PASSPHRASE
LIFETIME=0"""
 
    destroyCmd="""VERSION=MYPROXYv2
COMMAND=3
USERNAME=%s
PASSPHRASE=PASSPHRASE
LIFETIME=0"""

    changePassphraseCmd="""VERSION=MYPROXYv2
 COMMAND=4
 USERNAME=%s
 PASSPHRASE=%s
 NEW_PHRASE=%s
 LIFETIME=0"""
   
    storeCmd="""VERSION=MYPROXYv2
COMMAND=5
USERNAME=%s
PASSPHRASE=
LIFETIME=%d"""

    _hostCertSubDirPath = ('etc', 'hostcert.pem')
    _hostKeySubDirPath = ('etc', 'hostkey.pem')
    
    # Work out default location of proxy file if it exists.  This is set if a
    # call has been made previously to logon / get-delegation
    defProxyFile = sys.platform == 'win32' and 'proxy' or \
    sys.platform in ('linux2', 'darwin') and '/tmp/x509up_u%s'%(os.getuid()) \
    or None     
    
    # valid configuration property keywords
    propertyDefaults = {
       'hostname':              'localhost',
       'port':                  7512,
       'serverDN':              '',
       'serverCNPrefix':        '',
       'openSSLConfFilePath':   '',
       'proxyCertMaxLifetime':  43200,
       'proxyCertLifetime':     43200,
       'caCertFilePath':        None,
       'caCertDir':             '/etc/grid-security/certificates',
    }
    
    # Restrict attributes to the above properties, their equivalent 
    # protected values + extra OpenSSL config object.
    __slots__ = propertyDefaults.copy()
    __slots__.update(dict([('_'+k, v) for k,v in propertyDefaults.items()] +
                          [('_openSSLConfig', None),
                           ('_cfg',           None)]
                          )
                     )
        
    def __init__(self, cfgFilePath=None, **prop):
        """Make any initial settings for client connections to MyProxy
        
        Settings are held in a dictionary which can be set from **prop,
        a call to setProperties() or by passing settings in an XML file
        given by cfgFilePath
        
        @param cfgFilePath:   set properties via a configuration file
        @param **prop:         set properties via keywords - see 
        propertyDefaults class variable for a list of these
        """
        
        # Default settings.  Nb. '_' - override property methods in order to 
        # set defaults
        for opt, val in MyProxyClient.propertyDefaults.items():
            setattr(self, '_'+opt, val)

        # Configuration file used to get default subject when generating a
        # new proxy certificate request
        self._openSSLConfig = OpenSSLConfig()
        
        # Server host name - take from environment variable if available
        self.hostname = os.environ.get('MYPROXY_SERVER',
                                    MyProxyClient.propertyDefaults['hostname'])
            
        # ... and port number
        self.port = int(os.environ.get('MYPROXY_SERVER_PORT', 
                                       MyProxyClient.propertyDefaults['port']))

        # Server Distinguished Name
        self.serverDN = os.environ.get('MYPROXY_SERVER_DN',
                                    MyProxyClient.propertyDefaults['serverDN'])
            
        # keyword settings
        for opt, val in prop.items():
            setattr(self, opt, val)
        
        # If properties file is set any parameters settings in file will
        # override those set by input keyword
        if cfgFilePath is not None:
            self.parseConfig(cfg=cfgFilePath)


    def parseConfig(self, cfg, section='DEFAULT'):
        '''Extract parameters from _cfg config object'''
        
        if isinstance(cfg, basestring):
            cfgFilePath = os.path.expandvars(cfg)
            self._cfg = CaseSensitiveConfigParser()
            self._cfg.read(cfgFilePath)
        else:
            cfgFilePath = None
            self._cfg = cfg
        
        for key, val in self._cfg.items(section):
            setattr(self, key, val)
        
    # Get/Set Property methods
    def _getHostname(self):
        return self._hostname
    
    def _setHostname(self, val):
        if not isinstance(val, basestring):
            raise AttributeError("Expecting string type for hostname "
                                 "attribute")
        self._hostname = val
        
    hostname = property(fget=_getHostname,
                        fset=_setHostname,
                        doc="hostname of MyProxy server")
    
    def _getPort(self):
        return self._port
    
    def _setPort(self, val):
        if isinstance(val, basestring):
            self._port = int(val)
        elif isinstance(val, int):
            self._port = val
        else:
            raise AttributeError("Expecting int type for port attribute")
    
    port = property(fget=_getPort,
                    fset=_setPort,
                    doc="Port number for MyProxy server")
    
    def _getServerDN(self):
        return self._serverDN
    
    def _setServerDN(self, val):
        if not isinstance(val, basestring):
            raise AttributeError("Expecting string type for serverDN "
                                 "attribute")
        self._serverDN = val
    
    serverDN = property(fget=_getServerDN,
                        fset=_setServerDN,
                        doc="Distinguished Name for MyProxy Server "
                            "Certificate")
    
    def _getServerCNPrefix(self):
        return self._serverCNPrefix
    
    def _setServerCNPrefix(self, val):
        if not isinstance(val, basestring):
            raise AttributeError("Expecting string type for serverCNPrefix "
                                 "attribute")
        self._serverCNPrefix = val
    
    serverCNPrefix = property(fget=_getServerCNPrefix,
                              fset=_setServerCNPrefix,
                              doc="Prefix if any for Server Certificate DN "
                                  "Common Name e.g. 'host/'")
    
    def _getOpenSSLConfFilePath(self):
        return self._openSSLConfFilePath
    
    def _setOpenSSLConfFilePath(self, val):
        if not isinstance(val, basestring):
            raise AttributeError("Expecting string type for "
                                 "openSSLConfFilePath attribute")
        self._openSSLConfFilePath = os.path.expandvars(val)
        self._openSSLConfig.filePath = self._openSSLConfFilePath
        self._openSSLConfig.read()  
    
    openSSLConfFilePath = property(fget=_getOpenSSLConfFilePath,
                                   fset=_setOpenSSLConfFilePath,
                                   doc="file path for OpenSSL config file")
    
    def _getProxyCertMaxLifetime(self):
        return self._proxyCertMaxLifetime
    
    def _setProxyCertMaxLifetime(self, val):
        if isinstance(val, basestring):
            self._proxyCertMaxLifetime = int(val)
            
        elif isinstance(val, int):
            self._proxyCertMaxLifetime = val
        else:
            raise AttributeError("Expecting int type for proxyCertMaxLifetime "
                                 "attribute")
    
    proxyCertMaxLifetime = property(fget=_getProxyCertMaxLifetime,
                                    fset=_setProxyCertMaxLifetime,
                                    doc="Default max. lifetime allowed for "
                                        "Proxy Certificate retrieved - used "
                                        "by store method")
    
    def _getProxyCertLifetime(self):
        return self._proxyCertLifetime
    
    def _setProxyCertLifetime(self, val):
        if isinstance(val, basestring):
            self._proxyCertLifetime = int(val)
        elif isinstance(val, int):
            self._proxyCertLifetime = val
        else:
            raise AttributeError("Expecting int type for proxyCertLifetime "
                                 "attribute")
    
    proxyCertLifetime = property(fget=_getProxyCertLifetime,
                                 fset=_setProxyCertLifetime,
                                 doc="Default proxy cert. lifetime used in "
                                     "logon request")
    
    def _getCACertFilePath(self):
        return self._caCertFilePath
    
    def _setCACertFilePath(self, val):
        '''@type val: basestring
        @param val: file path for CA certificate to be used to verify 
        MyProxy server certificate'''
        
        if isinstance(val, basestring):
            if val == '':
                self._caCertFilePath = None
            else:
                self._caCertFilePath = os.path.expandvars(val)
                
        elif isinstance(val, None):
            raise AttributeError("Expecting string type for caCertFilePath "
                                 "attribute")       
        
    caCertFilePath = property(fget=_getCACertFilePath,
                              fset=_setCACertFilePath,
                              doc="CA certificate file path - MyProxy server "
                                  "certificate must validate against it and/"
                                  "or any present in caCertDir")

    def _getCACertDir(self):
        return self._caCertDir

    def _setCACertDir(self, val):
        '''Specify a directory containing PEM encoded CA certs. used for 
        validation of MyProxy server certificate.
        
        Set to None to make M2Crypto.SSL.Context.load_verify_locations ignore
        this parameter
        
        @type val: basestring/None
        @param val: directory path'''
        
        if isinstance(val, basestring):
            if val == '':
                self._caCertDir = None
            else:
                self._caCertDir = os.path.expandvars(val)
                
        elif isinstance(val, None):
            self._caCertDir = val    
        else:
            raise AttributeError("Expecting string or None type for caCertDir "
                                 "attribute")
        
    caCertDir = property(fget=_getCACertDir,
                         fset=_setCACertDir,
                         doc="directory containing PEM encoded CA "
                             "certificates.  Use along with caCertFilePath "
                             "setting to validate MyProxy server certificate")


    def _getOpenSSLConfig(self):
        "Get OpenSSLConfig object property method"
        return self._openSSLConfig
    
    openSSLConfig = property(fget=_getOpenSSLConfig,
                             doc="OpenSSLConfig object")

          
    def _initConnection(self, 
                        ownerCertFile=None, 
                        ownerKeyFile=None,
                        ownerPassphrase=None):
        """Initialise connection setting up SSL context and client and
        server side identity checks
        
        @type ownerCertFile: basestring
        @param ownerCertFile: client certificate and owner of credential
        to be acted on.  Can be a proxy cert + proxy's signing cert.  Cert
        and private key are not necessary for getDelegation / logon calls
        @type ownerKeyFile: basestring
        @param ownerKeyFile: client private key file
        @type ownerPassphrase: basestring
        @param ownerPassphrase: pass-phrase protecting private key if set - 
        not needed in the case of a proxy private key
        """

        # Must be version 3 for MyProxy
        context = SSL.Context(protocol='sslv3')

        if self.caCertFilePath or self.caCertDir:
            context.load_verify_locations(cafile=self.caCertFilePath,
                                          capath=self.caCertDir)
                            
            # Stop if peer's certificate can't be verified
            context.set_allow_unknown_ca(False)
        else:
            context.set_allow_unknown_ca(True)

        from arcs.gsi import Certificate
        if ownerCertFile:
            try:
                if isinstance(ownerCertFile, Certificate):
                    m2.ssl_ctx_passphrase_callback(context.ctx, lambda *ar: ownerPassphrase)
                    m2.ssl_ctx_use_x509(context.ctx, ownerCertFile._certificate.x509)
                    m2.ssl_ctx_use_rsa_privkey(context.ctx, ownerKeyFile.rsa)
                    if not m2.ssl_ctx_check_privkey(context.ctx):
                        raise ValueError, 'public/private key mismatch'
                else:
                    context.load_cert_chain(ownerCertFile,
                                        keyfile=ownerKeyFile,
                                        callback=lambda *ar, **kw: ownerPassphrase)
            except Exception, e:
                raise MyProxyClientConfigError("Loading certificate "
                                               "and private key for SSL "
                                               "connection [also check CA "
                                               "certificate settings]: %s" % e) 
            
            # Verify peer's certificate
            context.set_verify(SSL.verify_peer, 1) 
        
           
        # Disable for compatibility with myproxy server (er, globus)
        # globus doesn't handle this case, apparently, and instead
        # chokes in proxy delegation code
        context.set_options(m2.SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS)
        
        # connect to myproxy server
        conn = SSL.Connection(context, sock=socket.socket())
        
        # Check server host identity - if host doesn't match use explicit
        # 'serverDN' 
        # host/<hostname> one
        hostCheck = _HostCheck(host=self.hostname,
                               myProxyServerDN=self.serverDN,
                               cnHostPfx=self.serverCNPrefix)
        conn.set_post_connection_check_callback(hostCheck)
        
        return conn