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)
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