From 9e89819d55a3b6ee7fc56f3efb36f273e4e05c83 Mon Sep 17 00:00:00 2001
From: Roxanne Skelly <roxie@lindenlab.com>
Date: Wed, 8 Jul 2009 00:45:17 +0000
Subject: [PATCH] DEV-34822 - merge with 1.23 certificate notification code -r
 118191

ignore-dead-branch
---
 indra/llmessage/llcurl.cpp                    |   9 +
 indra/llmessage/llcurl.h                      |   1 +
 indra/newview/CMakeLists.txt                  |  25 +
 indra/newview/app_settings/settings.xml       |  11 +
 indra/newview/llfloaterland.cpp               |   1 -
 indra/newview/lllogininstance.cpp             |   9 +
 indra/newview/llsecapi.cpp                    |   5 +
 indra/newview/llsecapi.h                      | 311 ++++--
 indra/newview/llsechandler_basic.cpp          | 913 ++++++++++++++++--
 indra/newview/llsechandler_basic.h            | 149 ++-
 indra/newview/llstartup.cpp                   | 163 +++-
 indra/newview/llviewernetwork.cpp             |  16 +-
 indra/newview/llviewernetwork.h               |   4 +-
 indra/newview/llxmlrpclistener.cpp            |  18 +-
 indra/newview/llxmlrpctransaction.cpp         | 103 +-
 indra/newview/llxmlrpctransaction.h           |   3 +
 .../skins/default/xui/en/notifications.xml    |  42 +
 indra/newview/tests/llsecapi_test.cpp         | 188 ++++
 .../newview/tests/llsechandler_basic_test.cpp | 625 ++++++++++--
 indra/newview/tests/llviewernetwork_test.cpp  | 224 +++--
 indra/viewer_components/login/lllogin.cpp     |   5 +
 21 files changed, 2466 insertions(+), 359 deletions(-)
 create mode 100644 indra/newview/tests/llsecapi_test.cpp

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(&timestruct));
+
+#if LL_WINDOWS
+	return LLDate((F64)_mkgmtime(&timestruct));
+#else // LL_WINDOWS
+	return LLDate((F64)timegm(&timestruct));
+#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 (...) {
-- 
GitLab