diff options
| author | Roxanne Skelly <roxie@lindenlab.com> | 2009-07-08 00:45:17 +0000 | 
|---|---|---|
| committer | Roxanne Skelly <roxie@lindenlab.com> | 2009-07-08 00:45:17 +0000 | 
| commit | 9e89819d55a3b6ee7fc56f3efb36f273e4e05c83 (patch) | |
| tree | 1585010af9cafd82202c22ef9cb0db4967c74394 /indra | |
| parent | fe71dd340ab396b93bde45df438041af5d85fd47 (diff) | |
DEV-34822 - merge with 1.23
certificate notification code
-r 118191
ignore-dead-branch
Diffstat (limited to 'indra')
| -rw-r--r-- | indra/llmessage/llcurl.cpp | 9 | ||||
| -rw-r--r-- | indra/llmessage/llcurl.h | 1 | ||||
| -rw-r--r-- | indra/newview/CMakeLists.txt | 25 | ||||
| -rw-r--r-- | indra/newview/app_settings/settings.xml | 11 | ||||
| -rw-r--r-- | indra/newview/llfloaterland.cpp | 1 | ||||
| -rw-r--r-- | indra/newview/lllogininstance.cpp | 9 | ||||
| -rw-r--r-- | indra/newview/llsecapi.cpp | 5 | ||||
| -rw-r--r-- | indra/newview/llsecapi.h | 311 | ||||
| -rw-r--r-- | indra/newview/llsechandler_basic.cpp | 913 | ||||
| -rw-r--r-- | indra/newview/llsechandler_basic.h | 149 | ||||
| -rw-r--r-- | indra/newview/llstartup.cpp | 163 | ||||
| -rw-r--r-- | indra/newview/llviewernetwork.cpp | 16 | ||||
| -rw-r--r-- | indra/newview/llviewernetwork.h | 4 | ||||
| -rw-r--r-- | indra/newview/llxmlrpclistener.cpp | 18 | ||||
| -rw-r--r-- | indra/newview/llxmlrpctransaction.cpp | 103 | ||||
| -rw-r--r-- | indra/newview/llxmlrpctransaction.h | 3 | ||||
| -rw-r--r-- | indra/newview/skins/default/xui/en/notifications.xml | 42 | ||||
| -rw-r--r-- | indra/newview/tests/llsecapi_test.cpp | 188 | ||||
| -rw-r--r-- | indra/newview/tests/llsechandler_basic_test.cpp | 625 | ||||
| -rw-r--r-- | indra/newview/tests/llviewernetwork_test.cpp | 224 | ||||
| -rw-r--r-- | indra/viewer_components/login/lllogin.cpp | 5 | 
21 files changed, 2466 insertions, 359 deletions
| diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index dc02367a62..91e11b8c0d 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -895,6 +895,15 @@ void LLCurlEasyRequest::setReadCallback(curl_read_callback callback, void* userd  	}  } +void LLCurlEasyRequest::setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata) +{ +	if (mEasy) +	{ +		mEasy->setopt(CURLOPT_SSL_CTX_FUNCTION, (void*)callback); +		mEasy->setopt(CURLOPT_SSL_CTX_DATA, userdata); +	} +} +  void LLCurlEasyRequest::slist_append(const char* str)  {  	if (mEasy) diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h index 1bc1767966..b6a637ae5b 100644 --- a/indra/llmessage/llcurl.h +++ b/indra/llmessage/llcurl.h @@ -229,6 +229,7 @@ public:  	void setHeaderCallback(curl_header_callback callback, void* userdata);  	void setWriteCallback(curl_write_callback callback, void* userdata);  	void setReadCallback(curl_read_callback callback, void* userdata); +	void setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata);  	void slist_append(const char* str);  	void sendRequest(const std::string& url);  	void requestComplete(); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 4be6fb940e..35f0a5036d 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1803,6 +1803,31 @@ if (LL_TESTS)      "${CMAKE_SOURCE_DIR}/llmessage/tests/test_llsdmessage_peer.py"      ) +  set(test_libs  +    ${LLMESSAGE_LIBRARIES}  +    ${WINDOWS_LIBRARIES}  +    ${LLVFS_LIBRARIES} +    ${LLMATH_LIBRARIES} +    ${LLCOMMON_LIBRARIES}  +    ${GOOGLEMOCK_LIBRARIES} +    ${OPENSSL_LIBRARIES} +    ${CRYPTO_LIBRARIES} +  ) +    LL_ADD_INTEGRATION_TEST(llsechandler_basic +     llsechandler_basic.cpp +    "${test_libs}" +    ) + +  LL_ADD_INTEGRATION_TEST(llsecapi +     llsecapi.cpp +    "${test_libs}" +    ) + +  LL_ADD_INTEGRATION_TEST(llviewernetwork +     llviewernetwork.cpp +    "${test_libs}" +    ) +    #ADD_VIEWER_BUILD_TEST(llmemoryview viewer)    #ADD_VIEWER_BUILD_TEST(llagentaccess viewer)    #ADD_VIEWER_BUILD_TEST(llworldmap viewer) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 75ee389750..1641ab0ce1 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -1275,6 +1275,17 @@        <key>Value</key>        <integer>0</integer>      </map> +    <key>CertStore</key> +    <map> +      <key>Comment</key> +      <string>Specifies the Certificate Store for certificate trust verification</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>String</string> +      <key>Value</key> +      <string>default</string> +    </map>      <key>ChatBarStealsFocus</key>      <map>        <key>Comment</key> diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index 598a13de15..9b6e24f251 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -42,7 +42,6 @@  #include "llnotificationsutil.h"  #include "llparcel.h"  #include "message.h" -#include "lluserauth.h"  #include "llagent.h"  #include "llbutton.h" diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp index bb45cc93ea..e4b8becdd7 100644 --- a/indra/newview/lllogininstance.cpp +++ b/indra/newview/lllogininstance.cpp @@ -246,6 +246,15 @@ void LLLoginInstance::handleLoginFailure(const LLSD& event)  			LLSD data(LLSD::emptyMap());  			data["message"] = message_response;  			data["reply_pump"] = TOS_REPLY_PUMP; +			if(response.has("error_code")) +			{ +				data["error_code"] = response["error_code"]; +			} +			if(response.has("certificate")) +			{ +				data["certificate"] = response["certificate"]; +			} +			  			LLFloaterReg::showInstance("message_critical", data);  			LLEventPumps::instance().obtain(TOS_REPLY_PUMP)  				.listen(TOS_LISTENER_NAME, diff --git a/indra/newview/llsecapi.cpp b/indra/newview/llsecapi.cpp index cdf4a3fe01..70c247c2de 100644 --- a/indra/newview/llsecapi.cpp +++ b/indra/newview/llsecapi.cpp @@ -34,6 +34,7 @@  #include "llviewerprecompiledheaders.h"  #include "llsecapi.h"  #include "llsechandler_basic.h" +#include <openssl/evp.h>  #include <map> @@ -42,6 +43,9 @@ LLPointer<LLSecAPIHandler> gSecAPIHandler;  void initializeSecHandler()  { +	OpenSSL_add_all_algorithms(); +	OpenSSL_add_all_ciphers(); +	OpenSSL_add_all_digests();	  	gHandlerMap[BASIC_SECHANDLER] = new LLSecAPIBasicHandler();  	// Currently, we only have the Basic handler, so we can point the main sechandler @@ -76,6 +80,7 @@ std::ostream& operator <<(std::ostream& s, const LLCredential& cred)  } +  LLSD LLCredential::getLoginParams()  {  	LLSD result = LLSD::emptyMap(); diff --git a/indra/newview/llsecapi.h b/indra/newview/llsecapi.h index d456ca95b1..6fd12c044a 100644 --- a/indra/newview/llsecapi.h +++ b/indra/newview/llsecapi.h @@ -36,11 +36,16 @@  #include <openssl/x509.h>  #include <ostream> +#ifdef LL_WINDOWS +#pragma warning(disable:4250) +#endif // LL_WINDOWS +  // All error handling is via exceptions.  #define CERT_SUBJECT_NAME "subject_name"  #define CERT_ISSUER_NAME "issuer_name" +#define CERT_NAME_CN "commonName"  #define  CERT_SUBJECT_NAME_STRING "subject_name_string"  #define CERT_ISSUER_NAME_STRING "issuer_name_string" @@ -51,23 +56,62 @@  #define CERT_VALID_TO "valid_to"  #define CERT_SHA1_DIGEST "sha1_digest"  #define CERT_MD5_DIGEST "md5_digest" +#define CERT_HOSTNAME "hostname" +#define CERT_BASIC_CONSTRAINTS "basicConstraints" +#define CERT_BASIC_CONSTRAINTS_CA "CA" +#define CERT_BASIC_CONSTRAINTS_PATHLEN "pathLen" + +#define CERT_KEY_USAGE "keyUsage" +#define CERT_KU_DIGITAL_SIGNATURE    "digitalSignature" +#define CERT_KU_NON_REPUDIATION      "nonRepudiation" +#define CERT_KU_KEY_ENCIPHERMENT     "keyEncipherment" +#define CERT_KU_DATA_ENCIPHERMENT    "dataEncipherment" +#define CERT_KU_KEY_AGREEMENT        "keyAgreement" +#define CERT_KU_CERT_SIGN        "certSigning" +#define CERT_KU_CRL_SIGN             "crlSigning" +#define CERT_KU_ENCIPHER_ONLY        "encipherOnly" +#define CERT_KU_DECIPHER_ONLY        "decipherOnly"  #define BASIC_SECHANDLER "BASIC_SECHANDLER" +#define CERT_VALIDATION_DATE "validation_date" + +#define CERT_EXTENDED_KEY_USAGE "extendedKeyUsage" +#define CERT_EKU_SERVER_AUTH SN_server_auth + +// validate the current time lies within  +// the validation period of the cert +#define VALIDATION_POLICY_TIME 1 + +// validate that the CA, or some cert in the chain +// lies within the certificate store +#define VALIDATION_POLICY_TRUSTED 2 + +// validate that the subject name of +// the cert contains the passed in hostname +// or validates against the hostname +#define VALIDATION_POLICY_HOSTNAME 4 + + +// validate that the cert contains the SSL EKU +#define VALIDATION_POLICY_SSL_KU 8 + +// validate that the cert contains the SSL EKU +#define VALIDATION_POLICY_CA_KU 16 + +#define VALIDATION_POLICY_CA_BASIC_CONSTRAINTS 32 + +// validate that the cert is correct for SSL +#define VALIDATION_POLICY_SSL (VALIDATION_POLICY_TIME | \ +                               VALIDATION_POLICY_HOSTNAME | \ +                               VALIDATION_POLICY_TRUSTED | \ +                               VALIDATION_POLICY_SSL_KU | \ +                               VALIDATION_POLICY_CA_BASIC_CONSTRAINTS | \ +                               VALIDATION_POLICY_CA_KU) + + -// All error handling is via exceptions. -class LLCertException -{ -public: -	LLCertException(const char* msg) -	{ -		llerrs << "Certificate Error: " << msg << llendl; -		mMsg = std::string(msg); -	} -protected: -	std::string mMsg; -};  class LLProtectedDataException  { @@ -96,53 +140,88 @@ public:  	// return a PEM encoded certificate.  The encoding  	// includes the -----BEGIN CERTIFICATE----- and end certificate elements -	virtual std::string getPem()=0;  +	virtual std::string getPem() const=0;   	// return a DER encoded certificate -	virtual std::vector<U8> getBinary()=0;   +	virtual std::vector<U8> getBinary() const=0;    	// return an LLSD object containing information about the certificate  	// such as its name, signature, expiry time, serial number -	virtual LLSD getLLSD()=0;  +	virtual LLSD getLLSD() const=0;   	// return an openSSL X509 struct for the certificate -	virtual X509* getOpenSSLX509()=0; +	virtual X509* getOpenSSLX509() const=0;  }; +// class LLCertificateVector +// base class for a list of certificates. -// class LLCertificateChain -// Class representing a chain of certificates in order, with the  -// 0th element being the CA -class LLCertificateChain : public LLRefCount + +class LLCertificateVector : public LLRefCount  { -	LOG_CLASS(LLCertificateChain); -	static const int VT_SSL = 0; -	static const int VT_AGENT_DOMAIN = 1; -	static const int VT_GRID_DOMAIN = 1;  public: -	LLCertificateChain() {} -	virtual ~LLCertificateChain() {} +	LLCertificateVector() {}; +	virtual ~LLCertificateVector() {}; +	 +	// base iterator implementation class, providing +	// the functionality needed for the iterator class. +	class iterator_impl : public LLRefCount +	{ +	public: +		iterator_impl() {}; +		virtual ~iterator_impl() {}; +		virtual void seek(bool incr)=0; +		virtual LLPointer<iterator_impl> clone() const=0; +		virtual bool equals(const LLPointer<iterator_impl>& _iter) const=0; +		virtual LLPointer<LLCertificate> get()=0; +	}; +	 +	// iterator class +	class iterator +	{ +	public: +		iterator(LLPointer<iterator_impl> impl) : mImpl(impl) {} +		iterator() : mImpl(NULL) {} +		iterator(const iterator& _iter) {mImpl = _iter.mImpl->clone(); } +		~iterator() {} +		iterator& operator++() { if(mImpl.notNull()) mImpl->seek(true); return *this;} +		iterator& operator--() { if(mImpl.notNull()) mImpl->seek(false); return *this;} +		 +		iterator operator++(int) { iterator result = *this; if(mImpl.notNull()) mImpl->seek(true); return result;} +		iterator operator--(int) { iterator result = *this; if(mImpl.notNull()) mImpl->seek(false); return result;} +		LLPointer<LLCertificate> operator*() { return mImpl->get(); }		 +		 +		LLPointer<iterator_impl> mImpl; +	protected: +		friend bool operator==(const LLCertificateVector::iterator& _lhs, const LLCertificateVector::iterator& _rhs); +		bool equals(const iterator& _iter) const { return mImpl->equals(_iter.mImpl); } +	}; -	virtual X509_STORE getOpenSSLX509Store()=0;  // return an openssl X509_STORE   -	// for this store +	// numeric indexer +	virtual LLPointer<LLCertificate> operator[](int)=0; -	virtual void appendCert(const LLCertificate& cert)=0;  // append a cert to the end  -	//of the chain +	// Iteration +	virtual iterator begin()=0; -	virtual LLPointer<LLCertificate>& operator [](int index)=0; // retrieve a certificate  -	// from the chain by index -	// -1 == end of chain +	virtual iterator end()=0; -	virtual int len() const =0;  // return number of certificates in the chain +	// find a cert given params +	virtual iterator find(const LLSD& params) =0; -	// validate a certificate chain given the params. -	// validation type indicates whether it's simply an SSL cert, or  -	// something more specific -	virtual bool validate(int validation_type,  -						  const LLSD& validation_params) const =0; +	// return the number of certs in the store +	virtual int size() const = 0;	 +	 +	// append the cert to the store.  if a copy of the cert already exists in the store, it is removed first +	virtual void  add(LLPointer<LLCertificate> cert)=0; +	 +	// insert the cert to the store.  if a copy of the cert already exists in the store, it is removed first +	virtual void  insert(iterator location, LLPointer<LLCertificate> cert)=0;	 +	 +	// remove a certificate from the store +	virtual LLPointer<LLCertificate> erase(iterator cert)=0;	  }; @@ -151,43 +230,55 @@ public:  // certificates.  The store can be persisted, and can be used to validate  // a cert chain  // -class LLCertificateStore : public LLRefCount +class LLCertificateStore : virtual public LLCertificateVector  { +	  public: +	  	LLCertificateStore() {}  	virtual ~LLCertificateStore() {} -	virtual X509_STORE* getOpenSSLX509Store()=0;  // return an openssl X509_STORE   -	// for this store -	 -	// add a copy of a cert to the store -	virtual void  append(const LLCertificate& cert)=0; -	 -	// add a copy of a cert to the store -	virtual void insert(const int index, const LLCertificate& cert)=0; -	 -	// remove a certificate from the store -	virtual void remove(int index)=0; -	 -	// return a certificate at the index -	virtual LLPointer<LLCertificate> operator[](int index)=0; -	 -	// return the number of certs in the store -	virtual int len() const =0; -	 -	// load the store from a persisted location -	virtual void load(const std::string& store_id)=0; -	  	// persist the store  	virtual void save()=0;  	// return the store id -	virtual std::string storeId()=0; +	virtual std::string storeId() const=0; +}; + +// class LLCertificateChain +// Class representing a chain of certificates in order, with the  +// first element being the child cert. +class LLCertificateChain : virtual public LLCertificateVector +{	 + +public: +	LLCertificateChain() {} -	// validate a cert chain -	virtual bool validate(const LLCertificateChain& cert_chain) const=0; +	virtual ~LLCertificateChain() {} + +	// validate a certificate chain given the params. +	// Will throw exceptions on error +	 +	virtual void validate(int validation_policy, +						  LLPointer<LLCertificateStore> ca_store, +						  const LLSD& validation_params) =0;  }; + + + +inline +bool operator==(const LLCertificateVector::iterator& _lhs, const LLCertificateVector::iterator& _rhs) +{ +	return _lhs.equals(_rhs); +} +inline +bool operator!=(const LLCertificateVector::iterator& _lhs, const LLCertificateVector::iterator& _rhs) +{ +	return !(_lhs == _rhs); +} + +  //  // LLCredential - interface for credentials providing the following functionality:  // * persistance of credential information based on grid (for saving username/password) @@ -232,6 +323,98 @@ protected:  std::ostream& operator <<(std::ostream& s, const LLCredential& cred); +// All error handling is via exceptions. + +class LLCertException +{ +public: +	LLCertException(LLPointer<LLCertificate> cert, const char* msg) +	{ + +		mCert = cert; + +		LL_WARNS("SECAPI") << "Certificate Error: " << (std::string)msg << LL_ENDL; +		mMsg = (std::string)msg; +	} +	LLPointer<LLCertificate> getCert() { return mCert; } +	std::string getMessage() { return mMsg; } +protected: +	LLPointer<LLCertificate> mCert; +	std::string mMsg; +}; + +class LLInvalidCertificate : public LLCertException +{ +public: +	LLInvalidCertificate(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertInvalid") +	{ +	} +protected: +}; + +class LLCertValidationTrustException : public LLCertException +{ +public: +	LLCertValidationTrustException(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertUntrusted") +	{ +	} +protected: +}; + +class LLCertValidationHostnameException : public LLCertException +{ +public: +	LLCertValidationHostnameException(std::string hostname, +									  LLPointer<LLCertificate> cert) : LLCertException(cert, "CertInvalidHostname") +	{ +		mHostname = hostname; +	} +	 +	std::string getHostname() { return mHostname; } +protected: +	std::string mHostname; +}; + +class LLCertValidationExpirationException : public LLCertException +{ +public: +	LLCertValidationExpirationException(LLPointer<LLCertificate> cert, +										LLDate current_time) : LLCertException(cert, "CertExpired") +	{ +		mTime = current_time; +	} +	LLDate GetTime() { return mTime; } +protected: +	LLDate mTime; +}; + +class LLCertKeyUsageValidationException : public LLCertException +{ +public: +	LLCertKeyUsageValidationException(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertKeyUsage") +	{ +	} +protected: +}; + +class LLCertBasicConstraintsValidationException : public LLCertException +{ +public: +	LLCertBasicConstraintsValidationException(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertBasicConstraints") +	{ +	} +protected: +}; + +class LLCertValidationInvalidSignatureException : public LLCertException +{ +public: +	LLCertValidationInvalidSignatureException(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertInvalidSignature") +	{ +	} +protected: +}; +  // LLSecAPIHandler Class  // Interface handler class for the various security storage handlers.  class LLSecAPIHandler : public LLRefCount diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp index 4180f578b9..097cc395d7 100644 --- a/indra/newview/llsechandler_basic.cpp +++ b/indra/newview/llsechandler_basic.cpp @@ -20,7 +20,7 @@   * in the file doc/FLOSS-exception.txt in this software distribution, or   * online at http://secondlife.com/developers/opensource/flossexception   *  - * By copying, modifying or distributing this software, you acknowledge +LLS * By copying, modifying or distributing this software, you acknowledge   * that you have read and understood your obligations described above,   * and agree to abide by those obligations.   *  @@ -42,17 +42,26 @@  #include "llviewercontrol.h"  #include <vector>  #include <ios> +#include <openssl/ossl_typ.h>  #include <openssl/x509.h> +#include <openssl/x509v3.h>  #include <openssl/pem.h>  #include <openssl/asn1.h>  #include <openssl/rand.h> +#include <openssl/err.h>  #include <iostream>  #include <iomanip>  #include <time.h> + +  // 128 bits of salt data...  #define STORE_SALT_SIZE 16   #define BUFFER_READ_SIZE 256 +std::string cert_string_from_asn1_string(ASN1_STRING* value); +LLSD _basic_constraints_ext(X509* cert); +LLSD _key_usage_ext(X509* cert); +LLSD _ext_key_usage_ext(X509* cert);  LLBasicCertificate::LLBasicCertificate(const std::string& pem_cert)  @@ -61,14 +70,19 @@ LLBasicCertificate::LLBasicCertificate(const std::string& pem_cert)  	// BIO_new_mem_buf returns a read only bio, but takes a void* which isn't const  	// so we need to cast it.  	BIO * pem_bio = BIO_new_mem_buf((void*)pem_cert.c_str(), pem_cert.length()); -	 +	if(pem_bio == NULL) +	{ +		LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL; +		throw LLInvalidCertificate(this); +	}  	mCert = NULL;  	PEM_read_bio_X509(pem_bio, &mCert, 0, NULL);  	BIO_free(pem_bio);  	if (!mCert)  	{ -		throw LLCertException("Error parsing certificate"); +		throw LLInvalidCertificate(this);  	} +	_initLLSD();  } @@ -76,20 +90,23 @@ LLBasicCertificate::LLBasicCertificate(X509* pCert)  {  	if (!pCert || !pCert->cert_info)  	{ -		throw LLCertException("Invalid certificate"); +		throw LLInvalidCertificate(this);  	}	  	mCert = X509_dup(pCert); +	_initLLSD();  }  LLBasicCertificate::~LLBasicCertificate()   { - -	X509_free(mCert); +	if(mCert) +	{ +		X509_free(mCert); +	}  }  //  // retrieve the pem using the openssl functionality -std::string LLBasicCertificate::getPem() +std::string LLBasicCertificate::getPem() const  {   	char * pem_bio_chars = NULL;  	// a BIO is the equivalent of a 'std::stream', and @@ -98,7 +115,8 @@ std::string LLBasicCertificate::getPem()  	BIO *pem_bio = BIO_new(BIO_s_mem());  	if (!pem_bio)  	{ -		throw LLCertException("couldn't allocate memory buffer");		 +		LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL;		 +		return std::string();  	}  	PEM_write_bio_X509(pem_bio, mCert);  	int length = BIO_get_mem_data(pem_bio, &pem_bio_chars); @@ -109,14 +127,15 @@ std::string LLBasicCertificate::getPem()  // get the DER encoding for the cert  // DER is a binary encoding format for certs... -std::vector<U8> LLBasicCertificate::getBinary() +std::vector<U8> LLBasicCertificate::getBinary() const  {   	U8 * der_bio_data = NULL;  	// get a memory bio   	BIO *der_bio = BIO_new(BIO_s_mem());  	if (!der_bio)  	{ -		throw LLCertException("couldn't allocate memory buffer");		 +		LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL;			 +		return std::vector<U8>();  	}  	i2d_X509_bio(der_bio, mCert);  	int length = BIO_get_mem_data(der_bio, &der_bio_data); @@ -128,32 +147,131 @@ std::vector<U8> LLBasicCertificate::getBinary()  } -LLSD LLBasicCertificate::getLLSD() +LLSD LLBasicCertificate::getLLSD() const +{ +	return mLLSDInfo; +} + +// Initialize the LLSD info for the certificate +LLSD& LLBasicCertificate::_initLLSD()  {  -	LLSD result;  	// call the various helpers to build the LLSD -	result[CERT_SUBJECT_NAME] = cert_name_from_X509_NAME(X509_get_subject_name(mCert)); -	result[CERT_ISSUER_NAME] = cert_name_from_X509_NAME(X509_get_issuer_name(mCert)); -	result[CERT_SUBJECT_NAME_STRING] = cert_string_name_from_X509_NAME(X509_get_subject_name(mCert)); -	result[CERT_ISSUER_NAME_STRING] = cert_string_name_from_X509_NAME(X509_get_issuer_name(mCert)); +	mLLSDInfo[CERT_SUBJECT_NAME] = cert_name_from_X509_NAME(X509_get_subject_name(mCert)); +	mLLSDInfo[CERT_ISSUER_NAME] = cert_name_from_X509_NAME(X509_get_issuer_name(mCert)); +	mLLSDInfo[CERT_SUBJECT_NAME_STRING] = cert_string_name_from_X509_NAME(X509_get_subject_name(mCert)); +	mLLSDInfo[CERT_ISSUER_NAME_STRING] = cert_string_name_from_X509_NAME(X509_get_issuer_name(mCert));  	ASN1_INTEGER *sn = X509_get_serialNumber(mCert);  	if (sn != NULL)  	{ -		result[CERT_SERIAL_NUMBER] = cert_string_from_asn1_integer(sn); +		mLLSDInfo[CERT_SERIAL_NUMBER] = cert_string_from_asn1_integer(sn);  	} -	result[CERT_VALID_TO] = cert_date_from_asn1_time(X509_get_notAfter(mCert)); -	result[CERT_VALID_FROM] = cert_date_from_asn1_time(X509_get_notBefore(mCert)); -	result[CERT_SHA1_DIGEST] = cert_get_digest("sha1", mCert); -	result[CERT_MD5_DIGEST] = cert_get_digest("md5", mCert); +	mLLSDInfo[CERT_VALID_TO] = cert_date_from_asn1_time(X509_get_notAfter(mCert)); +	mLLSDInfo[CERT_VALID_FROM] = cert_date_from_asn1_time(X509_get_notBefore(mCert)); +	mLLSDInfo[CERT_SHA1_DIGEST] = cert_get_digest("sha1", mCert); +	mLLSDInfo[CERT_MD5_DIGEST] = cert_get_digest("md5", mCert); +	// add the known extensions +	mLLSDInfo[CERT_BASIC_CONSTRAINTS] = _basic_constraints_ext(mCert); +	mLLSDInfo[CERT_KEY_USAGE] = _key_usage_ext(mCert); +	mLLSDInfo[CERT_EXTENDED_KEY_USAGE] = _ext_key_usage_ext(mCert); +	return mLLSDInfo;  +} + +// Retrieve the basic constraints info +LLSD _basic_constraints_ext(X509* cert) +{ +	LLSD result; +	BASIC_CONSTRAINTS *bs = (BASIC_CONSTRAINTS *)X509_get_ext_d2i(cert, NID_basic_constraints, NULL, NULL); +	if(bs) +	{ +		result = LLSD::emptyMap(); +		// Determines whether the cert can be used as a CA +		result[CERT_BASIC_CONSTRAINTS_CA] = (bool)bs->ca; +	 +		if(bs->pathlen)  +		{ +			// the pathlen determines how deep a certificate chain can be from +			// this CA +			if((bs->pathlen->type == V_ASN1_NEG_INTEGER) +			   || !bs->ca)  +			{ +				result[CERT_BASIC_CONSTRAINTS_PATHLEN] = 0; +			}  +			else  +			{ +				result[CERT_BASIC_CONSTRAINTS_PATHLEN] = (int)ASN1_INTEGER_get(bs->pathlen); +			} +		} +	} +	return result; +} +// retrieve the key usage, which specifies how the cert can be used. +//  +LLSD _key_usage_ext(X509* cert) +{ +	LLSD result; +	ASN1_STRING *usage_str = (ASN1_STRING *)X509_get_ext_d2i(cert, NID_key_usage, NULL, NULL); +	if(usage_str) +	{ +		result = LLSD::emptyArray(); +		long usage = 0; +		if(usage_str->length > 0)  +		{ +			usage = usage_str->data[0]; +			if(usage_str->length > 1) +			{ +				usage |= usage_str->data[1] << 8; +			} +		} +		ASN1_STRING_free(usage_str); +		if(usage) +		{ +			if(usage & KU_DIGITAL_SIGNATURE) result.append(LLSD((std::string)CERT_KU_DIGITAL_SIGNATURE)); +			if(usage & KU_NON_REPUDIATION) result.append(LLSD((std::string)CERT_KU_NON_REPUDIATION)); +			if(usage & KU_KEY_ENCIPHERMENT) result.append(LLSD((std::string)CERT_KU_KEY_ENCIPHERMENT)); +			if(usage & KU_DATA_ENCIPHERMENT) result.append(LLSD((std::string)CERT_KU_DATA_ENCIPHERMENT)); +			if(usage & KU_KEY_AGREEMENT) result.append(LLSD((std::string)CERT_KU_KEY_AGREEMENT)); +			if(usage & KU_KEY_CERT_SIGN) result.append(LLSD((std::string)CERT_KU_CERT_SIGN));			 +			if(usage & KU_CRL_SIGN) result.append(LLSD((std::string)CERT_KU_CRL_SIGN));	 +			if(usage & KU_ENCIPHER_ONLY) result.append(LLSD((std::string)CERT_KU_ENCIPHER_ONLY));				 +			if(usage & KU_DECIPHER_ONLY) result.append(LLSD((std::string)CERT_KU_DECIPHER_ONLY));		 +		} +	} +	return result; +} -	return result;  +// retrieve the extended key usage for the cert +LLSD _ext_key_usage_ext(X509* cert) +{ +	LLSD result; +	EXTENDED_KEY_USAGE *eku = (EXTENDED_KEY_USAGE *)X509_get_ext_d2i(cert, NID_ext_key_usage, NULL, NULL); +	if(eku) +	{ +		result = LLSD::emptyArray(); +		while(sk_ASN1_OBJECT_num(eku)) +		{ +			ASN1_OBJECT *usage = sk_ASN1_OBJECT_pop(eku); +			if(usage) +			{ +				int nid = OBJ_obj2nid(usage); +				if (nid) +				{ +					std::string sn = OBJ_nid2sn(nid); +					result.append(sn); +				} +				ASN1_OBJECT_free(usage); +			} +		} +	} +	return result;  } -X509* LLBasicCertificate::getOpenSSLX509() +// retrieve an openssl x509 object, +// which must be freed by X509_free +X509* LLBasicCertificate::getOpenSSLX509() const  {   	return X509_dup(mCert);   }   @@ -203,13 +321,46 @@ LLSD cert_name_from_X509_NAME(X509_NAME* name)  std::string cert_string_from_asn1_integer(ASN1_INTEGER* value)  { +	std::string result;  	BIGNUM *bn = ASN1_INTEGER_to_BN(value, NULL); -	char * ascii_bn = BN_bn2hex(bn); +	if(bn) +	{ +		char * ascii_bn = BN_bn2hex(bn); -	BN_free(bn); +		if(ascii_bn) +		{ +			result = ascii_bn; +			OPENSSL_free(ascii_bn); +		} +		BN_free(bn); +	} +	return result; +} + +// Generate a string from an ASN1 integer.  ASN1 Integers are +// bignums, so they can be 'infinitely' long, therefore we +// cannot simply use a conversion to U64 or something. +// We retrieve as a readable string for UI -	std::string result(ascii_bn); -	OPENSSL_free(ascii_bn); +std::string cert_string_from_asn1_string(ASN1_STRING* value) +{ +	char * string_bio_chars = NULL; +	std::string result; +	// get a memory bio +	BIO *string_bio = BIO_new(BIO_s_mem()); +	if(!string_bio) +	{ +		// stream the name into the bio.  The name will be in the 'short name' format +		ASN1_STRING_print_ex(string_bio, value, ASN1_STRFLGS_RFC2253); +		int length = BIO_get_mem_data(string_bio, &string_bio_chars); +		result = std::string(string_bio_chars, length); +		BIO_free(string_bio); +	} +	else +	{ +		LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL; +	} +	  	return result;  } @@ -222,8 +373,9 @@ LLDate cert_date_from_asn1_time(ASN1_TIME* asn1_time)  	int i = asn1_time->length;  	if (i < 10) -		throw LLCertException("invalid certificate time value"); -	 +	{ +		return LLDate(); +	}	  	// convert the date from the ASN1 time (which is a string in ZULU time), to  	// a timeval.  	timestruct.tm_year = (asn1_time->data[0]-'0') * 10 + (asn1_time->data[1]-'0'); @@ -237,19 +389,23 @@ LLDate cert_date_from_asn1_time(ASN1_TIME* asn1_time)  	timestruct.tm_hour = (asn1_time->data[6]-'0') * 10 + (asn1_time->data[7]-'0');  	timestruct.tm_min = (asn1_time->data[8]-'0') * 10 + (asn1_time->data[9]-'0');  	timestruct.tm_sec = (asn1_time->data[10]-'0') * 10 + (asn1_time->data[11]-'0'); -	 -	return LLDate((F64)mktime(×truct)); + +#if LL_WINDOWS +	return LLDate((F64)_mkgmtime(×truct)); +#else // LL_WINDOWS +	return LLDate((F64)timegm(×truct)); +#endif // LL_WINDOWS  } +													     // Generate a string containing a digest.  The digest time is 'ssh1' or  // 'md5', and the resulting string is of the form "aa:12:5c:' and so on  std::string cert_get_digest(const std::string& digest_type, X509 *cert)  { -	unsigned char digest_data[1024]; -	unsigned int len = 1024; +	unsigned char digest_data[BUFFER_READ_SIZE]; +	unsigned int len = sizeof(digest_data);  	std::stringstream result;  	const EVP_MD* digest = NULL; -	OpenSSL_add_all_digests();  	// we could use EVP_get_digestbyname, but that requires initializer code which  	// would require us to complicate things by plumbing it into the system.  	if (digest_type == "md5") @@ -262,7 +418,7 @@ std::string cert_get_digest(const std::string& digest_type, X509 *cert)  	}  	else  	{ -		throw LLCertException("Invalid digest"); +		return std::string();  	}  	X509_digest(cert, digest, digest_data, &len); @@ -279,75 +435,588 @@ std::string cert_get_digest(const std::string& digest_type, X509 *cert)  } +// class LLBasicCertificateVector +// This class represents a list of certificates, implemented by a vector of certificate pointers. +// it contains implementations of the virtual functions for iterators, search, add, remove, etc. +// + +//  Find a certificate in the list. +// It will find a cert that has minimally the params listed, with the values being the same +LLBasicCertificateVector::iterator LLBasicCertificateVector::find(const LLSD& params) +{ +	BOOL found = FALSE; +	// loop through the entire vector comparing the values in the certs +	// against those passed in via the params. +	// params should be a map.  Only the items specified in the map will be +	// checked, but they must match exactly, even if they're maps or arrays. +	 +	for(iterator cert = begin(); +		cert != end(); +		cert++) +	{ + +			found= TRUE; +		LLSD cert_info = (*cert)->getLLSD(); +			for (LLSD::map_const_iterator param = params.beginMap(); +			 param != params.endMap(); +			 param++) +		{ + +			if (!cert_info.has((std::string)param->first) ||  +				(!valueCompareLLSD(cert_info[(std::string)param->first], param->second))) +			{ +				found = FALSE; +				break; +			} +		} +		if (found) +		{ +			return (cert); +		} +	} +	return end(); +} + +// Insert a certificate into the store.  If the certificate already  +// exists in the store, nothing is done. +void  LLBasicCertificateVector::insert(iterator _iter,  +									   LLPointer<LLCertificate> cert) +{ +	LLSD cert_info = cert->getLLSD(); +	if (cert_info.isMap() && cert_info.has(CERT_SHA1_DIGEST)) +	{ +		LLSD existing_cert_info = LLSD::emptyMap(); +		existing_cert_info[CERT_MD5_DIGEST] = cert_info[CERT_MD5_DIGEST]; +		if(find(existing_cert_info) == end()) +		{ +			BasicIteratorImpl *basic_iter = dynamic_cast<BasicIteratorImpl*>(_iter.mImpl.get()); +			mCerts.insert(basic_iter->mIter, cert); +		} +	} +} + +// remove a certificate from the store +LLPointer<LLCertificate> LLBasicCertificateVector::erase(iterator _iter) +{ +	 +	if (_iter != end()) +	{ +		BasicIteratorImpl *basic_iter = dynamic_cast<BasicIteratorImpl*>(_iter.mImpl.get()); +		LLPointer<LLCertificate> result = (*_iter); +		mCerts.erase(basic_iter->mIter); +		return result; +	} +	return NULL; +} + +  //  // LLBasicCertificateStore -//  +// This class represents a store of CA certificates.  The basic implementation +// uses a pem file such as the legacy CA.pem stored in the existing  +// SL implementation.  LLBasicCertificateStore::LLBasicCertificateStore(const std::string& filename)  { +	mFilename = filename; +	load_from_file(filename);  } -LLBasicCertificateStore::LLBasicCertificateStore(const X509_STORE* store) + +void LLBasicCertificateStore::load_from_file(const std::string& filename)  { +	// scan the PEM file extracting each certificate +	BIO* file_bio = BIO_new(BIO_s_file()); +	if(file_bio) +	{ +		if (BIO_read_filename(file_bio, filename.c_str()) > 0) +		{	 +			X509 *cert_x509 = NULL; +			while((PEM_read_bio_X509(file_bio, &cert_x509, 0, NULL)) &&  +				  (cert_x509 != NULL)) +			{ +				try +				{ +					add(new LLBasicCertificate(cert_x509)); +				} +				catch (...) +				{ +					LL_WARNS("SECAPI") << "Failure creating certificate from the certificate store file." << LL_ENDL; +				} +				X509_free(cert_x509); +				cert_x509 = NULL; +			} +			BIO_free(file_bio); +		} +	} +	else +	{ +		LL_WARNS("SECAPI") << "Could not allocate a file BIO" << LL_ENDL; +	}  } +  LLBasicCertificateStore::~LLBasicCertificateStore()  {  } -		 -X509_STORE* LLBasicCertificateStore::getOpenSSLX509Store() + +// persist the store +void LLBasicCertificateStore::save()  { -	return NULL; +	llofstream file_store(mFilename, llofstream::binary); +	if(!file_store.fail()) +	{ +		for(iterator cert = begin(); +			cert != end(); +			cert++) +		{ +			std::string pem = (*cert)->getPem(); +			if(!pem.empty()) +			{ +				file_store << (*cert)->getPem() << std::endl; +			} +		} +		file_store.close(); +	} +	else +	{ +		LL_WARNS("SECAPI") << "Could not open certificate store " << mFilename << "for save" << LL_ENDL; +	}  } -		 -		// add a copy of a cert to the store -void  LLBasicCertificateStore::append(const LLCertificate& cert) + +// return the store id +std::string LLBasicCertificateStore::storeId() const  { +	// this is the basic handler which uses the CA.pem store, +	// so we ignore this. +	return std::string("");  } -		 -		// add a copy of a cert to the store -void LLBasicCertificateStore::insert(const int index, const LLCertificate& cert) + + +// +// LLBasicCertificateChain +// This class represents a chain of certs, each cert being signed by the next cert +// in the chain.  Certs must be properly signed by the parent +LLBasicCertificateChain::LLBasicCertificateChain(const X509_STORE_CTX* store)  { + +	// we're passed in a context, which contains a cert, and a blob of untrusted +	// certificates which compose the chain. +	if((store == NULL) || (store->cert == NULL)) +	{ +		LL_WARNS("SECAPI") << "An invalid store context was passed in when trying to create a certificate chain" << LL_ENDL; +		return; +	} +	// grab the child cert +	LLPointer<LLCertificate> current = new LLBasicCertificate(store->cert); + +	add(current); +	if(store->untrusted != NULL) +	{ +		// if there are other certs in the chain, we build up a vector +		// of untrusted certs so we can search for the parents of each +		// consecutive cert. +		LLBasicCertificateVector untrusted_certs; +		for(int i = 0; i < sk_X509_num(store->untrusted); i++) +		{ +			LLPointer<LLCertificate> cert = new LLBasicCertificate(sk_X509_value(store->untrusted, i)); +			untrusted_certs.add(cert); + +		}		 +		while(untrusted_certs.size() > 0) +		{ +			LLSD find_data = LLSD::emptyMap(); +			LLSD cert_data = current->getLLSD(); +			// we simply build the chain via subject/issuer name as the +			// client should not have passed in multiple CA's with the same  +			// subject name.  If they did, it'll come out in the wash during +			// validation. +			find_data[CERT_SUBJECT_NAME_STRING] = cert_data[CERT_ISSUER_NAME_STRING];  +			LLBasicCertificateVector::iterator issuer = untrusted_certs.find(find_data); +			if (issuer != untrusted_certs.end()) +			{ +				current = untrusted_certs.erase(issuer); +				add(current); +			} +			else +			{ +				break; +			} +		} +	}  } -		 -		// remove a certificate from the store -void LLBasicCertificateStore::remove(int index) + + +// subdomain wildcard specifiers can be divided into 3 parts +// the part before the first *, the part after the first * but before +// the second *, and the part after the second *. +// It then iterates over the second for each place in the string +// that it matches.  ie if the subdomain was testfoofoobar, and +// the wildcard was test*foo*bar, it would match test, then +// recursively match foofoobar and foobar + +bool _cert_subdomain_wildcard_match(const std::string& subdomain, +									const std::string& wildcard)  { +	// split wildcard into the portion before the *, and the portion after + +	int wildcard_pos = wildcard.find_first_of('*');	 +	// check the case where there is no wildcard. +	if(wildcard_pos == wildcard.npos) +	{ +		return (subdomain == wildcard); +	} +	 +	// we need to match the first part of the subdomain string up to the wildcard +	// position +	if(subdomain.substr(0, wildcard_pos) != wildcard.substr(0, wildcard_pos)) +	{ +		// the first portions of the strings didn't match +		return FALSE; +	} +	 +	// as the portion of the wildcard string before the * matched, we need to check the +	// portion afterwards.  Grab that portion. +	std::string new_wildcard_string = wildcard.substr( wildcard_pos+1, wildcard.npos); +	if(new_wildcard_string.empty()) +	{ +		// we had nothing after the *, so it's an automatic match +		return TRUE; +	} +	 +	// grab the portion of the remaining wildcard string before the next '*'.  We need to find this +	// within the remaining subdomain string. and then recursively check. +	std::string new_wildcard_match_string = new_wildcard_string.substr(0, new_wildcard_string.find_first_of('*')); +	 +	// grab the portion of the subdomain after the part that matched the initial wildcard portion +	std::string new_subdomain = subdomain.substr(wildcard_pos, subdomain.npos); +	 +	// iterate through the current subdomain, finding instances of the match string. +	int sub_pos = new_subdomain.find_first_of(new_wildcard_match_string); +	while(sub_pos != std::string::npos) +	{ +		new_subdomain = new_subdomain.substr(sub_pos, std::string::npos); +		if(_cert_subdomain_wildcard_match(new_subdomain, new_wildcard_string)) +		{ +			return TRUE; +		} +		sub_pos = new_subdomain.find_first_of(new_wildcard_match_string, 1); + + +	} +	// didn't find any instances of the match string that worked in the subdomain, so fail. +	return FALSE;  } -		 -		// return a certificate at the index -LLPointer<LLCertificate> LLBasicCertificateStore::operator[](int index) + + +// RFC2459 does not address wildcards as part of it's name matching +// specification, and there is no RFC specifying wildcard matching, +// RFC2818 does a few statements about wildcard matching, but is very  +// general.  Generally, wildcard matching is per implementation, although +// it's pretty similar. +// in our case, we use the '*' wildcard character only, within each +// subdomain.  The hostname and the CN specification should have the +// same number of subdomains. +// We then iterate that algorithm over each subdomain. +bool _cert_hostname_wildcard_match(const std::string& hostname, const std::string& common_name)  { -	LLPointer<LLCertificate> result = NULL; -	return result; +	std::string new_hostname = hostname; +	std::string new_cn = common_name; +	int subdomain_pos = new_hostname.find_first_of('.'); +	int subcn_pos = new_cn.find_first_of('.'); +	 +	while((subcn_pos != std::string::npos) && (subdomain_pos != std::string::npos)) +	{ +		// snip out the first subdomain and cn element + +		if(!_cert_subdomain_wildcard_match(new_hostname.substr(0, subdomain_pos), +										   new_cn.substr(0, subcn_pos))) +		{ +			return FALSE; +		} +		new_hostname = new_hostname.substr(subdomain_pos+1, std::string::npos); +		new_cn = new_cn.substr(subcn_pos+1, std::string::npos); +		subdomain_pos = new_hostname.find_first_of('.'); +		subcn_pos = new_cn.find_first_of('.'); +	} +	return _cert_subdomain_wildcard_match(new_hostname, new_cn); +  } -		// return the number of certs in the store -int LLBasicCertificateStore::len() const + +// validate that the LLSD array in llsd_set contains the llsd_value  +bool _LLSDArrayIncludesValue(const LLSD& llsd_set, LLSD llsd_value)  { -	return 0; +	for(LLSD::array_const_iterator set_value = llsd_set.beginArray(); +		set_value != llsd_set.endArray(); +		set_value++) +	{ +		if(valueCompareLLSD((*set_value), llsd_value)) +		{ +			return TRUE; +		} +	} +	return FALSE;  } -		 -		// load the store from a persisted location -void LLBasicCertificateStore::load(const std::string& store_id) + +void _validateCert(int validation_policy, +				  const LLPointer<LLCertificate> cert, +				  const LLSD& validation_params, +				  int depth)  { -} + +	LLSD current_cert_info = cert->getLLSD();		 +	// check basic properties exist in the cert +	if(!current_cert_info.has(CERT_SUBJECT_NAME) || !current_cert_info.has(CERT_SUBJECT_NAME_STRING)) +	{ +		throw LLCertException(cert, "Cert doesn't have a Subject Name");				 +	} +	 +	if(!current_cert_info.has(CERT_ISSUER_NAME_STRING)) +	{ +		throw LLCertException(cert, "Cert doesn't have an Issuer Name");				 +	} +	 +	// check basic properties exist in the cert +	if(!current_cert_info.has(CERT_VALID_FROM) || !current_cert_info.has(CERT_VALID_TO)) +	{ +		throw LLCertException(cert, "Cert doesn't have an expiration period");				 +	} +	if (!current_cert_info.has(CERT_SHA1_DIGEST)) +	{ +		throw LLCertException(cert, "No SHA1 digest"); +	} + +	if (validation_policy & VALIDATION_POLICY_TIME) +	{ + +		LLDate validation_date(time(NULL)); +		if(validation_params.has(CERT_VALIDATION_DATE)) +		{ +			validation_date = validation_params[CERT_VALIDATION_DATE]; +		} -		// persist the store -void LLBasicCertificateStore::save() -{ +		if((validation_date < current_cert_info[CERT_VALID_FROM].asDate()) || +		   (validation_date > current_cert_info[CERT_VALID_TO].asDate())) +		{ +			throw LLCertValidationExpirationException(cert, validation_date); +		} +	} +	if (validation_policy & VALIDATION_POLICY_SSL_KU) +	{ +		if (current_cert_info.has(CERT_KEY_USAGE) && current_cert_info[CERT_KEY_USAGE].isArray() && +			(!(_LLSDArrayIncludesValue(current_cert_info[CERT_KEY_USAGE],  +									   LLSD((std::string)CERT_KU_DIGITAL_SIGNATURE))) || +			!(_LLSDArrayIncludesValue(current_cert_info[CERT_KEY_USAGE],  +									  LLSD((std::string)CERT_KU_KEY_ENCIPHERMENT))))) +		{ +			throw LLCertKeyUsageValidationException(cert); +		} +		// only validate EKU if the cert has it +		if(current_cert_info.has(CERT_EXTENDED_KEY_USAGE) && current_cert_info[CERT_EXTENDED_KEY_USAGE].isArray() &&	    +		   (!_LLSDArrayIncludesValue(current_cert_info[CERT_EXTENDED_KEY_USAGE],  +									LLSD((std::string)CERT_EKU_SERVER_AUTH)))) +		{ +			throw LLCertKeyUsageValidationException(cert);			 +		} +	} +	if (validation_policy & VALIDATION_POLICY_CA_KU) +	{ +		if (current_cert_info.has(CERT_KEY_USAGE) && current_cert_info[CERT_KEY_USAGE].isArray() && +			(!_LLSDArrayIncludesValue(current_cert_info[CERT_KEY_USAGE],  +									   (std::string)CERT_KU_CERT_SIGN))) +			{ +				throw LLCertKeyUsageValidationException(cert);						 +			} +	} +	 +	// validate basic constraints +	if ((validation_policy & VALIDATION_POLICY_CA_BASIC_CONSTRAINTS) && +		current_cert_info.has(CERT_BASIC_CONSTRAINTS) &&  +		current_cert_info[CERT_BASIC_CONSTRAINTS].isMap()) +	{ +		if(!current_cert_info[CERT_BASIC_CONSTRAINTS].has(CERT_BASIC_CONSTRAINTS_CA) || +		   !current_cert_info[CERT_BASIC_CONSTRAINTS][CERT_BASIC_CONSTRAINTS_CA]) +		{ +				throw LLCertBasicConstraintsValidationException(cert); +		} +		if (current_cert_info[CERT_BASIC_CONSTRAINTS].has(CERT_BASIC_CONSTRAINTS_PATHLEN) && +			((current_cert_info[CERT_BASIC_CONSTRAINTS][CERT_BASIC_CONSTRAINTS_PATHLEN].asInteger() != 0) && +			 (depth > current_cert_info[CERT_BASIC_CONSTRAINTS][CERT_BASIC_CONSTRAINTS_PATHLEN].asInteger()))) +		{ +			throw LLCertBasicConstraintsValidationException(cert);					 +		} +	}  } -		 -		// return the store id -std::string LLBasicCertificateStore::storeId() + +bool _verify_signature(LLPointer<LLCertificate> parent,  +					   LLPointer<LLCertificate> child)  { -	return std::string(""); -} +	bool verify_result = FALSE;  +	LLSD cert1 = parent->getLLSD(); +	LLSD cert2 = child->getLLSD(); +	X509 *signing_cert = parent->getOpenSSLX509(); +	X509 *child_cert = child->getOpenSSLX509(); +	if((signing_cert != NULL) && (child_cert != NULL)) +	{ +		EVP_PKEY *pkey = X509_get_pubkey(signing_cert); +		 -		// validate a cert chain -bool LLBasicCertificateStore::validate(const LLCertificateChain& cert_chain) const +		if(pkey) +		{ +			int verify_code = X509_verify(child_cert, pkey); +			verify_result = ( verify_code > 0); +			EVP_PKEY_free(pkey); +		} +		else +		{ +			LL_WARNS("SECAPI") << "Could not validate the cert chain signature, as the public key of the signing cert could not be retrieved" << LL_ENDL; +		} + +	} +	else +	{ +		LL_WARNS("SECAPI") << "Signature verification failed as there are no certs in the chain" << LL_ENDL; +	} +	if(child_cert) +	{ +		X509_free(child_cert); +	} +	if(signing_cert) +	{ +		X509_free(signing_cert); +	} +	return verify_result; +} + +// validate the certificate chain against a store. +// There are many aspects of cert validatioin policy involved in +// trust validation.  The policies in this validation algorithm include +// * Hostname matching for SSL certs +// * Expiration time matching +// * Signature validation +// * Chain trust (is the cert chain trusted against the store) +// * Basic constraints +// * key usage and extended key usage +// TODO: We should add 'authority key identifier' for chaining. +// This algorithm doesn't simply validate the chain by itself +// and verify the last cert is in the certificate store, or points +// to a cert in the store.  It validates whether any cert in the chain +// is trusted in the store, even if it's not the last one. +void LLBasicCertificateChain::validate(int validation_policy, +									   LLPointer<LLCertificateStore> ca_store, +									   const LLSD& validation_params)  { -	return FALSE; + +	if(size() < 1) +	{ +		throw LLCertException(NULL, "No certs in chain"); +	} +	iterator current_cert = begin(); +	LLSD 	current_cert_info = (*current_cert)->getLLSD(); +	LLSD validation_date; +	if (validation_params.has(CERT_VALIDATION_DATE)) +	{ +		validation_date = validation_params[CERT_VALIDATION_DATE]; +	} + +	if (validation_policy & VALIDATION_POLICY_HOSTNAME) +	{ +		if(!validation_params.has(CERT_HOSTNAME)) +		{ +			throw LLCertException((*current_cert), "No hostname passed in for validation");			 +		} +		if(!current_cert_info.has(CERT_SUBJECT_NAME) || !current_cert_info[CERT_SUBJECT_NAME].has(CERT_NAME_CN)) +		{ +			throw LLInvalidCertificate((*current_cert));				 +		} +		 +		LL_INFOS("SECAPI") << "Validating the hostname " << validation_params[CERT_HOSTNAME].asString() <<  +		     "against the cert CN " << current_cert_info[CERT_SUBJECT_NAME][CERT_NAME_CN].asString() << LL_ENDL; +		if(!_cert_hostname_wildcard_match(validation_params[CERT_HOSTNAME].asString(), +										  current_cert_info[CERT_SUBJECT_NAME][CERT_NAME_CN].asString())) +		{ +			throw LLCertValidationHostnameException(validation_params[CERT_HOSTNAME].asString(), +													(*current_cert)); +		} +	} +	 + +	int depth = 0; +	LLPointer<LLCertificate> previous_cert; +	// loop through the cert chain, validating the current cert against the next one. +	while(current_cert != end()) +	{ +		 +		int local_validation_policy = validation_policy; +		if(current_cert == begin()) +		{ +			// for the child cert, we don't validate CA stuff +			local_validation_policy &= ~(VALIDATION_POLICY_CA_KU |  +										 VALIDATION_POLICY_CA_BASIC_CONSTRAINTS); +		} +		else +		{ +			// for non-child certs, we don't validate SSL Key usage +			local_validation_policy &= ~VALIDATION_POLICY_SSL_KU;				 +			if(!_verify_signature((*current_cert), +								  previous_cert)) +			{ +			   throw LLCertValidationInvalidSignatureException(previous_cert); +			} +		} +		_validateCert(local_validation_policy, +					  (*current_cert), +					  validation_params, +					  depth); +		 +		// look for a CA in the CA store that may belong to this chain. +		LLSD cert_llsd = (*current_cert)->getLLSD(); +		LLSD cert_search_params = LLSD::emptyMap();		 +		// is the cert itself in the store? +		cert_search_params[CERT_SHA1_DIGEST] = cert_llsd[CERT_SHA1_DIGEST]; +		LLCertificateStore::iterator found_store_cert = ca_store->find(cert_search_params); +		if(found_store_cert != ca_store->end()) +		{ +			return; +		} +		 +		// is the parent in the cert store? +			 +		cert_search_params = LLSD::emptyMap(); +		cert_search_params[CERT_SUBJECT_NAME_STRING] = cert_llsd[CERT_ISSUER_NAME_STRING]; +		found_store_cert = ca_store->find(cert_search_params); +		 +		if(found_store_cert != ca_store->end()) +		{ +			LLSD foo = (*found_store_cert)->getLLSD(); +			// validate the store cert against the depth +			_validateCert(validation_policy & VALIDATION_POLICY_CA_BASIC_CONSTRAINTS, +						  (*found_store_cert), +						  LLSD(), +						  depth); +			 +			// verify the signature of the CA +			if(!_verify_signature((*found_store_cert), +								  (*current_cert))) +			{ +				throw LLCertValidationInvalidSignatureException(*current_cert); +			}			 +			// successfully validated. +			return; +		} +		previous_cert = (*current_cert); +		current_cert++; +			   depth++; +	} +	if (validation_policy & VALIDATION_POLICY_TRUSTED) +	{ +		LLPointer<LLCertificate> untrusted_ca_cert = (*this)[size()-1]; +		// we reached the end without finding a trusted cert. +		throw LLCertValidationTrustException((*this)[size()-1]); + +	}  } +  // LLSecAPIBasicHandler Class  // Interface handler class for the various security storage handlers. @@ -369,7 +1038,39 @@ LLSecAPIBasicHandler::LLSecAPIBasicHandler()  	mLegacyPasswordPath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "password.dat");  	mProtectedDataMap = LLSD::emptyMap(); +	 +	mProtectedDataFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, +															"bin_conf.dat");	  	_readProtectedData(); + +	std::string store_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, +															"CA.pem"); +	// copy the CA file to a user writable location so we can manipulate it. +	// for this provider, by using a user writable file, there is a risk that +	// an attacking program can modify the file, but OS dependent providers +	// will reduce that risk. +	// by using a user file, modifications will be limited to one user if +	// we read-only the main file + + +	if (!LLFile::isfile(store_file)) +	{ + +		std::string ca_file_path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "CA.pem"); +		llifstream ca_file(ca_file_path.c_str(), llifstream::binary | llifstream::in); +		llofstream copied_store_file(store_file.c_str(), llofstream::binary | llofstream::out); + +		while(!ca_file.fail()) +		{ +			char buffer[BUFFER_READ_SIZE]; +			ca_file.read(buffer, sizeof(buffer)); +			copied_store_file.write(buffer, ca_file.gcount()); +		} +		ca_file.close(); +		copied_store_file.close(); +	} +	LL_INFOS("SECAPI") << "Loading certificate store from " << store_file << LL_ENDL; +	mStore = new LLBasicCertificateStore(store_file);  }  LLSecAPIBasicHandler::~LLSecAPIBasicHandler() @@ -537,7 +1238,7 @@ LLPointer<LLCertificate> LLSecAPIBasicHandler::getCertificate(X509* openssl_cert  // instantiate a chain from an X509_STORE_CTX  LLPointer<LLCertificateChain> LLSecAPIBasicHandler::getCertificateChain(const X509_STORE_CTX* chain)  { -	LLPointer<LLCertificateChain> result = NULL; +	LLPointer<LLCertificateChain> result = new LLBasicCertificateChain(chain);  	return result;  } @@ -546,8 +1247,7 @@ LLPointer<LLCertificateChain> LLSecAPIBasicHandler::getCertificateChain(const X5  // persisted)  LLPointer<LLCertificateStore> LLSecAPIBasicHandler::getCertificateStore(const std::string& store_id)  { -	LLPointer<LLCertificateStore> result; -	return result; +	return mStore;  }  // retrieve protected data @@ -659,7 +1359,7 @@ void LLSecAPIBasicHandler::saveCredential(LLPointer<LLCredential> cred, bool sav  	{  		credential["authenticator"] = cred->getAuthenticator();  	} -	LL_INFOS("Credentials") << "Saving Credential " << cred->getGrid() << ":" << cred->userID() << " " << save_authenticator << LL_ENDL; +	LL_INFOS("SECAPI") << "Saving Credential " << cred->getGrid() << ":" << cred->userID() << " " << save_authenticator << LL_ENDL;  	setProtectedData("credential", cred->getGrid(), credential);  	//*TODO: If we're saving Agni credentials, should we write the  	// credentials to the legacy password.dat/etc? @@ -742,3 +1442,64 @@ std::string LLSecAPIBasicCredential::asString() const  } +bool valueCompareLLSD(const LLSD& lhs, const LLSD& rhs) +{ +	if (lhs.type() != rhs.type()) +	{ +		return FALSE; +	} +    if (lhs.isMap()) +	{ +		// iterate through the map, verifying the right hand side has all of the +		// values that the left hand side has. +		for (LLSD::map_const_iterator litt = lhs.beginMap(); +			 litt != lhs.endMap(); +			 litt++) +		{ +			if (!rhs.has(litt->first)) +			{ +				return FALSE; +			} +		} +		 +		// Now validate that the left hand side has everything the +		// right hand side has, and that the values are equal. +		for (LLSD::map_const_iterator ritt = rhs.beginMap(); +			 ritt != rhs.endMap(); +			 ritt++) +		{ +			if (!lhs.has(ritt->first)) +			{ +				return FALSE; +			} +			if (!valueCompareLLSD(lhs[ritt->first], ritt->second)) +			{ +				return FALSE; +			} +		} +		return TRUE; +	} +    else if (lhs.isArray()) +	{ +		LLSD::array_const_iterator ritt = rhs.beginArray(); +		// iterate through the array, comparing +		for (LLSD::array_const_iterator litt = lhs.beginArray(); +			 litt != lhs.endArray(); +			 litt++) +		{ +			if (!valueCompareLLSD(*ritt, *litt)) +			{ +				return FALSE; +			} +			ritt++; +		} +		 +		return (ritt == rhs.endArray()); +	} +    else +	{ +		// simple type, compare as string +		return (lhs.asString() == rhs.asString()); +	} +	 +} diff --git a/indra/newview/llsechandler_basic.h b/indra/newview/llsechandler_basic.h index 5d81b6e190..e041322260 100644 --- a/indra/newview/llsechandler_basic.h +++ b/indra/newview/llsechandler_basic.h @@ -57,59 +57,147 @@ public:  	virtual ~LLBasicCertificate(); -	virtual std::string getPem(); -	virtual std::vector<U8> getBinary(); -	virtual LLSD getLLSD(); +	virtual std::string getPem() const; +	virtual std::vector<U8> getBinary() const; +	virtual LLSD getLLSD() const; -	virtual X509* getOpenSSLX509(); +	virtual X509* getOpenSSLX509() const; +	 +	// set llsd elements for testing +	void setLLSD(const std::string name, const LLSD& value) { mLLSDInfo[name] = value; }  protected: +  	// certificates are stored as X509 objects, as validation and  	// other functionality is via openssl  	X509* mCert; +	 +	LLSD& _initLLSD(); +	LLSD mLLSDInfo;  }; -// class LLCertificateStore -// represents a store of certificates, typically a store of root CA -// certificates.  The store can be persisted, and can be used to validate -// a cert chain -// -class LLBasicCertificateStore : public LLCertificateStore + +// class LLBasicCertificateVector +// Class representing a list of certificates +// This implementation uses a stl vector of certificates. +class LLBasicCertificateVector : virtual public LLCertificateVector  { +	  public: -	LLBasicCertificateStore(const std::string& filename); -	LLBasicCertificateStore(const X509_STORE* store); -	virtual ~LLBasicCertificateStore(); +	LLBasicCertificateVector() {} -	virtual X509_STORE* getOpenSSLX509Store();  // return an openssl X509_STORE   -	// for this store +	virtual ~LLBasicCertificateVector() {} -	// add a copy of a cert to the store -	virtual void  append(const LLCertificate& cert); +	// Implementation of the basic iterator implementation. +	// The implementation uses a vector iterator derived from  +	// the vector in the LLBasicCertificateVector class +	class BasicIteratorImpl : public iterator_impl +	{ +	public: +		BasicIteratorImpl(std::vector<LLPointer<LLCertificate> >::iterator _iter) { mIter = _iter;} +		virtual ~BasicIteratorImpl() {}; +		// seek forward or back.  Used by the operator++/operator-- implementations +		virtual void seek(bool incr) +		{ +			if(incr) +			{ +				mIter++; +			} +			else +			{ +				mIter--; +			} +		} +		// create a copy of the iterator implementation class, used by the iterator copy constructor +		virtual LLPointer<iterator_impl> clone() const +		{ +			return new BasicIteratorImpl(mIter); +		} +		 +		virtual bool equals(const LLPointer<iterator_impl>& _iter) const +		{ +			const BasicIteratorImpl *rhs_iter = dynamic_cast<const BasicIteratorImpl *>(_iter.get()); +			return (mIter == rhs_iter->mIter); +		} +		virtual LLPointer<LLCertificate> get() +		{ +			return *mIter; +		} +	protected: +		friend class LLBasicCertificateVector; +		std::vector<LLPointer<LLCertificate> >::iterator mIter; +	}; -	// add a copy of a cert to the store -	virtual void insert(const int index, const LLCertificate& cert); +	// numeric index of the vector +	virtual LLPointer<LLCertificate> operator[](int _index) { return mCerts[_index];} -	// remove a certificate from the store -	virtual void remove(int index); +	// Iteration +	virtual iterator begin() { return iterator(new BasicIteratorImpl(mCerts.begin())); } +	 +	virtual iterator end() {  return iterator(new BasicIteratorImpl(mCerts.end())); } +	 +	// find a cert given params +	virtual iterator find(const LLSD& params); -	// return a certificate at the index -	virtual LLPointer<LLCertificate> operator[](int index);  	// return the number of certs in the store -	virtual int len() const; +	virtual int size() const { return mCerts.size(); }	 +	 +	// insert the cert to the store.  if a copy of the cert already exists in the store, it is removed first +	virtual void  add(LLPointer<LLCertificate> cert) { insert(end(), cert); } -	// load the store from a persisted location -	virtual void load(const std::string& store_id); +	// insert the cert to the store.  if a copy of the cert already exists in the store, it is removed first +	virtual void  insert(iterator _iter, LLPointer<LLCertificate> cert);	 +	 +	// remove a certificate from the store +	virtual LLPointer<LLCertificate> erase(iterator _iter); +	 +protected: +	std::vector<LLPointer<LLCertificate> >mCerts;	 +}; + +// class LLCertificateStore +// represents a store of certificates, typically a store of root CA +// certificates.  The store can be persisted, and can be used to validate +// a cert chain +// +class LLBasicCertificateStore : virtual public LLBasicCertificateVector, public LLCertificateStore +{ +public: +	LLBasicCertificateStore(const std::string& filename); +	void load_from_file(const std::string& filename); +	 +	virtual ~LLBasicCertificateStore();  	// persist the store  	virtual void save();  	// return the store id -	virtual std::string storeId(); +	virtual std::string storeId() const; -	// validate a cert chain -	virtual bool validate(const LLCertificateChain& cert_chain) const; +protected: +	std::vector<LLPointer<LLCertificate> >mCerts; +	std::string mFilename;  }; +// class LLCertificateChain +// Class representing a chain of certificates in order, with the  +// first element being the child cert. +class LLBasicCertificateChain : virtual public LLBasicCertificateVector, public LLCertificateChain +{ +	 +public: +	LLBasicCertificateChain(const X509_STORE_CTX * store); +	 +	virtual ~LLBasicCertificateChain() {} +	 +	// validate a certificate chain against a certificate store, using the +	// given validation policy. +	virtual void validate(int validation_policy, +						  LLPointer<LLCertificateStore> ca_store, +						  const LLSD& validation_params); +}; + + +  // LLSecAPIBasicCredential class  class LLSecAPIBasicCredential : public LLCredential  { @@ -182,10 +270,13 @@ protected:  	std::string mProtectedDataFilename;  	LLSD mProtectedDataMap; +	LLPointer<LLBasicCertificateStore> mStore;  	std::string mLegacyPasswordPath;  }; +bool valueCompareLLSD(const LLSD& lhs, const LLSD& rhs); +  #endif // LLSECHANDLER_BASIC diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index dd991c8eff..6f7a4e2f6a 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -261,6 +261,9 @@ bool callback_choose_gender(const LLSD& notification, const LLSD& response);  void init_start_screen(S32 location_id);  void release_start_screen();  void reset_login(); +LLSD transform_cert_args(LLPointer<LLCertificate> cert); +void general_cert_done(const LLSD& notification, const LLSD& response); +void trust_cert_done(const LLSD& notification, const LLSD& response);  void apply_udp_blacklist(const std::string& csv);  bool process_login_success_response();  void transition_back_to_login_panel(const std::string& emsg); @@ -1053,10 +1056,11 @@ bool idle_startup()  		{  			LL_INFOS("LLStartup") << "Login failed, LLLoginInstance::getResponse(): "  			                      << LLLoginInstance::getInstance()->getResponse() << LL_ENDL; +			LLSD response = LLLoginInstance::getInstance()->getResponse();  			// Still have error conditions that may need some   			// sort of handling. -			std::string reason_response = LLLoginInstance::getInstance()->getResponse("reason"); -			std::string message_response = LLLoginInstance::getInstance()->getResponse("message"); +			std::string reason_response = response["reason"]; +			std::string message_response = response["message"];  			if(!message_response.empty())  			{ @@ -1090,18 +1094,65 @@ bool idle_startup()  				LLLoginInstance::getInstance()->disconnect();  				LLAppViewer::instance()->forceQuit();  			} -			else +			else   			{ -				// Don't pop up a notification in the TOS case because -				// LLFloaterTOS::onCancel() already scolded the user. -				if (reason_response != "tos") +				if (reason_response != "tos")   				{ -					LLSD args; -					args["ERROR_MESSAGE"] = emsg.str(); -					LL_INFOS("LLStartup") << "Notification: " << args << LL_ENDL; -					LLNotificationsUtil::add("ErrorMessage", args, LLSD(), login_alert_done); +					// Don't pop up a notification in the TOS case because +					// LLFloaterTOS::onCancel() already scolded the user. +					std::string error_code; +					if(response.has("errorcode")) +					{ +						error_code = response["errorcode"].asString(); +					} +					if ((reason_response == "CURLError") &&  +						(error_code == "SSL_CACERT" || error_code == "SSL_PEER_CERTIFICATE") &&  +						response.has("certificate")) +					{ +						// This was a certificate error, so grab the certificate +						// and throw up the appropriate dialog. +						LLPointer<LLCertificate> certificate = gSecAPIHandler->getCertificate(response["certificate"]); +						if(certificate) +						{ +							LLSD args = transform_cert_args(certificate); + +							if(error_code == "SSL_CACERT") +							{ +								// if we are handling an untrusted CA, throw up the dialog                              +								// with the 'trust this CA' button.                                                     +								LLNotificationsUtil::add("TrustCertificateError", args, response, +														trust_cert_done); +								 +								show_connect_box = true; +							} +							else +							{ +								// the certificate exception returns a unique string for each type of exception.        +								// we grab this string via the LLUserAuth object, and use that to grab the localized    +								// string.                                                                              +								args["REASON"] = LLTrans::getString(message_response); +								 +								LLNotificationsUtil::add("GeneralCertificateError", args, response, +														 general_cert_done); +								 +								reset_login(); +								gSavedSettings.setBOOL("AutoLogin", FALSE); +								show_connect_box = true; +								 +							} + +						} +					} +					else  +					{ +						// This wasn't a certificate error, so throw up the normal +						// notificatioin message. +						LLSD args; +						args["ERROR_MESSAGE"] = emsg.str(); +						LL_INFOS("LLStartup") << "Notification: " << args << LL_ENDL; +						LLNotificationsUtil::add("ErrorMessage", args, LLSD(), login_alert_done); +					}  				} -  				//setup map of datetime strings to codes and slt & local time offset from utc  				// *TODO: Does this need to be here?  				LLStringOps::setupDatetimeInfo (false); @@ -1126,6 +1177,7 @@ bool idle_startup()  				LLNotificationsUtil::add("ErrorMessage", args, LLSD(), login_alert_done);  				transition_back_to_login_panel(emsg.str());  				show_connect_box = true; +				return FALSE;  			}  		}  		return FALSE; @@ -2370,7 +2422,9 @@ const std::string FEMALE_OUTFIT_FOLDER = "Female Shape & Outfit";  const S32 OPT_CLOSED_WINDOW = -1;  const S32 OPT_MALE = 0;  const S32 OPT_FEMALE = 1; - +const S32 OPT_TRUST_CERT = 0; +const S32 OPT_CANCEL_TRUST = 1; +	  bool callback_choose_gender(const LLSD& notification, const LLSD& response)  {	  	S32 option = LLNotificationsUtil::getSelectedOption(notification, response); @@ -2633,6 +2687,91 @@ bool login_alert_done(const LLSD& notification, const LLSD& response)  	return false;  } +// parse the certificate information into args for the  +// certificate notifications +LLSD transform_cert_args(LLPointer<LLCertificate> cert) +{ +	LLSD args = LLSD::emptyMap(); +	std::string value; +	LLSD cert_info = cert->getLLSD(); +	// convert all of the elements in the cert into                                         +	// args for the xml dialog, so we have flexability to                                   +	// display various parts of the cert by only modifying                                  +	// the cert alert dialog xml.                                                           +	for(LLSD::map_iterator iter = cert_info.beginMap(); +		iter != cert_info.endMap(); +		iter++) +	{ +		// key usage and extended key usage                                             +		// are actually arrays, and we want to format them as comma separated           +		// strings, so special case those.                                              +		LLSDSerialize::toXML(cert_info[iter->first], std::cout); +		if((iter->first== std::string(CERT_KEY_USAGE)) | +		   (iter->first == std::string(CERT_EXTENDED_KEY_USAGE))) +		{ +			value = ""; +			LLSD usage = cert_info[iter->first]; +			for (LLSD::array_iterator usage_iter = usage.beginArray(); +				 usage_iter != usage.endArray(); +				 usage_iter++) +			{ +				 +				if(usage_iter != usage.beginArray()) +				{ +					value += ", "; +				} +				 +				value += (*usage_iter).asString(); +			} +			 +		} +		else +		{ +			value = iter->second.asString(); +		} +		 +		std::string name = iter->first; +		std::transform(name.begin(), name.end(), name.begin(), +					   (int(*)(int))toupper); +		args[name.c_str()] = value; +	} +	return args; +} + + +// when we handle a cert error, give focus back to the login panel +void general_cert_done(const LLSD& notification, const LLSD& response) +{ +	LLStartUp::setStartupState( STATE_LOGIN_SHOW );			 +	LLPanelLogin::giveFocus(); +} + +// check to see if the user wants to trust the cert. +// if they do, add it to the cert store and  +void trust_cert_done(const LLSD& notification, const LLSD& response) +{ +	S32 option = LLNotification::getSelectedOption(notification, response);	 +	switch(option) +	{ +		case OPT_TRUST_CERT: +		{ +			LLPointer<LLCertificate> cert = gSecAPIHandler->getCertificate(notification["payload"]["certificate"]); +			LLPointer<LLCertificateStore> store = gSecAPIHandler->getCertificateStore(gSavedSettings.getString("CertStore"));			 +			store->add(cert); +			store->save(); +			LLStartUp::setStartupState( STATE_LOGIN_CLEANUP );	 +			break; +		} +		case OPT_CANCEL_TRUST: +			reset_login(); +			gSavedSettings.setBOOL("AutoLogin", FALSE);			 +			LLStartUp::setStartupState( STATE_LOGIN_SHOW );				 +		default: +			LLPanelLogin::giveFocus(); +			break; +	} + +}  void apply_udp_blacklist(const std::string& csv)  { diff --git a/indra/newview/llviewernetwork.cpp b/indra/newview/llviewernetwork.cpp index fcfaf1eef2..82dc459777 100644 --- a/indra/newview/llviewernetwork.cpp +++ b/indra/newview/llviewernetwork.cpp @@ -5,7 +5,7 @@   *   * $LicenseInfo:firstyear=2006&license=viewergpl$   *  - * Copyright (c) 2006-2007, Linden Research, Inc. + * Copyright (c) 2006-2010, Linden Research, Inc.   *    * Second Life Viewer Source Code   * The source code in this file ("Source Code") is provided by Linden Lab @@ -60,12 +60,6 @@ LLGridManager::LLGridManager()  } -LLGridManager::LLGridManager(const std::string& grid_file) -{ -	// initialize with an explicity grid file for testing. -	initialize(grid_file); -} -  //  // LLGridManager - class for managing the list of known grids, and the current  // selection @@ -240,12 +234,8 @@ void LLGridManager::initialize(const std::string& grid_file)  	// load a grid from the command line.  	// if the actual grid name is specified from the command line,  	// set it as the 'selected' grid. -	LLSD cmd_line_grid = gSavedSettings.getString("CmdLineGridChoice"); -	if (gSavedSettings.controlExists("CmdLineGridChoice")) -	{ -		mGridName = gSavedSettings.getString("CmdLineGridChoice"); -		LL_INFOS("GridManager") << "Grid Name: " << mGridName << LL_ENDL;		 -	} +	mGridName = gSavedSettings.getString("CmdLineGridChoice"); +	LL_INFOS("GridManager") << "Grid Name: " << mGridName << LL_ENDL;		  	// If a command line login URI was passed in, so we should add the command  	// line grid to the list of grids diff --git a/indra/newview/llviewernetwork.h b/indra/newview/llviewernetwork.h index 7b3ce9c499..0642845d54 100644 --- a/indra/newview/llviewernetwork.h +++ b/indra/newview/llviewernetwork.h @@ -5,7 +5,7 @@   *   * $LicenseInfo:firstyear=2006&license=viewergpl$   *  - * Copyright (c) 2006-2007, Linden Research, Inc. + * Copyright (c) 2006-2010, Linden Research, Inc.   *    * Second Life Viewer Source Code   * The source code in this file ("Source Code") is provided by Linden Lab @@ -43,6 +43,7 @@ extern const char* DEFAULT_LOGIN_PAGE;  #define GRID_LOGIN_PAGE_VALUE "login_page"  #define GRID_IS_SYSTEM_GRID_VALUE "system_grid"  #define GRID_IS_FAVORITE_VALUE "favorite" +#define GRID_IS_VISIBLE_VALUE "visible"  #define GRID_LOGIN_CREDENTIAL_PAGE_TYPE_VALUE "credential_type"  #define GRID_LOGIN_CREDENTIAL_PAGE_TYPE_AGENT "agent"  #define GRID_LOGIN_CREDENTIAL_PAGE_TYPE_ACCOUNT "account" @@ -78,7 +79,6 @@ public:  	// when the grid manager is instantiated, the default grids are automatically  	// loaded, and the grids favorites list is loaded from the xml file. -	LLGridManager(const std::string& grid_file);  	LLGridManager();  	~LLGridManager(); diff --git a/indra/newview/llxmlrpclistener.cpp b/indra/newview/llxmlrpclistener.cpp index 15417614af..8237132ac5 100644 --- a/indra/newview/llxmlrpclistener.cpp +++ b/indra/newview/llxmlrpclistener.cpp @@ -28,6 +28,7 @@  #include "llerror.h"  #include "stringize.h"  #include "llxmlrpctransaction.h" +#include "llsecapi.h"  #if LL_WINDOWS  #pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally @@ -356,7 +357,22 @@ public:                                       << data["errorcode"].asString()                                       << " (" << data["error"].asString() << ")"                                       << LL_ENDL; -        // In addition to CURLE_OK, LLUserAuth distinguishes different error +		 +		switch (curlcode) +		{ +			case CURLE_SSL_PEER_CERTIFICATE: +			case CURLE_SSL_CACERT: +			{ +				LLPointer<LLCertificate> error_cert(mTransaction->getErrorCert()); +				if(error_cert) +				{ +					data["certificate"] = error_cert->getPem(); +				} +				break; +			} +			default: +				break; +		}          // values of 'curlcode':          // CURLE_COULDNT_RESOLVE_HOST,          // CURLE_SSL_PEER_CERTIFICATE, diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index 70859e8ea5..da61840761 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -31,6 +31,9 @@   */  #include "llviewerprecompiledheaders.h" +#include <openssl/x509_vfy.h> +#include <openssl/ssl.h> +#include "llsecapi.h"  #include "llxmlrpctransaction.h"  #include "llxmlrpclistener.h" @@ -176,6 +179,8 @@ public:  	std::string			mResponseText;  	XMLRPC_REQUEST		mResponse; +	std::string         mCertStore; +	LLPointer<LLCertificate> mErrorCert;  	Impl(const std::string& uri, XMLRPC_REQUEST request, bool useGzip);  	Impl(const std::string& uri, @@ -190,7 +195,8 @@ public:  private:  	void init(XMLRPC_REQUEST request, bool useGzip); - +	static int _sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param); +	static CURLcode _sslCtxFunction(CURL * curl, void *sslctx, void *param);  	static size_t curlDownloadCallback(  		char* data, size_t size, size_t nmemb, void* user_data);  }; @@ -228,8 +234,74 @@ LLXMLRPCTransaction::Impl::Impl(const std::string& uri,      XMLRPC_RequestFree(request, 1);  } +// _sslCertVerifyCallback +// callback called when a cert verification is requested. +// calls SECAPI to validate the context +int LLXMLRPCTransaction::Impl::_sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param) +{ +	LLXMLRPCTransaction::Impl *transaction = (LLXMLRPCTransaction::Impl *)param; +	LLPointer<LLCertificateStore> store = gSecAPIHandler->getCertificateStore(transaction->mCertStore); +	LLPointer<LLCertificateChain> chain = gSecAPIHandler->getCertificateChain(ctx); +	LLSD validation_params = LLSD::emptyMap(); +	LLURI uri(transaction->mURI); +	validation_params[CERT_HOSTNAME] = uri.hostName(); +	try +	{ +		chain->validate(VALIDATION_POLICY_SSL, store, validation_params); +	} +	catch (LLCertValidationTrustException& cert_exception) +	{ +		// this exception is is handled differently than the general cert +		// exceptions, as we allow the user to actually add the certificate +		// for trust. +		// therefore we pass back a different error code +		// NOTE: We're currently 'wired' to pass around CURL error codes.  This is +		// somewhat clumsy, as we may run into errors that do not map directly to curl +		// error codes.  Should be refactored with login refactoring, perhaps. +		transaction->mCurlCode = CURLE_SSL_CACERT; +		// set the status directly.  set curl status generates error messages and we want +		// to use the fixed ones from the exceptions +		transaction->setStatus(StatusCURLError, cert_exception.getMessage(), std::string()); +		// We should probably have a more generic way of passing information +		// back to the error handlers. +		transaction->mErrorCert = cert_exception.getCert(); +		return 0;		 +	} +	catch (LLCertException& cert_exception) +	{ +		transaction->mCurlCode = CURLE_SSL_PEER_CERTIFICATE; +		// set the status directly.  set curl status generates error messages and we want +		// to use the fixed ones from the exceptions +		transaction->setStatus(StatusCURLError, cert_exception.getMessage(), std::string()); +		transaction->mErrorCert = cert_exception.getCert(); +		return 0; +	} +	catch (...) +	{ +		// any other odd error, we just handle as a connect error. +		transaction->mCurlCode = CURLE_SSL_CONNECT_ERROR; +		transaction->setCurlStatus(CURLE_SSL_CONNECT_ERROR); +		return 0; +	} +	return 1; +} +// _sslCtxFunction +// Callback function called when an SSL Context is created via CURL +// used to configure the context for custom cert validate(<, <#const & xs#>, <#T * #>, <#long #>)tion +// based on SECAPI +CURLcode LLXMLRPCTransaction::Impl::_sslCtxFunction(CURL * curl, void *sslctx, void *param) +{ +	SSL_CTX * ctx = (SSL_CTX *) sslctx; +	// disable any default verification for server certs +	SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); +	// set the verification callback. +	SSL_CTX_set_cert_verify_callback(ctx, _sslCertVerifyCallback, param); +	// the calls are void +	return CURLE_OK; +	 +}  void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip)  { @@ -237,6 +309,7 @@ void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip)  	{  		mCurlRequest = new LLCurlEasyRequest();  	} +	mErrorCert = NULL;  	if (gSavedSettings.getBOOL("BrowserProxyEnabled"))  	{ @@ -252,11 +325,13 @@ void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip)  //	mCurlRequest->setopt(CURLOPT_VERBOSE, 1); // usefull for debugging  	mCurlRequest->setopt(CURLOPT_NOSIGNAL, 1);  	mCurlRequest->setWriteCallback(&curlDownloadCallback, (void*)this); -    	BOOL vefifySSLCert = !gSavedSettings.getBOOL("NoVerifySSLCert"); +	BOOL vefifySSLCert = !gSavedSettings.getBOOL("NoVerifySSLCert"); +	mCertStore = gSavedSettings.getString("CertStore");  	mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, vefifySSLCert);  	mCurlRequest->setopt(CURLOPT_SSL_VERIFYHOST, vefifySSLCert ? 2 : 0);  	// Be a little impatient about establishing connections.  	mCurlRequest->setopt(CURLOPT_CONNECTTIMEOUT, 40L); +	mCurlRequest->setSSLCtxCallback(_sslCtxFunction, (void *)this);  	/* Setting the DNS cache timeout to -1 disables it completely.  	   This might help with bug #503 */ @@ -342,11 +417,19 @@ bool LLXMLRPCTransaction::Impl::process()  		{  			if (result != CURLE_OK)  			{ -				setCurlStatus(result); -				llwarns << "LLXMLRPCTransaction CURL error " -						<< mCurlCode << ": " << mCurlRequest->getErrorString() << llendl; -				llwarns << "LLXMLRPCTransaction request URI: " -						<< mURI << llendl; +				if ((result != CURLE_SSL_PEER_CERTIFICATE) && +					(result != CURLE_SSL_CACERT)) +				{ +					// if we have a curl error that's not already been handled +					// (a non cert error), then generate the error message as +					// appropriate +					setCurlStatus(result); +				 +					llwarns << "LLXMLRPCTransaction CURL error " +					<< mCurlCode << ": " << mCurlRequest->getErrorString() << llendl; +					llwarns << "LLXMLRPCTransaction request URI: " +					<< mURI << llendl; +				}  				return true;  			} @@ -424,7 +507,6 @@ void LLXMLRPCTransaction::Impl::setStatus(EStatus status,  			case StatusComplete:  				mStatusMessage = "(done)";  				break; -				  			default:  				// Usually this means that there's a problem with the login server,  				// not with the client.  Direct user to status page. @@ -540,6 +622,11 @@ std::string LLXMLRPCTransaction::statusMessage()  	return impl.mStatusMessage;  } +LLPointer<LLCertificate> LLXMLRPCTransaction::getErrorCert() +{ +	return impl.mErrorCert; +} +  std::string LLXMLRPCTransaction::statusURI()  {  	return impl.mStatusURI; diff --git a/indra/newview/llxmlrpctransaction.h b/indra/newview/llxmlrpctransaction.h index c835423d67..8beb2e2623 100644 --- a/indra/newview/llxmlrpctransaction.h +++ b/indra/newview/llxmlrpctransaction.h @@ -38,6 +38,7 @@  typedef struct _xmlrpc_request* XMLRPC_REQUEST;  typedef struct _xmlrpc_value* XMLRPC_VALUE;  	// foward decl of types from xmlrpc.h (this usage is type safe) +class LLCertificate;  class LLXMLRPCValue  	// a c++ wrapper around XMLRPC_VALUE @@ -115,6 +116,8 @@ public:  	EStatus status(int* curlCode);  		// return status, and extended CURL code, if code isn't null +	 +	LLPointer<LLCertificate> getErrorCert();  	std::string statusMessage();  		// return a message string, suitable for showing the user  	std::string statusURI(); diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 6d5f0bedb0..c4cbcb1dc8 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -2371,6 +2371,48 @@ Please choose the male or female avatar. You can change your mind later.       notext="Female"       yestext="Male"/>    </notification> +  <notification icon="alertmodal.tga" +		name="GeneralCertificateError" +		type="alertmodal"> +Could not connect to the server. +[REASON] + +SubjectName: [SUBJECT_NAME_STRING] +IssuerName: [ISSUER_NAME_STRING] +Valid From: [VALID_FROM] +Valid To: [VALID_TO] +MD5 Fingerprint: [SHA1_DIGEST] +SHA1 Fingerprint: [MD5_DIGEST] +Key Usage: [KEYUSAGE] +Extended Key Usage: [EXTENDEDKEYUSAGE] +Subject Key Identifier: [SUBJECTKEYIDENTIFIER] +    <usetemplate +     name="okbutton" +     yestext="OK"/> +   </notification> + +  <notification icon="alertmodal.tga" +		name="TrustCertificateError" +		type="alertmodal"> +The certification authority for this server is not known. + +Certificate Information: +SubjectName: [SUBJECT_NAME_STRING] +IssuerName: [ISSUER_NAME_STRING] +Valid From: [VALID_FROM] +Valid To: [VALID_TO] +MD5 Fingerprint: [SHA1_DIGEST] +SHA1 Fingerprint: [MD5_DIGEST] +Key Usage: [KEYUSAGE] +Extended Key Usage: [EXTENDEDKEYUSAGE] +Subject Key Identifier: [SUBJECTKEYIDENTIFIER] + +Would you like to trust this authority? +    <usetemplate +     name="okcancelbuttons" +     notext="Cancel" +     yestext="Trust"/> +  </notification>    <notification     icon="alertmodal.tga" diff --git a/indra/newview/tests/llsecapi_test.cpp b/indra/newview/tests/llsecapi_test.cpp new file mode 100644 index 0000000000..22bc47b6d3 --- /dev/null +++ b/indra/newview/tests/llsecapi_test.cpp @@ -0,0 +1,188 @@ +/**  + * @file llsecapi_test.cpp + * @author Roxie + * @date 2009-02-10 + * @brief Test the sec api functionality + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + *  + * Copyright (c) 2009, Linden Research, Inc. + *  + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden LregisterSecAPIab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab.  Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + *  + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + *  + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + *  + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ +#include "../llviewerprecompiledheaders.h" +#include "../llviewernetwork.h" +#include "../test/lltut.h" +#include "../llsecapi.h" +#include "../../llxml/llcontrol.h" + + +//----------------------------------------------------------------------------                +// Mock objects for the dependencies of the code we're testing                                + +LLControlGroup::LLControlGroup(const std::string& name) +: LLInstanceTracker<LLControlGroup, std::string>(name) {} +LLControlGroup::~LLControlGroup() {} +BOOL LLControlGroup::declareString(const std::string& name, +                                   const std::string& initial_val, +                                   const std::string& comment, +                                   BOOL persist) {return TRUE;} +void LLControlGroup::setString(const std::string& name, const std::string& val){} +std::string LLControlGroup::getString(const std::string& name) +{ +	return ""; +} + + +LLControlGroup gSavedSettings("test"); +class LLSecAPIBasicHandler : public LLSecAPIHandler +{ +protected: +	LLPointer<LLCertificateChain> mCertChain; +	LLPointer<LLCertificate> mCert; +	LLPointer<LLCertificateStore> mCertStore; +	LLSD mLLSD; +	 +public: +	LLSecAPIBasicHandler() {} +	 +	virtual ~LLSecAPIBasicHandler() {} +	 +	// instantiate a certificate from a pem string +	virtual LLPointer<LLCertificate> getCertificate(const std::string& pem_cert)  +	{  +		return mCert;  +	} +	 +	 +	// instiate a certificate from an openssl X509 structure +	virtual LLPointer<LLCertificate> getCertificate(X509* openssl_cert)  +	{	 +		return mCert;  +	} +	 +	 +	// instantiate a chain from an X509_STORE_CTX +	virtual LLPointer<LLCertificateChain> getCertificateChain(const X509_STORE_CTX* chain)  +	{  +		return mCertChain;  +	} +	 +	// instantiate a cert store given it's id.  if a persisted version +	// exists, it'll be loaded.  If not, one will be created (but not +	// persisted) +	virtual LLPointer<LLCertificateStore> getCertificateStore(const std::string& store_id)  +	{  +		return mCertStore;  +	} +	 +	// persist data in a protected store +	virtual void setProtectedData(const std::string& data_type, +								  const std::string& data_id, +								  const LLSD& data) {} +	 +	// retrieve protected data +	virtual LLSD getProtectedData(const std::string& data_type, +								  const std::string& data_id)  +	{  +		return mLLSD; +	} +	 +	virtual void deleteProtectedData(const std::string& data_type, +									 const std::string& data_id) +	{ +	} +	 +	virtual LLPointer<LLCredential> createCredential(const std::string& grid, +													 const LLSD& identifier,  +													 const LLSD& authenticator) +	{ +		LLPointer<LLCredential> cred = NULL; +		return cred; +	} +	 +	virtual LLPointer<LLCredential> loadCredential(const std::string& grid) +	{ +		LLPointer<LLCredential> cred = NULL; +		return cred; +	} +	 +	virtual void saveCredential(LLPointer<LLCredential> cred, bool save_authenticator) {} +	 +	virtual void deleteCredential(LLPointer<LLCredential> cred) {} +}; + +// ------------------------------------------------------------------------------------------- +// TUT +// ------------------------------------------------------------------------------------------- +namespace tut +{ +	// Test wrapper declaration : wrapping nothing for the moment +	struct secapiTest +	{ +		 +		secapiTest() +		{ +		} +		~secapiTest() +		{ +		} +	}; +	 +	// Tut templating thingamagic: test group, object and test instance +	typedef test_group<secapiTest> secapiTestFactory; +	typedef secapiTestFactory::object secapiTestObject; +	tut::secapiTestFactory tut_test("llsecapi"); +	 +	// --------------------------------------------------------------------------------------- +	// Test functions  +	// --------------------------------------------------------------------------------------- +	// registration +	template<> template<> +	void secapiTestObject::test<1>() +	{ +		// retrieve an unknown handler + +		ensure("'Unknown' handler should be NULL", !(BOOL)getSecHandler("unknown")); +		LLPointer<LLSecAPIHandler> test1_handler =  new LLSecAPIBasicHandler(); +		registerSecHandler("sectest1", test1_handler); +		ensure("'Unknown' handler should be NULL", !(BOOL)getSecHandler("unknown")); +		LLPointer<LLSecAPIHandler> retrieved_test1_handler = getSecHandler("sectest1"); +		ensure("Retrieved sectest1 handler should be the same",  +			   retrieved_test1_handler == test1_handler); +		 +		// insert a second handler +		LLPointer<LLSecAPIHandler> test2_handler =  new LLSecAPIBasicHandler(); +		registerSecHandler("sectest2", test2_handler); +		ensure("'Unknown' handler should be NULL", !(BOOL)getSecHandler("unknown")); +		retrieved_test1_handler = getSecHandler("sectest1"); +		ensure("Retrieved sectest1 handler should be the same",  +			   retrieved_test1_handler == test1_handler); + +		LLPointer<LLSecAPIHandler> retrieved_test2_handler = getSecHandler("sectest2"); +		ensure("Retrieved sectest1 handler should be the same",  +			   retrieved_test2_handler == test2_handler); +		 +	} +}
\ No newline at end of file diff --git a/indra/newview/tests/llsechandler_basic_test.cpp b/indra/newview/tests/llsechandler_basic_test.cpp index a5554d55a5..f52ebc198d 100644 --- a/indra/newview/tests/llsechandler_basic_test.cpp +++ b/indra/newview/tests/llsechandler_basic_test.cpp @@ -44,9 +44,60 @@  #include <ios>  #include <llsdserialize.h>  #include <openssl/pem.h> +#include <openssl/err.h> +#include <openssl/evp.h>  #include "llxorcipher.h" -LLControlGroup gSavedSettings; +#define ensure_throws(str, exc_type, cert, func, ...) \ +try \ +{ \ +func(__VA_ARGS__); \ +fail("throws, " str); \ +} \ +catch(exc_type& except) \ +{ \ +ensure("Exception cert is incorrect for " str, except.getCert() == cert); \ +} + +extern bool _cert_hostname_wildcard_match(const std::string& hostname, const std::string& wildcard_string); + +//----------------------------------------------------------------------------                +// Mock objects for the dependencies of the code we're testing                                + +std::string gFirstName; +std::string gLastName; +LLControlGroup::LLControlGroup(const std::string& name) +: LLInstanceTracker<LLControlGroup, std::string>(name) {} +LLControlGroup::~LLControlGroup() {} +BOOL LLControlGroup::declareString(const std::string& name, +                                   const std::string& initial_val, +                                   const std::string& comment, +                                   BOOL persist) {return TRUE;} +void LLControlGroup::setString(const std::string& name, const std::string& val){} +std::string LLControlGroup::getString(const std::string& name) +{ + +	if (name == "FirstName") +		return gFirstName; +	else if (name == "LastName") +		return gLastName; +	return ""; +} + +LLSD LLCredential::getLoginParams() +{ +	LLSD result = LLSD::emptyMap(); +	 +	// legacy credential +	result["passwd"] = "$1$testpasssd"; +	result["first"] = "myfirst"; +	result["last"] ="mylast"; +	return result; +} + + + +LLControlGroup gSavedSettings("test");  unsigned char gMACAddress[MAC_ADDRESS_BYTES] = {77,21,46,31,89,2};  // ------------------------------------------------------------------------------------------- @@ -57,44 +108,91 @@ namespace tut  	// Test wrapper declaration : wrapping nothing for the moment  	struct sechandler_basic_test  	{ -		std::string mPemTestCert; +		std::string mPemTestCert, mPemRootCert, mPemIntermediateCert, mPemChildCert;  		std::string mDerFormat; -		X509 *mX509TestCert; -		LLBasicCertificate* mTestCert; +		X509 *mX509TestCert, *mX509RootCert, *mX509IntermediateCert, *mX509ChildCert;  		sechandler_basic_test()  		{ +			OpenSSL_add_all_algorithms(); +			OpenSSL_add_all_ciphers(); +			OpenSSL_add_all_digests();	 +			ERR_load_crypto_strings(); +			gFirstName = ""; +			gLastName = "";  			LLFile::remove("test_password.dat");  			LLFile::remove("sechandler_settings.tmp");			  			mPemTestCert = "-----BEGIN CERTIFICATE-----\n" -"MIIEuDCCA6CgAwIBAgIBBDANBgkqhkiG9w0BAQUFADCBtDELMAkGA1UEBhMCQlIx\n" -"EzARBgNVBAoTCklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25h\n" -"bCBkZSBUZWNub2xvZ2lhIGRhIEluZm9ybWFjYW8gLSBJVEkxETAPBgNVBAcTCEJy\n" -"YXNpbGlhMQswCQYDVQQIEwJERjExMC8GA1UEAxMoQXV0b3JpZGFkZSBDZXJ0aWZp\n" -"Y2Fkb3JhIFJhaXogQnJhc2lsZWlyYTAeFw0wMTExMzAxMjU4MDBaFw0xMTExMzAy\n" -"MzU5MDBaMIG0MQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDE9MDsG\n" -"A1UECxM0SW5zdGl0dXRvIE5hY2lvbmFsIGRlIFRlY25vbG9naWEgZGEgSW5mb3Jt\n" -"YWNhbyAtIElUSTERMA8GA1UEBxMIQnJhc2lsaWExCzAJBgNVBAgTAkRGMTEwLwYD\n" -"VQQDEyhBdXRvcmlkYWRlIENlcnRpZmljYWRvcmEgUmFpeiBCcmFzaWxlaXJhMIIB\n" -"IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwPMudwX/hvm+Uh2b/lQAcHVA\n" -"isamaLkWdkwP9/S/tOKIgRrL6Oy+ZIGlOUdd6uYtk9Ma/3pUpgcfNAj0vYm5gsyj\n" -"Qo9emsc+x6m4VWwk9iqMZSCK5EQkAq/Ut4n7KuLE1+gdftwdIgxfUsPt4CyNrY50\n" -"QV57KM2UT8x5rrmzEjr7TICGpSUAl2gVqe6xaii+bmYR1QrmWaBSAG59LrkrjrYt\n" -"bRhFboUDe1DK+6T8s5L6k8c8okpbHpa9veMztDVC9sPJ60MWXh6anVKo1UcLcbUR\n" -"yEeNvZneVRKAAU6ouwdjDvwlsaKydFKwed0ToQ47bmUKgcm+wV3eTRk36UOnTwID\n" -"AQABo4HSMIHPME4GA1UdIARHMEUwQwYFYEwBAQAwOjA4BggrBgEFBQcCARYsaHR0\n" -"cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0RQQ2FjcmFpei5wZGYwPQYDVR0f\n" -"BDYwNDAyoDCgLoYsaHR0cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0xDUmFj\n" -"cmFpei5jcmwwHQYDVR0OBBYEFIr68VeEERM1kEL6V0lUaQ2kxPA3MA8GA1UdEwEB\n" -"/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAZA5c1\n" -"U/hgIh6OcgLAfiJgFWpvmDZWqlV30/bHFpj8iBobJSm5uDpt7TirYh1Uxe3fQaGl\n" -"YjJe+9zd+izPRbBqXPVQA34EXcwk4qpWuf1hHriWfdrx8AcqSqr6CuQFwSr75Fos\n" -"SzlwDADa70mT7wZjAmQhnZx2xJ6wfWlT9VQfS//JYeIc7Fue2JNLd00UOSMMaiK/\n" -"t79enKNHEA2fupH3vEigf5Eh4bVAN5VohrTm6MY53x7XQZZr1ME7a55lFEnSeT0u\n" -"mlOAjR2mAbvSM5X5oSZNrmetdzyTj2flCM8CC7MLab0kkdngRIlUBGHF1/S5nmPb\n" -"K+9A46sd33oqK8n8\n" -"-----END CERTIFICATE-----\n" -""; +				"MIIEuDCCA6CgAwIBAgIBBDANBgkqhkiG9w0BAQUFADCBtDELMAkGA1UEBhMCQlIx\n" +				"EzARBgNVBAoTCklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25h\n" +				"bCBkZSBUZWNub2xvZ2lhIGRhIEluZm9ybWFjYW8gLSBJVEkxETAPBgNVBAcTCEJy\n" +				"YXNpbGlhMQswCQYDVQQIEwJERjExMC8GA1UEAxMoQXV0b3JpZGFkZSBDZXJ0aWZp\n" +				"Y2Fkb3JhIFJhaXogQnJhc2lsZWlyYTAeFw0wMTExMzAxMjU4MDBaFw0xMTExMzAy\n" +				"MzU5MDBaMIG0MQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDE9MDsG\n" +				"A1UECxM0SW5zdGl0dXRvIE5hY2lvbmFsIGRlIFRlY25vbG9naWEgZGEgSW5mb3Jt\n" +				"YWNhbyAtIElUSTERMA8GA1UEBxMIQnJhc2lsaWExCzAJBgNVBAgTAkRGMTEwLwYD\n" +				"VQQDEyhBdXRvcmlkYWRlIENlcnRpZmljYWRvcmEgUmFpeiBCcmFzaWxlaXJhMIIB\n" +				"IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwPMudwX/hvm+Uh2b/lQAcHVA\n" +				"isamaLkWdkwP9/S/tOKIgRrL6Oy+ZIGlOUdd6uYtk9Ma/3pUpgcfNAj0vYm5gsyj\n" +				"Qo9emsc+x6m4VWwk9iqMZSCK5EQkAq/Ut4n7KuLE1+gdftwdIgxfUsPt4CyNrY50\n" +				"QV57KM2UT8x5rrmzEjr7TICGpSUAl2gVqe6xaii+bmYR1QrmWaBSAG59LrkrjrYt\n" +				"bRhFboUDe1DK+6T8s5L6k8c8okpbHpa9veMztDVC9sPJ60MWXh6anVKo1UcLcbUR\n" +				"yEeNvZneVRKAAU6ouwdjDvwlsaKydFKwed0ToQ47bmUKgcm+wV3eTRk36UOnTwID\n" +				"AQABo4HSMIHPME4GA1UdIARHMEUwQwYFYEwBAQAwOjA4BggrBgEFBQcCARYsaHR0\n" +				"cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0RQQ2FjcmFpei5wZGYwPQYDVR0f\n" +				"BDYwNDAyoDCgLoYsaHR0cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0xDUmFj\n" +				"cmFpei5jcmwwHQYDVR0OBBYEFIr68VeEERM1kEL6V0lUaQ2kxPA3MA8GA1UdEwEB\n" +				"/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAZA5c1\n" +				"U/hgIh6OcgLAfiJgFWpvmDZWqlV30/bHFpj8iBobJSm5uDpt7TirYh1Uxe3fQaGl\n" +				"YjJe+9zd+izPRbBqXPVQA34EXcwk4qpWuf1hHriWfdrx8AcqSqr6CuQFwSr75Fos\n" +				"SzlwDADa70mT7wZjAmQhnZx2xJ6wfWlT9VQfS//JYeIc7Fue2JNLd00UOSMMaiK/\n" +				"t79enKNHEA2fupH3vEigf5Eh4bVAN5VohrTm6MY53x7XQZZr1ME7a55lFEnSeT0u\n" +				"mlOAjR2mAbvSM5X5oSZNrmetdzyTj2flCM8CC7MLab0kkdngRIlUBGHF1/S5nmPb\n" +				"K+9A46sd33oqK8n8\n" +				"-----END CERTIFICATE-----\n"; + +			mPemRootCert = "-----BEGIN CERTIFICATE-----\n" +			"MIIB0TCCATqgAwIBAgIJANaTqrzEvHaRMA0GCSqGSIb3DQEBBAUAMBsxGTAXBgNV\n" +			"BAMTEFJveGllcyB0ZXN0IHJvb3QwHhcNMDkwNDE1MjEwNzQ3WhcNMTAwNDE1MjEw\n" +			"NzQ3WjAbMRkwFwYDVQQDExBSb3hpZXMgdGVzdCByb290MIGfMA0GCSqGSIb3DQEB\n" +			"AQUAA4GNADCBiQKBgQCpo5nDW6RNz9IHUVZd7Tw2XAQiBniDF4xH0N1w7sUYTiFq\n" +			"21mABsnOPJD3ra+MtOsXPHcaljm661JjTD8L40v5sfEbqDUPcOw76ClrPqnuAeyT\n" +			"38qk8DHku/mT8YdprevGZdVcUXQg3vosVzOL93HOOHK+u61mEEoM9W5xoNVEdQID\n" +			"AQABox0wGzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQQF\n" +			"AAOBgQAzn0aW/+zWPmcTbvxonyiYYUr9b4SOB/quhAkT8KT4ir1dcZAXRR59+kEn\n" +			"HSTu1FAodV0gvESqyobftF5hZ1XMxdJqGu//xP+YCwlv244G/0pp7KLI8ihNO2+N\n" +			"lPBUJgbo++ZkhiE1jotZi9Ay0Oedh3s/AfbMZPyfpJ23ll6+BA==\n" +			"-----END CERTIFICATE-----\n"; +			 +			 +			 +			mPemIntermediateCert = "-----BEGIN CERTIFICATE-----\n" +			"MIIBzzCCATigAwIBAgIBATANBgkqhkiG9w0BAQQFADAbMRkwFwYDVQQDExBSb3hp\n" +			"ZXMgdGVzdCByb290MB4XDTA5MDQxNTIxMzE1NloXDTEwMDQxNTIxMzE1NlowITEf\n" +			"MB0GA1UEAxMWUm94aWVzIGludGVybWVkaWF0ZSBDQTCBnzANBgkqhkiG9w0BAQEF\n" +			"AAOBjQAwgYkCgYEA15MM0W1R37rx/24Q2Qkb5bSiQZxTUcQAhJ2pA8mwUucXuCVt\n" +			"6ayI2TuN32nkjmsCgUkiT/bdXWp0OJo7/MXRIFeUNMCRxrpeFnxuigYEqbIXAdN6\n" +			"qu/vdG2X4PRv/v9Ijrju4cBEiKIldIgOurWEIfXEsVSFP2XmFQHesF04qDcCAwEA\n" +			"AaMdMBswDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQEEBQAD\n" +			"gYEAYljikYgak3W1jSo0vYthNHUy3lBVAKzDhpM96lY5OuXFslpCRX42zNL8X3kN\n" +			"U/4IaJUVtZqx8WsUXl1eXHzBCaXCftapV4Ir6cENLIsXCdXs8paFYzN5nPJA5GYU\n" +			"zWgkSEl1MEhNIc+bJW34vwi29EjrAShAhsIZ84Mt/lvD3Pc=\n" +			"-----END CERTIFICATE-----\n"; +			 +			mPemChildCert = "-----BEGIN CERTIFICATE-----\n" +			"MIIB5DCCAU0CBEnm9eUwDQYJKoZIhvcNAQEEBQAwITEfMB0GA1UEAxMWUm94aWVz\n" +			"IGludGVybWVkaWF0ZSBDQTAeFw0wOTA0MTYwMDAzNDlaFw0xMDA0MTYwMDAzNDla\n" +			"MCAxHjAcBgNVBAMTFWVuaWFjNjMubGluZGVubGFiLmNvbTCBnzANBgkqhkiG9w0B\n" +			"AQEFAAOBjQAwgYkCgYEAp9I5rofEzbjNht+9QejfnsIlEPqSxskoWKCG255TesWR\n" +			"RTmw9wafHQQkJk/VIsaU4RMBYHkknGbHX2dGvMHmKZoWUPSQ/8FZz09o0Qx3TNUZ\n" +			"l7KlGOD2d1c7ZxXDPqlLC6QW8DrE1/8zfwJ5cbYBXc8e7OKdSZeRrnwHyw4Q8r8C\n" +			"AwEAAaMvMC0wEwYDVR0lBAwwCgYIKwYBBQUHAwEwCQYDVR0TBAIwADALBgNVHQ8E\n" +			"BAMCBaAwDQYJKoZIhvcNAQEEBQADgYEAIG0M5tqYlXyMiGKPZfXy/R3M3ZZOapDk\n" +			"W0dsXJYXAc35ftwtn0VYu9CNnZCcli17/d+AKhkK8a/oGPazqudjFF6WLJLTXaY9\n" +			"NmhkJcOPADXkbyQPUPXzLe4YRrkEQeGhzMb4rKDQ1TKAcXfs0Y068pTpsixNSxja\n" +			"NhAUUcve5Is=\n" +			"-----END CERTIFICATE-----\n"; +			  			mDerFormat = "MIIEuDCCA6CgAwIBAgIBBDANBgkqhkiG9w0BAQUFADCBtDELMAkGA1UEBhMCQlIxEzARBgNVBAoT"  "CklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25hbCBkZSBUZWNub2xvZ2lhIGRh"  "IEluZm9ybWFjYW8gLSBJVEkxETAPBgNVBAcTCEJyYXNpbGlhMQswCQYDVQQIEwJERjExMC8GA1UE" @@ -118,24 +216,33 @@ namespace tut  "1ME7a55lFEnSeT0umlOAjR2mAbvSM5X5oSZNrmetdzyTj2flCM8CC7MLab0kkdngRIlUBGHF1/S5"  "nmPbK+9A46sd33oqK8n8"; -			mTestCert = new LLBasicCertificate(mPemTestCert); -			 -			gSavedSettings.cleanup(); -			gSavedSettings.declareString("FirstName", "", "", FALSE); -			gSavedSettings.declareString("LastName", "", "", FALSE);  			mX509TestCert = NULL; -			BIO * validation_bio = BIO_new_mem_buf((void*)mPemTestCert.c_str(), mPemTestCert.length()); +			mX509RootCert = NULL; +			mX509IntermediateCert = NULL; +			mX509ChildCert = NULL; +			BIO * validation_bio = BIO_new_mem_buf((void*)mPemTestCert.c_str(), mPemTestCert.length());			  			PEM_read_bio_X509(validation_bio, &mX509TestCert, 0, NULL);  			BIO_free(validation_bio); - +			validation_bio = BIO_new_mem_buf((void*)mPemRootCert.c_str(), mPemRootCert.length()); +			PEM_read_bio_X509(validation_bio, &mX509RootCert, 0, NULL); +			BIO_free(validation_bio); +			validation_bio = BIO_new_mem_buf((void*)mPemIntermediateCert.c_str(), mPemIntermediateCert.length()); +			PEM_read_bio_X509(validation_bio, &mX509IntermediateCert, 0, NULL); +			BIO_free(validation_bio);	 +			validation_bio = BIO_new_mem_buf((void*)mPemChildCert.c_str(), mPemChildCert.length()); +			PEM_read_bio_X509(validation_bio, &mX509ChildCert, 0, NULL); +			BIO_free(validation_bio);				  		}  		~sechandler_basic_test()  		{  			LLFile::remove("test_password.dat");  			LLFile::remove("sechandler_settings.tmp"); -			delete mTestCert; +			LLFile::remove("mycertstore.pem");  			X509_free(mX509TestCert); +			X509_free(mX509RootCert); +			X509_free(mX509IntermediateCert); +			X509_free(mX509ChildCert);  		}  	}; @@ -152,18 +259,18 @@ namespace tut  	void sechandler_basic_test_object::test<1>()  	{ -  		char buffer[4096]; - +		LLPointer<LLCertificate> test_cert = new LLBasicCertificate(mPemTestCert); +		  		ensure_equals("Resultant pem is correct", -			   mPemTestCert, mTestCert->getPem()); -		std::vector<U8> binary_cert = mTestCert->getBinary(); +			   mPemTestCert, test_cert->getPem()); +		std::vector<U8> binary_cert = test_cert->getBinary();  		apr_base64_encode(buffer, (const char *)&binary_cert[0], binary_cert.size());  		ensure_equals("Der Format is correct", memcmp(buffer, mDerFormat.c_str(), mDerFormat.length()), 0); -		LLSD llsd_cert = mTestCert->getLLSD(); +		LLSD llsd_cert = test_cert->getLLSD();  		std::ostringstream llsd_value;  		llsd_value << LLSDOStreamer<LLSDNotationFormatter>(llsd_cert) << std::endl;  		std::string llsd_cert_str = llsd_value.str(); @@ -194,10 +301,15 @@ namespace tut  		ensure_equals("serial number", (std::string)llsd_cert["serial_number"], "04");  		// sha1 digest is giving a weird value, and I've no idea why...feh  		//ensure_equals("sha1 digest", (std::string)llsd_cert["sha1_digest"], "8e:fd:ca:bc:93:e6:1e:92:5d:4d:1d:ed:18:1a:43:20:a4:67:a1:39"); -		ensure_equals("valid from", (std::string)llsd_cert["valid_from"], "2001-11-30T20:58:00Z"); -		ensure_equals("valid to", (std::string)llsd_cert["valid_to"], "2011-12-01T07:59:00Z"); +		ensure_equals("valid from", (std::string)llsd_cert["valid_from"], "2001-11-30T12:58:00Z"); +		ensure_equals("valid to", (std::string)llsd_cert["valid_to"], "2011-11-30T23:59:00Z"); +		LLSD expectedKeyUsage = LLSD::emptyArray(); +		expectedKeyUsage.append(LLSD((std::string)"certSigning")); +		expectedKeyUsage.append(LLSD((std::string)"crlSigning")); +		ensure("key usage", valueCompareLLSD(llsd_cert["keyUsage"], expectedKeyUsage)); +		ensure("basic constraints", (bool)llsd_cert["basicConstraints"]["CA"]); -		ensure("x509 is equal", !X509_cmp(mX509TestCert, mTestCert->getOpenSSLX509())); +		ensure("x509 is equal", !X509_cmp(mX509TestCert, test_cert->getOpenSSLX509()));  	} @@ -319,7 +431,6 @@ namespace tut  	void sechandler_basic_test_object::test<3>()  	{  		LLPointer<LLSecAPIBasicHandler> handler = new LLSecAPIBasicHandler("sechandler_settings.tmp", "test_password.dat"); -		  		LLSD my_id = LLSD::emptyMap();  		LLSD my_authenticator = LLSD::emptyMap(); @@ -349,7 +460,7 @@ namespace tut  		// test loading of a credential, that hasn't been saved, without  		// any legacy saved credential data -		LLPointer<LLCredential> my_new_cred = handler->loadCredential("my_grid"); +		LLPointer<LLCredential> my_new_cred = handler->loadCredential("my_grid2");  		ensure("unknown credential load test", my_new_cred->getIdentifier().isMap());  		ensure("unknown credential load test", !my_new_cred->getIdentifier().has("type"));		  		ensure("unknown credential load test", my_new_cred->getAuthenticator().isMap()); @@ -379,10 +490,8 @@ namespace tut  		// test loading of an unknown credential with legacy saved username, but without  		// saved password - -		gSavedSettings.setString("FirstName", "myfirstname"); -		gSavedSettings.setString("LastName", "mylastname"); - +		gFirstName = "myfirstname"; +		gLastName = "mylastname";  		my_new_cred = handler->loadCredential("my_legacy_grid");  		ensure_equals("legacy credential with no password: type",   					  (const std::string)my_new_cred->getIdentifier()["type"], "agent"); @@ -438,19 +547,413 @@ namespace tut  		ensure("no authenticator values were saved", my_new_cred->getAuthenticator().isUndefined());  	} +	// test cert vector +	template<> template<> +	void sechandler_basic_test_object::test<4>() +	{ +		 +		// validate create from empty vector +		LLPointer<LLBasicCertificateVector> test_vector = new LLBasicCertificateVector(); +		ensure_equals("when loading with nothing, we should result in no certs in vector", test_vector->size(), 0); +		 +		test_vector->add(new LLBasicCertificate(mPemTestCert)); +		ensure_equals("one element in vector", test_vector->size(), 1); +		test_vector->add(new LLBasicCertificate(mPemChildCert)); +		ensure_equals("two elements in vector after add", test_vector->size(), 2); +		 +		test_vector->add(new LLBasicCertificate(mPemChildCert)); +		ensure_equals("two elements in vector after re-add", test_vector->size(), 2); +		// validate order +		X509* test_cert = (*test_vector)[0]->getOpenSSLX509();		 +		ensure("first cert added remains first cert", !X509_cmp(test_cert, mX509TestCert)); +		X509_free(test_cert); +		 +		test_cert = (*test_vector)[1]->getOpenSSLX509();	 +		ensure("adding a duplicate cert", !X509_cmp(test_cert, mX509ChildCert)); +		X509_free(test_cert);		 +		 +		// +		// validate iterator +		// +		LLBasicCertificateVector::iterator current_cert = test_vector->begin(); +		LLBasicCertificateVector::iterator copy_current_cert = current_cert; +		// operator++(int) +		ensure("validate iterator++ element in vector is expected cert", *current_cert++ == (*test_vector)[0]); +		ensure("validate 2nd iterator++ element in vector is expected cert", *current_cert++ == (*test_vector)[1]); +		ensure("validate end iterator++", current_cert == test_vector->end()); +		 +		// copy  +		ensure("validate copy iterator element in vector is expected cert", *copy_current_cert == (*test_vector)[0]);		 +		 +		// operator--(int) +		current_cert--; +		ensure("validate iterator-- element in vector is expected cert", *current_cert-- == (*test_vector)[1]);		 +		ensure("validate iterator-- element in vector is expected cert", *current_cert == (*test_vector)[0]); +		 +		ensure("begin iterator is equal", current_cert == test_vector->begin()); +		 +		// operator++ +		ensure("validate ++iterator element in vector is expected cert", *++current_cert == (*test_vector)[1]);				 +		ensure("end of cert vector after ++iterator", ++current_cert == test_vector->end()); +		// operator-- +		ensure("validate --iterator element in vector is expected cert", *--current_cert == (*test_vector)[1]);		 +		ensure("validate 2nd --iterator element in vector is expected cert", *--current_cert == (*test_vector)[0]);		 +		 +		// validate remove +		// validate create from empty vector +		test_vector = new LLBasicCertificateVector(); +		test_vector->add(new LLBasicCertificate(mPemTestCert)); +		test_vector->add(new LLBasicCertificate(mPemChildCert)); +		test_vector->erase(test_vector->begin()); +		ensure_equals("one element in store after remove", test_vector->size(), 1); +		test_cert = (*test_vector)[0]->getOpenSSLX509(); +		ensure("validate cert was removed", !X509_cmp(test_cert, mX509ChildCert)); +		X509_free(test_cert); +		 +		// validate insert +		test_vector->insert(test_vector->begin(), new LLBasicCertificate(mPemChildCert)); +		test_cert = (*test_vector)[0]->getOpenSSLX509(); +		 +		ensure("validate cert was inserted", !X509_cmp(test_cert, mX509ChildCert)); +		X509_free(test_cert);	 +		//validate find +		LLSD find_info = LLSD::emptyMap(); +		test_vector->insert(test_vector->begin(), new LLBasicCertificate(mPemRootCert)); +		find_info["issuer_name"] = LLSD::emptyMap(); +		find_info["issuer_name"]["commonName"] = "Roxies intermediate CA"; +		find_info["md5_digest"] = "97:24:c7:4c:d4:ba:2d:0e:9c:a1:18:8e:3a:c6:1f:c3"; +		current_cert = test_vector->find(find_info); +		ensure("found", current_cert != test_vector->end()); +		ensure("found cert", (*current_cert).get() == (*test_vector)[1].get()); +		find_info["sha1_digest"] = "bad value"; +		current_cert =test_vector->find(find_info); +		ensure("didn't find cert", current_cert == test_vector->end());		 +	}	 +	  	// test cert store  	template<> template<> -	void sechandler_basic_test_object::test<4>() +	void sechandler_basic_test_object::test<5>()  	{ +		// validate load with nothing +		LLFile::remove("mycertstore.pem"); +		LLPointer<LLBasicCertificateStore> test_store = new LLBasicCertificateStore("mycertstore.pem"); +		ensure_equals("when loading with nothing, we should result in no certs in store", test_store->size(), 0); +		 +		// validate load with empty file +		test_store->save(); +		test_store = NULL; +		test_store = new LLBasicCertificateStore("mycertstore.pem"); +		ensure_equals("when loading with nothing, we should result in no certs in store", test_store->size(), 0); +		test_store=NULL; +		  		// instantiate a cert store from a file -		llofstream certstorefile("mycertstore.pem", std::ios::out | std::ios::binary); - -		certstorefile << mPemTestCert; +		llofstream certstorefile("mycertstore.pem", std::ios::out); +		certstorefile << mPemChildCert << std::endl << mPemTestCert << std::endl;  		certstorefile.close(); -		// LLBasicCertificateStore test_store("mycertstore.pem"); -		// X509* test_cert = test_store[0]->getOpenSSLX509(); +		// validate loaded certs +		test_store = new LLBasicCertificateStore("mycertstore.pem"); +		ensure_equals("two elements in store", test_store->size(), 2); +		 +		// operator[] +		X509* test_cert = (*test_store)[0]->getOpenSSLX509(); + +		ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); +		X509_free(test_cert); +		test_cert = (*test_store)[1]->getOpenSSLX509(); +		ensure("validate second element in store is expected cert", !X509_cmp(test_cert, mX509TestCert));	 +		X509_free(test_cert); + + +		// validate save +		LLFile::remove("mycertstore.pem"); +		test_store->save(); +		test_store = NULL; +		test_store = new LLBasicCertificateStore("mycertstore.pem"); +		ensure_equals("two elements in store after save", test_store->size(), 2);				 +		LLCertificateStore::iterator current_cert = test_store->begin();		 +		test_cert = (*current_cert)->getOpenSSLX509(); +		ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); +		current_cert++; +		X509_free(test_cert); +		test_cert = (*current_cert)->getOpenSSLX509(); +		ensure("validate second element in store is expected cert", !X509_cmp(test_cert, mX509TestCert));	 +		X509_free(test_cert); +		current_cert++; +		ensure("end of cert store", current_cert == test_store->end()); +		 +	} +	 +	// cert name wildcard matching +	template<> template<> +	void sechandler_basic_test_object::test<6>() +	{ +		ensure("simple name match",  +			   _cert_hostname_wildcard_match("foo", "foo")); +		 +		ensure("simple name match, with end period",  +			   _cert_hostname_wildcard_match("foo.", "foo.")); +		 +		ensure("simple name match, with begin period",  +			   _cert_hostname_wildcard_match(".foo", ".foo"));		 +		 +		ensure("simple name match, with subdomain",  +			   _cert_hostname_wildcard_match("foo.bar", "foo.bar"));	 +		 +		ensure("stutter name match",  +			   _cert_hostname_wildcard_match("foobbbbfoo", "foo*bbbfoo"));			 +		 +		ensure("simple name match, with beginning wildcard",  +			   _cert_hostname_wildcard_match("foobar", "*bar"));	 +		 +		ensure("simple name match, with ending wildcard",  +			   _cert_hostname_wildcard_match("foobar", "foo*")); +		 +		ensure("simple name match, with beginning null wildcard",  +			   _cert_hostname_wildcard_match("foobar", "*foobar"));			 + +		ensure("simple name match, with ending null wildcard",  +			   _cert_hostname_wildcard_match("foobar", "foobar*")); +		 +		ensure("simple name match, with embedded wildcard",  +			   _cert_hostname_wildcard_match("foobar", "f*r"));		 +		 +		ensure("simple name match, with embedded null wildcard",  +			   _cert_hostname_wildcard_match("foobar", "foo*bar")); + +		ensure("simple name match, with dual embedded wildcard",  +			   _cert_hostname_wildcard_match("foobar", "f*o*ar"));		 + +		ensure("simple name mismatch",  +			   !_cert_hostname_wildcard_match("bar", "foo")); +		 +		ensure("simple name mismatch, with end period",  +			   !_cert_hostname_wildcard_match("foobar.", "foo.")); +		 +		ensure("simple name mismatch, with begin period",  +			   !_cert_hostname_wildcard_match(".foobar", ".foo"));		 +		 +		ensure("simple name mismatch, with subdomain",  +			   !_cert_hostname_wildcard_match("foobar.bar", "foo.bar"));	 +		 +		ensure("simple name mismatch, with beginning wildcard",  +			   !_cert_hostname_wildcard_match("foobara", "*bar"));	 +		 +		ensure("simple name mismatch, with ending wildcard",  +			   !_cert_hostname_wildcard_match("oobar", "foo*")); +		 +		ensure("simple name mismatch, with embedded wildcard",  +			   !_cert_hostname_wildcard_match("oobar", "f*r"));		 +		 +		ensure("simple name mismatch, with dual embedded wildcard",  +			   !_cert_hostname_wildcard_match("foobar", "f*d*ar")); +		 +		ensure("simple wildcard",  +			   _cert_hostname_wildcard_match("foobar", "*")); +		 +		ensure("long domain",  +			   _cert_hostname_wildcard_match("foo.bar.com", "foo.bar.com")); +		 +		ensure("long domain with multiple wildcards",  +			   _cert_hostname_wildcard_match("foo.bar.com", "*.b*r.com"));	 -		// ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509TestCert)); +		ensure("end periods",  +			   _cert_hostname_wildcard_match("foo.bar.com.", "*.b*r.com."));	 +		 +		ensure("mismatch end period",  +			   !_cert_hostname_wildcard_match("foo.bar.com.", "*.b*r.com")); +		 +		ensure("mismatch end period2",  +			   !_cert_hostname_wildcard_match("foo.bar.com", "*.b*r.com."));				 +	} +	 +	// test cert chain +	template<> template<> +	void sechandler_basic_test_object::test<7>() +	{ +		// validate create from empty chain +		LLPointer<LLBasicCertificateChain> test_chain = new LLBasicCertificateChain(NULL); +		ensure_equals("when loading with nothing, we should result in no certs in chain", test_chain->size(), 0); + +		// Single cert in the chain. +		X509_STORE_CTX *test_store = X509_STORE_CTX_new(); +		test_store->cert = mX509ChildCert;		 +		test_store->untrusted = NULL; +		test_chain = new LLBasicCertificateChain(test_store); +		X509_STORE_CTX_free(test_store); +		ensure_equals("two elements in store", test_chain->size(), 1);		 +		X509* test_cert = (*test_chain)[0]->getOpenSSLX509(); +		ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); +		X509_free(test_cert);		 +		 +		// cert + CA +		 +		test_store = X509_STORE_CTX_new(); +		test_store->cert = mX509ChildCert; +		test_store->untrusted = sk_X509_new_null(); +		sk_X509_push(test_store->untrusted, mX509IntermediateCert); +		test_chain = new LLBasicCertificateChain(test_store); +		X509_STORE_CTX_free(test_store); +		ensure_equals("two elements in store", test_chain->size(), 2);	 +		test_cert = (*test_chain)[0]->getOpenSSLX509(); +		ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); +		X509_free(test_cert); +		test_cert = (*test_chain)[1]->getOpenSSLX509(); +		ensure("validate second element in store is expected cert", !X509_cmp(test_cert, mX509IntermediateCert));	 +		X509_free(test_cert); + +		// cert + nonrelated +		 +		test_store = X509_STORE_CTX_new(); +		test_store->cert = mX509ChildCert; +		test_store->untrusted = sk_X509_new_null(); +		sk_X509_push(test_store->untrusted, mX509TestCert); +		test_chain = new LLBasicCertificateChain(test_store); +		X509_STORE_CTX_free(test_store); +		ensure_equals("two elements in store", test_chain->size(), 1);	 +		test_cert = (*test_chain)[0]->getOpenSSLX509(); +		ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); +		X509_free(test_cert); +		 +		// cert + CA + nonrelated +		test_store = X509_STORE_CTX_new(); +		test_store->cert = mX509ChildCert; +		test_store->untrusted = sk_X509_new_null(); +		sk_X509_push(test_store->untrusted, mX509IntermediateCert); +		sk_X509_push(test_store->untrusted, mX509TestCert); +		test_chain = new LLBasicCertificateChain(test_store); +		X509_STORE_CTX_free(test_store); +		ensure_equals("two elements in store", test_chain->size(), 2);	 +		test_cert = (*test_chain)[0]->getOpenSSLX509(); +		ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); +		X509_free(test_cert); +		test_cert = (*test_chain)[1]->getOpenSSLX509(); +		ensure("validate second element in store is expected cert", !X509_cmp(test_cert, mX509IntermediateCert));	 +		X509_free(test_cert); + +		// cert + intermediate + CA  +		test_store = X509_STORE_CTX_new(); +		test_store->cert = mX509ChildCert; +		test_store->untrusted = sk_X509_new_null(); +		sk_X509_push(test_store->untrusted, mX509IntermediateCert); +		sk_X509_push(test_store->untrusted, mX509RootCert); +		test_chain = new LLBasicCertificateChain(test_store); +		X509_STORE_CTX_free(test_store); +		ensure_equals("three elements in store", test_chain->size(), 3);	 +		test_cert = (*test_chain)[0]->getOpenSSLX509(); +		ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); +		X509_free(test_cert); +		test_cert = (*test_chain)[1]->getOpenSSLX509(); +		ensure("validate second element in store is expected cert", !X509_cmp(test_cert, mX509IntermediateCert));	 +		X509_free(test_cert); + +		test_cert = (*test_chain)[2]->getOpenSSLX509(); +		ensure("validate second element in store is expected cert", !X509_cmp(test_cert, mX509RootCert));	 +		X509_free(test_cert);		 +	} +	// test cert validation +	template<> template<> +	void sechandler_basic_test_object::test<8>() +	{ +		// start with a trusted store with our known root cert +		LLFile::remove("mycertstore.pem"); +		LLPointer<LLBasicCertificateStore> test_store = new LLBasicCertificateStore("mycertstore.pem"); +		test_store->add(new LLBasicCertificate(mX509RootCert)); +		LLSD validation_params; +		 +		// validate basic trust for a chain containing only the intermediate cert.  (1 deep) +		LLPointer<LLBasicCertificateChain> test_chain = new LLBasicCertificateChain(NULL); + +		test_chain->add(new LLBasicCertificate(mX509IntermediateCert)); + +		test_chain->validate(0, test_store, validation_params); + +		// add the root certificate to the chain and revalidate +		test_chain->add(new LLBasicCertificate(mX509RootCert));	 +		test_chain->validate(0, test_store, validation_params); + +		// add the child cert at the head of the chain, and revalidate (3 deep chain) +		test_chain->insert(test_chain->begin(), new LLBasicCertificate(mX509ChildCert)); +		test_chain->validate(0, test_store, validation_params); + +		// basic failure cases +		test_chain = new LLBasicCertificateChain(NULL); +		//validate with only the child cert +		test_chain->add(new LLBasicCertificate(mX509ChildCert)); +		ensure_throws("no CA, with only a child cert",  +					  LLCertValidationTrustException,  +					  (*test_chain)[0], +					  test_chain->validate,  +					  VALIDATION_POLICY_TRUSTED,  +					  test_store,  +					  validation_params); + + +		// validate without the trust flag. +		test_chain->validate(0, test_store, validation_params);		 + +		// clear out the store +		test_store = new LLBasicCertificateStore("mycertstore.pem"); +		// append the intermediate cert +		test_chain->add(new LLBasicCertificate(mX509IntermediateCert));		 +		ensure_throws("no CA, with child and intermediate certs",  +					  LLCertValidationTrustException,  +					  (*test_chain)[1], +					  test_chain->validate,  +					  VALIDATION_POLICY_TRUSTED,  +					  test_store,  +					  validation_params); +		// validate without the trust flag +		test_chain->validate(0, test_store, validation_params); + +		// Test time validity +		LLSD child_info = (*test_chain)[0]->getLLSD(); +		validation_params = LLSD::emptyMap(); +		validation_params[CERT_VALIDATION_DATE] = LLDate(child_info[CERT_VALID_FROM].asDate().secondsSinceEpoch() + 1.0);   +		test_chain->validate(VALIDATION_POLICY_TIME, test_store, validation_params); + +		validation_params = LLSD::emptyMap();		 +		validation_params[CERT_VALIDATION_DATE] = child_info[CERT_VALID_FROM].asDate(); +		 +		validation_params[CERT_VALIDATION_DATE] = LLDate(child_info[CERT_VALID_FROM].asDate().secondsSinceEpoch() - 1.0); + 		 +		// test not yet valid +		ensure_throws("Child cert not yet valid",  +					  LLCertValidationExpirationException,  +					  (*test_chain)[0], +					  test_chain->validate,  +					  VALIDATION_POLICY_TIME,  +					  test_store,  +					  validation_params);	 +		validation_params = LLSD::emptyMap();		 +		validation_params[CERT_VALIDATION_DATE] = LLDate(child_info[CERT_VALID_TO].asDate().secondsSinceEpoch() + 1.0); + 		 +		// test cert expired +		ensure_throws("Child cert expired",  +					  LLCertValidationExpirationException,  +					  (*test_chain)[0], +					  test_chain->validate,  +					  VALIDATION_POLICY_TIME,  +					  test_store,  +					  validation_params); + +		// test SSL KU +		// validate basic trust for a chain containing child and intermediate. +		test_chain = new LLBasicCertificateChain(NULL); +		test_chain->add(new LLBasicCertificate(mX509ChildCert)); +		test_chain->add(new LLBasicCertificate(mX509IntermediateCert)); +		test_chain->validate(VALIDATION_POLICY_SSL_KU, test_store, validation_params);	 + +		test_chain = new LLBasicCertificateChain(NULL); +		test_chain->add(new LLBasicCertificate(mX509TestCert)); + +		ensure_throws("Cert doesn't have ku",  +					  LLCertKeyUsageValidationException,  +					  (*test_chain)[0], +					  test_chain->validate,  +					  VALIDATION_POLICY_SSL_KU,  +					  test_store,  +					  validation_params);  	}  }; + diff --git a/indra/newview/tests/llviewernetwork_test.cpp b/indra/newview/tests/llviewernetwork_test.cpp index c7a6b2ad15..f341482d6f 100644 --- a/indra/newview/tests/llviewernetwork_test.cpp +++ b/indra/newview/tests/llviewernetwork_test.cpp @@ -37,7 +37,51 @@  #include "../../llxml/llcontrol.h"  #include "llfile.h" -LLControlGroup gSavedSettings; +//----------------------------------------------------------------------------                +// Mock objects for the dependencies of the code we're testing                                + +LLControlGroup::LLControlGroup(const std::string& name) +: LLInstanceTracker<LLControlGroup, std::string>(name) {} +LLControlGroup::~LLControlGroup() {} +BOOL LLControlGroup::declareString(const std::string& name, +                                   const std::string& initial_val, +                                   const std::string& comment, +                                   BOOL persist) {return TRUE;} +void LLControlGroup::setString(const std::string& name, const std::string& val){} + +std::string gCmdLineLoginURI; +std::string gCmdLineGridChoice; +std::string gCmdLineHelperURI; +std::string gLoginPage; +std::string gCurrentGrid; +std::string LLControlGroup::getString(const std::string& name) +{ +	if (name == "CmdLineGridChoice") +		return gCmdLineGridChoice; +	else if (name == "CmdLineHelperURI") +		return gCmdLineHelperURI; +	else if (name == "LoginPage") +		return gLoginPage; +	else if (name == "CurrentGrid") +		return gCurrentGrid; +	return ""; +} + +LLSD LLControlGroup::getLLSD(const std::string& name) +{ +	if (name == "CmdLineLoginURI") +	{ +		if(!gCmdLineLoginURI.empty()) +		{ +			return LLSD(gCmdLineLoginURI); +		} +	} +	return LLSD(); +} + + +LLControlGroup gSavedSettings("test"); +  const char *gSampleGridFile = "<llsd><map>"  "<key>grid1</key><map>"  "  <key>favorite</key><integer>1</integer>" @@ -68,13 +112,12 @@ namespace tut  	{  		viewerNetworkTest()  		{ -			gSavedSettings.cleanup(); -			gSavedSettings.cleanup(); -			gSavedSettings.declareString("CmdLineGridChoice", "", "", FALSE); -			gSavedSettings.declareString("CmdLineHelperURI", "", "", FALSE); -			gSavedSettings.declareString("LoginPage", "", "", FALSE); -			gSavedSettings.declareString("CurrentGrid", "", "", FALSE); -			gSavedSettings.declareLLSD("CmdLineLoginURI", LLSD(), "", FALSE); +			LLFile::remove("grid_test.xml"); +			gCmdLineLoginURI.clear(); +			gCmdLineGridChoice.clear(); +			gCmdLineHelperURI.clear(); +			gLoginPage.clear(); +			gCurrentGrid.clear();			  		}  		~viewerNetworkTest()  		{ @@ -95,20 +138,25 @@ namespace tut  	void viewerNetworkTestObject::test<1>()  	{ -		LLGridManager manager("grid_test.xml"); +		LLGridManager *manager = LLGridManager::getInstance(); +		// grid file doesn't exist +		manager->initialize("grid_test.xml");  		// validate that some of the defaults are available. -		std::map<std::string, std::string> known_grids = manager.getKnownGrids(); +		std::map<std::string, std::string> known_grids = manager->getKnownGrids();  #ifndef LL_RELEASE_FOR_DOWNLOAD  		ensure_equals("Known grids is a string-string map of size 18", known_grids.size(), 18); +		ensure_equals("Agni has the right name and label",  +					  known_grids[std::string("util.agni.lindenlab.com")], std::string("Agni"));  #else // LL_RELEASE_FOR_DOWNLOAD -		ensure_equals("Known grids is a string-string map of size 18", known_grids.size(), 2); +		ensure_equals("Known grids is a string-string map of size 2", known_grids.size(), 2); +		ensure_equals("Agni has the right name and label",  +					  known_grids[std::string("util.agni.lindenlab.com")], std::string("Secondlife.com"));		  #endif // LL_RELEASE_FOR_DOWNLOAD -		ensure_equals("Agni has the right name and label",  -					  known_grids[std::string("util.agni.lindenlab.com")], std::string("Agni")); +  		ensure_equals("None exists", known_grids[""], "None"); -		LLSD grid = manager.getGridInfo("util.agni.lindenlab.com"); +		LLSD grid = LLGridManager::getInstance()->getGridInfo("util.agni.lindenlab.com");  		ensure("Grid info for agni is a map", grid.isMap());  		ensure_equals("name is correct for agni",   					  grid[GRID_NAME_VALUE].asString(), std::string("util.agni.lindenlab.com")); @@ -130,8 +178,8 @@ namespace tut  		ensure_equals("Agni login page is correct",  					  grid[GRID_LOGIN_PAGE_VALUE].asString(),   					  std::string("http://secondlife.com/app/login/")); -		ensure("Agni is not a favorite", -			   !grid.has(GRID_IS_FAVORITE_VALUE)); +		ensure("Agni is a favorite", +			   grid.has(GRID_IS_FAVORITE_VALUE));  		ensure("Agni is a system grid",   			   grid.has(GRID_IS_SYSTEM_GRID_VALUE));  		ensure("Grid file wasn't greated as it wasn't saved",  @@ -146,20 +194,25 @@ namespace tut  		gridfile << gSampleGridFile;  		gridfile.close(); -		LLGridManager manager("grid_test.xml"); -		std::map<std::string, std::string> known_grids = manager.getKnownGrids(); +		LLGridManager::getInstance()->initialize("grid_test.xml"); +		std::map<std::string, std::string> known_grids = LLGridManager::getInstance()->getKnownGrids();  #ifndef LL_RELEASE_FOR_DOWNLOAD  		ensure_equals("adding a grid via a grid file increases known grid size",   					  known_grids.size(), 19); +		ensure_equals("Agni is still there after we've added a grid via a grid file",  +					  known_grids["util.agni.lindenlab.com"], std::string("Agni")); +	  #else  		ensure_equals("adding a grid via a grid file increases known grid size",   					  known_grids.size(), 3); -#endif  		ensure_equals("Agni is still there after we've added a grid via a grid file",  -					  known_grids["util.agni.lindenlab.com"], std::string("Agni")); +					  known_grids["util.agni.lindenlab.com"], std::string("Secondlife.com")); + +#endif +		  		// assure Agni doesn't get overwritten -		LLSD grid = manager.getGridInfo("util.agni.lindenlab.com"); +		LLSD grid = LLGridManager::getInstance()->getGridInfo("util.agni.lindenlab.com");  #ifndef LL_RELEASE_FOR_DOWNLOAD  		ensure_equals("Agni grid label was not modified by grid file",   					  grid[GRID_LABEL_VALUE].asString(), std::string("Agni")); @@ -181,14 +234,14 @@ namespace tut  		ensure_equals("Agni login page the same after grid file",   					  grid[GRID_LOGIN_PAGE_VALUE].asString(),   					  std::string("http://secondlife.com/app/login/")); -		ensure("Agni still not favorite after grid file",  -			   !grid.has(GRID_IS_FAVORITE_VALUE)); +		ensure("Agni still a favorite after grid file",  +			   grid.has(GRID_IS_FAVORITE_VALUE));  		ensure("Agni system grid still set after grid file",   			   grid.has(GRID_IS_SYSTEM_GRID_VALUE));  		ensure_equals("Grid file adds to name<->label map",   					  known_grids["grid1"], std::string("mylabel")); -		grid = manager.getGridInfo("grid1"); +		grid = LLGridManager::getInstance()->getGridInfo("grid1");  		ensure_equals("grid file grid name is set",  					  grid[GRID_NAME_VALUE].asString(), std::string("grid1"));  		ensure_equals("grid file label is set",  @@ -217,22 +270,16 @@ namespace tut  	template<> template<>  	void viewerNetworkTestObject::test<3>()  	{	 -		LLSD loginURI = std::string("https://my.login.uri/cgi-bin/login.cgi"); -		gSavedSettings.setLLSD("CmdLineLoginURI", loginURI); -		LLGridManager manager("grid_test.xml"); +		gCmdLineLoginURI = "https://my.login.uri/cgi-bin/login.cgi"; +		LLGridManager::getInstance()->initialize("grid_test.xml");  		// with single login uri specified. -		std::map<std::string, std::string> known_grids = manager.getKnownGrids(); -#ifndef LL_RELEASE_FOR_DOWNLOAD +		std::map<std::string, std::string> known_grids = LLGridManager::getInstance()->getKnownGrids();  		ensure_equals("adding a command line grid increases known grid size",   					  known_grids.size(), 19); -#else -		ensure_equals("adding a command line grid increases known grid size",  -					  known_grids.size(), 3); -#endif  		ensure_equals("Command line grid is added to the list of grids",   					  known_grids["my.login.uri"], std::string("my.login.uri")); -		LLSD grid = manager.getGridInfo("my.login.uri"); +		LLSD grid = LLGridManager::getInstance()->getGridInfo("my.login.uri");  		ensure_equals("Command line grid name is set",  					  grid[GRID_NAME_VALUE].asString(), std::string("my.login.uri"));  		ensure_equals("Command line grid label is set",  @@ -254,19 +301,14 @@ namespace tut  			   !grid.has(GRID_IS_SYSTEM_GRID_VALUE));		  		// now try a command line with a custom grid identifier -		gSavedSettings.setString("CmdLineGridChoice", "mycustomgridchoice"); -		manager = LLGridManager("grid_test.xml"); -		known_grids = manager.getKnownGrids(); -#ifndef LL_RELEASE_FOR_DOWNLOAD +		gCmdLineGridChoice = "mycustomgridchoice";		 +		LLGridManager::getInstance()->initialize("grid_test.xml"); +		known_grids = LLGridManager::getInstance()->getKnownGrids();  		ensure_equals("adding a command line grid with custom name increases known grid size",   					  known_grids.size(), 19); -#else -		ensure_equals("adding a command line grid with custom name inceases known grid size",  -					  known_grids.size(), 3); -#endif  		ensure_equals("Custom Command line grid is added to the list of grids",   					  known_grids["mycustomgridchoice"], std::string("mycustomgridchoice")); -		grid = manager.getGridInfo("mycustomgridchoice"); +		grid = LLGridManager::getInstance()->getGridInfo("mycustomgridchoice");  		ensure_equals("Custom Command line grid name is set",  					  grid[GRID_NAME_VALUE].asString(), std::string("mycustomgridchoice"));  		ensure_equals("Custom Command line grid label is set",  @@ -278,16 +320,16 @@ namespace tut  					  std::string("https://my.login.uri/cgi-bin/login.cgi"));  		// add a helperuri -		gSavedSettings.setString("CmdLineHelperURI", "myhelperuri"); -		manager = LLGridManager("grid_test.xml"); -		grid = manager.getGridInfo("mycustomgridchoice");		 +		gCmdLineHelperURI = "myhelperuri"; +		LLGridManager::getInstance()->initialize("grid_test.xml"); +		grid = LLGridManager::getInstance()->getGridInfo("mycustomgridchoice");		  		ensure_equals("Validate command line helper uri",   					  grid[GRID_HELPER_URI_VALUE].asString(), std::string("myhelperuri"));		  		// add a login page -		gSavedSettings.setString("LoginPage", "myloginpage"); -		manager = LLGridManager("grid_test.xml"); -		grid = manager.getGridInfo("mycustomgridchoice");		 +		gLoginPage = "myloginpage"; +		LLGridManager::getInstance()->initialize("grid_test.xml"); +		grid = LLGridManager::getInstance()->getGridInfo("mycustomgridchoice");		  		ensure_equals("Validate command line helper uri",   					  grid[GRID_LOGIN_PAGE_VALUE].asString(), std::string("myloginpage"));			  	} @@ -301,36 +343,34 @@ namespace tut  		// adding a grid with simply a name will populate the values.  		grid[GRID_NAME_VALUE] = "myaddedgrid"; -		loginURI.append(std::string("https://my.login.uri/cgi-bin/login.cgi")); -		gSavedSettings.setLLSD("CmdLineLoginURI", loginURI); -		LLGridManager manager("grid_test.xml"); -		manager.addGrid(grid); -		manager.setGridChoice("util.agni.lindenlab.com"); +		LLGridManager::getInstance()->initialize("grid_test.xml"); +		LLGridManager::getInstance()->addGrid(grid); +		LLGridManager::getInstance()->setGridChoice("util.agni.lindenlab.com");  #ifndef LL_RELEASE_FOR_DOWNLOAD		 -		ensure_equals("getGridLabel", manager.getGridLabel(), std::string("Agni")); +		ensure_equals("getGridLabel", LLGridManager::getInstance()->getGridLabel(), std::string("Agni"));  #else // LL_RELEASE_FOR_DOWNLOAD -		ensure_equals("getGridLabel", manager.getGridLabel(), std::string("Secondlife.com"));		 +		ensure_equals("getGridLabel", LLGridManager::getInstance()->getGridLabel(), std::string("Secondlife.com"));		  #endif // LL_RELEASE_FOR_DOWNLOAD -		ensure_equals("getGridName", manager.getGridName(),  +		ensure_equals("getGridName", LLGridManager::getInstance()->getGridName(),   					  std::string("util.agni.lindenlab.com")); -		ensure_equals("getHelperURI", manager.getHelperURI(),  +		ensure_equals("getHelperURI", LLGridManager::getInstance()->getHelperURI(),   					  std::string("https://secondlife.com/helpers/")); -		ensure_equals("getLoginPage", manager.getLoginPage(),  +		ensure_equals("getLoginPage", LLGridManager::getInstance()->getLoginPage(),   					  std::string("http://secondlife.com/app/login/")); -		ensure_equals("getLoginPage2", manager.getLoginPage("util.agni.lindenlab.com"),  +		ensure_equals("getLoginPage2", LLGridManager::getInstance()->getLoginPage("util.agni.lindenlab.com"),   					  std::string("http://secondlife.com/app/login/")); -		ensure("Is Agni a production grid", manager.isInProductionGrid());		 +		ensure("Is Agni a production grid", LLGridManager::getInstance()->isInProductionGrid());		  		std::vector<std::string> uris; -		manager.getLoginURIs(uris); +		LLGridManager::getInstance()->getLoginURIs(uris);  		ensure_equals("getLoginURIs size", uris.size(), 1);  		ensure_equals("getLoginURIs", uris[0],   					  std::string("https://login.agni.lindenlab.com/cgi-bin/login.cgi")); -		manager.setGridChoice("myaddedgrid"); -		ensure_equals("getGridLabel", manager.getGridLabel(), std::string("myaddedgrid"));		 -		ensure("Is myaddedgrid a production grid", !manager.isInProductionGrid()); +		LLGridManager::getInstance()->setGridChoice("myaddedgrid"); +		ensure_equals("getGridLabel", LLGridManager::getInstance()->getGridLabel(), std::string("myaddedgrid"));		 +		ensure("Is myaddedgrid a production grid", !LLGridManager::getInstance()->isInProductionGrid()); -		manager.setFavorite(); -		grid = manager.getGridInfo("myaddedgrid"); +		LLGridManager::getInstance()->setFavorite(); +		grid = LLGridManager::getInstance()->getGridInfo("myaddedgrid");  		ensure("setting favorite", grid.has(GRID_IS_FAVORITE_VALUE));  	} @@ -338,12 +378,12 @@ namespace tut  	template<> template<>  	void viewerNetworkTestObject::test<5>()  	{ -		LLGridManager manager("grid_test.xml"); +		LLGridManager::getInstance()->initialize("grid_test.xml");  		LLSD grid = LLSD::emptyMap();  		// adding a grid with simply a name will populate the values.  		grid[GRID_NAME_VALUE] = "myaddedgrid"; -		manager.addGrid(grid); -		grid = manager.getGridInfo("myaddedgrid"); +		LLGridManager::getInstance()->addGrid(grid); +		grid = LLGridManager::getInstance()->getGridInfo("myaddedgrid");  		ensure_equals("name based grid has name value",   					  grid[GRID_NAME_VALUE].asString(), @@ -380,29 +420,29 @@ namespace tut  	{  		// try with initial grid list without a grid file,  		// without setting the grid to a saveable favorite. -		LLGridManager manager("grid_test.xml"); +		LLGridManager::getInstance()->initialize("grid_test.xml");  		LLSD grid = LLSD::emptyMap();  		grid[GRID_NAME_VALUE] = std::string("mynewgridname"); -		manager.addGrid(grid); -		manager.saveFavorites(); +		LLGridManager::getInstance()->addGrid(grid); +		LLGridManager::getInstance()->saveFavorites();  		ensure("Grid file exists after saving",   			   LLFile::isfile("grid_test.xml")); -		manager = LLGridManager("grid_test.xml"); +		LLGridManager::getInstance()->initialize("grid_test.xml");  		// should not be there -		std::map<std::string, std::string> known_grids = manager.getKnownGrids(); +		std::map<std::string, std::string> known_grids = LLGridManager::getInstance()->getKnownGrids();  		ensure("New grid wasn't added to persisted list without being marked a favorite",  					  known_grids.find(std::string("mynewgridname")) == known_grids.end());  		// mark a grid a favorite to make sure it's persisted -		manager.addGrid(grid); -		manager.setGridChoice("mynewgridname"); -		manager.setFavorite(); -		manager.saveFavorites(); +		LLGridManager::getInstance()->addGrid(grid); +		LLGridManager::getInstance()->setGridChoice("mynewgridname"); +		LLGridManager::getInstance()->setFavorite(); +		LLGridManager::getInstance()->saveFavorites();  		ensure("Grid file exists after saving",   			   LLFile::isfile("grid_test.xml")); -		manager = LLGridManager("grid_test.xml"); +		LLGridManager::getInstance()->initialize("grid_test.xml");  		// should not be there -		known_grids = manager.getKnownGrids(); +		known_grids = LLGridManager::getInstance()->getKnownGrids();  		ensure("New grid wasn't added to persisted list after being marked a favorite",  					  known_grids.find(std::string("mynewgridname")) !=  					  known_grids.end()); @@ -417,28 +457,28 @@ namespace tut  		gridfile << gSampleGridFile;  		gridfile.close(); -		LLGridManager manager("grid_test.xml"); +		LLGridManager::getInstance()->initialize("grid_test.xml");  		LLSD grid = LLSD::emptyMap();  		grid[GRID_NAME_VALUE] = std::string("mynewgridname"); -		manager.addGrid(grid); -		manager.saveFavorites(); +		LLGridManager::getInstance()->addGrid(grid); +		LLGridManager::getInstance()->saveFavorites();  		// validate we didn't lose existing favorites -		manager = LLGridManager("grid_test.xml"); -		std::map<std::string, std::string> known_grids = manager.getKnownGrids(); +		LLGridManager::getInstance()->initialize("grid_test.xml"); +		std::map<std::string, std::string> known_grids = LLGridManager::getInstance()->getKnownGrids();  		ensure("New grid wasn't added to persisted list after being marked a favorite",  			   known_grids.find(std::string("grid1")) !=  			   known_grids.end());  		// add a grid -		manager.addGrid(grid); -		manager.setGridChoice("mynewgridname"); -		manager.setFavorite(); -		manager.saveFavorites(); -		known_grids = manager.getKnownGrids(); +		LLGridManager::getInstance()->addGrid(grid); +		LLGridManager::getInstance()->setGridChoice("mynewgridname"); +		LLGridManager::getInstance()->setFavorite(); +		LLGridManager::getInstance()->saveFavorites(); +		known_grids = LLGridManager::getInstance()->getKnownGrids();  		ensure("New grid wasn't added to persisted list after being marked a favorite",  			   known_grids.find(std::string("grid1")) !=  			   known_grids.end()); -		known_grids = manager.getKnownGrids(); +		known_grids = LLGridManager::getInstance()->getKnownGrids();  		ensure("New grid wasn't added to persisted list after being marked a favorite",  			   known_grids.find(std::string("mynewgridname")) !=  			   known_grids.end()); diff --git a/indra/viewer_components/login/lllogin.cpp b/indra/viewer_components/login/lllogin.cpp index 7723c3ca8f..452930a3b3 100644 --- a/indra/viewer_components/login/lllogin.cpp +++ b/indra/viewer_components/login/lllogin.cpp @@ -292,7 +292,12 @@ void LLLogin::Impl::login_(LLCoros::self& self, std::string uri, LLSD login_para  	// to success, add a data/message and data/reason fields.  	LLSD error_response;  	error_response["reason"] = mAuthResponse["status"]; +	error_response["errorcode"] = mAuthResponse["errorcode"];  	error_response["message"] = mAuthResponse["error"]; +	if(mAuthResponse.has("certificate")) +	{ +		error_response["certificate"] = mAuthResponse["certificate"]; +	}  	sendProgressEvent("offline", "fail.login", error_response);  	}  	catch (...) { | 
