1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/cert/nss_profile_filter_chromeos.h"
6
7#include "base/strings/string_piece.h"
8#include "base/strings/stringprintf.h"
9#include "net/cert/x509_certificate.h"
10
11namespace net {
12
13namespace {
14
15std::string CertSlotsString(CERTCertificate* cert) {
16  std::string result;
17  crypto::ScopedPK11SlotList slots_for_cert(
18      PK11_GetAllSlotsForCert(cert, NULL));
19  for (PK11SlotListElement* slot_element =
20           PK11_GetFirstSafe(slots_for_cert.get());
21       slot_element;
22       slot_element =
23           PK11_GetNextSafe(slots_for_cert.get(), slot_element, PR_FALSE)) {
24    if (!result.empty())
25      result += ',';
26    base::StringAppendF(&result,
27                        "%lu:%lu",
28                        PK11_GetModuleID(slot_element->slot),
29                        PK11_GetSlotID(slot_element->slot));
30  }
31  return result;
32}
33
34}  // namespace
35
36NSSProfileFilterChromeOS::NSSProfileFilterChromeOS() {}
37
38NSSProfileFilterChromeOS::NSSProfileFilterChromeOS(
39    const NSSProfileFilterChromeOS& other) {
40  public_slot_.reset(other.public_slot_ ?
41      PK11_ReferenceSlot(other.public_slot_.get()) :
42      NULL);
43  private_slot_.reset(other.private_slot_ ?
44      PK11_ReferenceSlot(other.private_slot_.get()) :
45      NULL);
46  system_slot_.reset(
47      other.system_slot_ ? PK11_ReferenceSlot(other.system_slot_.get()) : NULL);
48}
49
50NSSProfileFilterChromeOS::~NSSProfileFilterChromeOS() {}
51
52NSSProfileFilterChromeOS& NSSProfileFilterChromeOS::operator=(
53    const NSSProfileFilterChromeOS& other) {
54  public_slot_.reset(other.public_slot_ ?
55      PK11_ReferenceSlot(other.public_slot_.get()) :
56      NULL);
57  private_slot_.reset(other.private_slot_ ?
58      PK11_ReferenceSlot(other.private_slot_.get()) :
59      NULL);
60  system_slot_.reset(
61      other.system_slot_ ? PK11_ReferenceSlot(other.system_slot_.get()) : NULL);
62  return *this;
63}
64
65void NSSProfileFilterChromeOS::Init(crypto::ScopedPK11Slot public_slot,
66                                    crypto::ScopedPK11Slot private_slot,
67                                    crypto::ScopedPK11Slot system_slot) {
68  // crypto::ScopedPK11Slot actually holds a reference counted object.
69  // Because scoped_ptr<T> assignment is a no-op if it already points to
70  // the same pointer, a reference would be leaked because .Pass() does
71  // not release its reference, and the receiving object won't free
72  // its copy.
73  if (public_slot_.get() != public_slot.get())
74    public_slot_ = public_slot.Pass();
75  if (private_slot_.get() != private_slot.get())
76    private_slot_ = private_slot.Pass();
77  if (system_slot_.get() != system_slot.get())
78    system_slot_ = system_slot.Pass();
79}
80
81bool NSSProfileFilterChromeOS::IsModuleAllowed(PK11SlotInfo* slot) const {
82  // If this is one of the public/private slots for this profile or the system
83  // slot, allow it.
84  if (slot == public_slot_.get() || slot == private_slot_.get() ||
85      slot == system_slot_.get()) {
86    return true;
87  }
88  // Allow the root certs module.
89  if (PK11_HasRootCerts(slot))
90    return true;
91  // If it's from the read-only slots, allow it.
92  if (PK11_IsInternal(slot) && !PK11_IsRemovable(slot))
93    return true;
94  // If |public_slot_| or |private_slot_| is null, there isn't a way to get the
95  // modules to use in the final test.
96  if (!public_slot_.get() || !private_slot_.get())
97    return false;
98  // If this is not the internal (file-system) module or the TPM module, allow
99  // it. This would allow smartcards/etc, although ChromeOS doesn't currently
100  // support that. (This assumes that private_slot_ and system_slot_ are on the
101  // same module.)
102  DCHECK(!system_slot_.get() ||
103         PK11_GetModule(private_slot_.get()) ==
104             PK11_GetModule(system_slot_.get()));
105  SECMODModule* module_for_slot = PK11_GetModule(slot);
106  if (module_for_slot != PK11_GetModule(public_slot_.get()) &&
107      module_for_slot != PK11_GetModule(private_slot_.get())) {
108    return true;
109  }
110  return false;
111}
112
113bool NSSProfileFilterChromeOS::IsCertAllowed(CERTCertificate* cert) const {
114  crypto::ScopedPK11SlotList slots_for_cert(
115      PK11_GetAllSlotsForCert(cert, NULL));
116  if (!slots_for_cert) {
117    DVLOG(2) << "cert no slots: " << base::StringPiece(cert->nickname);
118    return false;
119  }
120
121  for (PK11SlotListElement* slot_element =
122           PK11_GetFirstSafe(slots_for_cert.get());
123       slot_element;
124       slot_element =
125           PK11_GetNextSafe(slots_for_cert.get(), slot_element, PR_FALSE)) {
126    if (IsModuleAllowed(slot_element->slot)) {
127      DVLOG(3) << "cert from " << CertSlotsString(cert)
128               << " allowed: " << base::StringPiece(cert->nickname);
129      PK11_FreeSlotListElement(slots_for_cert.get(), slot_element);
130      return true;
131    }
132  }
133  DVLOG(2) << "cert from " << CertSlotsString(cert)
134           << " filtered: " << base::StringPiece(cert->nickname);
135  return false;
136}
137
138NSSProfileFilterChromeOS::CertNotAllowedForProfilePredicate::
139    CertNotAllowedForProfilePredicate(const NSSProfileFilterChromeOS& filter)
140    : filter_(filter) {}
141
142bool NSSProfileFilterChromeOS::CertNotAllowedForProfilePredicate::operator()(
143    const scoped_refptr<X509Certificate>& cert) const {
144  return !filter_.IsCertAllowed(cert->os_cert_handle());
145}
146
147NSSProfileFilterChromeOS::ModuleNotAllowedForProfilePredicate::
148    ModuleNotAllowedForProfilePredicate(const NSSProfileFilterChromeOS& filter)
149    : filter_(filter) {}
150
151bool NSSProfileFilterChromeOS::ModuleNotAllowedForProfilePredicate::operator()(
152    const scoped_refptr<CryptoModule>& module) const {
153  return !filter_.IsModuleAllowed(module->os_module_handle());
154}
155
156}  // namespace net
157
158