1 // Copyright (c) 2012 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.
5 #include "content/browser/cert_store_impl.h"
10 #include "base/bind.h"
11 #include "base/stl_util.h"
12 #include "content/browser/renderer_host/render_process_host_impl.h"
13 #include "content/browser/renderer_host/render_view_host_impl.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "content/public/browser/notification_service.h"
16 #include "content/public/browser/notification_types.h"
20 explicit MatchSecond(const T
& t
) : value(t
) {}
22 template<typename Pair
>
23 bool operator()(const Pair
& p
) const {
24 return (value
== p
.second
);
32 CertStore
* CertStore::GetInstance() {
33 return CertStoreImpl::GetInstance();
37 CertStoreImpl
* CertStoreImpl::GetInstance() {
38 return Singleton
<CertStoreImpl
>::get();
41 CertStoreImpl::CertStoreImpl() : next_cert_id_(1) {
42 if (BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
43 RegisterForNotification();
45 BrowserThread::PostTask(
46 BrowserThread::UI
, FROM_HERE
,
47 base::Bind(&CertStoreImpl::RegisterForNotification
,
48 base::Unretained(this)));
52 CertStoreImpl::~CertStoreImpl() {
55 void CertStoreImpl::RegisterForNotification() {
56 // We watch for RenderProcess termination, as this is how we clear
57 // certificates for now.
58 // TODO(jcampan): we should be listening to events such as resource cached/
59 // removed from cache, and remove the cert when we know it
60 // is not used anymore.
62 registrar_
.Add(this, NOTIFICATION_RENDERER_PROCESS_TERMINATED
,
63 NotificationService::AllBrowserContextsAndSources());
64 registrar_
.Add(this, NOTIFICATION_RENDERER_PROCESS_CLOSED
,
65 NotificationService::AllBrowserContextsAndSources());
68 int CertStoreImpl::StoreCert(net::X509Certificate
* cert
, int process_id
) {
70 base::AutoLock
auto_lock(cert_lock_
);
74 // Do we already know this cert?
75 ReverseCertMap::iterator cert_iter
= cert_to_id_
.find(cert
);
76 if (cert_iter
== cert_to_id_
.end()) {
77 cert_id
= next_cert_id_
++;
78 // We use 0 as an invalid cert_id value. In the unlikely event that
79 // next_cert_id_ wraps around, we reset it to 1.
80 if (next_cert_id_
== 0)
83 id_to_cert_
[cert_id
] = cert
;
84 cert_to_id_
[cert
] = cert_id
;
86 cert_id
= cert_iter
->second
;
89 // Let's update process_id_to_cert_id_.
90 std::pair
<IDMap::iterator
, IDMap::iterator
> process_ids
=
91 process_id_to_cert_id_
.equal_range(process_id
);
92 if (std::find_if(process_ids
.first
, process_ids
.second
,
93 MatchSecond
<int>(cert_id
)) == process_ids
.second
) {
94 process_id_to_cert_id_
.insert(std::make_pair(process_id
, cert_id
));
97 // And cert_id_to_process_id_.
98 std::pair
<IDMap::iterator
, IDMap::iterator
> cert_ids
=
99 cert_id_to_process_id_
.equal_range(cert_id
);
100 if (std::find_if(cert_ids
.first
, cert_ids
.second
,
101 MatchSecond
<int>(process_id
)) == cert_ids
.second
) {
102 cert_id_to_process_id_
.insert(std::make_pair(cert_id
, process_id
));
108 bool CertStoreImpl::RetrieveCert(int cert_id
,
109 scoped_refptr
<net::X509Certificate
>* cert
) {
110 base::AutoLock
auto_lock(cert_lock_
);
112 CertMap::iterator iter
= id_to_cert_
.find(cert_id
);
113 if (iter
== id_to_cert_
.end())
116 *cert
= iter
->second
;
120 void CertStoreImpl::RemoveCertInternal(int cert_id
) {
121 CertMap::iterator cert_iter
= id_to_cert_
.find(cert_id
);
122 DCHECK(cert_iter
!= id_to_cert_
.end());
124 ReverseCertMap::iterator id_iter
= cert_to_id_
.find(cert_iter
->second
);
125 DCHECK(id_iter
!= cert_to_id_
.end());
126 cert_to_id_
.erase(id_iter
);
128 cert_iter
->second
->Release();
129 id_to_cert_
.erase(cert_iter
);
132 void CertStoreImpl::RemoveCertsForRenderProcesHost(int process_id
) {
133 base::AutoLock
auto_lock(cert_lock_
);
135 // We iterate through all the cert ids for that process.
136 std::pair
<IDMap::iterator
, IDMap::iterator
> process_ids
=
137 process_id_to_cert_id_
.equal_range(process_id
);
138 for (IDMap::iterator ids_iter
= process_ids
.first
;
139 ids_iter
!= process_ids
.second
; ++ids_iter
) {
140 int cert_id
= ids_iter
->second
;
141 // Find all the processes referring to this cert id in
142 // cert_id_to_process_id_, then locate the process being removed within
144 std::pair
<IDMap::iterator
, IDMap::iterator
> cert_ids
=
145 cert_id_to_process_id_
.equal_range(cert_id
);
146 IDMap::iterator proc_iter
=
147 std::find_if(cert_ids
.first
, cert_ids
.second
,
148 MatchSecond
<int>(process_id
));
149 DCHECK(proc_iter
!= cert_ids
.second
);
151 // Before removing, determine if no other processes refer to the current
152 // cert id. If |proc_iter| (the current process) is the lower bound of
153 // processes containing the current cert id and if |next_proc_iter| is the
154 // upper bound (the first process that does not), then only one process,
155 // the one being removed, refers to the cert id.
156 IDMap::iterator next_proc_iter
= proc_iter
;
158 bool last_process_for_cert_id
=
159 (proc_iter
== cert_ids
.first
&& next_proc_iter
== cert_ids
.second
);
160 cert_id_to_process_id_
.erase(proc_iter
);
162 if (last_process_for_cert_id
) {
163 // The current cert id is not referenced by any other processes, so
164 // remove it from id_to_cert_ and cert_to_id_.
165 RemoveCertInternal(cert_id
);
168 if (process_ids
.first
!= process_ids
.second
)
169 process_id_to_cert_id_
.erase(process_ids
.first
, process_ids
.second
);
172 void CertStoreImpl::Observe(int type
,
173 const NotificationSource
& source
,
174 const NotificationDetails
& details
) {
175 DCHECK(type
== NOTIFICATION_RENDERER_PROCESS_TERMINATED
||
176 type
== NOTIFICATION_RENDERER_PROCESS_CLOSED
);
177 RenderProcessHost
* rph
= Source
<RenderProcessHost
>(source
).ptr();
179 RemoveCertsForRenderProcesHost(rph
->GetID());
182 } // namespace content