version 1.107.2.7, 2014/07/23 16:57:58
|
version 1.110, 2011/08/11 13:08:59
|
|
|
# include <openssl/err.h> | # include <openssl/err.h> |
# include <openssl/ssl.h> | # include <openssl/ssl.h> |
# include <openssl/rand.h> | # include <openssl/rand.h> |
# include <openssl/tls1.h> |
|
#else | #else |
# define SSL_CTX void | # define SSL_CTX void |
#endif // end of PEGASUS_HAS_SSL | #endif // end of PEGASUS_HAS_SSL |
|
|
// return 1 if revoked, 0 otherwise | // return 1 if revoked, 0 otherwise |
// | // |
int SSLCallback::verificationCRLCallback( | int SSLCallback::verificationCRLCallback( |
int, |
int ok, |
X509_STORE_CTX* ctx, | X509_STORE_CTX* ctx, |
X509_STORE* sslCRLStore) | X509_STORE* sslCRLStore) |
{ | { |
|
|
| |
//check whether the subject's certificate is revoked | //check whether the subject's certificate is revoked |
X509_REVOKED* revokedCert = NULL; | X509_REVOKED* revokedCert = NULL; |
for (int i = 0; i < numRevoked; i++) |
for (int i = 0; i < sk_X509_REVOKED_num(revokedCerts); i++) |
{ | { |
revokedCert = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i); | revokedCert = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i); |
//a matching serial number indicates revocation | //a matching serial number indicates revocation |
|
|
const String& crlPath, | const String& crlPath, |
SSLCertificateVerifyFunction* verifyCert, | SSLCertificateVerifyFunction* verifyCert, |
const String& randomFile, | const String& randomFile, |
const String& cipherSuite, |
const String& cipherSuite) |
const Boolean& sslCompatibility) |
|
{ | { |
PEG_METHOD_ENTER(TRC_SSL, "SSLContextRep::SSLContextRep()"); | PEG_METHOD_ENTER(TRC_SSL, "SSLContextRep::SSLContextRep()"); |
| |
|
|
_crlPath = crlPath; | _crlPath = crlPath; |
_certificateVerifyFunction = verifyCert; | _certificateVerifyFunction = verifyCert; |
_cipherSuite = cipherSuite; | _cipherSuite = cipherSuite; |
_sslCompatibility = sslCompatibility; |
|
// | // |
// If a truststore and/or peer verification function is specified, | // If a truststore and/or peer verification function is specified, |
// enable peer verification | // enable peer verification |
// | // |
_verifyPeer = (trustStore.size() != 0 || verifyCert != NULL); |
_verifyPeer = (trustStore != String::EMPTY || verifyCert != NULL); |
| |
_randomInit(randomFile); | _randomInit(randomFile); |
| |
|
|
_certificateVerifyFunction = sslContextRep._certificateVerifyFunction; | _certificateVerifyFunction = sslContextRep._certificateVerifyFunction; |
_randomFile = sslContextRep._randomFile; | _randomFile = sslContextRep._randomFile; |
_cipherSuite = sslContextRep._cipherSuite; | _cipherSuite = sslContextRep._cipherSuite; |
_sslCompatibility = sslContextRep._sslCompatibility; |
|
_sslContext = _makeSSLContext(); | _sslContext = _makeSSLContext(); |
| |
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
|
|
{ | { |
PEG_METHOD_ENTER(TRC_SSL, "SSLContextRep::_randomInit()"); | PEG_METHOD_ENTER(TRC_SSL, "SSLContextRep::_randomInit()"); |
| |
|
Boolean ret; |
|
int retVal = 0; |
| |
#if defined(PEGASUS_SSL_RANDOMFILE) && !defined(PEGASUS_OS_PASE) | #if defined(PEGASUS_SSL_RANDOMFILE) && !defined(PEGASUS_OS_PASE) |
if ( RAND_status() == 0 ) | if ( RAND_status() == 0 ) |
|
|
// | // |
// Try the given random seed file | // Try the given random seed file |
// | // |
Boolean ret = FileSystem::exists(randomFile); |
ret = FileSystem::exists(randomFile); |
if (ret) | if (ret) |
{ | { |
int retVal = RAND_load_file(randomFile.getCString(), -1); |
retVal = RAND_load_file(randomFile.getCString(), -1); |
if ( retVal < 0 ) | if ( retVal < 0 ) |
{ | { |
PEG_TRACE((TRC_SSL, Tracer::LEVEL1, | PEG_TRACE((TRC_SSL, Tracer::LEVEL1, |
|
|
{ | { |
PEG_METHOD_ENTER(TRC_SSL, "SSLContextRep::_makeSSLContext()"); | PEG_METHOD_ENTER(TRC_SSL, "SSLContextRep::_makeSSLContext()"); |
| |
|
SSL_CTX * sslContext = 0; |
| |
// | // |
// create SSL Context Area | // create SSL Context Area |
// | // |
SSL_CTX *sslContext = NULL; |
|
if (!(sslContext = SSL_CTX_new(SSLv23_method()))) | if (!(sslContext = SSL_CTX_new(SSLv23_method()))) |
{ | { |
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
|
|
throw SSLException(parms); | throw SSLException(parms); |
} | } |
| |
|
|
int options = SSL_OP_ALL; |
|
|
|
|
|
|
|
SSL_CTX_set_options(sslContext, options); |
|
if ( _sslCompatibility == false ) |
|
{ |
|
|
|
#ifdef TLS1_2_VERSION |
|
// Enable only TLSv1.2 and disable all other protocol (SSL v2, SSL v3, |
|
// TLS v1.0, TLSv1.1) |
|
|
|
options = SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_SSLv3; |
|
#else |
|
PEG_METHOD_EXIT(); |
|
MessageLoaderParms parms( |
|
" Common.SSLContext.TLS_1_2_PROTO_NOT_SUPPORTED", |
|
"TLSv1.2 protocol support is not detected on this system. " |
|
" To run in less secured mode, set sslBackwardCompatibility=true" |
|
" in planned config file and start cimserver."); |
|
throw SSLException(parms); |
|
#endif |
|
} |
|
|
|
// sslv2 is off permanently even if sslCompatibility is true |
|
options |= SSL_OP_NO_SSLv2; |
|
SSL_CTX_set_options(sslContext, options); |
|
|
|
#ifdef PEGASUS_SSL_WEAKENCRYPTION | #ifdef PEGASUS_SSL_WEAKENCRYPTION |
if (!(SSL_CTX_set_cipher_list(sslContext, SSL_TXT_EXP40))) | if (!(SSL_CTX_set_cipher_list(sslContext, SSL_TXT_EXP40))) |
{ | { |
SSL_CTX_free(sslContext); |
|
sslContext = NULL; |
|
|
|
MessageLoaderParms parms( | MessageLoaderParms parms( |
"Common.SSLContext.COULD_NOT_SET_CIPHER_LIST", | "Common.SSLContext.COULD_NOT_SET_CIPHER_LIST", |
"Could not set the cipher list"); | "Could not set the cipher list"); |
|
|
} | } |
#endif | #endif |
| |
if (_cipherSuite.size() != 0) |
if (_cipherSuite != String::EMPTY) |
{ | { |
if (!(SSL_CTX_set_cipher_list(sslContext, _cipherSuite.getCString()))) | if (!(SSL_CTX_set_cipher_list(sslContext, _cipherSuite.getCString()))) |
{ | { |
SSL_CTX_free(sslContext); |
|
sslContext = NULL; |
|
|
|
PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL3, | PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL3, |
"---> SSL: Cipher Suite could not be specified"); | "---> SSL: Cipher Suite could not be specified"); |
MessageLoaderParms parms( | MessageLoaderParms parms( |
|
|
throw SSLException(parms); | throw SSLException(parms); |
} | } |
else | else |
{ |
|
PEG_TRACE((TRC_SSL, Tracer::LEVEL3, | PEG_TRACE((TRC_SSL, Tracer::LEVEL3, |
"---> SSL: Cipher suite set to %s", | "---> SSL: Cipher suite set to %s", |
(const char *)_cipherSuite.getCString())); | (const char *)_cipherSuite.getCString())); |
} | } |
} |
|
| |
// | // |
// set overall SSL Context flags | // set overall SSL Context flags |
// | // |
// For OpenSSLversion >1.0.0 use SSL_OP_NO_COMPRESSION to disable the |
|
// compression For TLS 1.2 version, compression does not suffer from |
|
// CRIME attack so don.t disable compression For other OpenSSL versions |
|
// zero out the compression methods. |
|
#ifdef SSL_OP_NO_COMPRESSION |
|
#ifndef TLS1_2_VERSION |
|
SSL_CTX_set_options(sslContext, SSL_OP_NO_COMPRESSION); |
|
#endif |
|
#elif OPENSSL_VERSION_NUMBER >= 0x00908000L |
|
sk_SSL_COMP_zero(SSL_COMP_get_compression_methods()); |
|
#endif |
|
SSL_CTX_set_quiet_shutdown(sslContext, 1); | SSL_CTX_set_quiet_shutdown(sslContext, 1); |
SSL_CTX_set_mode(sslContext, SSL_MODE_AUTO_RETRY); | SSL_CTX_set_mode(sslContext, SSL_MODE_AUTO_RETRY); |
SSL_CTX_set_mode(sslContext, SSL_MODE_ENABLE_PARTIAL_WRITE); | SSL_CTX_set_mode(sslContext, SSL_MODE_ENABLE_PARTIAL_WRITE); |
SSL_CTX_set_session_cache_mode(sslContext, SSL_SESS_CACHE_OFF); | SSL_CTX_set_session_cache_mode(sslContext, SSL_SESS_CACHE_OFF); |
| |
#ifdef SSL_MODE_RELEASE_BUFFERS |
int options = SSL_OP_ALL; |
// Keep memory usage as low as possible |
#ifndef PEGASUS_ENABLE_SSLV2 //SSLv2 is disabled by default |
SSL_CTX_set_mode (sslContext, SSL_MODE_RELEASE_BUFFERS); |
options |= SSL_OP_NO_SSLv2; |
#endif | #endif |
|
SSL_CTX_set_options(sslContext, options); |
| |
if (_verifyPeer) | if (_verifyPeer) |
{ | { |
|
|
| |
if (_certificateVerifyFunction != NULL) | if (_certificateVerifyFunction != NULL) |
{ | { |
PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL4, |
PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL3, |
"---> SSL: certificate verification callback specified"); | "---> SSL: certificate verification callback specified"); |
SSL_CTX_set_verify(sslContext, | SSL_CTX_set_verify(sslContext, |
SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, prepareForCallback); | SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, prepareForCallback); |
} | } |
else | else |
{ | { |
PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL4, |
PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL3, |
"---> SSL: Trust Store specified"); | "---> SSL: Trust Store specified"); |
SSL_CTX_set_verify(sslContext, | SSL_CTX_set_verify(sslContext, |
SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE | | SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE | |
|
|
// Check if there is CA certificate file or directory specified. If | // Check if there is CA certificate file or directory specified. If |
// specified, and is not empty, load the certificates from the Trust store. | // specified, and is not empty, load the certificates from the Trust store. |
// | // |
if (_trustStore.size() != 0) |
if (_trustStore != String::EMPTY) |
{ | { |
// | // |
// The truststore may be a single file of CA certificates OR | // The truststore may be a single file of CA certificates OR |
|
|
// | // |
if (FileSystem::isDirectory(_trustStore)) | if (FileSystem::isDirectory(_trustStore)) |
{ | { |
PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL4, |
PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL3, |
"---> SSL: Truststore is a directory"); | "---> SSL: Truststore is a directory"); |
// | // |
// load certificates from the trust store | // load certificates from the trust store |
// | // |
PEG_TRACE((TRC_SSL, Tracer::LEVEL4, |
PEG_TRACE((TRC_SSL, Tracer::LEVEL3, |
"---> SSL: Loading certificates from the trust store: %s", | "---> SSL: Loading certificates from the trust store: %s", |
(const char*)_trustStore.getCString())); | (const char*)_trustStore.getCString())); |
| |
|
|
MessageLoaderParms parms( | MessageLoaderParms parms( |
"Common.SSLContext.COULD_NOT_LOAD_CERTIFICATES", | "Common.SSLContext.COULD_NOT_LOAD_CERTIFICATES", |
"Could not load certificates in to trust store."); | "Could not load certificates in to trust store."); |
SSL_CTX_free(sslContext); |
|
sslContext = NULL; |
|
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
throw SSLException(parms); | throw SSLException(parms); |
} | } |
|
|
// | // |
// load certificates from the trust store | // load certificates from the trust store |
// | // |
PEG_TRACE((TRC_SSL, Tracer::LEVEL4, |
PEG_TRACE((TRC_SSL, Tracer::LEVEL3, |
"---> SSL: Loading certificates from the trust store: %s", | "---> SSL: Loading certificates from the trust store: %s", |
(const char*)_trustStore.getCString())); | (const char*)_trustStore.getCString())); |
| |
|
|
MessageLoaderParms parms( | MessageLoaderParms parms( |
"Common.SSLContext.COULD_NOT_LOAD_CERTIFICATES", | "Common.SSLContext.COULD_NOT_LOAD_CERTIFICATES", |
"Could not load certificates in to trust store."); | "Could not load certificates in to trust store."); |
SSL_CTX_free(sslContext); |
|
sslContext = NULL; |
|
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
throw SSLException(parms); | throw SSLException(parms); |
} | } |
|
|
} | } |
} | } |
| |
if (_crlPath.size() != 0) |
if (_crlPath != String::EMPTY) |
{ | { |
// need to save this -- can we make it static since there's only | // need to save this -- can we make it static since there's only |
// one CRL for cimserver? | // one CRL for cimserver? |
|
|
_crlStore.reset(X509_STORE_new()); | _crlStore.reset(X509_STORE_new()); |
if (_crlStore.get() == NULL) | if (_crlStore.get() == NULL) |
{ | { |
SSL_CTX_free(sslContext); |
|
sslContext = NULL; |
|
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
throw PEGASUS_STD(bad_alloc)(); | throw PEGASUS_STD(bad_alloc)(); |
} | } |
|
|
// during server startup | // during server startup |
if (FileSystem::isDirectory(_crlPath)) | if (FileSystem::isDirectory(_crlPath)) |
{ | { |
PEG_TRACE((TRC_SSL, Tracer::LEVEL4, |
PEG_TRACE((TRC_SSL, Tracer::LEVEL3, |
"---> SSL: CRL store is a directory in %s", | "---> SSL: CRL store is a directory in %s", |
(const char*)_crlPath.getCString())); | (const char*)_crlPath.getCString())); |
| |
|
|
"Common.SSLContext.COULD_NOT_LOAD_CRLS", | "Common.SSLContext.COULD_NOT_LOAD_CRLS", |
"Could not load certificate revocation list."); | "Could not load certificate revocation list."); |
_crlStore.reset(); | _crlStore.reset(); |
SSL_CTX_free(sslContext); |
|
sslContext = NULL; |
|
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
throw SSLException(parms); | throw SSLException(parms); |
} | } |
|
|
} | } |
else | else |
{ | { |
PEG_TRACE((TRC_SSL, Tracer::LEVEL4, |
PEG_TRACE((TRC_SSL, Tracer::LEVEL3, |
"---> SSL: CRL store is the file %s", | "---> SSL: CRL store is the file %s", |
(const char*)_crlPath.getCString())); | (const char*)_crlPath.getCString())); |
| |
|
|
"Common.SSLContext.COULD_NOT_LOAD_CRLS", | "Common.SSLContext.COULD_NOT_LOAD_CRLS", |
"Could not load certificate revocation list."); | "Could not load certificate revocation list."); |
_crlStore.reset(); | _crlStore.reset(); |
SSL_CTX_free(sslContext); |
|
sslContext = NULL; |
|
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
throw SSLException(parms); | throw SSLException(parms); |
} | } |
|
|
X509_LOOKUP_load_file( | X509_LOOKUP_load_file( |
pLookup, (const char*)_crlPath.getCString(), X509_FILETYPE_PEM); | pLookup, (const char*)_crlPath.getCString(), X509_FILETYPE_PEM); |
| |
PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL4, |
PEG_TRACE_CSTRING(TRC_SSL, Tracer::LEVEL3, |
"---> SSL: Successfully configured CRL file"); | "---> SSL: Successfully configured CRL file"); |
} | } |
} | } |
|
|
// certificate) specified. If specified, validate and load the | // certificate) specified. If specified, validate and load the |
// certificate. | // certificate. |
// | // |
if (_certPath.size() != 0) |
if (_certPath != String::EMPTY) |
{ | { |
// | // |
// load the specified server certificates | // load the specified server certificates |
// | // |
PEG_TRACE((TRC_SSL, Tracer::LEVEL4, |
PEG_TRACE((TRC_SSL, Tracer::LEVEL3, |
"---> SSL: Loading server certificate from: %s", | "---> SSL: Loading server certificate from: %s", |
(const char*)_certPath.getCString())); | (const char*)_certPath.getCString())); |
| |
|
|
"Common.SSLContext.COULD_NOT_ACCESS_SERVER_CERTIFICATE", | "Common.SSLContext.COULD_NOT_ACCESS_SERVER_CERTIFICATE", |
"Could not access server certificate in $0.", | "Could not access server certificate in $0.", |
(const char*)_certPath.getCString()); | (const char*)_certPath.getCString()); |
|
|
SSL_CTX_free(sslContext); |
|
sslContext = NULL; |
|
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
throw SSLException(parms); | throw SSLException(parms); |
} | } |
|
|
// As of 2.4, if a keyfile is specified, its location is verified | // As of 2.4, if a keyfile is specified, its location is verified |
// during server startup and will throw an error if the path is invalid. | // during server startup and will throw an error if the path is invalid. |
// | // |
if (_keyPath.size() == 0) |
if (_keyPath == String::EMPTY) |
{ | { |
PEG_TRACE((TRC_SSL, Tracer::LEVEL4, |
PEG_TRACE((TRC_SSL, Tracer::LEVEL3, |
"---> SSL: Key file empty, loading private key from " | "---> SSL: Key file empty, loading private key from " |
"certificate file: %s",(const char*)_certPath.getCString())); | "certificate file: %s",(const char*)_certPath.getCString())); |
// | // |
|
|
MessageLoaderParms parms( | MessageLoaderParms parms( |
"Common.SSLContext.COULD_NOT_GET_PRIVATE_KEY", | "Common.SSLContext.COULD_NOT_GET_PRIVATE_KEY", |
"Could not get private key."); | "Could not get private key."); |
SSL_CTX_free(sslContext); |
|
sslContext = NULL; |
|
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
throw SSLException(parms); | throw SSLException(parms); |
} | } |
|
|
// private key) specified and the key was not already loaded. | // private key) specified and the key was not already loaded. |
// If specified, validate and load the key. | // If specified, validate and load the key. |
// | // |
if (_keyPath.size() != 0 && !keyLoaded ) |
if (_keyPath != String::EMPTY && !keyLoaded) |
{ | { |
PEG_TRACE((TRC_SSL, Tracer::LEVEL4, |
PEG_TRACE((TRC_SSL, Tracer::LEVEL3, |
"---> SSL: loading private key from: %s", | "---> SSL: loading private key from: %s", |
(const char*)_keyPath.getCString())); | (const char*)_keyPath.getCString())); |
// | // |
|
|
MessageLoaderParms parms( | MessageLoaderParms parms( |
"Common.SSLContext.COULD_NOT_GET_PRIVATE_KEY", | "Common.SSLContext.COULD_NOT_GET_PRIVATE_KEY", |
"Could not get private key."); | "Could not get private key."); |
SSL_CTX_free(sslContext); |
|
sslContext = NULL; |
|
PEG_METHOD_EXIT(); | PEG_METHOD_EXIT(); |
throw SSLException(parms); | throw SSLException(parms); |
} | } |
|
|
// | // |
| |
SSLContextRep::SSLContextRep( | SSLContextRep::SSLContextRep( |
const String&, |
const String& trustStore, |
const String&, |
const String& certPath, |
const String&, |
const String& keyPath, |
const String&, |
const String& crlPath, |
SSLCertificateVerifyFunction*, |
SSLCertificateVerifyFunction* verifyCert, |
const String&, |
const String& randomFile, |
const String&, |
const String& cipherSuite) |
const Boolean&) |
|
{ | { |
} | } |
| |
SSLContextRep::SSLContextRep(const SSLContextRep&) {} |
SSLContextRep::SSLContextRep(const SSLContextRep& sslContextRep) {} |
| |
SSLContextRep::~SSLContextRep() {} | SSLContextRep::~SSLContextRep() {} |
| |
SSL_CTX* SSLContextRep::_makeSSLContext() { return 0; } | SSL_CTX* SSLContextRep::_makeSSLContext() { return 0; } |
| |
Boolean SSLContextRep::_verifyPrivateKey( | Boolean SSLContextRep::_verifyPrivateKey( |
SSL_CTX*, |
SSL_CTX *ctx, |
const String&) |
const String& keyPath) |
{ | { |
return false; | return false; |
} | } |
|
|
return SharedPtr<X509_STORE, FreeX509STOREPtr>(); | return SharedPtr<X509_STORE, FreeX509STOREPtr>(); |
} | } |
| |
void SSLContextRep::setCRLStore(X509_STORE*) { } |
void SSLContextRep::setCRLStore(X509_STORE* store) { } |
| |
Boolean SSLContextRep::isPeerVerificationEnabled() const { return false; } | Boolean SSLContextRep::isPeerVerificationEnabled() const { return false; } |
| |
|
|
String::EMPTY, | String::EMPTY, |
verifyCert, | verifyCert, |
randomFile, | randomFile, |
String::EMPTY, |
String::EMPTY); |
false); |
|
} | } |
| |
SSLContext::SSLContext( | SSLContext::SSLContext( |
|
|
const String& crlPath, | const String& crlPath, |
SSLCertificateVerifyFunction* verifyCert, | SSLCertificateVerifyFunction* verifyCert, |
const String& randomFile, | const String& randomFile, |
const String& cipherSuite, |
const String& cipherSuite) |
const Boolean& sslCompatibility) |
|
{ | { |
#ifndef PEGASUS_ENABLE_SSL_CRL_VERIFICATION | #ifndef PEGASUS_ENABLE_SSL_CRL_VERIFICATION |
if (crlPath.size() > 0) | if (crlPath.size() > 0) |
|
|
#endif | #endif |
_rep = new SSLContextRep( | _rep = new SSLContextRep( |
trustStore, certPath, keyPath, crlPath, verifyCert, randomFile, | trustStore, certPath, keyPath, crlPath, verifyCert, randomFile, |
cipherSuite,sslCompatibility); |
cipherSuite); |
} | } |
#endif | #endif |
| |
|
|
const String& certPath, | const String& certPath, |
const String& keyPath, | const String& keyPath, |
SSLCertificateVerifyFunction* verifyCert, | SSLCertificateVerifyFunction* verifyCert, |
String, |
String trustStoreUserName, |
const String& randomFile) | const String& randomFile) |
{ | { |
_rep = new SSLContextRep( | _rep = new SSLContextRep( |