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
tlsv1_cred_alloc(void)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
tlsv1_cred_free(struct tlsv1_credentials * cred)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
tlsv1_add_cert_der(struct x509_certificate ** chain,const u8 * buf,size_t len)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
search_tag(const char * tag,const u8 * buf,size_t len)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
tlsv1_add_cert(struct x509_certificate ** chain,const u8 * buf,size_t len)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
tlsv1_set_cert_chain(struct x509_certificate ** chain,const char * cert,const u8 * cert_blob,size_t cert_blob_len)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 */
tlsv1_set_ca_cert(struct tlsv1_credentials * cred,const char * cert,const u8 * cert_blob,size_t cert_blob_len,const char * path)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 */
tlsv1_set_cert(struct tlsv1_credentials * cred,const char * cert,const u8 * cert_blob,size_t cert_blob_len)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
tlsv1_set_key(struct tlsv1_credentials * cred,const u8 * key,size_t len)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 */
tlsv1_set_private_key(struct tlsv1_credentials * cred,const char * private_key,const char * private_key_passwd,const u8 * private_key_blob,size_t private_key_blob_len)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
tlsv1_set_dhparams_der(struct tlsv1_credentials * cred,const u8 * dh,size_t len)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
tlsv1_set_dhparams_blob(struct tlsv1_credentials * cred,const u8 * buf,size_t len)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 */
tlsv1_set_dhparams(struct tlsv1_credentials * cred,const char * dh_file,const u8 * dh_blob,size_t dh_blob_len)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 }
423