[Curl] implement CertificateInfo::summaryInfo
[WebKit.git] / Source / WebCore / platform / network / curl / OpenSSLHelper.cpp
1 /*
2  * Copyright (C) 2019 Sony Interactive Entertainment Inc.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "OpenSSLHelper.h"
28
29 #include <openssl/x509v3.h>
30 #include <wtf/DateMath.h>
31 #include <wtf/HexNumber.h>
32 #include <wtf/Seconds.h>
33 #include <wtf/text/StringBuilder.h>
34
35 namespace OpenSSL {
36
37 template<typename> struct deleter;
38 template<> struct deleter<X509> {
39     void operator()(X509* x509)
40     {
41         if (x509)
42             X509_free(x509);
43     }
44 };
45
46 class StackOfGeneralName {
47 public:
48     StackOfGeneralName(const X509* x509, int nid)
49         : m_names { static_cast<STACK_OF(GENERAL_NAME)*>(X509_get_ext_d2i(x509, nid, nullptr, nullptr)) }
50     {
51     }
52
53     ~StackOfGeneralName()
54     {
55         if (m_names)
56             sk_GENERAL_NAME_pop_free(m_names, GENERAL_NAME_free);
57     }
58
59     operator bool() { return m_names; }
60
61     int count() { return sk_GENERAL_NAME_num(m_names); }
62     GENERAL_NAME* item(int i) { return sk_GENERAL_NAME_value(m_names, i); }
63
64 private:
65     STACK_OF(GENERAL_NAME)* m_names { nullptr };
66 };
67
68 class StackOfX509 {
69 public:
70     StackOfX509(X509_STORE_CTX* ctx)
71         : m_certs { X509_STORE_CTX_get1_chain(ctx) }
72     {
73     }
74
75     ~StackOfX509()
76     {
77         if (m_certs)
78             sk_X509_pop_free(m_certs, X509_free);
79     }
80
81     int count() { return sk_X509_num(m_certs); }
82     X509* item(int i) { return sk_X509_value(m_certs, i); }
83
84 private:
85     STACK_OF(X509)* m_certs { nullptr };
86 };
87
88 class BIO {
89 public:
90     BIO()
91         : m_bio { ::BIO_new(::BIO_s_mem()) }
92     {
93     }
94
95     BIO(X509* data)
96         : m_bio { ::BIO_new(::BIO_s_mem()) }
97     {
98         ::PEM_write_bio_X509(m_bio, data);
99     }
100
101     BIO(const Vector<uint8_t>& data)
102         : m_bio { ::BIO_new_mem_buf(data.data(), data.size()) }
103     {
104     }
105
106     ~BIO()
107     {
108         if (m_bio)
109             ::BIO_free(m_bio);
110     }
111
112     Optional<Vector<uint8_t>> getDataAsVector() const
113     {
114         uint8_t* data { nullptr };
115         auto length = ::BIO_get_mem_data(m_bio, &data);
116         if (length < 0)
117             return WTF::nullopt;
118
119         Vector<uint8_t> result;
120         result.append(data, length);
121         return result;
122     }
123
124     String getDataAsString() const
125     {
126         uint8_t* data { nullptr };
127         auto length = ::BIO_get_mem_data(m_bio, &data);
128         if (length < 0)
129             return String();
130
131         return String(data, length);
132     }
133
134     std::unique_ptr<X509, deleter<X509>> readX509()
135     {
136         return std::unique_ptr<X509, deleter<X509>>(::PEM_read_bio_X509(m_bio, nullptr, 0, nullptr));
137     }
138
139     ::BIO* get() { return m_bio; }
140
141 private:
142     ::BIO* m_bio { nullptr };
143 };
144
145
146 static Vector<WebCore::CertificateInfo::Certificate> pemDataFromCtx(X509_STORE_CTX* ctx)
147 {
148     Vector<WebCore::CertificateInfo::Certificate> result;
149     StackOfX509 certs { ctx };
150
151     for (int i = 0; i < certs.count(); i++) {
152         BIO bio(certs.item(i));
153
154         if (auto certificate = bio.getDataAsVector())
155             result.append(WTFMove(*certificate));
156         else
157             return { };
158     }
159
160     return result;
161 }
162
163 Optional<WebCore::CertificateInfo> createCertificateInfo(X509_STORE_CTX* ctx)
164 {
165     if (!ctx)
166         return WTF::nullopt;
167
168     return WebCore::CertificateInfo(X509_STORE_CTX_get_error(ctx), pemDataFromCtx(ctx));
169 }
170
171 static String toString(const ASN1_STRING* name)
172 {
173     unsigned char* data { nullptr };
174     auto length = ASN1_STRING_to_UTF8(&data, name);
175     if (length <= 0)
176         return String();
177
178     String result(data, length);
179     OPENSSL_free(data);
180     return result;
181 }
182
183 static String getCommonName(const X509* x509)
184 {
185     auto subjectName = X509_get_subject_name(x509);
186     if (!subjectName)
187         return String();
188
189     auto index = X509_NAME_get_index_by_NID(subjectName, NID_commonName, -1);
190     if (index < 0)
191         return String();
192
193     auto commonNameEntry = X509_NAME_get_entry(subjectName, index);
194     if (!commonNameEntry)
195         return String();
196
197     auto commonNameEntryData = X509_NAME_ENTRY_get_data(commonNameEntry);
198     if (!commonNameEntryData)
199         return String();
200
201     return toString(commonNameEntryData);
202 }
203
204 static String getSubjectName(const X509* x509)
205 {
206     static const unsigned long flags = (ASN1_STRFLGS_RFC2253 | ASN1_STRFLGS_ESC_QUOTE | XN_FLAG_SEP_CPLUS_SPC | XN_FLAG_DN_REV | XN_FLAG_FN_NONE | XN_FLAG_SPC_EQ) & ~ASN1_STRFLGS_ESC_MSB;
207
208     auto subjectName = X509_get_subject_name(x509);
209     if (!subjectName)
210         return String();
211
212     BIO bio;
213     auto length = X509_NAME_print_ex(bio.get(), subjectName, 0, flags);
214     if (length <= 0)
215         return String();
216
217     return bio.getDataAsString();
218 }
219
220 static Optional<Seconds> convertASN1TimeToSeconds(const ASN1_TIME* ans1Time)
221 {
222     if (!ans1Time)
223         return WTF::nullopt;
224
225     if ((ans1Time->type != V_ASN1_UTCTIME && ans1Time->type != V_ASN1_GENERALIZEDTIME) || !ans1Time->data)
226         return WTF::nullopt;
227
228     // UTCTIME         : YYmmddHHMM[SS]
229     // GENERALIZEDTIME : YYYYmmddHHMM[SS]
230     int digitLength = ans1Time->type == V_ASN1_UTCTIME ? 10 : 12;
231     if (ans1Time->length < digitLength)
232         return WTF::nullopt;
233
234     auto data = ans1Time->data;
235     for (int i = 0; i < digitLength; i++) {
236         if (!isASCIIDigit(data[i]))
237             return WTF::nullopt;
238     }
239
240     struct tm time { };
241
242     if (ans1Time->type == V_ASN1_UTCTIME) {
243         time.tm_year = (data[0] - '0') * 10 + (data[1] - '0');
244         if (time.tm_year < 50)
245             time.tm_year += 100;
246     } else {
247         time.tm_year = (data[0] - '0') * 1000 + (data[1] - '0') * 100 + (data[2] - '0') * 10 + (data[3] - '0');
248         time.tm_year -= 1900;
249     }
250
251     data += ans1Time->type == V_ASN1_UTCTIME ? 2 : 4;
252
253     time.tm_mon = (data[0] - '0') * 10 + (data[1] - '0') - 1;
254     if (time.tm_mon < 0 || time.tm_mon > 11)
255         return WTF::nullopt;
256     time.tm_mday = (data[2] - '0') * 10 + (data[3] - '0');
257     time.tm_hour = (data[4] - '0') * 10 + (data[5] - '0');
258     time.tm_min = (data[6] - '0') * 10 + (data[7] - '0');
259
260     if ((ans1Time->length >= digitLength + 2) && isASCIIDigit(data[8]) && isASCIIDigit(data[9]))
261         time.tm_sec = (data[8] - '0') * 10 + (data[9] - '0');
262
263     auto gmtTime = mktime(&time);
264     auto localTimeOffset = calculateLocalTimeOffset(gmtTime * 1000.0);
265     return Seconds(gmtTime + (localTimeOffset.offset / 1000.0));
266 }
267
268 static void getSubjectAltName(const X509* x509, Vector<String>& dnsNames, Vector<String>& ipAddresses)
269 {
270     StackOfGeneralName sanList(x509, NID_subject_alt_name);
271     if (!sanList)
272         return;
273
274     auto num = sanList.count();
275     for (auto i = 0; i < num; i++) {
276         auto* value = sanList.item(i);
277         if (!value)
278             continue;
279
280         if (value->type == GEN_DNS) {
281             auto dnsName = toString(value->d.dNSName);
282             if (!dnsName.isNull())
283                 dnsNames.append(WTFMove(dnsName));
284         } else if (value->type == GEN_IPADD) {
285             auto data = value->d.iPAddress->data;
286             if (value->d.iPAddress->length == 4)
287                 ipAddresses.append(makeString(data[0], ".", data[1], ".", data[2], ".", data[3]));
288             else if (value->d.iPAddress->length == 16) {
289                 StringBuilder ipAddress;
290                 for (int i = 0; i < 8; i++) {
291                     ipAddress.append(makeString(hex(data[0] << 8 | data[1], 4)));
292                     if (i != 7)
293                         ipAddress.append(":");
294                     data += 2;
295                 }
296                 ipAddresses.append(ipAddress.toString());
297             }
298         }
299     }
300 }
301
302 Optional<WebCore::CertificateInfo::SummaryInfo> createSummaryInfo(const Vector<uint8_t>& pem)
303 {
304     BIO bio { pem };
305     auto x509 = bio.readX509();
306     if (!x509)
307         return WTF::nullopt;
308
309     WebCore::CertificateInfo::SummaryInfo summaryInfo;
310
311     summaryInfo.subject = getCommonName(x509.get());
312     if (summaryInfo.subject.isNull())
313         summaryInfo.subject = getSubjectName(x509.get());
314
315     if (auto notBefore = convertASN1TimeToSeconds(X509_get0_notBefore(x509.get())))
316         summaryInfo.validFrom = *notBefore;
317
318     if (auto notAfter = convertASN1TimeToSeconds(X509_get0_notAfter(x509.get())))
319         summaryInfo.validUntil = *notAfter;
320
321     getSubjectAltName(x509.get(), summaryInfo.dnsNames, summaryInfo.ipAddresses);
322
323     return summaryInfo;
324 }
325
326 }