| Index: net/base/ssl_client_socket_nss.cc
|
| ===================================================================
|
| --- net/base/ssl_client_socket_nss.cc (revision 17158)
|
| +++ net/base/ssl_client_socket_nss.cc (working copy)
|
| @@ -2,8 +2,55 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| +// This file includes code GetDefaultCertNickname(), derived from
|
| +// nsNSSCertificate::defaultServerNickName()
|
| +// in mozilla/security/manager/ssl/src/nsNSSCertificate.cpp
|
| +// and SSLClientSocketNSS::OwnAuthCertHandler() derived from
|
| +// AuthCertificateCallback() in
|
| +// mozilla/security/manager/ssl/src/nsNSSCallbacks.cpp.
|
| +
|
| +/* ***** BEGIN LICENSE BLOCK *****
|
| + * Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
| + *
|
| + * The contents of this file are subject to the Mozilla Public License Version
|
| + * 1.1 (the "License"); you may not use this file except in compliance with
|
| + * the License. You may obtain a copy of the License at
|
| + * http://www.mozilla.org/MPL/
|
| + *
|
| + * Software distributed under the License is distributed on an "AS IS" basis,
|
| + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
| + * for the specific language governing rights and limitations under the
|
| + * License.
|
| + *
|
| + * The Original Code is the Netscape security libraries.
|
| + *
|
| + * The Initial Developer of the Original Code is
|
| + * Netscape Communications Corporation.
|
| + * Portions created by the Initial Developer are Copyright (C) 2000
|
| + * the Initial Developer. All Rights Reserved.
|
| + *
|
| + * Contributor(s):
|
| + * Ian McGreer <mcgreer@netscape.com>
|
| + * Javier Delgadillo <javi@netscape.com>
|
| + * Kai Engert <kengert@redhat.com>
|
| + *
|
| + * Alternatively, the contents of this file may be used under the terms of
|
| + * either the GNU General Public License Version 2 or later (the "GPL"), or
|
| + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
| + * in which case the provisions of the GPL or the LGPL are applicable instead
|
| + * of those above. If you wish to allow use of your version of this file only
|
| + * under the terms of either the GPL or the LGPL, and not to allow others to
|
| + * use your version of this file under the terms of the MPL, indicate your
|
| + * decision by deleting the provisions above and replace them with the notice
|
| + * and other provisions required by the GPL or the LGPL. If you do not delete
|
| + * the provisions above, a recipient may use your version of this file under
|
| + * the terms of any one of the MPL, the GPL or the LGPL.
|
| + *
|
| + * ***** END LICENSE BLOCK ***** */
|
| +
|
| #include "net/base/ssl_client_socket_nss.h"
|
|
|
| +#include <certdb.h>
|
| #include <nspr.h>
|
| #include <nss.h>
|
| #include <secerr.h>
|
| @@ -47,6 +94,48 @@
|
|
|
| namespace {
|
|
|
| +// Gets default certificate nickname from cert.
|
| +// Derived from nsNSSCertificate::defaultServerNickname
|
| +// in mozilla/security/manager/ssl/src/nsNSSCertificate.cpp.
|
| +std::string GetDefaultCertNickname(
|
| + net::X509Certificate::OSCertHandle cert) {
|
| + if (cert == NULL)
|
| + return "";
|
| +
|
| + char* name = CERT_GetCommonName(&cert->subject);
|
| + if (!name) {
|
| + // Certs without common names are strange, but they do exist...
|
| + // Let's try to use another string for the nickname
|
| + name = CERT_GetOrgUnitName(&cert->subject);
|
| + if (!name)
|
| + name = CERT_GetOrgName(&cert->subject);
|
| + if (!name)
|
| + name = CERT_GetLocalityName(&cert->subject);
|
| + if (!name)
|
| + name = CERT_GetStateName(&cert->subject);
|
| + if (!name)
|
| + name = CERT_GetCountryName(&cert->subject);
|
| + if (!name)
|
| + return "";
|
| + }
|
| + int count = 1;
|
| + std::string nickname;
|
| + while (1) {
|
| + if (count == 1) {
|
| + nickname = name;
|
| + } else {
|
| + nickname = StringPrintf("%s #%d", name, count);
|
| + }
|
| + PRBool conflict = SEC_CertNicknameConflict(
|
| + const_cast<char*>(nickname.c_str()), &cert->derSubject, cert->dbhandle);
|
| + if (!conflict)
|
| + break;
|
| + count++;
|
| + }
|
| + PR_FREEIF(name);
|
| + return nickname;
|
| +}
|
| +
|
| int NetErrorFromNSPRError(PRErrorCode err) {
|
| // TODO(port): fill this out as we learn what's important
|
| switch (err) {
|
| @@ -108,6 +197,7 @@
|
| user_callback_(NULL),
|
| user_buf_len_(0),
|
| server_cert_error_(0),
|
| + cert_list_(NULL),
|
| completed_handshake_(false),
|
| next_state_(STATE_NONE),
|
| nss_fd_(NULL),
|
| @@ -270,23 +360,29 @@
|
| void SSLClientSocketNSS::Disconnect() {
|
| EnterFunction("");
|
|
|
| + // TODO(wtc): Send SSL close_notify alert.
|
| + if (nss_fd_ != NULL) {
|
| + InvalidateSessionIfBadCertificate();
|
| + PR_Close(nss_fd_);
|
| + nss_fd_ = NULL;
|
| + }
|
| +
|
| + transport_->Disconnect();
|
| +
|
| // Reset object state
|
| transport_send_busy_ = false;
|
| transport_recv_busy_ = false;
|
| user_buf_ = NULL;
|
| user_buf_len_ = 0;
|
| + server_cert_ = NULL;
|
| server_cert_error_ = OK;
|
| + if (cert_list_) {
|
| + CERT_DestroyCertList(cert_list_);
|
| + cert_list_ = NULL;
|
| + }
|
| completed_handshake_ = false;
|
| nss_bufs_ = NULL;
|
|
|
| - // TODO(wtc): Send SSL close_notify alert.
|
| - if (nss_fd_ != NULL) {
|
| - InvalidateSessionIfBadCertificate();
|
| - PR_Close(nss_fd_);
|
| - nss_fd_ = NULL;
|
| - }
|
| -
|
| - transport_->Disconnect();
|
| LeaveFunction("");
|
| }
|
|
|
| @@ -364,6 +460,9 @@
|
| if (nss_cert) {
|
| server_cert_ = X509Certificate::CreateFromHandle(
|
| nss_cert, X509Certificate::SOURCE_FROM_NETWORK);
|
| + DCHECK(!cert_list_);
|
| + cert_list_ = CERT_GetCertChainFromCert(
|
| + nss_cert, PR_Now(), certUsageSSLCA);
|
| }
|
| }
|
| return server_cert_;
|
| @@ -552,6 +651,8 @@
|
|
|
| // static
|
| // NSS calls this if an incoming certificate needs to be verified.
|
| +// Derived from AuthCertificateCallback() in
|
| +// mozilla/source/security/manager/ssl/src/nsNSSCallbacks.cpp.
|
| SECStatus SSLClientSocketNSS::OwnAuthCertHandler(void* arg,
|
| PRFileDesc* socket,
|
| PRBool checksig,
|
| @@ -560,10 +661,39 @@
|
|
|
| // Remember the certificate as it will no longer be accessible if the
|
| // handshake fails.
|
| - that->UpdateServerCert();
|
| + scoped_refptr<X509Certificate> cert = that->UpdateServerCert();
|
|
|
| - return SSL_AuthCertificate(CERT_GetDefaultCertDB(), socket, checksig,
|
| + SECStatus rv = SSL_AuthCertificate(CERT_GetDefaultCertDB(), socket, checksig,
|
| is_server);
|
| + if (rv == SECSuccess && that->cert_list_) {
|
| + // Remember the intermediate CA certs if the server sends them to us.
|
| + for (CERTCertListNode* node = CERT_LIST_HEAD(that->cert_list_);
|
| + !CERT_LIST_END(node, that->cert_list_);
|
| + node = CERT_LIST_NEXT(node)) {
|
| + if (node->cert->slot || node->cert->isRoot || node->cert->isperm ||
|
| + node->cert == cert->os_cert_handle()) {
|
| + // Some certs we don't want to remember are:
|
| + // - found on a token.
|
| + // - the root cert.
|
| + // - already stored in perm db.
|
| + // - the server cert itself.
|
| + continue;
|
| + }
|
| +
|
| + // We have found a CA cert that we want to remember.
|
| + std::string nickname(GetDefaultCertNickname(node->cert));
|
| + if (!nickname.empty()) {
|
| + PK11SlotInfo* slot = PK11_GetInternalKeySlot();
|
| + if (slot) {
|
| + PK11_ImportCert(slot, node->cert, CK_INVALID_HANDLE,
|
| + const_cast<char*>(nickname.c_str()), PR_FALSE);
|
| + PK11_FreeSlot(slot);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + return rv;
|
| }
|
|
|
| // static
|
|
|