xref: /freebsd/contrib/wpa/hs20/client/est.c (revision 13ea0450a9c8742119d36f3bf8f47accdce46e54)
1 /*
2  * Hotspot 2.0 OSU client - EST client
3  * Copyright (c) 2012-2014, Qualcomm Atheros, Inc.
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 #include <openssl/err.h>
11 #include <openssl/evp.h>
12 #include <openssl/pem.h>
13 #include <openssl/pkcs7.h>
14 #include <openssl/rsa.h>
15 #include <openssl/asn1.h>
16 #include <openssl/asn1t.h>
17 #include <openssl/x509.h>
18 #include <openssl/x509v3.h>
19 #ifdef OPENSSL_IS_BORINGSSL
20 #include <openssl/buf.h>
21 #endif /* OPENSSL_IS_BORINGSSL */
22 
23 #include "common.h"
24 #include "utils/base64.h"
25 #include "utils/xml-utils.h"
26 #include "utils/http-utils.h"
27 #include "osu_client.h"
28 
29 
30 static int pkcs7_to_cert(struct hs20_osu_client *ctx, const u8 *pkcs7,
31 			 size_t len, char *pem_file, char *der_file)
32 {
33 #ifdef OPENSSL_IS_BORINGSSL
34 	CBS pkcs7_cbs;
35 #else /* OPENSSL_IS_BORINGSSL */
36 	PKCS7 *p7 = NULL;
37 	const unsigned char *p = pkcs7;
38 #endif /* OPENSSL_IS_BORINGSSL */
39 	STACK_OF(X509) *certs;
40 	int i, num, ret = -1;
41 	BIO *out = NULL;
42 
43 #ifdef OPENSSL_IS_BORINGSSL
44 	certs = sk_X509_new_null();
45 	if (!certs)
46 		goto fail;
47 	CBS_init(&pkcs7_cbs, pkcs7, len);
48 	if (!PKCS7_get_certificates(certs, &pkcs7_cbs)) {
49 		wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s",
50 			   ERR_error_string(ERR_get_error(), NULL));
51 		write_result(ctx, "Could not parse PKCS#7 object from EST");
52 		goto fail;
53 	}
54 #else /* OPENSSL_IS_BORINGSSL */
55 	p7 = d2i_PKCS7(NULL, &p, len);
56 	if (p7 == NULL) {
57 		wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s",
58 			   ERR_error_string(ERR_get_error(), NULL));
59 		write_result(ctx, "Could not parse PKCS#7 object from EST");
60 		goto fail;
61 	}
62 
63 	switch (OBJ_obj2nid(p7->type)) {
64 	case NID_pkcs7_signed:
65 		certs = p7->d.sign->cert;
66 		break;
67 	case NID_pkcs7_signedAndEnveloped:
68 		certs = p7->d.signed_and_enveloped->cert;
69 		break;
70 	default:
71 		certs = NULL;
72 		break;
73 	}
74 #endif /* OPENSSL_IS_BORINGSSL */
75 
76 	if (!certs || ((num = sk_X509_num(certs)) == 0)) {
77 		wpa_printf(MSG_INFO, "No certificates found in PKCS#7 object");
78 		write_result(ctx, "No certificates found in PKCS#7 object");
79 		goto fail;
80 	}
81 
82 	if (der_file) {
83 		FILE *f = fopen(der_file, "wb");
84 		if (f == NULL)
85 			goto fail;
86 		i2d_X509_fp(f, sk_X509_value(certs, 0));
87 		fclose(f);
88 	}
89 
90 	if (pem_file) {
91 		out = BIO_new(BIO_s_file());
92 		if (out == NULL ||
93 		    BIO_write_filename(out, pem_file) <= 0)
94 			goto fail;
95 
96 		for (i = 0; i < num; i++) {
97 			X509 *cert = sk_X509_value(certs, i);
98 			X509_print(out, cert);
99 			PEM_write_bio_X509(out, cert);
100 			BIO_puts(out, "\n");
101 		}
102 	}
103 
104 	ret = 0;
105 
106 fail:
107 #ifdef OPENSSL_IS_BORINGSSL
108 	if (certs)
109 		sk_X509_pop_free(certs, X509_free);
110 #else /* OPENSSL_IS_BORINGSSL */
111 	PKCS7_free(p7);
112 #endif /* OPENSSL_IS_BORINGSSL */
113 	if (out)
114 		BIO_free_all(out);
115 
116 	return ret;
117 }
118 
119 
120 int est_load_cacerts(struct hs20_osu_client *ctx, const char *url)
121 {
122 	char *buf, *resp;
123 	size_t buflen;
124 	unsigned char *pkcs7;
125 	size_t pkcs7_len, resp_len;
126 	int res;
127 
128 	buflen = os_strlen(url) + 100;
129 	buf = os_malloc(buflen);
130 	if (buf == NULL)
131 		return -1;
132 
133 	os_snprintf(buf, buflen, "%s/cacerts", url);
134 	wpa_printf(MSG_INFO, "Download EST cacerts from %s", buf);
135 	write_summary(ctx, "Download EST cacerts from %s", buf);
136 	ctx->no_osu_cert_validation = 1;
137 	http_ocsp_set(ctx->http, 1);
138 	res = http_download_file(ctx->http, buf, "Cert/est-cacerts.txt",
139 				 ctx->ca_fname);
140 	http_ocsp_set(ctx->http,
141 		      (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
142 	ctx->no_osu_cert_validation = 0;
143 	if (res < 0) {
144 		wpa_printf(MSG_INFO, "Failed to download EST cacerts from %s",
145 			   buf);
146 		write_result(ctx, "Failed to download EST cacerts from %s",
147 			     buf);
148 		os_free(buf);
149 		return -1;
150 	}
151 	os_free(buf);
152 
153 	resp = os_readfile("Cert/est-cacerts.txt", &resp_len);
154 	if (resp == NULL) {
155 		wpa_printf(MSG_INFO, "Could not read Cert/est-cacerts.txt");
156 		write_result(ctx, "Could not read EST cacerts");
157 		return -1;
158 	}
159 
160 	pkcs7 = base64_decode((unsigned char *) resp, resp_len, &pkcs7_len);
161 	if (pkcs7 && pkcs7_len < resp_len / 2) {
162 		wpa_printf(MSG_INFO, "Too short base64 decode (%u bytes; downloaded %u bytes) - assume this was binary",
163 			   (unsigned int) pkcs7_len, (unsigned int) resp_len);
164 		os_free(pkcs7);
165 		pkcs7 = NULL;
166 	}
167 	if (pkcs7 == NULL) {
168 		wpa_printf(MSG_INFO, "EST workaround - Could not decode base64, assume this is DER encoded PKCS7");
169 		pkcs7 = os_malloc(resp_len);
170 		if (pkcs7) {
171 			os_memcpy(pkcs7, resp, resp_len);
172 			pkcs7_len = resp_len;
173 		}
174 	}
175 	os_free(resp);
176 
177 	if (pkcs7 == NULL) {
178 		wpa_printf(MSG_INFO, "Could not fetch PKCS7 cacerts");
179 		write_result(ctx, "Could not fetch EST PKCS#7 cacerts");
180 		return -1;
181 	}
182 
183 	res = pkcs7_to_cert(ctx, pkcs7, pkcs7_len, "Cert/est-cacerts.pem",
184 			    NULL);
185 	os_free(pkcs7);
186 	if (res < 0) {
187 		wpa_printf(MSG_INFO, "Could not parse CA certs from PKCS#7 cacerts response");
188 		write_result(ctx, "Could not parse CA certs from EST PKCS#7 cacerts response");
189 		return -1;
190 	}
191 	unlink("Cert/est-cacerts.txt");
192 
193 	return 0;
194 }
195 
196 
197 /*
198  * CsrAttrs ::= SEQUENCE SIZE (0..MAX) OF AttrOrOID
199  *
200  * AttrOrOID ::= CHOICE {
201  *   oid OBJECT IDENTIFIER,
202  *   attribute Attribute }
203  *
204  * Attribute ::= SEQUENCE {
205  *   type OBJECT IDENTIFIER,
206  *   values SET SIZE(1..MAX) OF OBJECT IDENTIFIER }
207  */
208 
209 typedef struct {
210 	ASN1_OBJECT *type;
211 	STACK_OF(ASN1_OBJECT) *values;
212 } Attribute;
213 
214 typedef struct {
215 	int type;
216 	union {
217 		ASN1_OBJECT *oid;
218 		Attribute *attribute;
219 	} d;
220 } AttrOrOID;
221 
222 typedef struct {
223 	int type;
224 	STACK_OF(AttrOrOID) *attrs;
225 } CsrAttrs;
226 
227 ASN1_SEQUENCE(Attribute) = {
228 	ASN1_SIMPLE(Attribute, type, ASN1_OBJECT),
229 	ASN1_SET_OF(Attribute, values, ASN1_OBJECT)
230 } ASN1_SEQUENCE_END(Attribute);
231 
232 ASN1_CHOICE(AttrOrOID) = {
233 	ASN1_SIMPLE(AttrOrOID, d.oid, ASN1_OBJECT),
234 	ASN1_SIMPLE(AttrOrOID, d.attribute, Attribute)
235 } ASN1_CHOICE_END(AttrOrOID);
236 
237 ASN1_CHOICE(CsrAttrs) = {
238 	ASN1_SEQUENCE_OF(CsrAttrs, attrs, AttrOrOID)
239 } ASN1_CHOICE_END(CsrAttrs);
240 
241 IMPLEMENT_ASN1_FUNCTIONS(CsrAttrs);
242 
243 
244 static void add_csrattrs_oid(struct hs20_osu_client *ctx, ASN1_OBJECT *oid,
245 			     STACK_OF(X509_EXTENSION) *exts)
246 {
247 	char txt[100];
248 	int res;
249 
250 	if (!oid)
251 		return;
252 
253 	res = OBJ_obj2txt(txt, sizeof(txt), oid, 1);
254 	if (res < 0 || res >= (int) sizeof(txt))
255 		return;
256 
257 	if (os_strcmp(txt, "1.2.840.113549.1.9.7") == 0) {
258 		wpa_printf(MSG_INFO, "TODO: csrattr challengePassword");
259 	} else if (os_strcmp(txt, "1.2.840.113549.1.1.11") == 0) {
260 		wpa_printf(MSG_INFO, "csrattr sha256WithRSAEncryption");
261 	} else {
262 		wpa_printf(MSG_INFO, "Ignore unsupported csrattr oid %s", txt);
263 	}
264 }
265 
266 
267 static void add_csrattrs_ext_req(struct hs20_osu_client *ctx,
268 				 STACK_OF(ASN1_OBJECT) *values,
269 				 STACK_OF(X509_EXTENSION) *exts)
270 {
271 	char txt[100];
272 	int i, num, res;
273 
274 	num = sk_ASN1_OBJECT_num(values);
275 	for (i = 0; i < num; i++) {
276 		ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(values, i);
277 
278 		res = OBJ_obj2txt(txt, sizeof(txt), oid, 1);
279 		if (res < 0 || res >= (int) sizeof(txt))
280 			continue;
281 
282 		if (os_strcmp(txt, "1.3.6.1.1.1.1.22") == 0) {
283 			wpa_printf(MSG_INFO, "TODO: extReq macAddress");
284 		} else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.3") == 0) {
285 			wpa_printf(MSG_INFO, "TODO: extReq imei");
286 		} else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.4") == 0) {
287 			wpa_printf(MSG_INFO, "TODO: extReq meid");
288 		} else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.5") == 0) {
289 			wpa_printf(MSG_INFO, "TODO: extReq DevId");
290 		} else {
291 			wpa_printf(MSG_INFO, "Ignore unsupported cstattr extensionsRequest %s",
292 				   txt);
293 		}
294 	}
295 }
296 
297 
298 static void add_csrattrs_attr(struct hs20_osu_client *ctx, Attribute *attr,
299 			      STACK_OF(X509_EXTENSION) *exts)
300 {
301 	char txt[100], txt2[100];
302 	int i, num, res;
303 
304 	if (!attr || !attr->type || !attr->values)
305 		return;
306 
307 	res = OBJ_obj2txt(txt, sizeof(txt), attr->type, 1);
308 	if (res < 0 || res >= (int) sizeof(txt))
309 		return;
310 
311 	if (os_strcmp(txt, "1.2.840.113549.1.9.14") == 0) {
312 		add_csrattrs_ext_req(ctx, attr->values, exts);
313 		return;
314 	}
315 
316 	num = sk_ASN1_OBJECT_num(attr->values);
317 	for (i = 0; i < num; i++) {
318 		ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(attr->values, i);
319 
320 		res = OBJ_obj2txt(txt2, sizeof(txt2), oid, 1);
321 		if (res < 0 || res >= (int) sizeof(txt2))
322 			continue;
323 
324 		wpa_printf(MSG_INFO, "Ignore unsupported cstattr::attr %s oid %s",
325 			   txt, txt2);
326 	}
327 }
328 
329 
330 static void add_csrattrs(struct hs20_osu_client *ctx, CsrAttrs *csrattrs,
331 			 STACK_OF(X509_EXTENSION) *exts)
332 {
333 	int i, num;
334 
335 	if (!csrattrs || ! csrattrs->attrs)
336 		return;
337 
338 #ifdef OPENSSL_IS_BORINGSSL
339 	num = sk_num(CHECKED_CAST(_STACK *, STACK_OF(AttrOrOID) *,
340 				  csrattrs->attrs));
341 	for (i = 0; i < num; i++) {
342 		AttrOrOID *ao = sk_value(
343 			CHECKED_CAST(_STACK *, const STACK_OF(AttrOrOID) *,
344 				     csrattrs->attrs), i);
345 		switch (ao->type) {
346 		case 0:
347 			add_csrattrs_oid(ctx, ao->d.oid, exts);
348 			break;
349 		case 1:
350 			add_csrattrs_attr(ctx, ao->d.attribute, exts);
351 			break;
352 		}
353 	}
354 #else /* OPENSSL_IS_BORINGSSL */
355 	num = SKM_sk_num(AttrOrOID, csrattrs->attrs);
356 	for (i = 0; i < num; i++) {
357 		AttrOrOID *ao = SKM_sk_value(AttrOrOID, csrattrs->attrs, i);
358 		switch (ao->type) {
359 		case 0:
360 			add_csrattrs_oid(ctx, ao->d.oid, exts);
361 			break;
362 		case 1:
363 			add_csrattrs_attr(ctx, ao->d.attribute, exts);
364 			break;
365 		}
366 	}
367 #endif /* OPENSSL_IS_BORINGSSL */
368 }
369 
370 
371 static int generate_csr(struct hs20_osu_client *ctx, char *key_pem,
372 			char *csr_pem, char *est_req, char *old_cert,
373 			CsrAttrs *csrattrs)
374 {
375 	EVP_PKEY_CTX *pctx = NULL;
376 	EVP_PKEY *pkey = NULL;
377 	RSA *rsa;
378 	X509_REQ *req = NULL;
379 	int ret = -1;
380 	unsigned int val;
381 	X509_NAME *subj = NULL;
382 	char name[100];
383 	STACK_OF(X509_EXTENSION) *exts = NULL;
384 	X509_EXTENSION *ex;
385 	BIO *out;
386 	CONF *ctmp = NULL;
387 
388 	wpa_printf(MSG_INFO, "Generate RSA private key");
389 	write_summary(ctx, "Generate RSA private key");
390 	pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
391 	if (!pctx)
392 		return -1;
393 
394 	if (EVP_PKEY_keygen_init(pctx) <= 0)
395 		goto fail;
396 
397 	if (EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, 2048) <= 0)
398 		goto fail;
399 
400 	if (EVP_PKEY_keygen(pctx, &pkey) <= 0)
401 		goto fail;
402 	EVP_PKEY_CTX_free(pctx);
403 	pctx = NULL;
404 
405 	rsa = EVP_PKEY_get1_RSA(pkey);
406 	if (rsa == NULL)
407 		goto fail;
408 
409 	if (key_pem) {
410 		FILE *f = fopen(key_pem, "wb");
411 		if (f == NULL)
412 			goto fail;
413 		if (!PEM_write_RSAPrivateKey(f, rsa, NULL, NULL, 0, NULL,
414 					     NULL)) {
415 			wpa_printf(MSG_INFO, "Could not write private key: %s",
416 				   ERR_error_string(ERR_get_error(), NULL));
417 			fclose(f);
418 			goto fail;
419 		}
420 		fclose(f);
421 	}
422 
423 	wpa_printf(MSG_INFO, "Generate CSR");
424 	write_summary(ctx, "Generate CSR");
425 	req = X509_REQ_new();
426 	if (req == NULL)
427 		goto fail;
428 
429 	if (old_cert) {
430 		FILE *f;
431 		X509 *cert;
432 		int res;
433 
434 		f = fopen(old_cert, "r");
435 		if (f == NULL)
436 			goto fail;
437 		cert = PEM_read_X509(f, NULL, NULL, NULL);
438 		fclose(f);
439 
440 		if (cert == NULL)
441 			goto fail;
442 		res = X509_REQ_set_subject_name(req,
443 						X509_get_subject_name(cert));
444 		X509_free(cert);
445 		if (!res)
446 			goto fail;
447 	} else {
448 		os_get_random((u8 *) &val, sizeof(val));
449 		os_snprintf(name, sizeof(name), "cert-user-%u", val);
450 		subj = X509_NAME_new();
451 		if (subj == NULL ||
452 		    !X509_NAME_add_entry_by_txt(subj, "CN", MBSTRING_ASC,
453 						(unsigned char *) name,
454 						-1, -1, 0) ||
455 		    !X509_REQ_set_subject_name(req, subj))
456 			goto fail;
457 		X509_NAME_free(subj);
458 		subj = NULL;
459 	}
460 
461 	if (!X509_REQ_set_pubkey(req, pkey))
462 		goto fail;
463 
464 	exts = sk_X509_EXTENSION_new_null();
465 	if (!exts)
466 		goto fail;
467 
468 	ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_basic_constraints,
469 				  "CA:FALSE");
470 	if (ex == NULL ||
471 	    !sk_X509_EXTENSION_push(exts, ex))
472 		goto fail;
473 
474 	ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_key_usage,
475 				  "nonRepudiation,digitalSignature,keyEncipherment");
476 	if (ex == NULL ||
477 	    !sk_X509_EXTENSION_push(exts, ex))
478 		goto fail;
479 
480 	ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_ext_key_usage,
481 				  "1.3.6.1.4.1.40808.1.1.2");
482 	if (ex == NULL ||
483 	    !sk_X509_EXTENSION_push(exts, ex))
484 		goto fail;
485 
486 	add_csrattrs(ctx, csrattrs, exts);
487 
488 	if (!X509_REQ_add_extensions(req, exts))
489 		goto fail;
490 	sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
491 	exts = NULL;
492 
493 	if (!X509_REQ_sign(req, pkey, EVP_sha256()))
494 		goto fail;
495 
496 	out = BIO_new(BIO_s_mem());
497 	if (out) {
498 		char *txt;
499 		size_t rlen;
500 
501 #if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL)
502 		X509_REQ_print(out, req);
503 #endif
504 		rlen = BIO_ctrl_pending(out);
505 		txt = os_malloc(rlen + 1);
506 		if (txt) {
507 			int res = BIO_read(out, txt, rlen);
508 			if (res > 0) {
509 				txt[res] = '\0';
510 				wpa_printf(MSG_MSGDUMP, "OpenSSL: Certificate request:\n%s",
511 					   txt);
512 			}
513 			os_free(txt);
514 		}
515 		BIO_free(out);
516 	}
517 
518 	if (csr_pem) {
519 		FILE *f = fopen(csr_pem, "w");
520 		if (f == NULL)
521 			goto fail;
522 #if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL)
523 		X509_REQ_print_fp(f, req);
524 #endif
525 		if (!PEM_write_X509_REQ(f, req)) {
526 			fclose(f);
527 			goto fail;
528 		}
529 		fclose(f);
530 	}
531 
532 	if (est_req) {
533 		BIO *mem = BIO_new(BIO_s_mem());
534 		BUF_MEM *ptr;
535 		char *pos, *end, *buf_end;
536 		FILE *f;
537 
538 		if (mem == NULL)
539 			goto fail;
540 		if (!PEM_write_bio_X509_REQ(mem, req)) {
541 			BIO_free(mem);
542 			goto fail;
543 		}
544 
545 		BIO_get_mem_ptr(mem, &ptr);
546 		pos = ptr->data;
547 		buf_end = pos + ptr->length;
548 
549 		/* Remove START/END lines */
550 		while (pos < buf_end && *pos != '\n')
551 			pos++;
552 		if (pos == buf_end) {
553 			BIO_free(mem);
554 			goto fail;
555 		}
556 		pos++;
557 
558 		end = pos;
559 		while (end < buf_end && *end != '-')
560 			end++;
561 
562 		f = fopen(est_req, "w");
563 		if (f == NULL) {
564 			BIO_free(mem);
565 			goto fail;
566 		}
567 		fwrite(pos, end - pos, 1, f);
568 		fclose(f);
569 
570 		BIO_free(mem);
571 	}
572 
573 	ret = 0;
574 fail:
575 	if (exts)
576 		sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
577 	if (subj)
578 		X509_NAME_free(subj);
579 	if (req)
580 		X509_REQ_free(req);
581 	if (pkey)
582 		EVP_PKEY_free(pkey);
583 	if (pctx)
584 		EVP_PKEY_CTX_free(pctx);
585 	return ret;
586 }
587 
588 
589 int est_build_csr(struct hs20_osu_client *ctx, const char *url)
590 {
591 	char *buf;
592 	size_t buflen;
593 	int res;
594 	char old_cert_buf[200];
595 	char *old_cert = NULL;
596 	CsrAttrs *csrattrs = NULL;
597 
598 	buflen = os_strlen(url) + 100;
599 	buf = os_malloc(buflen);
600 	if (buf == NULL)
601 		return -1;
602 
603 	os_snprintf(buf, buflen, "%s/csrattrs", url);
604 	wpa_printf(MSG_INFO, "Download csrattrs from %s", buf);
605 	write_summary(ctx, "Download EST csrattrs from %s", buf);
606 	ctx->no_osu_cert_validation = 1;
607 	http_ocsp_set(ctx->http, 1);
608 	res = http_download_file(ctx->http, buf, "Cert/est-csrattrs.txt",
609 				 ctx->ca_fname);
610 	http_ocsp_set(ctx->http,
611 		      (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
612 	ctx->no_osu_cert_validation = 0;
613 	os_free(buf);
614 	if (res < 0) {
615 		wpa_printf(MSG_INFO, "Failed to download EST csrattrs - assume no extra attributes are needed");
616 	} else {
617 		size_t resp_len;
618 		char *resp;
619 		unsigned char *attrs;
620 		const unsigned char *pos;
621 		size_t attrs_len;
622 
623 		resp = os_readfile("Cert/est-csrattrs.txt", &resp_len);
624 		if (resp == NULL) {
625 			wpa_printf(MSG_INFO, "Could not read csrattrs");
626 			return -1;
627 		}
628 
629 		attrs = base64_decode((unsigned char *) resp, resp_len,
630 				      &attrs_len);
631 		os_free(resp);
632 
633 		if (attrs == NULL) {
634 			wpa_printf(MSG_INFO, "Could not base64 decode csrattrs");
635 			return -1;
636 		}
637 		unlink("Cert/est-csrattrs.txt");
638 
639 		pos = attrs;
640 		csrattrs = d2i_CsrAttrs(NULL, &pos, attrs_len);
641 		os_free(attrs);
642 		if (csrattrs == NULL) {
643 			wpa_printf(MSG_INFO, "Failed to parse csrattrs ASN.1");
644 			/* Continue assuming no additional requirements */
645 		}
646 	}
647 
648 	if (ctx->client_cert_present) {
649 		os_snprintf(old_cert_buf, sizeof(old_cert_buf),
650 			    "SP/%s/client-cert.pem", ctx->fqdn);
651 		old_cert = old_cert_buf;
652 	}
653 
654 	res = generate_csr(ctx, "Cert/privkey-plain.pem", "Cert/est-req.pem",
655 			   "Cert/est-req.b64", old_cert, csrattrs);
656 	if (csrattrs)
657 		CsrAttrs_free(csrattrs);
658 
659 	return res;
660 }
661 
662 
663 int est_simple_enroll(struct hs20_osu_client *ctx, const char *url,
664 		      const char *user, const char *pw)
665 {
666 	char *buf, *resp, *req, *req2;
667 	size_t buflen, resp_len, len, pkcs7_len;
668 	unsigned char *pkcs7;
669 	char client_cert_buf[200];
670 	char client_key_buf[200];
671 	const char *client_cert = NULL, *client_key = NULL;
672 	int res;
673 
674 	req = os_readfile("Cert/est-req.b64", &len);
675 	if (req == NULL) {
676 		wpa_printf(MSG_INFO, "Could not read Cert/req.b64");
677 		return -1;
678 	}
679 	req2 = os_realloc(req, len + 1);
680 	if (req2 == NULL) {
681 		os_free(req);
682 		return -1;
683 	}
684 	req2[len] = '\0';
685 	req = req2;
686 	wpa_printf(MSG_DEBUG, "EST simpleenroll request: %s", req);
687 
688 	buflen = os_strlen(url) + 100;
689 	buf = os_malloc(buflen);
690 	if (buf == NULL) {
691 		os_free(req);
692 		return -1;
693 	}
694 
695 	if (ctx->client_cert_present) {
696 		os_snprintf(buf, buflen, "%s/simplereenroll", url);
697 		os_snprintf(client_cert_buf, sizeof(client_cert_buf),
698 			    "SP/%s/client-cert.pem", ctx->fqdn);
699 		client_cert = client_cert_buf;
700 		os_snprintf(client_key_buf, sizeof(client_key_buf),
701 			    "SP/%s/client-key.pem", ctx->fqdn);
702 		client_key = client_key_buf;
703 	} else
704 		os_snprintf(buf, buflen, "%s/simpleenroll", url);
705 	wpa_printf(MSG_INFO, "EST simpleenroll URL: %s", buf);
706 	write_summary(ctx, "EST simpleenroll URL: %s", buf);
707 	ctx->no_osu_cert_validation = 1;
708 	http_ocsp_set(ctx->http, 1);
709 	resp = http_post(ctx->http, buf, req, "application/pkcs10",
710 			 "Content-Transfer-Encoding: base64",
711 			 ctx->ca_fname, user, pw, client_cert, client_key,
712 			 &resp_len);
713 	http_ocsp_set(ctx->http,
714 		      (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
715 	ctx->no_osu_cert_validation = 0;
716 	os_free(buf);
717 	if (resp == NULL) {
718 		wpa_printf(MSG_INFO, "EST certificate enrollment failed");
719 		write_result(ctx, "EST certificate enrollment failed");
720 		return -1;
721 	}
722 	wpa_printf(MSG_DEBUG, "EST simpleenroll response: %s", resp);
723 
724 	pkcs7 = base64_decode((unsigned char *) resp, resp_len, &pkcs7_len);
725 	if (pkcs7 == NULL) {
726 		wpa_printf(MSG_INFO, "EST workaround - Could not decode base64, assume this is DER encoded PKCS7");
727 		pkcs7 = os_malloc(resp_len);
728 		if (pkcs7) {
729 			os_memcpy(pkcs7, resp, resp_len);
730 			pkcs7_len = resp_len;
731 		}
732 	}
733 	os_free(resp);
734 
735 	if (pkcs7 == NULL) {
736 		wpa_printf(MSG_INFO, "Failed to parse simpleenroll base64 response");
737 		write_result(ctx, "Failed to parse EST simpleenroll base64 response");
738 		return -1;
739 	}
740 
741 	res = pkcs7_to_cert(ctx, pkcs7, pkcs7_len, "Cert/est_cert.pem",
742 			    "Cert/est_cert.der");
743 	os_free(pkcs7);
744 
745 	if (res < 0) {
746 		wpa_printf(MSG_INFO, "EST: Failed to extract certificate from PKCS7 file");
747 		write_result(ctx, "EST: Failed to extract certificate from EST PKCS7 file");
748 		return -1;
749 	}
750 
751 	wpa_printf(MSG_INFO, "EST simple%senroll completed successfully",
752 		   ctx->client_cert_present ? "re" : "");
753 	write_summary(ctx, "EST simple%senroll completed successfully",
754 		      ctx->client_cert_present ? "re" : "");
755 
756 	return 0;
757 }
758