summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-rw-r--r--indra/llmessage/llcurl.cpp9
-rw-r--r--indra/llmessage/llcurl.h1
-rw-r--r--indra/newview/CMakeLists.txt25
-rw-r--r--indra/newview/app_settings/settings.xml11
-rw-r--r--indra/newview/llfloaterland.cpp1
-rw-r--r--indra/newview/lllogininstance.cpp9
-rw-r--r--indra/newview/llsecapi.cpp5
-rw-r--r--indra/newview/llsecapi.h311
-rw-r--r--indra/newview/llsechandler_basic.cpp913
-rw-r--r--indra/newview/llsechandler_basic.h149
-rw-r--r--indra/newview/llstartup.cpp163
-rw-r--r--indra/newview/llviewernetwork.cpp16
-rw-r--r--indra/newview/llviewernetwork.h4
-rw-r--r--indra/newview/llxmlrpclistener.cpp18
-rw-r--r--indra/newview/llxmlrpctransaction.cpp103
-rw-r--r--indra/newview/llxmlrpctransaction.h3
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml42
-rw-r--r--indra/newview/tests/llsecapi_test.cpp188
-rw-r--r--indra/newview/tests/llsechandler_basic_test.cpp625
-rw-r--r--indra/newview/tests/llviewernetwork_test.cpp224
-rw-r--r--indra/viewer_components/login/lllogin.cpp5
21 files changed, 2466 insertions, 359 deletions
diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp
index dc02367a62..91e11b8c0d 100644
--- a/indra/llmessage/llcurl.cpp
+++ b/indra/llmessage/llcurl.cpp
@@ -895,6 +895,15 @@ void LLCurlEasyRequest::setReadCallback(curl_read_callback callback, void* userd
}
}
+void LLCurlEasyRequest::setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata)
+{
+ if (mEasy)
+ {
+ mEasy->setopt(CURLOPT_SSL_CTX_FUNCTION, (void*)callback);
+ mEasy->setopt(CURLOPT_SSL_CTX_DATA, userdata);
+ }
+}
+
void LLCurlEasyRequest::slist_append(const char* str)
{
if (mEasy)
diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h
index 1bc1767966..b6a637ae5b 100644
--- a/indra/llmessage/llcurl.h
+++ b/indra/llmessage/llcurl.h
@@ -229,6 +229,7 @@ public:
void setHeaderCallback(curl_header_callback callback, void* userdata);
void setWriteCallback(curl_write_callback callback, void* userdata);
void setReadCallback(curl_read_callback callback, void* userdata);
+ void setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata);
void slist_append(const char* str);
void sendRequest(const std::string& url);
void requestComplete();
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 4be6fb940e..35f0a5036d 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -1803,6 +1803,31 @@ if (LL_TESTS)
"${CMAKE_SOURCE_DIR}/llmessage/tests/test_llsdmessage_peer.py"
)
+ set(test_libs
+ ${LLMESSAGE_LIBRARIES}
+ ${WINDOWS_LIBRARIES}
+ ${LLVFS_LIBRARIES}
+ ${LLMATH_LIBRARIES}
+ ${LLCOMMON_LIBRARIES}
+ ${GOOGLEMOCK_LIBRARIES}
+ ${OPENSSL_LIBRARIES}
+ ${CRYPTO_LIBRARIES}
+ )
+ LL_ADD_INTEGRATION_TEST(llsechandler_basic
+ llsechandler_basic.cpp
+ "${test_libs}"
+ )
+
+ LL_ADD_INTEGRATION_TEST(llsecapi
+ llsecapi.cpp
+ "${test_libs}"
+ )
+
+ LL_ADD_INTEGRATION_TEST(llviewernetwork
+ llviewernetwork.cpp
+ "${test_libs}"
+ )
+
#ADD_VIEWER_BUILD_TEST(llmemoryview viewer)
#ADD_VIEWER_BUILD_TEST(llagentaccess viewer)
#ADD_VIEWER_BUILD_TEST(llworldmap viewer)
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 75ee389750..1641ab0ce1 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -1275,6 +1275,17 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>CertStore</key>
+ <map>
+ <key>Comment</key>
+ <string>Specifies the Certificate Store for certificate trust verification</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>default</string>
+ </map>
<key>ChatBarStealsFocus</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp
index 598a13de15..9b6e24f251 100644
--- a/indra/newview/llfloaterland.cpp
+++ b/indra/newview/llfloaterland.cpp
@@ -42,7 +42,6 @@
#include "llnotificationsutil.h"
#include "llparcel.h"
#include "message.h"
-#include "lluserauth.h"
#include "llagent.h"
#include "llbutton.h"
diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp
index bb45cc93ea..e4b8becdd7 100644
--- a/indra/newview/lllogininstance.cpp
+++ b/indra/newview/lllogininstance.cpp
@@ -246,6 +246,15 @@ void LLLoginInstance::handleLoginFailure(const LLSD& event)
LLSD data(LLSD::emptyMap());
data["message"] = message_response;
data["reply_pump"] = TOS_REPLY_PUMP;
+ if(response.has("error_code"))
+ {
+ data["error_code"] = response["error_code"];
+ }
+ if(response.has("certificate"))
+ {
+ data["certificate"] = response["certificate"];
+ }
+
LLFloaterReg::showInstance("message_critical", data);
LLEventPumps::instance().obtain(TOS_REPLY_PUMP)
.listen(TOS_LISTENER_NAME,
diff --git a/indra/newview/llsecapi.cpp b/indra/newview/llsecapi.cpp
index cdf4a3fe01..70c247c2de 100644
--- a/indra/newview/llsecapi.cpp
+++ b/indra/newview/llsecapi.cpp
@@ -34,6 +34,7 @@
#include "llviewerprecompiledheaders.h"
#include "llsecapi.h"
#include "llsechandler_basic.h"
+#include <openssl/evp.h>
#include <map>
@@ -42,6 +43,9 @@ LLPointer<LLSecAPIHandler> gSecAPIHandler;
void initializeSecHandler()
{
+ OpenSSL_add_all_algorithms();
+ OpenSSL_add_all_ciphers();
+ OpenSSL_add_all_digests();
gHandlerMap[BASIC_SECHANDLER] = new LLSecAPIBasicHandler();
// Currently, we only have the Basic handler, so we can point the main sechandler
@@ -76,6 +80,7 @@ std::ostream& operator <<(std::ostream& s, const LLCredential& cred)
}
+
LLSD LLCredential::getLoginParams()
{
LLSD result = LLSD::emptyMap();
diff --git a/indra/newview/llsecapi.h b/indra/newview/llsecapi.h
index d456ca95b1..6fd12c044a 100644
--- a/indra/newview/llsecapi.h
+++ b/indra/newview/llsecapi.h
@@ -36,11 +36,16 @@
#include <openssl/x509.h>
#include <ostream>
+#ifdef LL_WINDOWS
+#pragma warning(disable:4250)
+#endif // LL_WINDOWS
+
// All error handling is via exceptions.
#define CERT_SUBJECT_NAME "subject_name"
#define CERT_ISSUER_NAME "issuer_name"
+#define CERT_NAME_CN "commonName"
#define CERT_SUBJECT_NAME_STRING "subject_name_string"
#define CERT_ISSUER_NAME_STRING "issuer_name_string"
@@ -51,23 +56,62 @@
#define CERT_VALID_TO "valid_to"
#define CERT_SHA1_DIGEST "sha1_digest"
#define CERT_MD5_DIGEST "md5_digest"
+#define CERT_HOSTNAME "hostname"
+#define CERT_BASIC_CONSTRAINTS "basicConstraints"
+#define CERT_BASIC_CONSTRAINTS_CA "CA"
+#define CERT_BASIC_CONSTRAINTS_PATHLEN "pathLen"
+
+#define CERT_KEY_USAGE "keyUsage"
+#define CERT_KU_DIGITAL_SIGNATURE "digitalSignature"
+#define CERT_KU_NON_REPUDIATION "nonRepudiation"
+#define CERT_KU_KEY_ENCIPHERMENT "keyEncipherment"
+#define CERT_KU_DATA_ENCIPHERMENT "dataEncipherment"
+#define CERT_KU_KEY_AGREEMENT "keyAgreement"
+#define CERT_KU_CERT_SIGN "certSigning"
+#define CERT_KU_CRL_SIGN "crlSigning"
+#define CERT_KU_ENCIPHER_ONLY "encipherOnly"
+#define CERT_KU_DECIPHER_ONLY "decipherOnly"
#define BASIC_SECHANDLER "BASIC_SECHANDLER"
+#define CERT_VALIDATION_DATE "validation_date"
+
+#define CERT_EXTENDED_KEY_USAGE "extendedKeyUsage"
+#define CERT_EKU_SERVER_AUTH SN_server_auth
+
+// validate the current time lies within
+// the validation period of the cert
+#define VALIDATION_POLICY_TIME 1
+
+// validate that the CA, or some cert in the chain
+// lies within the certificate store
+#define VALIDATION_POLICY_TRUSTED 2
+
+// validate that the subject name of
+// the cert contains the passed in hostname
+// or validates against the hostname
+#define VALIDATION_POLICY_HOSTNAME 4
+
+
+// validate that the cert contains the SSL EKU
+#define VALIDATION_POLICY_SSL_KU 8
+
+// validate that the cert contains the SSL EKU
+#define VALIDATION_POLICY_CA_KU 16
+
+#define VALIDATION_POLICY_CA_BASIC_CONSTRAINTS 32
+
+// validate that the cert is correct for SSL
+#define VALIDATION_POLICY_SSL (VALIDATION_POLICY_TIME | \
+ VALIDATION_POLICY_HOSTNAME | \
+ VALIDATION_POLICY_TRUSTED | \
+ VALIDATION_POLICY_SSL_KU | \
+ VALIDATION_POLICY_CA_BASIC_CONSTRAINTS | \
+ VALIDATION_POLICY_CA_KU)
+
+
-// All error handling is via exceptions.
-class LLCertException
-{
-public:
- LLCertException(const char* msg)
- {
- llerrs << "Certificate Error: " << msg << llendl;
- mMsg = std::string(msg);
- }
-protected:
- std::string mMsg;
-};
class LLProtectedDataException
{
@@ -96,53 +140,88 @@ public:
// return a PEM encoded certificate. The encoding
// includes the -----BEGIN CERTIFICATE----- and end certificate elements
- virtual std::string getPem()=0;
+ virtual std::string getPem() const=0;
// return a DER encoded certificate
- virtual std::vector<U8> getBinary()=0;
+ virtual std::vector<U8> getBinary() const=0;
// return an LLSD object containing information about the certificate
// such as its name, signature, expiry time, serial number
- virtual LLSD getLLSD()=0;
+ virtual LLSD getLLSD() const=0;
// return an openSSL X509 struct for the certificate
- virtual X509* getOpenSSLX509()=0;
+ virtual X509* getOpenSSLX509() const=0;
};
+// class LLCertificateVector
+// base class for a list of certificates.
-// class LLCertificateChain
-// Class representing a chain of certificates in order, with the
-// 0th element being the CA
-class LLCertificateChain : public LLRefCount
+
+class LLCertificateVector : public LLRefCount
{
- LOG_CLASS(LLCertificateChain);
- static const int VT_SSL = 0;
- static const int VT_AGENT_DOMAIN = 1;
- static const int VT_GRID_DOMAIN = 1;
public:
- LLCertificateChain() {}
- virtual ~LLCertificateChain() {}
+ LLCertificateVector() {};
+ virtual ~LLCertificateVector() {};
+
+ // base iterator implementation class, providing
+ // the functionality needed for the iterator class.
+ class iterator_impl : public LLRefCount
+ {
+ public:
+ iterator_impl() {};
+ virtual ~iterator_impl() {};
+ virtual void seek(bool incr)=0;
+ virtual LLPointer<iterator_impl> clone() const=0;
+ virtual bool equals(const LLPointer<iterator_impl>& _iter) const=0;
+ virtual LLPointer<LLCertificate> get()=0;
+ };
+
+ // iterator class
+ class iterator
+ {
+ public:
+ iterator(LLPointer<iterator_impl> impl) : mImpl(impl) {}
+ iterator() : mImpl(NULL) {}
+ iterator(const iterator& _iter) {mImpl = _iter.mImpl->clone(); }
+ ~iterator() {}
+ iterator& operator++() { if(mImpl.notNull()) mImpl->seek(true); return *this;}
+ iterator& operator--() { if(mImpl.notNull()) mImpl->seek(false); return *this;}
+
+ iterator operator++(int) { iterator result = *this; if(mImpl.notNull()) mImpl->seek(true); return result;}
+ iterator operator--(int) { iterator result = *this; if(mImpl.notNull()) mImpl->seek(false); return result;}
+ LLPointer<LLCertificate> operator*() { return mImpl->get(); }
+
+ LLPointer<iterator_impl> mImpl;
+ protected:
+ friend bool operator==(const LLCertificateVector::iterator& _lhs, const LLCertificateVector::iterator& _rhs);
+ bool equals(const iterator& _iter) const { return mImpl->equals(_iter.mImpl); }
+ };
- virtual X509_STORE getOpenSSLX509Store()=0; // return an openssl X509_STORE
- // for this store
+ // numeric indexer
+ virtual LLPointer<LLCertificate> operator[](int)=0;
- virtual void appendCert(const LLCertificate& cert)=0; // append a cert to the end
- //of the chain
+ // Iteration
+ virtual iterator begin()=0;
- virtual LLPointer<LLCertificate>& operator [](int index)=0; // retrieve a certificate
- // from the chain by index
- // -1 == end of chain
+ virtual iterator end()=0;
- virtual int len() const =0; // return number of certificates in the chain
+ // find a cert given params
+ virtual iterator find(const LLSD& params) =0;
- // validate a certificate chain given the params.
- // validation type indicates whether it's simply an SSL cert, or
- // something more specific
- virtual bool validate(int validation_type,
- const LLSD& validation_params) const =0;
+ // return the number of certs in the store
+ virtual int size() const = 0;
+
+ // append the cert to the store. if a copy of the cert already exists in the store, it is removed first
+ virtual void add(LLPointer<LLCertificate> cert)=0;
+
+ // insert the cert to the store. if a copy of the cert already exists in the store, it is removed first
+ virtual void insert(iterator location, LLPointer<LLCertificate> cert)=0;
+
+ // remove a certificate from the store
+ virtual LLPointer<LLCertificate> erase(iterator cert)=0;
};
@@ -151,43 +230,55 @@ public:
// certificates. The store can be persisted, and can be used to validate
// a cert chain
//
-class LLCertificateStore : public LLRefCount
+class LLCertificateStore : virtual public LLCertificateVector
{
+
public:
+
LLCertificateStore() {}
virtual ~LLCertificateStore() {}
- virtual X509_STORE* getOpenSSLX509Store()=0; // return an openssl X509_STORE
- // for this store
-
- // add a copy of a cert to the store
- virtual void append(const LLCertificate& cert)=0;
-
- // add a copy of a cert to the store
- virtual void insert(const int index, const LLCertificate& cert)=0;
-
- // remove a certificate from the store
- virtual void remove(int index)=0;
-
- // return a certificate at the index
- virtual LLPointer<LLCertificate> operator[](int index)=0;
-
- // return the number of certs in the store
- virtual int len() const =0;
-
- // load the store from a persisted location
- virtual void load(const std::string& store_id)=0;
-
// persist the store
virtual void save()=0;
// return the store id
- virtual std::string storeId()=0;
+ virtual std::string storeId() const=0;
+};
+
+// class LLCertificateChain
+// Class representing a chain of certificates in order, with the
+// first element being the child cert.
+class LLCertificateChain : virtual public LLCertificateVector
+{
+
+public:
+ LLCertificateChain() {}
- // validate a cert chain
- virtual bool validate(const LLCertificateChain& cert_chain) const=0;
+ virtual ~LLCertificateChain() {}
+
+ // validate a certificate chain given the params.
+ // Will throw exceptions on error
+
+ virtual void validate(int validation_policy,
+ LLPointer<LLCertificateStore> ca_store,
+ const LLSD& validation_params) =0;
};
+
+
+
+inline
+bool operator==(const LLCertificateVector::iterator& _lhs, const LLCertificateVector::iterator& _rhs)
+{
+ return _lhs.equals(_rhs);
+}
+inline
+bool operator!=(const LLCertificateVector::iterator& _lhs, const LLCertificateVector::iterator& _rhs)
+{
+ return !(_lhs == _rhs);
+}
+
+
//
// LLCredential - interface for credentials providing the following functionality:
// * persistance of credential information based on grid (for saving username/password)
@@ -232,6 +323,98 @@ protected:
std::ostream& operator <<(std::ostream& s, const LLCredential& cred);
+// All error handling is via exceptions.
+
+class LLCertException
+{
+public:
+ LLCertException(LLPointer<LLCertificate> cert, const char* msg)
+ {
+
+ mCert = cert;
+
+ LL_WARNS("SECAPI") << "Certificate Error: " << (std::string)msg << LL_ENDL;
+ mMsg = (std::string)msg;
+ }
+ LLPointer<LLCertificate> getCert() { return mCert; }
+ std::string getMessage() { return mMsg; }
+protected:
+ LLPointer<LLCertificate> mCert;
+ std::string mMsg;
+};
+
+class LLInvalidCertificate : public LLCertException
+{
+public:
+ LLInvalidCertificate(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertInvalid")
+ {
+ }
+protected:
+};
+
+class LLCertValidationTrustException : public LLCertException
+{
+public:
+ LLCertValidationTrustException(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertUntrusted")
+ {
+ }
+protected:
+};
+
+class LLCertValidationHostnameException : public LLCertException
+{
+public:
+ LLCertValidationHostnameException(std::string hostname,
+ LLPointer<LLCertificate> cert) : LLCertException(cert, "CertInvalidHostname")
+ {
+ mHostname = hostname;
+ }
+
+ std::string getHostname() { return mHostname; }
+protected:
+ std::string mHostname;
+};
+
+class LLCertValidationExpirationException : public LLCertException
+{
+public:
+ LLCertValidationExpirationException(LLPointer<LLCertificate> cert,
+ LLDate current_time) : LLCertException(cert, "CertExpired")
+ {
+ mTime = current_time;
+ }
+ LLDate GetTime() { return mTime; }
+protected:
+ LLDate mTime;
+};
+
+class LLCertKeyUsageValidationException : public LLCertException
+{
+public:
+ LLCertKeyUsageValidationException(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertKeyUsage")
+ {
+ }
+protected:
+};
+
+class LLCertBasicConstraintsValidationException : public LLCertException
+{
+public:
+ LLCertBasicConstraintsValidationException(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertBasicConstraints")
+ {
+ }
+protected:
+};
+
+class LLCertValidationInvalidSignatureException : public LLCertException
+{
+public:
+ LLCertValidationInvalidSignatureException(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertInvalidSignature")
+ {
+ }
+protected:
+};
+
// LLSecAPIHandler Class
// Interface handler class for the various security storage handlers.
class LLSecAPIHandler : public LLRefCount
diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp
index 4180f578b9..097cc395d7 100644
--- a/indra/newview/llsechandler_basic.cpp
+++ b/indra/newview/llsechandler_basic.cpp
@@ -20,7 +20,7 @@
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at http://secondlife.com/developers/opensource/flossexception
*
- * By copying, modifying or distributing this software, you acknowledge
+LLS * By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
@@ -42,17 +42,26 @@
#include "llviewercontrol.h"
#include <vector>
#include <ios>
+#include <openssl/ossl_typ.h>
#include <openssl/x509.h>
+#include <openssl/x509v3.h>
#include <openssl/pem.h>
#include <openssl/asn1.h>
#include <openssl/rand.h>
+#include <openssl/err.h>
#include <iostream>
#include <iomanip>
#include <time.h>
+
+
// 128 bits of salt data...
#define STORE_SALT_SIZE 16
#define BUFFER_READ_SIZE 256
+std::string cert_string_from_asn1_string(ASN1_STRING* value);
+LLSD _basic_constraints_ext(X509* cert);
+LLSD _key_usage_ext(X509* cert);
+LLSD _ext_key_usage_ext(X509* cert);
LLBasicCertificate::LLBasicCertificate(const std::string& pem_cert)
@@ -61,14 +70,19 @@ LLBasicCertificate::LLBasicCertificate(const std::string& pem_cert)
// BIO_new_mem_buf returns a read only bio, but takes a void* which isn't const
// so we need to cast it.
BIO * pem_bio = BIO_new_mem_buf((void*)pem_cert.c_str(), pem_cert.length());
-
+ if(pem_bio == NULL)
+ {
+ LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL;
+ throw LLInvalidCertificate(this);
+ }
mCert = NULL;
PEM_read_bio_X509(pem_bio, &mCert, 0, NULL);
BIO_free(pem_bio);
if (!mCert)
{
- throw LLCertException("Error parsing certificate");
+ throw LLInvalidCertificate(this);
}
+ _initLLSD();
}
@@ -76,20 +90,23 @@ LLBasicCertificate::LLBasicCertificate(X509* pCert)
{
if (!pCert || !pCert->cert_info)
{
- throw LLCertException("Invalid certificate");
+ throw LLInvalidCertificate(this);
}
mCert = X509_dup(pCert);
+ _initLLSD();
}
LLBasicCertificate::~LLBasicCertificate()
{
-
- X509_free(mCert);
+ if(mCert)
+ {
+ X509_free(mCert);
+ }
}
//
// retrieve the pem using the openssl functionality
-std::string LLBasicCertificate::getPem()
+std::string LLBasicCertificate::getPem() const
{
char * pem_bio_chars = NULL;
// a BIO is the equivalent of a 'std::stream', and
@@ -98,7 +115,8 @@ std::string LLBasicCertificate::getPem()
BIO *pem_bio = BIO_new(BIO_s_mem());
if (!pem_bio)
{
- throw LLCertException("couldn't allocate memory buffer");
+ LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL;
+ return std::string();
}
PEM_write_bio_X509(pem_bio, mCert);
int length = BIO_get_mem_data(pem_bio, &pem_bio_chars);
@@ -109,14 +127,15 @@ std::string LLBasicCertificate::getPem()
// get the DER encoding for the cert
// DER is a binary encoding format for certs...
-std::vector<U8> LLBasicCertificate::getBinary()
+std::vector<U8> LLBasicCertificate::getBinary() const
{
U8 * der_bio_data = NULL;
// get a memory bio
BIO *der_bio = BIO_new(BIO_s_mem());
if (!der_bio)
{
- throw LLCertException("couldn't allocate memory buffer");
+ LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL;
+ return std::vector<U8>();
}
i2d_X509_bio(der_bio, mCert);
int length = BIO_get_mem_data(der_bio, &der_bio_data);
@@ -128,32 +147,131 @@ std::vector<U8> LLBasicCertificate::getBinary()
}
-LLSD LLBasicCertificate::getLLSD()
+LLSD LLBasicCertificate::getLLSD() const
+{
+ return mLLSDInfo;
+}
+
+// Initialize the LLSD info for the certificate
+LLSD& LLBasicCertificate::_initLLSD()
{
- LLSD result;
// call the various helpers to build the LLSD
- result[CERT_SUBJECT_NAME] = cert_name_from_X509_NAME(X509_get_subject_name(mCert));
- result[CERT_ISSUER_NAME] = cert_name_from_X509_NAME(X509_get_issuer_name(mCert));
- result[CERT_SUBJECT_NAME_STRING] = cert_string_name_from_X509_NAME(X509_get_subject_name(mCert));
- result[CERT_ISSUER_NAME_STRING] = cert_string_name_from_X509_NAME(X509_get_issuer_name(mCert));
+ mLLSDInfo[CERT_SUBJECT_NAME] = cert_name_from_X509_NAME(X509_get_subject_name(mCert));
+ mLLSDInfo[CERT_ISSUER_NAME] = cert_name_from_X509_NAME(X509_get_issuer_name(mCert));
+ mLLSDInfo[CERT_SUBJECT_NAME_STRING] = cert_string_name_from_X509_NAME(X509_get_subject_name(mCert));
+ mLLSDInfo[CERT_ISSUER_NAME_STRING] = cert_string_name_from_X509_NAME(X509_get_issuer_name(mCert));
ASN1_INTEGER *sn = X509_get_serialNumber(mCert);
if (sn != NULL)
{
- result[CERT_SERIAL_NUMBER] = cert_string_from_asn1_integer(sn);
+ mLLSDInfo[CERT_SERIAL_NUMBER] = cert_string_from_asn1_integer(sn);
}
- result[CERT_VALID_TO] = cert_date_from_asn1_time(X509_get_notAfter(mCert));
- result[CERT_VALID_FROM] = cert_date_from_asn1_time(X509_get_notBefore(mCert));
- result[CERT_SHA1_DIGEST] = cert_get_digest("sha1", mCert);
- result[CERT_MD5_DIGEST] = cert_get_digest("md5", mCert);
+ mLLSDInfo[CERT_VALID_TO] = cert_date_from_asn1_time(X509_get_notAfter(mCert));
+ mLLSDInfo[CERT_VALID_FROM] = cert_date_from_asn1_time(X509_get_notBefore(mCert));
+ mLLSDInfo[CERT_SHA1_DIGEST] = cert_get_digest("sha1", mCert);
+ mLLSDInfo[CERT_MD5_DIGEST] = cert_get_digest("md5", mCert);
+ // add the known extensions
+ mLLSDInfo[CERT_BASIC_CONSTRAINTS] = _basic_constraints_ext(mCert);
+ mLLSDInfo[CERT_KEY_USAGE] = _key_usage_ext(mCert);
+ mLLSDInfo[CERT_EXTENDED_KEY_USAGE] = _ext_key_usage_ext(mCert);
+ return mLLSDInfo;
+}
+
+// Retrieve the basic constraints info
+LLSD _basic_constraints_ext(X509* cert)
+{
+ LLSD result;
+ BASIC_CONSTRAINTS *bs = (BASIC_CONSTRAINTS *)X509_get_ext_d2i(cert, NID_basic_constraints, NULL, NULL);
+ if(bs)
+ {
+ result = LLSD::emptyMap();
+ // Determines whether the cert can be used as a CA
+ result[CERT_BASIC_CONSTRAINTS_CA] = (bool)bs->ca;
+
+ if(bs->pathlen)
+ {
+ // the pathlen determines how deep a certificate chain can be from
+ // this CA
+ if((bs->pathlen->type == V_ASN1_NEG_INTEGER)
+ || !bs->ca)
+ {
+ result[CERT_BASIC_CONSTRAINTS_PATHLEN] = 0;
+ }
+ else
+ {
+ result[CERT_BASIC_CONSTRAINTS_PATHLEN] = (int)ASN1_INTEGER_get(bs->pathlen);
+ }
+ }
+ }
+ return result;
+}
+// retrieve the key usage, which specifies how the cert can be used.
+//
+LLSD _key_usage_ext(X509* cert)
+{
+ LLSD result;
+ ASN1_STRING *usage_str = (ASN1_STRING *)X509_get_ext_d2i(cert, NID_key_usage, NULL, NULL);
+ if(usage_str)
+ {
+ result = LLSD::emptyArray();
+ long usage = 0;
+ if(usage_str->length > 0)
+ {
+ usage = usage_str->data[0];
+ if(usage_str->length > 1)
+ {
+ usage |= usage_str->data[1] << 8;
+ }
+ }
+ ASN1_STRING_free(usage_str);
+ if(usage)
+ {
+ if(usage & KU_DIGITAL_SIGNATURE) result.append(LLSD((std::string)CERT_KU_DIGITAL_SIGNATURE));
+ if(usage & KU_NON_REPUDIATION) result.append(LLSD((std::string)CERT_KU_NON_REPUDIATION));
+ if(usage & KU_KEY_ENCIPHERMENT) result.append(LLSD((std::string)CERT_KU_KEY_ENCIPHERMENT));
+ if(usage & KU_DATA_ENCIPHERMENT) result.append(LLSD((std::string)CERT_KU_DATA_ENCIPHERMENT));
+ if(usage & KU_KEY_AGREEMENT) result.append(LLSD((std::string)CERT_KU_KEY_AGREEMENT));
+ if(usage & KU_KEY_CERT_SIGN) result.append(LLSD((std::string)CERT_KU_CERT_SIGN));
+ if(usage & KU_CRL_SIGN) result.append(LLSD((std::string)CERT_KU_CRL_SIGN));
+ if(usage & KU_ENCIPHER_ONLY) result.append(LLSD((std::string)CERT_KU_ENCIPHER_ONLY));
+ if(usage & KU_DECIPHER_ONLY) result.append(LLSD((std::string)CERT_KU_DECIPHER_ONLY));
+ }
+ }
+ return result;
+}
- return result;
+// retrieve the extended key usage for the cert
+LLSD _ext_key_usage_ext(X509* cert)
+{
+ LLSD result;
+ EXTENDED_KEY_USAGE *eku = (EXTENDED_KEY_USAGE *)X509_get_ext_d2i(cert, NID_ext_key_usage, NULL, NULL);
+ if(eku)
+ {
+ result = LLSD::emptyArray();
+ while(sk_ASN1_OBJECT_num(eku))
+ {
+ ASN1_OBJECT *usage = sk_ASN1_OBJECT_pop(eku);
+ if(usage)
+ {
+ int nid = OBJ_obj2nid(usage);
+ if (nid)
+ {
+ std::string sn = OBJ_nid2sn(nid);
+ result.append(sn);
+ }
+ ASN1_OBJECT_free(usage);
+ }
+ }
+ }
+ return result;
}
-X509* LLBasicCertificate::getOpenSSLX509()
+// retrieve an openssl x509 object,
+// which must be freed by X509_free
+X509* LLBasicCertificate::getOpenSSLX509() const
{
return X509_dup(mCert);
}
@@ -203,13 +321,46 @@ LLSD cert_name_from_X509_NAME(X509_NAME* name)
std::string cert_string_from_asn1_integer(ASN1_INTEGER* value)
{
+ std::string result;
BIGNUM *bn = ASN1_INTEGER_to_BN(value, NULL);
- char * ascii_bn = BN_bn2hex(bn);
+ if(bn)
+ {
+ char * ascii_bn = BN_bn2hex(bn);
- BN_free(bn);
+ if(ascii_bn)
+ {
+ result = ascii_bn;
+ OPENSSL_free(ascii_bn);
+ }
+ BN_free(bn);
+ }
+ return result;
+}
+
+// Generate a string from an ASN1 integer. ASN1 Integers are
+// bignums, so they can be 'infinitely' long, therefore we
+// cannot simply use a conversion to U64 or something.
+// We retrieve as a readable string for UI
- std::string result(ascii_bn);
- OPENSSL_free(ascii_bn);
+std::string cert_string_from_asn1_string(ASN1_STRING* value)
+{
+ char * string_bio_chars = NULL;
+ std::string result;
+ // get a memory bio
+ BIO *string_bio = BIO_new(BIO_s_mem());
+ if(!string_bio)
+ {
+ // stream the name into the bio. The name will be in the 'short name' format
+ ASN1_STRING_print_ex(string_bio, value, ASN1_STRFLGS_RFC2253);
+ int length = BIO_get_mem_data(string_bio, &string_bio_chars);
+ result = std::string(string_bio_chars, length);
+ BIO_free(string_bio);
+ }
+ else
+ {
+ LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL;
+ }
+
return result;
}
@@ -222,8 +373,9 @@ LLDate cert_date_from_asn1_time(ASN1_TIME* asn1_time)
int i = asn1_time->length;
if (i < 10)
- throw LLCertException("invalid certificate time value");
-
+ {
+ return LLDate();
+ }
// convert the date from the ASN1 time (which is a string in ZULU time), to
// a timeval.
timestruct.tm_year = (asn1_time->data[0]-'0') * 10 + (asn1_time->data[1]-'0');
@@ -237,19 +389,23 @@ LLDate cert_date_from_asn1_time(ASN1_TIME* asn1_time)
timestruct.tm_hour = (asn1_time->data[6]-'0') * 10 + (asn1_time->data[7]-'0');
timestruct.tm_min = (asn1_time->data[8]-'0') * 10 + (asn1_time->data[9]-'0');
timestruct.tm_sec = (asn1_time->data[10]-'0') * 10 + (asn1_time->data[11]-'0');
-
- return LLDate((F64)mktime(&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 (...) {