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