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