diff options
23 files changed, 806 insertions, 10 deletions
diff --git a/chrome/browser/renderer_host/buffered_resource_handler.cc b/chrome/browser/renderer_host/buffered_resource_handler.cc index 347bf69..68514ad 100644 --- a/chrome/browser/renderer_host/buffered_resource_handler.cc +++ b/chrome/browser/renderer_host/buffered_resource_handler.cc @@ -14,6 +14,7 @@ #include "chrome/browser/renderer_host/download_throttling_resource_handler.h" #include "chrome/browser/renderer_host/resource_dispatcher_host.h" #include "chrome/browser/renderer_host/resource_dispatcher_host_request_info.h" +#include "chrome/browser/renderer_host/x509_user_cert_resource_handler.h" #include "chrome/common/url_constants.h" #include "net/base/io_buffer.h" #include "net/base/mime_sniffer.h" @@ -293,6 +294,50 @@ bool BufferedResourceHandler::CompleteResponseStarted(int request_id, // TODO(paulg): Only download if the context from the renderer allows it. ResourceDispatcherHostRequestInfo* info = ResourceDispatcherHost::InfoForRequest(request_); + std::string mime_type; + request_->GetMimeType(&mime_type); + + // Check if this is an X.509 certificate, if yes, let it be handled + // by X509UserCertResourceHandler. + if (mime_type == "application/x-x509-user-cert") { + + // This is entirely similar to how DownloadThrottlingResourceHandler + // works except we are doing it for an X.509 client certificates. + + if (response_->response_head.headers && // Can be NULL if FTP. + response_->response_head.headers->response_code() / 100 != 2) { + // The response code indicates that this is an error page, but we are + // expecting an X.509 user certificate. We follow Firefox here and show + // our own error page instead of handling the error page as a + // certificate. + // TODO(abarth): We should abstract the response_code test, but this kind + // of check is scattered throughout our codebase. + request_->SimulateError(net::ERR_FILE_NOT_FOUND); + return false; + } + + scoped_refptr<X509UserCertResourceHandler> x509_cert_handler = + new X509UserCertResourceHandler(host_, request_); + + if (bytes_read_) { + // A Read has already occured and we need to copy the data into the + // EventHandler. + net::IOBuffer* buf = NULL; + int buf_len = 0; + x509_cert_handler->OnWillRead(request_id, &buf, &buf_len, bytes_read_); + CHECK((buf_len >= bytes_read_) && (bytes_read_ >= 0)); + memcpy(buf->data(), read_buffer_->data(), bytes_read_); + } + + // Inform the renderer that this will be handled entirely by the browser. + real_handler_->OnResponseStarted(info->request_id(), response_); + URLRequestStatus status(URLRequestStatus::HANDLED_EXTERNALLY, 0); + real_handler_->OnResponseCompleted(info->request_id(), status, + std::string()); + + // This is handled entirely within the browser, so just reset the handler. + real_handler_ = x509_cert_handler; + } if (info->allow_download() && ShouldDownload(NULL)) { if (response_->response_head.headers && // Can be NULL if FTP. @@ -310,14 +355,14 @@ bool BufferedResourceHandler::CompleteResponseStarted(int request_id, scoped_refptr<DownloadThrottlingResourceHandler> download_handler = new DownloadThrottlingResourceHandler(host_, - request_, - request_->url(), - info->child_id(), - info->route_id(), - request_id, - in_complete); + request_, + request_->url(), + info->child_id(), + info->route_id(), + request_id, + in_complete); if (bytes_read_) { - // a Read has already occurred and we need to copy the data into the + // A Read has already occurred and we need to copy the data into the // EventHandler. net::IOBuffer* buf = NULL; int buf_len = 0; diff --git a/chrome/browser/renderer_host/resource_message_filter.cc b/chrome/browser/renderer_host/resource_message_filter.cc index 0650fa7..dc49500 100644 --- a/chrome/browser/renderer_host/resource_message_filter.cc +++ b/chrome/browser/renderer_host/resource_message_filter.cc @@ -46,6 +46,7 @@ #include "chrome/common/render_messages.h" #include "chrome/common/url_constants.h" #include "chrome/common/worker_messages.h" +#include "net/base/keygen_handler.h" #include "net/base/mime_util.h" #include "net/base/load_flags.h" #include "net/http/http_cache.h" @@ -381,6 +382,7 @@ bool ResourceMessageFilter::OnMessageReceived(const IPC::Message& msg) { OnCloseIdleConnections) IPC_MESSAGE_HANDLER(ViewHostMsg_SetCacheMode, OnSetCacheMode) IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_GetFileSize, OnGetFileSize) + IPC_MESSAGE_HANDLER(ViewHostMsg_Keygen, OnKeygen) #if defined(USE_TCMALLOC) IPC_MESSAGE_HANDLER(ViewHostMsg_RendererTcmalloc, OnRendererTcmalloc) #endif @@ -1110,6 +1112,16 @@ void ResourceMessageFilter::ReplyGetFileSize(int64 result, void* param) { Release(); } +void ResourceMessageFilter::OnKeygen(uint32 key_size_index, + const std::string& challenge_string, + const GURL& url, + std::string* signed_public_key) { + scoped_ptr<net::KeygenHandler> keygen_handler( + new net::KeygenHandler(key_size_index, + challenge_string)); + *signed_public_key = keygen_handler->GenKeyAndSignChallenge(); +} + #if defined(USE_TCMALLOC) void ResourceMessageFilter::OnRendererTcmalloc(base::ProcessId pid, const std::string& output) { diff --git a/chrome/browser/renderer_host/resource_message_filter.h b/chrome/browser/renderer_host/resource_message_filter.h index 029fb20..f0029a5 100644 --- a/chrome/browser/renderer_host/resource_message_filter.h +++ b/chrome/browser/renderer_host/resource_message_filter.h @@ -277,7 +277,8 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter, void OnGetFileSize(const FilePath& path, IPC::Message* reply_msg); void ReplyGetFileSize(int64 result, void* param); - + void OnKeygen(uint32 key_size_index, const std::string& challenge_string, + const GURL& url, std::string* signed_public_key); #if defined(OS_LINUX) void SendDelayedReply(IPC::Message* reply_msg); void DoOnGetScreenInfo(gfx::NativeViewId view, IPC::Message* reply_msg); diff --git a/chrome/browser/renderer_host/x509_user_cert_resource_handler.cc b/chrome/browser/renderer_host/x509_user_cert_resource_handler.cc new file mode 100644 index 0000000..76dcedd --- /dev/null +++ b/chrome/browser/renderer_host/x509_user_cert_resource_handler.cc @@ -0,0 +1,100 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/renderer_host/x509_user_cert_resource_handler.h" + +#include "base/string_util.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/browser/download/download_file.h" +#include "chrome/browser/renderer_host/resource_dispatcher_host.h" +#include "chrome/browser/renderer_host/resource_dispatcher_host_request_info.h" +#include "chrome/common/url_constants.h" +#include "net/base/cert_database.h" +#include "net/base/io_buffer.h" +#include "net/base/mime_sniffer.h" +#include "net/base/mime_util.h" +#include "net/http/http_response_headers.h" + +X509UserCertResourceHandler::X509UserCertResourceHandler( + ResourceDispatcherHost* host, URLRequest* request) + : host_(host), + request_(request), + content_length_(0), + buffer_(new DownloadBuffer), + read_buffer_(NULL), + resource_buffer_(NULL) { +} + +bool X509UserCertResourceHandler::OnRequestRedirected(int request_id, + const GURL& url, + ResourceResponse* resp, + bool* defer) { + url_ = url; + return true; +} + +bool X509UserCertResourceHandler::OnResponseStarted(int request_id, + ResourceResponse* resp) { + return (resp->response_head.mime_type == "application/x-x509-user-cert"); +} + +bool X509UserCertResourceHandler::OnWillRead(int request_id, + net::IOBuffer** buf, + int* buf_size, + int min_size) { + // TODO(gauravsh): Should we use 'min_size' here? + DCHECK(buf && buf_size); + if (!read_buffer_) { + read_buffer_ = new net::IOBuffer(kReadBufSize); + } + *buf = read_buffer_.get(); + *buf_size = kReadBufSize; + + return true; +} + +bool X509UserCertResourceHandler::OnReadCompleted(int request_id, + int* bytes_read) { + if (!*bytes_read) + return true; + + // We have more data to read. + DCHECK(read_buffer_); + content_length_ += *bytes_read; + + // Release the ownership of the buffer, and store a reference + // to it. A new one will be allocated in OnWillRead(). + net::IOBuffer* buffer = NULL; + read_buffer_.swap(&buffer); + // TODO(gauravsh): Should this be handled by a separate thread? + buffer_->contents.push_back(std::make_pair(buffer, *bytes_read)); + + return true; +} + +bool X509UserCertResourceHandler::OnResponseCompleted( + int request_id, + const URLRequestStatus& urs, + const std::string& sec_info) { + // TODO(gauravsh): Verify that 'request_id' was actually a keygen form post + // and only then import the certificate. + scoped_ptr<net::CertDatabase> cert_db(new net::CertDatabase()); + AssembleResource(); + + return cert_db->AddUserCert(resource_buffer_->data(), content_length_); +} + +void X509UserCertResourceHandler::AssembleResource() { + size_t bytes_copied = 0; + resource_buffer_ = new net::IOBuffer(content_length_); + + for (size_t i = 0; i < buffer_->contents.size(); ++i) { + net::IOBuffer* data = buffer_->contents[i].first; + const int data_len = buffer_->contents[i].second; + DCHECK(bytes_copied + data_len <= content_length_); + memcpy(resource_buffer_->data() + bytes_copied, data->data(), data_len); + bytes_copied += data_len; + } +} diff --git a/chrome/browser/renderer_host/x509_user_cert_resource_handler.h b/chrome/browser/renderer_host/x509_user_cert_resource_handler.h new file mode 100644 index 0000000..78dffad --- /dev/null +++ b/chrome/browser/renderer_host/x509_user_cert_resource_handler.h @@ -0,0 +1,58 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_RENDERER_HOST_X509_USER_CERT_RESOURCE_HANDLER_H_ +#define CHROME_BROWSER_RENDERER_HOST_X509_USER_CERT_RESOURCE_HANDLER_H_ + +#include <string> + +#include "chrome/browser/renderer_host/resource_dispatcher_host.h" +#include "chrome/browser/renderer_host/resource_handler.h" +#include "chrome/browser/download/download_file.h" + +// This class handles the "application/x-x509-user-cert" mime-type +// which is a certificate generated by a CA after a previous +// <keygen> form post. + +class X509UserCertResourceHandler : public ResourceHandler { + public: + X509UserCertResourceHandler(ResourceDispatcherHost* host, + URLRequest* request); + + // Not needed, as this event handler ought to be the final resource. + bool OnRequestRedirected(int request_id, const GURL& url, + ResourceResponse* resp, bool* defer); + + // Check if this indeed an X509 cert. + bool OnResponseStarted(int request_id, ResourceResponse* resp); + + // Create a new buffer to store received data. + bool OnWillRead(int request_id, net::IOBuffer** buf, int* buf_size, + int min_size); + + // A read was completed, maybe allocate a new buffer for further data. + bool OnReadCompleted(int request_id, int* bytes_read); + + // Done downloading the certificate. + bool OnResponseCompleted(int request_id, + const URLRequestStatus& urs, + const std::string& sec_info); + + + private: + void AssembleResource(); + + GURL url_; + ResourceDispatcherHost* host_; + URLRequest* request_; + size_t content_length_; + scoped_ptr<DownloadBuffer> buffer_; + scoped_refptr<net::IOBuffer> read_buffer_; + scoped_refptr<net::IOBuffer> resource_buffer_; // Downloaded certificate. + static const int kReadBufSize = 32768; + + DISALLOW_COPY_AND_ASSIGN(X509UserCertResourceHandler); +}; + +#endif // CHROME_BROWSER_RENDERER_HOST_X509_USER_CERT__RESOURCE_HANDLER_H_ diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index add4723..43ea689 100755 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -1965,6 +1965,8 @@ 'browser/renderer_host/sync_resource_handler.h', 'browser/renderer_host/web_cache_manager.cc', 'browser/renderer_host/web_cache_manager.h', + 'browser/renderer_host/x509_user_cert_resource_handler.cc', + 'browser/renderer_host/x509_user_cert_resource_handler.h', 'browser/rlz/rlz.cc', 'browser/rlz/rlz.h', 'browser/safe_browsing/bloom_filter.cc', diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index 1b39dae..ab4397c 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -1923,4 +1923,17 @@ IPC_BEGIN_MESSAGES(ViewHost) IPC_MESSAGE_CONTROL1(ViewHostMsg_SocketStream_Close, int /* socket_id */) + //--------------------------------------------------------------------------- + // Request for cryptographic operation messages: + // These are messages from the renderer to the browser to perform a + // cryptographic operation. + + // Asks the browser process to generate a keypair for grabbing a client + // certificate from a CA (<keygen> tag), and returns the signed public + // key and challenge string. + IPC_SYNC_MESSAGE_CONTROL3_1(ViewHostMsg_Keygen, + uint32 /* key size index */, + std::string /* challenge string */, + GURL /* URL of requestor */, + std::string /* signed public key and challenge */) IPC_END_MESSAGES(ViewHost) diff --git a/chrome/renderer/renderer_webkitclient_impl.cc b/chrome/renderer/renderer_webkitclient_impl.cc index 69db1df..1f95c1ff 100644 --- a/chrome/renderer/renderer_webkitclient_impl.cc +++ b/chrome/renderer/renderer_webkitclient_impl.cc @@ -23,6 +23,7 @@ #include "chrome/renderer/render_thread.h" #include "chrome/renderer/renderer_webstoragenamespace_impl.h" #include "chrome/renderer/visitedlink_slave.h" +#include "googleurl/src/gurl.h" #include "webkit/api/public/WebString.h" #include "webkit/api/public/WebURL.h" #include "webkit/appcache/web_application_cache_host_impl.h" @@ -314,3 +315,18 @@ long long RendererWebKitClientImpl::databaseGetFileSize( message_id), message_id, 0LL); } + +//------------------------------------------------------------------------------ + +WebKit::WebString RendererWebKitClientImpl::signedPublicKeyAndChallengeString( + unsigned key_size_index, + const WebKit::WebString& challenge, + const WebKit::WebURL& url) { + std::string signed_public_key; + RenderThread::current()->Send(new ViewHostMsg_Keygen( + static_cast<uint32>(key_size_index), + webkit_glue::WebStringToStdString(challenge), + GURL(url) , + &signed_public_key)); + return webkit_glue::StdStringToWebString(signed_public_key); +} diff --git a/chrome/renderer/renderer_webkitclient_impl.h b/chrome/renderer/renderer_webkitclient_impl.h index d9b7fc3..c5ac687 100644 --- a/chrome/renderer/renderer_webkitclient_impl.h +++ b/chrome/renderer/renderer_webkitclient_impl.h @@ -52,6 +52,10 @@ class RendererWebKitClientImpl : public webkit_glue::WebKitClientImpl { bool sync_dir); virtual long databaseGetFileAttributes(const WebKit::WebString& file_name); virtual long long databaseGetFileSize(const WebKit::WebString& file_name); + virtual WebKit::WebString signedPublicKeyAndChallengeString( + unsigned key_size_index, + const WebKit::WebString& challenge, + const WebKit::WebURL& url); virtual WebKit::WebApplicationCacheHost* createApplicationCacheHost( WebKit::WebApplicationCacheHostClient*); diff --git a/net/base/cert_database.h b/net/base/cert_database.h new file mode 100644 index 0000000..e78c22e --- /dev/null +++ b/net/base/cert_database.h @@ -0,0 +1,34 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_BASE_CERT_DATABASE_H_ +#define NET_BASE_CERT_DATABASE_H_ + +#include "net/base/x509_certificate.h" + +namespace net { + +// This class provides functions to manipulate the local +// certificate store. + +// TODO(gauravsh): This class could be augmented with methods +// for all operations that manipulate the underlying system +// certificate store. + +class CertDatabase { + public: + CertDatabase(); + + // Extract and Store User (Client) Certificate from a data blob. + // Return true if successful. + bool AddUserCert(const char* data, int len); + + private: + void Init(); + DISALLOW_COPY_AND_ASSIGN(CertDatabase); +}; + +} // namespace net + +#endif // NET_BASE_CERT_DATABASE_H_ diff --git a/net/base/cert_database_mac.cc b/net/base/cert_database_mac.cc new file mode 100644 index 0000000..5caf9db --- /dev/null +++ b/net/base/cert_database_mac.cc @@ -0,0 +1,24 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/base/cert_database.h" + +#include "base/logging.h" + +namespace net { + +CertDatabase::CertDatabase() { + NOTIMPLEMENTED(); +} + +bool CertDatabase::AddUserCert(const char* data, int len) { + NOTIMPLEMENTED(); + return false; +} + +void CertDatabase::Init() { + NOTIMPLEMENTED(); +} + +} // namespace net diff --git a/net/base/cert_database_nss.cc b/net/base/cert_database_nss.cc new file mode 100644 index 0000000..5f43fef --- /dev/null +++ b/net/base/cert_database_nss.cc @@ -0,0 +1,102 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/base/cert_database.h" + +// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=455424 +// until NSS 3.12.2 comes out and we update to it. +#define Lock FOO_NSS_Lock +#include <pk11pub.h> +#include <secmod.h> +#include <ssl.h> +#include <nssb64.h> // NSSBase64_EncodeItem() +#include <secder.h> // DER_Encode() +#include <cryptohi.h> // SEC_DerSignData() +#include <keyhi.h> // SECKEY_CreateSubjectPublicKeyInfo() +#undef Lock + +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/nss_init.h" + +namespace net { + +CertDatabase::CertDatabase() { + Init(); +} + +bool CertDatabase::AddUserCert(const char* data, int len) { + CERTCertificate* cert = NULL; + PK11SlotInfo* slot = NULL; + std::string nickname; + bool is_success = true; + + // Make a copy of "data" since CERT_DecodeCertPackage + // might modify it. + char* data_copy = new char[len]; + memcpy(data_copy, data, len); + + // Parse into a certificate structure. + cert = CERT_DecodeCertFromPackage(data_copy, len); + delete [] data_copy; + if (!cert) { + LOG(ERROR) << "Couldn't create a temporary certificate"; + return false; + } + + // Check if the private key corresponding to the certificate exist + // We shouldn't accept any random client certificate sent by a CA. + + // Note: The NSS source documentation wrongly suggests that this + // also imports the certificate if the private key exists. This + // doesn't seem to be the case. + + slot = PK11_KeyForCertExists(cert, NULL, NULL); + if (!slot) { + LOG(ERROR) << "No corresponding private key in store"; + CERT_DestroyCertificate(cert); + return false; + } + PK11_FreeSlot(slot); + slot = NULL; + + // TODO(gauravsh): We also need to make sure another certificate + // doesn't already exist for the same private key. + + // Create a nickname for this certificate. + // We use the scheme used by Firefox: + // --> <subject's common name>'s <issuer's common name> ID. + // + + std::string username, ca_name; + char* temp_username = CERT_GetCommonName(&cert->subject); + char* temp_ca_name = CERT_GetCommonName(&cert->issuer); + if (temp_username) { + username = temp_username; + PORT_Free(temp_username); + } + if (temp_ca_name) { + ca_name = temp_ca_name; + PORT_Free(temp_ca_name); + } + nickname = username + "'s " + ca_name + " ID"; + + slot = PK11_ImportCertForKey(cert, + const_cast<char*>(nickname.c_str()), + NULL); + if (slot) { + PK11_FreeSlot(slot); + } else { + LOG(ERROR) << "Couldn't import user certificate."; + is_success = false; + } + CERT_DestroyCertificate(cert); + return is_success; +} + +void CertDatabase::Init() { + base::EnsureNSSInit(); +} + +} // namespace net diff --git a/net/base/cert_database_win.cc b/net/base/cert_database_win.cc new file mode 100644 index 0000000..5caf9db --- /dev/null +++ b/net/base/cert_database_win.cc @@ -0,0 +1,24 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/base/cert_database.h" + +#include "base/logging.h" + +namespace net { + +CertDatabase::CertDatabase() { + NOTIMPLEMENTED(); +} + +bool CertDatabase::AddUserCert(const char* data, int len) { + NOTIMPLEMENTED(); + return false; +} + +void CertDatabase::Init() { + NOTIMPLEMENTED(); +} + +} // namespace net diff --git a/net/base/keygen_handler.h b/net/base/keygen_handler.h new file mode 100644 index 0000000..346b577 --- /dev/null +++ b/net/base/keygen_handler.h @@ -0,0 +1,27 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_BASE_KEYGEN_HANDLER_H_ +#define NET_BASE_KEYGEN_HANDLER_H_ + +#include <string> + +namespace net { + +// This class handles keypair generation for generating client +// certificates via the Netscape <keygen> tag. + +class KeygenHandler { + public: + KeygenHandler(int key_size_index, const std::string& challenge); + std::string GenKeyAndSignChallenge(); + + private: + int key_size_index_; + std::string challenge_; +}; + +} // namespace net + +#endif // NET_BASE_KEYGEN_HANDLER_H_ diff --git a/net/base/keygen_handler_mac.cc b/net/base/keygen_handler_mac.cc new file mode 100644 index 0000000..f6b4551 --- /dev/null +++ b/net/base/keygen_handler_mac.cc @@ -0,0 +1,23 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/base/keygen_handler.h" + +#include "base/logging.h" + +namespace net { + +KeygenHandler::KeygenHandler(int key_size_index, + const std::string& challenge) + : key_size_index_(key_size_index), + challenge_(challenge) { + NOTIMPLEMENTED(); +} + +std::string KeygenHandler::GenKeyAndSignChallenge() { + NOTIMPLEMENTED(); + return std::string(); +} + +} // namespace net diff --git a/net/base/keygen_handler_nss.cc b/net/base/keygen_handler_nss.cc new file mode 100644 index 0000000..9707dfc --- /dev/null +++ b/net/base/keygen_handler_nss.cc @@ -0,0 +1,255 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/base/keygen_handler.h" + +// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=455424 +// until NSS 3.12.2 comes out and we update to it. +#define Lock FOO_NSS_Lock +#include <pk11pub.h> +#include <secmod.h> +#include <ssl.h> +#include <nssb64.h> // NSSBase64_EncodeItem() +#include <secder.h> // DER_Encode() +#include <cryptohi.h> // SEC_DerSignData() +#include <keyhi.h> // SECKEY_CreateSubjectPublicKeyInfo() +#undef Lock + +#include "base/nss_init.h" +#include "base/logging.h" + +namespace net { + +const int64 DEFAULT_RSA_PUBLIC_EXPONENT = 0x10001; + +// Template for creating the signed public key structure to be sent to the CA. +DERTemplate SECAlgorithmIDTemplate[] = { + { DER_SEQUENCE, + 0, NULL, sizeof(SECAlgorithmID) }, + { DER_OBJECT_ID, + offsetof(SECAlgorithmID, algorithm), }, + { DER_OPTIONAL | DER_ANY, + offsetof(SECAlgorithmID, parameters), }, + { 0, } +}; + +DERTemplate CERTSubjectPublicKeyInfoTemplate[] = { + { DER_SEQUENCE, + 0, NULL, sizeof(CERTSubjectPublicKeyInfo) }, + { DER_INLINE, + offsetof(CERTSubjectPublicKeyInfo, algorithm), + SECAlgorithmIDTemplate, }, + { DER_BIT_STRING, + offsetof(CERTSubjectPublicKeyInfo, subjectPublicKey), }, + { 0, } +}; + +DERTemplate CERTPublicKeyAndChallengeTemplate[] = { + { DER_SEQUENCE, + 0, NULL, sizeof(CERTPublicKeyAndChallenge) }, + { DER_ANY, + offsetof(CERTPublicKeyAndChallenge, spki), }, + { DER_IA5_STRING, + offsetof(CERTPublicKeyAndChallenge, challenge), }, + { 0, } +}; + +// This maps displayed strings indicating level of keysecurity in the <keygen> +// menu to the key size in bits. +// TODO(gauravsh): Should this mapping be moved else where? +int RSAkeySizeMap[] = {2048, 1024}; + +KeygenHandler::KeygenHandler(int key_size_index, + const std::string& challenge) + : key_size_index_(key_size_index), + challenge_(challenge) { +} + +// This function is largely copied from the Firefox's +// <keygen> implementation in security/manager/ssl/src/nsKeygenHandler.cpp +// FIXME(gauravsh): Do we need a copy of the Mozilla license here? + +std::string KeygenHandler::GenKeyAndSignChallenge() { + // Key pair generation mechanism - only RSA is supported at present. + PRUint32 keyGenMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; // from nss/pkcs11t.h + char *keystring = NULL; // Temporary store for result/ + + // Temporary structures used for generating the result + // in the right format. + PK11SlotInfo *slot = NULL; + PK11RSAGenParams rsaKeyGenParams; // Keygen parameters. + SECOidTag algTag; // used by SEC_DerSignData(). + SECKEYPrivateKey *privateKey = NULL; + SECKEYPublicKey *publicKey = NULL; + CERTSubjectPublicKeyInfo *spkInfo = NULL; + PRArenaPool *arena = NULL; + SECStatus sec_rv =SECFailure; + SECItem spkiItem; + SECItem pkacItem; + SECItem signedItem; + CERTPublicKeyAndChallenge pkac; + void *keyGenParams; + pkac.challenge.data = NULL; + bool isSuccess = true; // Set to false as soon as a step fails. + + std::string result_blob; // the result. + + // Ensure NSS is initialized. + base::EnsureNSSInit(); + + slot = PK11_GetInternalKeySlot(); + if (!slot) { + LOG(ERROR) << "Couldn't get Internal key slot!"; + isSuccess = false; + goto failure; + } + + switch (keyGenMechanism) { + case CKM_RSA_PKCS_KEY_PAIR_GEN: + rsaKeyGenParams.keySizeInBits = RSAkeySizeMap[key_size_index_]; + rsaKeyGenParams.pe = DEFAULT_RSA_PUBLIC_EXPONENT; + keyGenParams = &rsaKeyGenParams; + + algTag = SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION; // from <nss/secoidt.h>. + break; + default: + // TODO(gauravsh): If we ever support other mechanisms, + // this can be changed. + LOG(ERROR) << "Only RSA keygen mechanism is supported"; + isSuccess = false; + goto failure; + break; + } + + // Need to make sure that the token was initialized. + // Assume a null password. + sec_rv = PK11_Authenticate(slot, PR_TRUE, NULL); + if (SECSuccess != sec_rv) { + LOG(ERROR) << "Couldn't initialze PK11 token!"; + isSuccess = false; + goto failure; + } + + LOG(INFO) << "Creating key pair..."; + privateKey = PK11_GenerateKeyPair(slot, + keyGenMechanism, + keyGenParams, + &publicKey, + PR_TRUE, // isPermanent? + PR_TRUE, // isSensitive? + NULL); + LOG(INFO) << "done."; + + if (!privateKey) { + LOG(INFO) << "Generation of Keypair failed!"; + isSuccess = false; + goto failure; + } + + // The CA expects the signed public key in a specific format + // Let's create that now. + + // Create a subject public key info from the public key. + spkInfo = SECKEY_CreateSubjectPublicKeyInfo(publicKey); + if (!spkInfo) { + LOG(ERROR) << "Couldn't create SubjectPublicKeyInfo from public key"; + isSuccess = false; + goto failure; + } + + // Temporary work store used by NSS. + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + LOG(ERROR) << "PORT_NewArena: Couldn't allocate memory"; + isSuccess = false; + goto failure; + } + + // DER encode the whole subjectPublicKeyInfo. + sec_rv = DER_Encode(arena, &spkiItem, CERTSubjectPublicKeyInfoTemplate, + spkInfo); + if (SECSuccess != sec_rv) { + LOG(ERROR) << "Couldn't DER Encode subjectPublicKeyInfo"; + isSuccess = false; + goto failure; + } + + // Set up the PublicKeyAndChallenge data structure, then DER encode it. + pkac.spki = spkiItem; + pkac.challenge.len = challenge_.length(); + pkac.challenge.data = (unsigned char *)strdup(challenge_.c_str()); + if (!pkac.challenge.data) { + LOG(ERROR) << "Out of memory while making a copy of challenge data"; + isSuccess = false; + goto failure; + } + sec_rv = DER_Encode(arena, &pkacItem, CERTPublicKeyAndChallengeTemplate, + &pkac); + if (SECSuccess != sec_rv) { + LOG(ERROR) << "Couldn't DER Encode PublicKeyAndChallenge"; + isSuccess = false; + goto failure; + } + + // Sign the DER encoded PublicKeyAndChallenge. + sec_rv = SEC_DerSignData(arena, &signedItem, pkacItem.data, pkacItem.len, + privateKey, algTag); + if (SECSuccess != sec_rv) { + LOG(ERROR) << "Couldn't sign the DER encoded PublicKeyandChallenge"; + isSuccess = false; + goto failure; + } + + // Convert the signed public key and challenge into base64/ascii. + keystring = NSSBase64_EncodeItem(arena, + NULL, // NSS will allocate a buffer for us. + 0, + &signedItem); + if (!keystring) { + LOG(ERROR) << "Couldn't convert signed public key into base64"; + isSuccess = false; + goto failure; + } + + result_blob = keystring; + + failure: + if (!isSuccess) { + LOG(ERROR) << "SSL Keygen failed!"; + } else { + LOG(INFO) << "SSl Keygen succeeded!"; + } + + // Do cleanups + if (privateKey) { + // TODO(gauravsh): We still need to maintain the private key because it's + // used for certificate enrollment checks. + + // PK11_DestroyTokenObject(privateKey->pkcs11Slot,privateKey->pkcs11ID); + // SECKEY_DestroyPrivateKey(privateKey); + } + + if (publicKey) { + PK11_DestroyTokenObject(publicKey->pkcs11Slot, publicKey->pkcs11ID); + } + if (spkInfo) { + SECKEY_DestroySubjectPublicKeyInfo(spkInfo); + } + if (publicKey) { + SECKEY_DestroyPublicKey(publicKey); + } + if (arena) { + PORT_FreeArena(arena, PR_TRUE); + } + if (slot != NULL) { + PK11_FreeSlot(slot); + } + if (pkac.challenge.data) { + free(pkac.challenge.data); + } + + return (isSuccess ? result_blob : std::string()); +} + +} // namespace net diff --git a/net/base/keygen_handler_win.cc b/net/base/keygen_handler_win.cc new file mode 100644 index 0000000..f6b4551 --- /dev/null +++ b/net/base/keygen_handler_win.cc @@ -0,0 +1,23 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/base/keygen_handler.h" + +#include "base/logging.h" + +namespace net { + +KeygenHandler::KeygenHandler(int key_size_index, + const std::string& challenge) + : key_size_index_(key_size_index), + challenge_(challenge) { + NOTIMPLEMENTED(); +} + +std::string KeygenHandler::GenKeyAndSignChallenge() { + NOTIMPLEMENTED(); + return std::string(); +} + +} // namespace net diff --git a/net/base/mime_util.cc b/net/base/mime_util.cc index 0fbb8c3..83e41a9 100644 --- a/net/base/mime_util.cc +++ b/net/base/mime_util.cc @@ -233,6 +233,7 @@ static const char* const supported_non_image_types[] = { "application/xhtml+xml", "application/rss+xml", "application/atom+xml", + "application/x-x509-user-cert", "multipart/x-mixed-replace" }; diff --git a/net/net.gyp b/net/net.gyp index aa6e94a..5e630cd 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -29,6 +29,10 @@ 'base/bzip2_filter.cc', 'base/bzip2_filter.h', 'base/cache_type.h', + 'base/cert_database.h', + 'base/cert_database_mac.cc', + 'base/cert_database_nss.cc', + 'base/cert_database_win.cc', 'base/cert_status_flags.cc', 'base/cert_status_flags.h', 'base/cert_verifier.cc', @@ -74,6 +78,10 @@ 'base/host_resolver_proc.h', 'base/io_buffer.cc', 'base/io_buffer.h', + 'base/keygen_handler.h', + 'base/keygen_handler_mac.cc', + 'base/keygen_handler_nss.cc', + 'base/keygen_handler_win.cc', 'base/listen_socket.cc', 'base/listen_socket.h', 'base/load_flags.h', @@ -172,6 +180,8 @@ }, { # else: OS != "linux" 'sources!': [ + 'base/cert_database_nss.cc', + 'base/keygen_handler_nss.cc', 'base/nss_memio.c', 'base/nss_memio.h', 'base/x509_certificate_nss.cc', diff --git a/webkit/api/public/WebKitClient.h b/webkit/api/public/WebKitClient.h index 905355f..9d742a3 100644 --- a/webkit/api/public/WebKitClient.h +++ b/webkit/api/public/WebKitClient.h @@ -147,6 +147,15 @@ namespace WebKit { // Returns the size of the given database file virtual long long databaseGetFileSize(const WebString& fileName) = 0; + // Keygen -------------------------------------------------------------- + + // Handle the <keygen> tag for generating client certificates + // Returns a base64 encoded signed copy of a public key from a newly + // generated key pair and the supplied challenge string. keySizeindex + // specifies the strength of the key. + virtual WebString signedPublicKeyAndChallengeString(unsigned keySizeIndex, + const WebKit::WebString& challenge, + const WebKit::WebURL& url) = 0; // Message Ports ------------------------------------------------------- diff --git a/webkit/api/src/ChromiumBridge.cpp b/webkit/api/src/ChromiumBridge.cpp index e227683..ab8393c 100644 --- a/webkit/api/src/ChromiumBridge.cpp +++ b/webkit/api/src/ChromiumBridge.cpp @@ -269,8 +269,9 @@ long long ChromiumBridge::databaseGetFileSize(const String& fileName) String ChromiumBridge::signedPublicKeyAndChallengeString( unsigned keySizeIndex, const String& challenge, const KURL& url) { - notImplemented(); - return String(); + return webKitClient()->signedPublicKeyAndChallengeString(keySizeIndex, + WebString(challenge), + WebURL(url)); } // Language ------------------------------------------------------------------- diff --git a/webkit/glue/webkitclient_impl.cc b/webkit/glue/webkitclient_impl.cc index c549dab..b45c37a 100644 --- a/webkit/glue/webkitclient_impl.cc +++ b/webkit/glue/webkitclient_impl.cc @@ -332,6 +332,14 @@ long long WebKitClientImpl::databaseGetFileSize( return 0; } +WebKit::WebString WebKitClientImpl::signedPublicKeyAndChallengeString( + unsigned key_size_index, + const WebKit::WebString& challenge, + const WebKit::WebURL& url) { + NOTREACHED(); + return WebKit::WebString(); +} + bool WebKitClientImpl::fileExists(const WebKit::WebString& path) { FilePath::StringType file_path = webkit_glue::WebStringToFilePathString(path); return file_util::PathExists(FilePath(file_path)); diff --git a/webkit/glue/webkitclient_impl.h b/webkit/glue/webkitclient_impl.h index 9686730..e6f5c42 100644 --- a/webkit/glue/webkitclient_impl.h +++ b/webkit/glue/webkitclient_impl.h @@ -52,6 +52,10 @@ class WebKitClientImpl : public WebKit::WebKitClient { virtual long databaseGetFileAttributes(const WebKit::WebString& file_name); virtual long long databaseGetFileSize(const WebKit::WebString& file_name); + virtual WebKit::WebString signedPublicKeyAndChallengeString( + unsigned key_size_index, const WebKit::WebString& challenge, + const WebKit::WebURL& url); + virtual bool fileExists(const WebKit::WebString& path); virtual bool deleteFile(const WebKit::WebString& path); virtual bool deleteEmptyDirectory(const WebKit::WebString& path); |