Merge branch 'vendor/OPENSSL'
[dragonfly.git] / contrib / hostapd / src / tls / tlsv1_cred.c
1 /*
2  * TLSv1 credentials
3  * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  */
14
15 #include "includes.h"
16
17 #include "common.h"
18 #include "base64.h"
19 #include "crypto.h"
20 #include "x509v3.h"
21 #include "tlsv1_cred.h"
22
23
24 struct tlsv1_credentials * tlsv1_cred_alloc(void)
25 {
26         struct tlsv1_credentials *cred;
27         cred = os_zalloc(sizeof(*cred));
28         return cred;
29 }
30
31
32 void tlsv1_cred_free(struct tlsv1_credentials *cred)
33 {
34         if (cred == NULL)
35                 return;
36
37         x509_certificate_chain_free(cred->trusted_certs);
38         x509_certificate_chain_free(cred->cert);
39         crypto_private_key_free(cred->key);
40         os_free(cred->dh_p);
41         os_free(cred->dh_g);
42         os_free(cred);
43 }
44
45
46 static int tlsv1_add_cert_der(struct x509_certificate **chain,
47                               const u8 *buf, size_t len)
48 {
49         struct x509_certificate *cert;
50         char name[128];
51
52         cert = x509_certificate_parse(buf, len);
53         if (cert == NULL) {
54                 wpa_printf(MSG_INFO, "TLSv1: %s - failed to parse certificate",
55                            __func__);
56                 return -1;
57         }
58
59         cert->next = *chain;
60         *chain = cert;
61
62         x509_name_string(&cert->subject, name, sizeof(name));
63         wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name);
64
65         return 0;
66 }
67
68
69 static const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----";
70 static const char *pem_cert_end = "-----END CERTIFICATE-----";
71
72
73 static const u8 * search_tag(const char *tag, const u8 *buf, size_t len)
74 {
75         size_t i, plen;
76
77         plen = os_strlen(tag);
78         if (len < plen)
79                 return NULL;
80
81         for (i = 0; i < len - plen; i++) {
82                 if (os_memcmp(buf + i, tag, plen) == 0)
83                         return buf + i;
84         }
85
86         return NULL;
87 }
88
89
90 static int tlsv1_add_cert(struct x509_certificate **chain,
91                           const u8 *buf, size_t len)
92 {
93         const u8 *pos, *end;
94         unsigned char *der;
95         size_t der_len;
96
97         pos = search_tag(pem_cert_begin, buf, len);
98         if (!pos) {
99                 wpa_printf(MSG_DEBUG, "TLSv1: No PEM certificate tag found - "
100                            "assume DER format");
101                 return tlsv1_add_cert_der(chain, buf, len);
102         }
103
104         wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format certificate into "
105                    "DER format");
106
107         while (pos) {
108                 pos += os_strlen(pem_cert_begin);
109                 end = search_tag(pem_cert_end, pos, buf + len - pos);
110                 if (end == NULL) {
111                         wpa_printf(MSG_INFO, "TLSv1: Could not find PEM "
112                                    "certificate end tag (%s)", pem_cert_end);
113                         return -1;
114                 }
115
116                 der = base64_decode(pos, end - pos, &der_len);
117                 if (der == NULL) {
118                         wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM "
119                                    "certificate");
120                         return -1;
121                 }
122
123                 if (tlsv1_add_cert_der(chain, der, der_len) < 0) {
124                         wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM "
125                                    "certificate after DER conversion");
126                         os_free(der);
127                         return -1;
128                 }
129
130                 os_free(der);
131
132                 end += os_strlen(pem_cert_end);
133                 pos = search_tag(pem_cert_begin, end, buf + len - end);
134         }
135
136         return 0;
137 }
138
139
140 static int tlsv1_set_cert_chain(struct x509_certificate **chain,
141                                 const char *cert, const u8 *cert_blob,
142                                 size_t cert_blob_len)
143 {
144         if (cert_blob)
145                 return tlsv1_add_cert(chain, cert_blob, cert_blob_len);
146
147         if (cert) {
148                 u8 *buf;
149                 size_t len;
150                 int ret;
151
152                 buf = (u8 *) os_readfile(cert, &len);
153                 if (buf == NULL) {
154                         wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
155                                    cert);
156                         return -1;
157                 }
158
159                 ret = tlsv1_add_cert(chain, buf, len);
160                 os_free(buf);
161                 return ret;
162         }
163
164         return 0;
165 }
166
167
168 /**
169  * tlsv1_set_ca_cert - Set trusted CA certificate(s)
170  * @cred: TLSv1 credentials from tlsv1_cred_alloc()
171  * @cert: File or reference name for X.509 certificate in PEM or DER format
172  * @cert_blob: cert as inlined data or %NULL if not used
173  * @cert_blob_len: ca_cert_blob length
174  * @path: Path to CA certificates (not yet supported)
175  * Returns: 0 on success, -1 on failure
176  */
177 int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert,
178                       const u8 *cert_blob, size_t cert_blob_len,
179                       const char *path)
180 {
181         if (tlsv1_set_cert_chain(&cred->trusted_certs, cert,
182                                  cert_blob, cert_blob_len) < 0)
183                 return -1;
184
185         if (path) {
186                 /* TODO: add support for reading number of certificate files */
187                 wpa_printf(MSG_INFO, "TLSv1: Use of CA certificate directory "
188                            "not yet supported");
189                 return -1;
190         }
191
192         return 0;
193 }
194
195
196 /**
197  * tlsv1_set_cert - Set certificate
198  * @cred: TLSv1 credentials from tlsv1_cred_alloc()
199  * @cert: File or reference name for X.509 certificate in PEM or DER format
200  * @cert_blob: cert as inlined data or %NULL if not used
201  * @cert_blob_len: cert_blob length
202  * Returns: 0 on success, -1 on failure
203  */
204 int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert,
205                    const u8 *cert_blob, size_t cert_blob_len)
206 {
207         return tlsv1_set_cert_chain(&cred->cert, cert,
208                                     cert_blob, cert_blob_len);
209 }
210
211
212 static int tlsv1_set_key(struct tlsv1_credentials *cred,
213                          const u8 *key, size_t len)
214 {
215         cred->key = crypto_private_key_import(key, len);
216         if (cred->key == NULL) {
217                 wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key");
218                 return -1;
219         }
220         return 0;
221 }
222
223
224 /**
225  * tlsv1_set_private_key - Set private key
226  * @cred: TLSv1 credentials from tlsv1_cred_alloc()
227  * @private_key: File or reference name for the key in PEM or DER format
228  * @private_key_passwd: Passphrase for decrypted private key, %NULL if no
229  * passphrase is used.
230  * @private_key_blob: private_key as inlined data or %NULL if not used
231  * @private_key_blob_len: private_key_blob length
232  * Returns: 0 on success, -1 on failure
233  */
234 int tlsv1_set_private_key(struct tlsv1_credentials *cred,
235                           const char *private_key,
236                           const char *private_key_passwd,
237                           const u8 *private_key_blob,
238                           size_t private_key_blob_len)
239 {
240         crypto_private_key_free(cred->key);
241         cred->key = NULL;
242
243         if (private_key_blob)
244                 return tlsv1_set_key(cred, private_key_blob,
245                                      private_key_blob_len);
246
247         if (private_key) {
248                 u8 *buf;
249                 size_t len;
250                 int ret;
251
252                 buf = (u8 *) os_readfile(private_key, &len);
253                 if (buf == NULL) {
254                         wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
255                                    private_key);
256                         return -1;
257                 }
258
259                 ret = tlsv1_set_key(cred, buf, len);
260                 os_free(buf);
261                 return ret;
262         }
263
264         return 0;
265 }
266
267
268 static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred,
269                                   const u8 *dh, size_t len)
270 {
271         struct asn1_hdr hdr;
272         const u8 *pos, *end;
273
274         pos = dh;
275         end = dh + len;
276
277         /*
278          * DHParameter ::= SEQUENCE {
279          *   prime INTEGER, -- p
280          *   base INTEGER, -- g
281          *   privateValueLength INTEGER OPTIONAL }
282          */
283
284         /* DHParamer ::= SEQUENCE */
285         if (asn1_get_next(pos, len, &hdr) < 0 ||
286             hdr.class != ASN1_CLASS_UNIVERSAL ||
287             hdr.tag != ASN1_TAG_SEQUENCE) {
288                 wpa_printf(MSG_DEBUG, "DH: DH parameters did not start with a "
289                            "valid SEQUENCE - found class %d tag 0x%x",
290                            hdr.class, hdr.tag);
291                 return -1;
292         }
293         pos = hdr.payload;
294
295         /* prime INTEGER */
296         if (asn1_get_next(pos, end - pos, &hdr) < 0)
297                 return -1;
298
299         if (hdr.class != ASN1_CLASS_UNIVERSAL ||
300             hdr.tag != ASN1_TAG_INTEGER) {
301                 wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for p; "
302                            "class=%d tag=0x%x", hdr.class, hdr.tag);
303                 return -1;
304         }
305
306         wpa_hexdump(MSG_MSGDUMP, "DH: prime (p)", hdr.payload, hdr.length);
307         if (hdr.length == 0)
308                 return -1;
309         os_free(cred->dh_p);
310         cred->dh_p = os_malloc(hdr.length);
311         if (cred->dh_p == NULL)
312                 return -1;
313         os_memcpy(cred->dh_p, hdr.payload, hdr.length);
314         cred->dh_p_len = hdr.length;
315         pos = hdr.payload + hdr.length;
316
317         /* base INTEGER */
318         if (asn1_get_next(pos, end - pos, &hdr) < 0)
319                 return -1;
320
321         if (hdr.class != ASN1_CLASS_UNIVERSAL ||
322             hdr.tag != ASN1_TAG_INTEGER) {
323                 wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for g; "
324                            "class=%d tag=0x%x", hdr.class, hdr.tag);
325                 return -1;
326         }
327
328         wpa_hexdump(MSG_MSGDUMP, "DH: base (g)", hdr.payload, hdr.length);
329         if (hdr.length == 0)
330                 return -1;
331         os_free(cred->dh_g);
332         cred->dh_g = os_malloc(hdr.length);
333         if (cred->dh_g == NULL)
334                 return -1;
335         os_memcpy(cred->dh_g, hdr.payload, hdr.length);
336         cred->dh_g_len = hdr.length;
337
338         return 0;
339 }
340
341
342 static const char *pem_dhparams_begin = "-----BEGIN DH PARAMETERS-----";
343 static const char *pem_dhparams_end = "-----END DH PARAMETERS-----";
344
345
346 static int tlsv1_set_dhparams_blob(struct tlsv1_credentials *cred,
347                                    const u8 *buf, size_t len)
348 {
349         const u8 *pos, *end;
350         unsigned char *der;
351         size_t der_len;
352
353         pos = search_tag(pem_dhparams_begin, buf, len);
354         if (!pos) {
355                 wpa_printf(MSG_DEBUG, "TLSv1: No PEM dhparams tag found - "
356                            "assume DER format");
357                 return tlsv1_set_dhparams_der(cred, buf, len);
358         }
359
360         wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format dhparams into DER "
361                    "format");
362
363         pos += os_strlen(pem_dhparams_begin);
364         end = search_tag(pem_dhparams_end, pos, buf + len - pos);
365         if (end == NULL) {
366                 wpa_printf(MSG_INFO, "TLSv1: Could not find PEM dhparams end "
367                            "tag (%s)", pem_dhparams_end);
368                 return -1;
369         }
370
371         der = base64_decode(pos, end - pos, &der_len);
372         if (der == NULL) {
373                 wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM dhparams");
374                 return -1;
375         }
376
377         if (tlsv1_set_dhparams_der(cred, der, der_len) < 0) {
378                 wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM dhparams "
379                            "DER conversion");
380                 os_free(der);
381                 return -1;
382         }
383
384         os_free(der);
385
386         return 0;
387 }
388
389
390 /**
391  * tlsv1_set_dhparams - Set Diffie-Hellman parameters
392  * @cred: TLSv1 credentials from tlsv1_cred_alloc()
393  * @dh_file: File or reference name for the DH params in PEM or DER format
394  * @dh_blob: DH params as inlined data or %NULL if not used
395  * @dh_blob_len: dh_blob length
396  * Returns: 0 on success, -1 on failure
397  */
398 int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file,
399                        const u8 *dh_blob, size_t dh_blob_len)
400 {
401         if (dh_blob)
402                 return tlsv1_set_dhparams_blob(cred, dh_blob, dh_blob_len);
403
404         if (dh_file) {
405                 u8 *buf;
406                 size_t len;
407                 int ret;
408
409                 buf = (u8 *) os_readfile(dh_file, &len);
410                 if (buf == NULL) {
411                         wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
412                                    dh_file);
413                         return -1;
414                 }
415
416                 ret = tlsv1_set_dhparams_blob(cred, buf, len);
417                 os_free(buf);
418                 return ret;
419         }
420
421         return 0;
422 }