1 /**
2 * Certificate Store
3 * 
4 * Copyright:
5 * (C) 1999-2010,2013 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.cert.x509.certstor;
12 
13 import botan.constants;
14 
15 import botan.cert.x509.x509cert;
16 import botan.cert.x509.x509_crl;
17 import botan.utils.types;
18 import std.file;
19 
20 version(X509):
21 
22 /**
23 * Certificate Store Interface
24 */
25 interface CertificateStore
26 {
27 public:
28     /**
29     * Subject DN and (optionally) key identifier
30     */
31     X509Certificate findCertRef(in X509DN subject_dn, const ref Vector!ubyte key_id) const;
32 
33     final X509Certificate findCert()(in X509DN subject_dn, auto const ref Vector!ubyte key_id) const {
34         return findCertRef(subject_dn, key_id);
35     }
36 
37     X509CRL findCrlFor(in X509Certificate subject) const;
38 
39 
40     final bool certificateKnown(in X509Certificate cert) const
41     {
42         if (!*cert) return false;
43 		auto cert_ = findCert(cert.subjectDn(), cert.subjectKeyId());
44 		if (!*cert_) return false;
45         return cert_ != X509Certificate.init;
46     }
47 
48     // remove this (used by TLSServer)
49     Vector!X509DN allSubjects() const;
50 }
51 
52 /**
53 * In Memory Certificate Store
54 */
55 final class CertificateStoreInMemory : CertificateStore
56 {
57 public:
58     /**
59     * Attempt to parse all files in dir (including subdirectories)
60     * as certificates. Ignores errors.
61     */
62     this(in string dir)
63     {
64         if (dir == "")
65             return;
66         foreach(string name; dirEntries(dir, SpanMode.breadth)) {
67             if (isFile(name))
68                 m_certs.pushBack(X509Certificate(name));
69         }
70     }
71 
72     this() {}
73 
74     void addCertificate(X509Certificate cert)
75     {
76         foreach (const cert_stored; m_certs[])
77         {
78             if (cert_stored == cert)
79                 return;
80         }
81         
82         m_certs.pushBack(cert);
83     }
84 
85     override Vector!X509DN allSubjects() const
86     {
87         Vector!X509DN subjects;
88         foreach (ref cert; m_certs[]) {
89 			auto subj_dn = cert.subjectDn();
90             subjects.pushBack(subj_dn.clone);
91 		}
92         return subjects;
93     }
94 
95     override X509Certificate findCertRef(in X509DN subject_dn, const ref Vector!ubyte key_id) const
96     {
97         return certSearch(subject_dn, key_id, m_certs);
98     }
99 
100     void addCrl(X509CRL crl)
101     {
102         X509DN crl_issuer = crl.issuerDn();
103         
104         foreach (ref crl_stored; m_crls[])
105         {
106             // Found an update of a previously existing one; replace it
107             if (crl_stored.issuerDn() == crl_issuer)
108             {
109                 if (crl_stored.thisUpdate() <= crl.thisUpdate())
110                     crl_stored = crl;
111                 return;
112             }
113         }
114         
115         // Totally new CRL, add to the list
116         m_crls.pushBack(crl);
117     }
118 
119     override X509CRL findCrlFor(in X509Certificate subject) const
120     {
121         const Vector!ubyte key_id = subject.authorityKeyId();
122         
123         foreach (crl; m_crls[])
124         {
125             // Only compare key ids if set in both call and in the CRL
126             if (key_id.length)
127             {
128                 Vector!ubyte akid = crl.authorityKeyId();
129                 
130                 if (akid.length && akid != key_id) // no match
131                     continue;
132             }
133 
134             if (crl.issuerDn() == subject.issuerDn())
135                 return crl;
136         }
137         
138         return X509CRL.init;
139     }
140 
141 private:
142     // TODO: Add indexing on the DN and key id to avoid linear search
143     Vector!X509Certificate m_certs;
144     Vector!X509CRL m_crls;
145 }
146 
147 final class CertificateStoreOverlay : CertificateStore
148 {
149 public:
150     this(const ref Vector!X509Certificate certs)
151     {
152         foreach (ref cert; certs[]) {
153             m_certs ~= cert;
154         }
155     }
156 
157     override X509CRL findCrlFor(in X509Certificate subject) const { return X509CRL.init; }
158 
159     override Vector!X509DN allSubjects() const
160     {
161         Vector!X509DN subjects;
162         foreach (cert; m_certs[])
163             subjects.pushBack(cert.subjectDn().clone);
164         return subjects.move;
165     }
166 
167     override X509Certificate findCertRef(in X509DN subject_dn, const ref Vector!ubyte key_id) const
168     {
169         return certSearch(subject_dn, key_id, m_certs);
170     }
171 private:
172     Vector!X509Certificate m_certs;
173 }
174 
175 X509Certificate certSearch(in X509DN subject_dn, 
176                            const ref Vector!ubyte key_id, 
177                            const ref Vector!X509Certificate certs)
178 {
179     foreach (cert; certs[])
180     {
181         // Only compare key ids if set in both call and in the cert
182         if (key_id.length)
183         {
184             const Vector!ubyte skid = cert.subjectKeyId();
185             if (skid.length && skid != key_id) // no match
186                 continue;
187         }
188         
189         if (cert.subjectDn() == subject_dn) {
190             return cert;
191         }
192     }
193     
194     return X509Certificate.init;
195 }