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 		auto cert_ = findCert(cert.subjectDn(), cert.subjectKeyId());
43 		if (!*cert_) return false;
44         return cert_ != X509Certificate.init;
45     }
46 
47     // remove this (used by TLSServer)
48     Vector!X509DN allSubjects() const;
49 }
50 
51 /**
52 * In Memory Certificate Store
53 */
54 final class CertificateStoreInMemory : CertificateStore
55 {
56 public:
57     /**
58     * Attempt to parse all files in dir (including subdirectories)
59     * as certificates. Ignores errors.
60     */
61     this(in string dir)
62     {
63         if (dir == "")
64             return;
65         foreach(string name; dirEntries(dir, SpanMode.breadth)) {
66             if (isFile(name))
67                 m_certs.pushBack(X509Certificate(name));
68         }
69     }
70 
71     this() {}
72 
73     void addCertificate(X509Certificate cert)
74     {
75         foreach (const cert_stored; m_certs[])
76         {
77             if (cert_stored == cert)
78                 return;
79         }
80         
81         m_certs.pushBack(cert);
82     }
83 
84     override Vector!X509DN allSubjects() const
85     {
86         Vector!X509DN subjects;
87         foreach (ref cert; m_certs[]) {
88 			auto subj_dn = cert.subjectDn();
89             subjects.pushBack(subj_dn.dup);
90 		}
91         return subjects;
92     }
93 
94     override X509Certificate findCertRef(in X509DN subject_dn, const ref Vector!ubyte key_id) const
95     {
96         return certSearch(subject_dn, key_id, m_certs);
97     }
98 
99     void addCrl(X509CRL crl)
100     {
101         X509DN crl_issuer = crl.issuerDn();
102         
103         foreach (ref crl_stored; m_crls[])
104         {
105             // Found an update of a previously existing one; replace it
106             if (crl_stored.issuerDn() == crl_issuer)
107             {
108                 if (crl_stored.thisUpdate() <= crl.thisUpdate())
109                     crl_stored = crl;
110                 return;
111             }
112         }
113         
114         // Totally new CRL, add to the list
115         m_crls.pushBack(crl);
116     }
117 
118     override X509CRL findCrlFor(in X509Certificate subject) const
119     {
120         const Vector!ubyte key_id = subject.authorityKeyId();
121         
122         foreach (crl; m_crls[])
123         {
124             // Only compare key ids if set in both call and in the CRL
125             if (key_id.length)
126             {
127                 Vector!ubyte akid = crl.authorityKeyId();
128                 
129                 if (akid.length && akid != key_id) // no match
130                     continue;
131             }
132 
133             if (crl.issuerDn() == subject.issuerDn())
134                 return crl;
135         }
136         
137         return X509CRL.init;
138     }
139 
140 private:
141     // TODO: Add indexing on the DN and key id to avoid linear search
142     Vector!X509Certificate m_certs;
143     Vector!X509CRL m_crls;
144 }
145 
146 final class CertificateStoreOverlay : CertificateStore
147 {
148 public:
149     this(const ref Vector!X509Certificate certs)
150     {
151         foreach (ref cert; certs[]) {
152             m_certs ~= cert;
153         }
154     }
155 
156     override X509CRL findCrlFor(in X509Certificate subject) const { return X509CRL.init; }
157 
158     override Vector!X509DN allSubjects() const
159     {
160         Vector!X509DN subjects;
161         foreach (cert; m_certs[])
162             subjects.pushBack(cert.subjectDn().dup);
163         return subjects.move;
164     }
165 
166     override X509Certificate findCertRef(in X509DN subject_dn, const ref Vector!ubyte key_id) const
167     {
168         return certSearch(subject_dn, key_id, m_certs);
169     }
170 private:
171     Vector!X509Certificate m_certs;
172 }
173 
174 X509Certificate certSearch(in X509DN subject_dn, 
175                            const ref Vector!ubyte key_id, 
176                            const ref Vector!X509Certificate certs)
177 {
178     foreach (cert; certs[])
179     {
180         // Only compare key ids if set in both call and in the cert
181         if (key_id.length)
182         {
183             const Vector!ubyte skid = cert.subjectKeyId();
184             
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 }