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