]>
Commit | Line | Data |
---|---|---|
1cb3eda9 JM |
1 | /* |
2 | * HTTP wrapper for libcurl | |
4d65deda | 3 | * Copyright (c) 2012-2014, Qualcomm Atheros, Inc. |
1cb3eda9 JM |
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 <curl/curl.h> | |
11 | #ifdef EAP_TLS_OPENSSL | |
12 | #include <openssl/ssl.h> | |
13 | #include <openssl/asn1.h> | |
14 | #include <openssl/asn1t.h> | |
15 | #include <openssl/x509v3.h> | |
16 | ||
17 | #ifdef SSL_set_tlsext_status_type | |
18 | #ifndef OPENSSL_NO_TLSEXT | |
19 | #define HAVE_OCSP | |
20 | #include <openssl/err.h> | |
21 | #include <openssl/ocsp.h> | |
22 | #endif /* OPENSSL_NO_TLSEXT */ | |
23 | #endif /* SSL_set_tlsext_status_type */ | |
24 | #endif /* EAP_TLS_OPENSSL */ | |
25 | ||
26 | #include "common.h" | |
27 | #include "xml-utils.h" | |
28 | #include "http-utils.h" | |
8d27efa8 JM |
29 | #ifdef EAP_TLS_OPENSSL |
30 | #include "crypto/tls_openssl.h" | |
31 | #endif /* EAP_TLS_OPENSSL */ | |
1cb3eda9 JM |
32 | |
33 | ||
4b970ee5 JM |
34 | #if OPENSSL_VERSION_NUMBER < 0x10100000L |
35 | static const unsigned char * ASN1_STRING_get0_data(const ASN1_STRING *x) | |
36 | { | |
37 | return ASN1_STRING_data((ASN1_STRING *) x); | |
38 | } | |
39 | #endif /* OpenSSL < 1.1.0 */ | |
40 | ||
41 | ||
1cb3eda9 JM |
42 | struct http_ctx { |
43 | void *ctx; | |
44 | struct xml_node_ctx *xml; | |
45 | CURL *curl; | |
46 | struct curl_slist *curl_hdr; | |
47 | char *svc_address; | |
48 | char *svc_ca_fname; | |
49 | char *svc_username; | |
50 | char *svc_password; | |
51 | char *svc_client_cert; | |
52 | char *svc_client_key; | |
53 | char *curl_buf; | |
54 | size_t curl_buf_len; | |
55 | ||
56 | int (*cert_cb)(void *ctx, struct http_cert *cert); | |
57 | void *cert_cb_ctx; | |
58 | ||
59 | enum { | |
60 | NO_OCSP, OPTIONAL_OCSP, MANDATORY_OCSP | |
61 | } ocsp; | |
62 | X509 *peer_cert; | |
63 | X509 *peer_issuer; | |
64 | X509 *peer_issuer_issuer; | |
65 | ||
66 | const char *last_err; | |
67 | }; | |
68 | ||
69 | ||
70 | static void clear_curl(struct http_ctx *ctx) | |
71 | { | |
72 | if (ctx->curl) { | |
73 | curl_easy_cleanup(ctx->curl); | |
74 | ctx->curl = NULL; | |
75 | } | |
76 | if (ctx->curl_hdr) { | |
77 | curl_slist_free_all(ctx->curl_hdr); | |
78 | ctx->curl_hdr = NULL; | |
79 | } | |
80 | } | |
81 | ||
82 | ||
83 | static void clone_str(char **dst, const char *src) | |
84 | { | |
85 | os_free(*dst); | |
86 | if (src) | |
87 | *dst = os_strdup(src); | |
88 | else | |
89 | *dst = NULL; | |
90 | } | |
91 | ||
92 | ||
93 | static void debug_dump(struct http_ctx *ctx, const char *title, | |
94 | const char *buf, size_t len) | |
95 | { | |
4d65deda JM |
96 | char *txt; |
97 | size_t i; | |
98 | ||
99 | for (i = 0; i < len; i++) { | |
100 | if (buf[i] < 32 && buf[i] != '\t' && buf[i] != '\n' && | |
101 | buf[i] != '\r') { | |
102 | wpa_hexdump_ascii(MSG_MSGDUMP, title, buf, len); | |
103 | return; | |
104 | } | |
105 | } | |
106 | ||
107 | txt = os_malloc(len + 1); | |
1cb3eda9 JM |
108 | if (txt == NULL) |
109 | return; | |
110 | os_memcpy(txt, buf, len); | |
111 | txt[len] = '\0'; | |
112 | while (len > 0) { | |
113 | len--; | |
4d65deda JM |
114 | if (txt[len] == '\n' || txt[len] == '\r') |
115 | txt[len] = '\0'; | |
116 | else | |
117 | break; | |
1cb3eda9 JM |
118 | } |
119 | wpa_printf(MSG_MSGDUMP, "%s[%s]", title, txt); | |
120 | os_free(txt); | |
121 | } | |
122 | ||
123 | ||
124 | static int curl_cb_debug(CURL *curl, curl_infotype info, char *buf, size_t len, | |
125 | void *userdata) | |
126 | { | |
127 | struct http_ctx *ctx = userdata; | |
128 | switch (info) { | |
129 | case CURLINFO_TEXT: | |
130 | debug_dump(ctx, "CURLINFO_TEXT", buf, len); | |
131 | break; | |
132 | case CURLINFO_HEADER_IN: | |
133 | debug_dump(ctx, "CURLINFO_HEADER_IN", buf, len); | |
134 | break; | |
135 | case CURLINFO_HEADER_OUT: | |
136 | debug_dump(ctx, "CURLINFO_HEADER_OUT", buf, len); | |
137 | break; | |
138 | case CURLINFO_DATA_IN: | |
139 | debug_dump(ctx, "CURLINFO_DATA_IN", buf, len); | |
140 | break; | |
141 | case CURLINFO_DATA_OUT: | |
142 | debug_dump(ctx, "CURLINFO_DATA_OUT", buf, len); | |
143 | break; | |
144 | case CURLINFO_SSL_DATA_IN: | |
145 | wpa_printf(MSG_DEBUG, "debug - CURLINFO_SSL_DATA_IN - %d", | |
146 | (int) len); | |
147 | break; | |
148 | case CURLINFO_SSL_DATA_OUT: | |
149 | wpa_printf(MSG_DEBUG, "debug - CURLINFO_SSL_DATA_OUT - %d", | |
150 | (int) len); | |
151 | break; | |
152 | case CURLINFO_END: | |
153 | wpa_printf(MSG_DEBUG, "debug - CURLINFO_END - %d", | |
154 | (int) len); | |
155 | break; | |
156 | } | |
157 | return 0; | |
158 | } | |
159 | ||
160 | ||
1cb3eda9 JM |
161 | static size_t curl_cb_write(void *ptr, size_t size, size_t nmemb, |
162 | void *userdata) | |
163 | { | |
164 | struct http_ctx *ctx = userdata; | |
165 | char *n; | |
1cb3eda9 JM |
166 | n = os_realloc(ctx->curl_buf, ctx->curl_buf_len + size * nmemb + 1); |
167 | if (n == NULL) | |
168 | return 0; | |
169 | ctx->curl_buf = n; | |
170 | os_memcpy(n + ctx->curl_buf_len, ptr, size * nmemb); | |
171 | n[ctx->curl_buf_len + size * nmemb] = '\0'; | |
172 | ctx->curl_buf_len += size * nmemb; | |
173 | return size * nmemb; | |
174 | } | |
175 | ||
176 | ||
177 | #ifdef EAP_TLS_OPENSSL | |
178 | ||
179 | static void debug_dump_cert(const char *title, X509 *cert) | |
180 | { | |
181 | BIO *out; | |
182 | char *txt; | |
183 | size_t rlen; | |
184 | ||
185 | out = BIO_new(BIO_s_mem()); | |
186 | if (!out) | |
187 | return; | |
188 | ||
189 | X509_print_ex(out, cert, XN_FLAG_COMPAT, X509_FLAG_COMPAT); | |
190 | rlen = BIO_ctrl_pending(out); | |
191 | txt = os_malloc(rlen + 1); | |
192 | if (txt) { | |
193 | int res = BIO_read(out, txt, rlen); | |
194 | if (res > 0) { | |
195 | txt[res] = '\0'; | |
196 | wpa_printf(MSG_MSGDUMP, "%s:\n%s", title, txt); | |
197 | } | |
198 | os_free(txt); | |
199 | } | |
200 | BIO_free(out); | |
201 | } | |
202 | ||
203 | ||
204 | static void add_alt_name_othername(struct http_ctx *ctx, struct http_cert *cert, | |
205 | OTHERNAME *o) | |
206 | { | |
207 | char txt[100]; | |
208 | int res; | |
209 | struct http_othername *on; | |
210 | ASN1_TYPE *val; | |
211 | ||
212 | on = os_realloc_array(cert->othername, cert->num_othername + 1, | |
213 | sizeof(struct http_othername)); | |
214 | if (on == NULL) | |
215 | return; | |
216 | cert->othername = on; | |
217 | on = &on[cert->num_othername]; | |
218 | os_memset(on, 0, sizeof(*on)); | |
219 | ||
220 | res = OBJ_obj2txt(txt, sizeof(txt), o->type_id, 1); | |
221 | if (res < 0 || res >= (int) sizeof(txt)) | |
222 | return; | |
223 | ||
224 | on->oid = os_strdup(txt); | |
225 | if (on->oid == NULL) | |
226 | return; | |
227 | ||
228 | val = o->value; | |
229 | on->data = val->value.octet_string->data; | |
230 | on->len = val->value.octet_string->length; | |
231 | ||
232 | cert->num_othername++; | |
233 | } | |
234 | ||
235 | ||
236 | static void add_alt_name_dns(struct http_ctx *ctx, struct http_cert *cert, | |
237 | ASN1_STRING *name) | |
238 | { | |
239 | char *buf; | |
240 | char **n; | |
241 | ||
242 | buf = NULL; | |
243 | if (ASN1_STRING_to_UTF8((unsigned char **) &buf, name) < 0) | |
244 | return; | |
245 | ||
246 | n = os_realloc_array(cert->dnsname, cert->num_dnsname + 1, | |
247 | sizeof(char *)); | |
248 | if (n == NULL) | |
249 | return; | |
250 | ||
251 | cert->dnsname = n; | |
252 | n[cert->num_dnsname] = buf; | |
253 | cert->num_dnsname++; | |
254 | } | |
255 | ||
256 | ||
257 | static void add_alt_name(struct http_ctx *ctx, struct http_cert *cert, | |
258 | const GENERAL_NAME *name) | |
259 | { | |
260 | switch (name->type) { | |
261 | case GEN_OTHERNAME: | |
262 | add_alt_name_othername(ctx, cert, name->d.otherName); | |
263 | break; | |
264 | case GEN_DNS: | |
265 | add_alt_name_dns(ctx, cert, name->d.dNSName); | |
266 | break; | |
267 | } | |
268 | } | |
269 | ||
270 | ||
271 | static void add_alt_names(struct http_ctx *ctx, struct http_cert *cert, | |
272 | GENERAL_NAMES *names) | |
273 | { | |
274 | int num, i; | |
275 | ||
276 | num = sk_GENERAL_NAME_num(names); | |
277 | for (i = 0; i < num; i++) { | |
278 | const GENERAL_NAME *name; | |
279 | name = sk_GENERAL_NAME_value(names, i); | |
280 | add_alt_name(ctx, cert, name); | |
281 | } | |
282 | } | |
283 | ||
284 | ||
285 | /* RFC 3709 */ | |
286 | ||
287 | typedef struct { | |
288 | X509_ALGOR *hashAlg; | |
289 | ASN1_OCTET_STRING *hashValue; | |
290 | } HashAlgAndValue; | |
291 | ||
292 | typedef struct { | |
293 | STACK_OF(HashAlgAndValue) *refStructHash; | |
294 | STACK_OF(ASN1_IA5STRING) *refStructURI; | |
295 | } LogotypeReference; | |
296 | ||
297 | typedef struct { | |
298 | ASN1_IA5STRING *mediaType; | |
299 | STACK_OF(HashAlgAndValue) *logotypeHash; | |
300 | STACK_OF(ASN1_IA5STRING) *logotypeURI; | |
301 | } LogotypeDetails; | |
302 | ||
303 | typedef struct { | |
304 | int type; | |
305 | union { | |
306 | ASN1_INTEGER *numBits; | |
307 | ASN1_INTEGER *tableSize; | |
308 | } d; | |
309 | } LogotypeImageResolution; | |
310 | ||
311 | typedef struct { | |
312 | ASN1_INTEGER *type; /* LogotypeImageType ::= INTEGER */ | |
313 | ASN1_INTEGER *fileSize; | |
314 | ASN1_INTEGER *xSize; | |
315 | ASN1_INTEGER *ySize; | |
316 | LogotypeImageResolution *resolution; | |
317 | ASN1_IA5STRING *language; | |
318 | } LogotypeImageInfo; | |
319 | ||
320 | typedef struct { | |
321 | LogotypeDetails *imageDetails; | |
322 | LogotypeImageInfo *imageInfo; | |
323 | } LogotypeImage; | |
324 | ||
325 | typedef struct { | |
326 | ASN1_INTEGER *fileSize; | |
327 | ASN1_INTEGER *playTime; | |
328 | ASN1_INTEGER *channels; | |
329 | ASN1_INTEGER *sampleRate; | |
330 | ASN1_IA5STRING *language; | |
331 | } LogotypeAudioInfo; | |
332 | ||
333 | typedef struct { | |
334 | LogotypeDetails *audioDetails; | |
335 | LogotypeAudioInfo *audioInfo; | |
336 | } LogotypeAudio; | |
337 | ||
338 | typedef struct { | |
339 | STACK_OF(LogotypeImage) *image; | |
340 | STACK_OF(LogotypeAudio) *audio; | |
341 | } LogotypeData; | |
342 | ||
343 | typedef struct { | |
344 | int type; | |
345 | union { | |
346 | LogotypeData *direct; | |
347 | LogotypeReference *indirect; | |
348 | } d; | |
349 | } LogotypeInfo; | |
350 | ||
351 | typedef struct { | |
352 | ASN1_OBJECT *logotypeType; | |
353 | LogotypeInfo *info; | |
354 | } OtherLogotypeInfo; | |
355 | ||
356 | typedef struct { | |
357 | STACK_OF(LogotypeInfo) *communityLogos; | |
358 | LogotypeInfo *issuerLogo; | |
359 | LogotypeInfo *subjectLogo; | |
360 | STACK_OF(OtherLogotypeInfo) *otherLogos; | |
361 | } LogotypeExtn; | |
362 | ||
363 | ASN1_SEQUENCE(HashAlgAndValue) = { | |
364 | ASN1_SIMPLE(HashAlgAndValue, hashAlg, X509_ALGOR), | |
365 | ASN1_SIMPLE(HashAlgAndValue, hashValue, ASN1_OCTET_STRING) | |
366 | } ASN1_SEQUENCE_END(HashAlgAndValue); | |
367 | ||
368 | ASN1_SEQUENCE(LogotypeReference) = { | |
369 | ASN1_SEQUENCE_OF(LogotypeReference, refStructHash, HashAlgAndValue), | |
370 | ASN1_SEQUENCE_OF(LogotypeReference, refStructURI, ASN1_IA5STRING) | |
371 | } ASN1_SEQUENCE_END(LogotypeReference); | |
372 | ||
373 | ASN1_SEQUENCE(LogotypeDetails) = { | |
374 | ASN1_SIMPLE(LogotypeDetails, mediaType, ASN1_IA5STRING), | |
375 | ASN1_SEQUENCE_OF(LogotypeDetails, logotypeHash, HashAlgAndValue), | |
376 | ASN1_SEQUENCE_OF(LogotypeDetails, logotypeURI, ASN1_IA5STRING) | |
377 | } ASN1_SEQUENCE_END(LogotypeDetails); | |
378 | ||
379 | ASN1_CHOICE(LogotypeImageResolution) = { | |
380 | ASN1_IMP(LogotypeImageResolution, d.numBits, ASN1_INTEGER, 1), | |
381 | ASN1_IMP(LogotypeImageResolution, d.tableSize, ASN1_INTEGER, 2) | |
382 | } ASN1_CHOICE_END(LogotypeImageResolution); | |
383 | ||
384 | ASN1_SEQUENCE(LogotypeImageInfo) = { | |
385 | ASN1_IMP_OPT(LogotypeImageInfo, type, ASN1_INTEGER, 0), | |
386 | ASN1_SIMPLE(LogotypeImageInfo, fileSize, ASN1_INTEGER), | |
387 | ASN1_SIMPLE(LogotypeImageInfo, xSize, ASN1_INTEGER), | |
388 | ASN1_SIMPLE(LogotypeImageInfo, ySize, ASN1_INTEGER), | |
389 | ASN1_OPT(LogotypeImageInfo, resolution, LogotypeImageResolution), | |
390 | ASN1_IMP_OPT(LogotypeImageInfo, language, ASN1_IA5STRING, 4), | |
391 | } ASN1_SEQUENCE_END(LogotypeImageInfo); | |
392 | ||
393 | ASN1_SEQUENCE(LogotypeImage) = { | |
394 | ASN1_SIMPLE(LogotypeImage, imageDetails, LogotypeDetails), | |
395 | ASN1_OPT(LogotypeImage, imageInfo, LogotypeImageInfo) | |
396 | } ASN1_SEQUENCE_END(LogotypeImage); | |
397 | ||
398 | ASN1_SEQUENCE(LogotypeAudioInfo) = { | |
399 | ASN1_SIMPLE(LogotypeAudioInfo, fileSize, ASN1_INTEGER), | |
400 | ASN1_SIMPLE(LogotypeAudioInfo, playTime, ASN1_INTEGER), | |
401 | ASN1_SIMPLE(LogotypeAudioInfo, channels, ASN1_INTEGER), | |
402 | ASN1_IMP_OPT(LogotypeAudioInfo, sampleRate, ASN1_INTEGER, 3), | |
403 | ASN1_IMP_OPT(LogotypeAudioInfo, language, ASN1_IA5STRING, 4) | |
404 | } ASN1_SEQUENCE_END(LogotypeAudioInfo); | |
405 | ||
406 | ASN1_SEQUENCE(LogotypeAudio) = { | |
407 | ASN1_SIMPLE(LogotypeAudio, audioDetails, LogotypeDetails), | |
408 | ASN1_OPT(LogotypeAudio, audioInfo, LogotypeAudioInfo) | |
409 | } ASN1_SEQUENCE_END(LogotypeAudio); | |
410 | ||
411 | ASN1_SEQUENCE(LogotypeData) = { | |
412 | ASN1_SEQUENCE_OF_OPT(LogotypeData, image, LogotypeImage), | |
413 | ASN1_IMP_SEQUENCE_OF_OPT(LogotypeData, audio, LogotypeAudio, 1) | |
414 | } ASN1_SEQUENCE_END(LogotypeData); | |
415 | ||
416 | ASN1_CHOICE(LogotypeInfo) = { | |
417 | ASN1_IMP(LogotypeInfo, d.direct, LogotypeData, 0), | |
418 | ASN1_IMP(LogotypeInfo, d.indirect, LogotypeReference, 1) | |
419 | } ASN1_CHOICE_END(LogotypeInfo); | |
420 | ||
421 | ASN1_SEQUENCE(OtherLogotypeInfo) = { | |
422 | ASN1_SIMPLE(OtherLogotypeInfo, logotypeType, ASN1_OBJECT), | |
423 | ASN1_SIMPLE(OtherLogotypeInfo, info, LogotypeInfo) | |
424 | } ASN1_SEQUENCE_END(OtherLogotypeInfo); | |
425 | ||
426 | ASN1_SEQUENCE(LogotypeExtn) = { | |
427 | ASN1_EXP_SEQUENCE_OF_OPT(LogotypeExtn, communityLogos, LogotypeInfo, 0), | |
428 | ASN1_EXP_OPT(LogotypeExtn, issuerLogo, LogotypeInfo, 1), | |
429 | ASN1_EXP_OPT(LogotypeExtn, issuerLogo, LogotypeInfo, 2), | |
430 | ASN1_EXP_SEQUENCE_OF_OPT(LogotypeExtn, otherLogos, OtherLogotypeInfo, 3) | |
431 | } ASN1_SEQUENCE_END(LogotypeExtn); | |
432 | ||
433 | IMPLEMENT_ASN1_FUNCTIONS(LogotypeExtn); | |
434 | ||
cc299402 JM |
435 | #ifdef OPENSSL_IS_BORINGSSL |
436 | #define sk_LogotypeInfo_num(st) \ | |
437 | sk_num(CHECKED_CAST(_STACK *, STACK_OF(LogotypeInfo) *, (st))) | |
438 | #define sk_LogotypeInfo_value(st, i) (LogotypeInfo *) \ | |
439 | sk_value(CHECKED_CAST(_STACK *, const STACK_OF(LogotypeInfo) *, (st)), (i)) | |
440 | #define sk_LogotypeImage_num(st) \ | |
441 | sk_num(CHECKED_CAST(_STACK *, STACK_OF(LogotypeImage) *, (st))) | |
442 | #define sk_LogotypeImage_value(st, i) (LogotypeImage *) \ | |
443 | sk_value(CHECKED_CAST(_STACK *, const STACK_OF(LogotypeImage) *, (st)), (i)) | |
444 | #define sk_LogotypeAudio_num(st) \ | |
445 | sk_num(CHECKED_CAST(_STACK *, STACK_OF(LogotypeAudio) *, (st))) | |
446 | #define sk_LogotypeAudio_value(st, i) (LogotypeAudio *) \ | |
447 | sk_value(CHECK_CAST(_STACK *, const STACK_OF(LogotypeAudio) *, (st)), (i)) | |
448 | #define sk_HashAlgAndValue_num(st) \ | |
449 | sk_num(CHECKED_CAST(_STACK *, STACK_OF(HashAlgAndValue) *, (st))) | |
450 | #define sk_HashAlgAndValue_value(st, i) (HashAlgAndValue *) \ | |
451 | sk_value(CHECKED_CAST(_STACK *, const STACK_OF(HashAlgAndValue) *, (st)), (i)) | |
452 | #define sk_ASN1_IA5STRING_num(st) \ | |
453 | sk_num(CHECKED_CAST(_STACK *, STACK_OF(ASN1_IA5STRING) *, (st))) | |
454 | #define sk_ASN1_IA5STRING_value(st, i) (ASN1_IA5STRING *) \ | |
455 | sk_value(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_IA5STRING) *, (st)), (i)) | |
456 | #else /* OPENSSL_IS_BORINGSSL */ | |
37f487bb | 457 | #if OPENSSL_VERSION_NUMBER < 0x10100000L |
1cb3eda9 JM |
458 | #define sk_LogotypeInfo_num(st) SKM_sk_num(LogotypeInfo, (st)) |
459 | #define sk_LogotypeInfo_value(st, i) SKM_sk_value(LogotypeInfo, (st), (i)) | |
460 | #define sk_LogotypeImage_num(st) SKM_sk_num(LogotypeImage, (st)) | |
461 | #define sk_LogotypeImage_value(st, i) SKM_sk_value(LogotypeImage, (st), (i)) | |
39b420f7 JM |
462 | #define sk_LogotypeAudio_num(st) SKM_sk_num(LogotypeAudio, (st)) |
463 | #define sk_LogotypeAudio_value(st, i) SKM_sk_value(LogotypeAudio, (st), (i)) | |
1cb3eda9 JM |
464 | #define sk_HashAlgAndValue_num(st) SKM_sk_num(HashAlgAndValue, (st)) |
465 | #define sk_HashAlgAndValue_value(st, i) SKM_sk_value(HashAlgAndValue, (st), (i)) | |
466 | #define sk_ASN1_IA5STRING_num(st) SKM_sk_num(ASN1_IA5STRING, (st)) | |
467 | #define sk_ASN1_IA5STRING_value(st, i) SKM_sk_value(ASN1_IA5STRING, (st), (i)) | |
37f487bb BG |
468 | #else |
469 | DEFINE_STACK_OF(LogotypeInfo) | |
470 | DEFINE_STACK_OF(LogotypeImage) | |
471 | DEFINE_STACK_OF(LogotypeAudio) | |
472 | DEFINE_STACK_OF(HashAlgAndValue) | |
473 | DEFINE_STACK_OF(ASN1_IA5STRING) | |
474 | #endif | |
cc299402 | 475 | #endif /* OPENSSL_IS_BORINGSSL */ |
1cb3eda9 JM |
476 | |
477 | ||
478 | static void add_logo(struct http_ctx *ctx, struct http_cert *hcert, | |
479 | HashAlgAndValue *hash, ASN1_IA5STRING *uri) | |
480 | { | |
481 | char txt[100]; | |
482 | int res, len; | |
483 | struct http_logo *n; | |
484 | ||
485 | if (hash == NULL || uri == NULL) | |
486 | return; | |
487 | ||
488 | res = OBJ_obj2txt(txt, sizeof(txt), hash->hashAlg->algorithm, 1); | |
489 | if (res < 0 || res >= (int) sizeof(txt)) | |
490 | return; | |
491 | ||
492 | n = os_realloc_array(hcert->logo, hcert->num_logo + 1, | |
493 | sizeof(struct http_logo)); | |
494 | if (n == NULL) | |
495 | return; | |
496 | hcert->logo = n; | |
497 | n = &hcert->logo[hcert->num_logo]; | |
498 | os_memset(n, 0, sizeof(*n)); | |
499 | ||
500 | n->alg_oid = os_strdup(txt); | |
501 | if (n->alg_oid == NULL) | |
502 | return; | |
503 | ||
504 | n->hash_len = ASN1_STRING_length(hash->hashValue); | |
4b970ee5 JM |
505 | n->hash = os_memdup(ASN1_STRING_get0_data(hash->hashValue), |
506 | n->hash_len); | |
1cb3eda9 JM |
507 | if (n->hash == NULL) { |
508 | os_free(n->alg_oid); | |
509 | return; | |
510 | } | |
1cb3eda9 JM |
511 | |
512 | len = ASN1_STRING_length(uri); | |
513 | n->uri = os_malloc(len + 1); | |
514 | if (n->uri == NULL) { | |
515 | os_free(n->alg_oid); | |
516 | os_free(n->hash); | |
517 | return; | |
518 | } | |
4b970ee5 | 519 | os_memcpy(n->uri, ASN1_STRING_get0_data(uri), len); |
1cb3eda9 JM |
520 | n->uri[len] = '\0'; |
521 | ||
522 | hcert->num_logo++; | |
523 | } | |
524 | ||
525 | ||
526 | static void add_logo_direct(struct http_ctx *ctx, struct http_cert *hcert, | |
527 | LogotypeData *data) | |
528 | { | |
529 | int i, num; | |
530 | ||
531 | if (data->image == NULL) | |
532 | return; | |
533 | ||
534 | num = sk_LogotypeImage_num(data->image); | |
535 | for (i = 0; i < num; i++) { | |
536 | LogotypeImage *image; | |
537 | LogotypeDetails *details; | |
538 | int j, hash_num, uri_num; | |
539 | HashAlgAndValue *found_hash = NULL; | |
540 | ||
541 | image = sk_LogotypeImage_value(data->image, i); | |
542 | if (image == NULL) | |
543 | continue; | |
544 | ||
545 | details = image->imageDetails; | |
546 | if (details == NULL) | |
547 | continue; | |
548 | ||
549 | hash_num = sk_HashAlgAndValue_num(details->logotypeHash); | |
550 | for (j = 0; j < hash_num; j++) { | |
551 | HashAlgAndValue *hash; | |
552 | char txt[100]; | |
553 | int res; | |
554 | hash = sk_HashAlgAndValue_value(details->logotypeHash, | |
555 | j); | |
556 | if (hash == NULL) | |
557 | continue; | |
558 | res = OBJ_obj2txt(txt, sizeof(txt), | |
559 | hash->hashAlg->algorithm, 1); | |
560 | if (res < 0 || res >= (int) sizeof(txt)) | |
561 | continue; | |
562 | if (os_strcmp(txt, "2.16.840.1.101.3.4.2.1") == 0) { | |
563 | found_hash = hash; | |
564 | break; | |
565 | } | |
566 | } | |
567 | ||
568 | if (!found_hash) { | |
569 | wpa_printf(MSG_DEBUG, "OpenSSL: No SHA256 hash found for the logo"); | |
570 | continue; | |
571 | } | |
572 | ||
573 | uri_num = sk_ASN1_IA5STRING_num(details->logotypeURI); | |
574 | for (j = 0; j < uri_num; j++) { | |
575 | ASN1_IA5STRING *uri; | |
576 | uri = sk_ASN1_IA5STRING_value(details->logotypeURI, j); | |
577 | add_logo(ctx, hcert, found_hash, uri); | |
578 | } | |
579 | } | |
580 | } | |
581 | ||
582 | ||
583 | static void add_logo_indirect(struct http_ctx *ctx, struct http_cert *hcert, | |
584 | LogotypeReference *ref) | |
585 | { | |
586 | int j, hash_num, uri_num; | |
587 | ||
588 | hash_num = sk_HashAlgAndValue_num(ref->refStructHash); | |
589 | uri_num = sk_ASN1_IA5STRING_num(ref->refStructURI); | |
590 | if (hash_num != uri_num) { | |
591 | wpa_printf(MSG_INFO, "Unexpected LogotypeReference array size difference %d != %d", | |
592 | hash_num, uri_num); | |
593 | return; | |
594 | } | |
595 | ||
596 | for (j = 0; j < hash_num; j++) { | |
597 | HashAlgAndValue *hash; | |
598 | ASN1_IA5STRING *uri; | |
599 | hash = sk_HashAlgAndValue_value(ref->refStructHash, j); | |
600 | uri = sk_ASN1_IA5STRING_value(ref->refStructURI, j); | |
601 | add_logo(ctx, hcert, hash, uri); | |
602 | } | |
603 | } | |
604 | ||
605 | ||
39b420f7 JM |
606 | static void i2r_HashAlgAndValue(HashAlgAndValue *hash, BIO *out, int indent) |
607 | { | |
608 | int i; | |
609 | const unsigned char *data; | |
610 | ||
611 | BIO_printf(out, "%*shashAlg: ", indent, ""); | |
612 | i2a_ASN1_OBJECT(out, hash->hashAlg->algorithm); | |
613 | BIO_printf(out, "\n"); | |
614 | ||
615 | BIO_printf(out, "%*shashValue: ", indent, ""); | |
616 | data = hash->hashValue->data; | |
617 | for (i = 0; i < hash->hashValue->length; i++) | |
618 | BIO_printf(out, "%s%02x", i > 0 ? ":" : "", data[i]); | |
619 | BIO_printf(out, "\n"); | |
620 | } | |
621 | ||
622 | static void i2r_LogotypeDetails(LogotypeDetails *details, BIO *out, int indent) | |
623 | { | |
624 | int i, num; | |
625 | ||
626 | BIO_printf(out, "%*sLogotypeDetails\n", indent, ""); | |
627 | if (details->mediaType) { | |
628 | BIO_printf(out, "%*smediaType: ", indent, ""); | |
629 | ASN1_STRING_print(out, details->mediaType); | |
630 | BIO_printf(out, "\n"); | |
631 | } | |
632 | ||
633 | num = details->logotypeHash ? | |
634 | sk_HashAlgAndValue_num(details->logotypeHash) : 0; | |
635 | for (i = 0; i < num; i++) { | |
636 | HashAlgAndValue *hash; | |
637 | hash = sk_HashAlgAndValue_value(details->logotypeHash, i); | |
638 | i2r_HashAlgAndValue(hash, out, indent); | |
639 | } | |
640 | ||
641 | num = details->logotypeURI ? | |
642 | sk_ASN1_IA5STRING_num(details->logotypeURI) : 0; | |
643 | for (i = 0; i < num; i++) { | |
644 | ASN1_IA5STRING *uri; | |
645 | uri = sk_ASN1_IA5STRING_value(details->logotypeURI, i); | |
646 | BIO_printf(out, "%*slogotypeURI: ", indent, ""); | |
647 | ASN1_STRING_print(out, uri); | |
648 | BIO_printf(out, "\n"); | |
649 | } | |
650 | } | |
651 | ||
652 | static void i2r_LogotypeImageInfo(LogotypeImageInfo *info, BIO *out, int indent) | |
653 | { | |
654 | long val; | |
655 | ||
656 | BIO_printf(out, "%*sLogotypeImageInfo\n", indent, ""); | |
657 | if (info->type) { | |
658 | val = ASN1_INTEGER_get(info->type); | |
659 | BIO_printf(out, "%*stype: %ld\n", indent, "", val); | |
660 | } else { | |
661 | BIO_printf(out, "%*stype: default (1)\n", indent, ""); | |
662 | } | |
2088ecb9 JM |
663 | val = ASN1_INTEGER_get(info->fileSize); |
664 | BIO_printf(out, "%*sfileSize: %ld\n", indent, "", val); | |
39b420f7 JM |
665 | val = ASN1_INTEGER_get(info->xSize); |
666 | BIO_printf(out, "%*sxSize: %ld\n", indent, "", val); | |
667 | val = ASN1_INTEGER_get(info->ySize); | |
668 | BIO_printf(out, "%*sySize: %ld\n", indent, "", val); | |
669 | if (info->resolution) { | |
2088ecb9 JM |
670 | BIO_printf(out, "%*sresolution [%d]\n", indent, "", |
671 | info->resolution->type); | |
672 | switch (info->resolution->type) { | |
673 | case 0: | |
674 | val = ASN1_INTEGER_get(info->resolution->d.numBits); | |
675 | BIO_printf(out, "%*snumBits: %ld\n", indent, "", val); | |
676 | break; | |
677 | case 1: | |
678 | val = ASN1_INTEGER_get(info->resolution->d.tableSize); | |
679 | BIO_printf(out, "%*stableSize: %ld\n", indent, "", val); | |
680 | break; | |
681 | } | |
39b420f7 JM |
682 | } |
683 | if (info->language) { | |
684 | BIO_printf(out, "%*slanguage: ", indent, ""); | |
685 | ASN1_STRING_print(out, info->language); | |
686 | BIO_printf(out, "\n"); | |
687 | } | |
688 | } | |
689 | ||
690 | static void i2r_LogotypeImage(LogotypeImage *image, BIO *out, int indent) | |
691 | { | |
692 | BIO_printf(out, "%*sLogotypeImage\n", indent, ""); | |
693 | if (image->imageDetails) { | |
694 | i2r_LogotypeDetails(image->imageDetails, out, indent + 4); | |
695 | } | |
696 | if (image->imageInfo) { | |
697 | i2r_LogotypeImageInfo(image->imageInfo, out, indent + 4); | |
698 | } | |
699 | } | |
700 | ||
701 | static void i2r_LogotypeData(LogotypeData *data, const char *title, BIO *out, | |
702 | int indent) | |
703 | { | |
704 | int i, num; | |
705 | ||
706 | BIO_printf(out, "%*s%s - LogotypeData\n", indent, "", title); | |
707 | ||
708 | num = data->image ? sk_LogotypeImage_num(data->image) : 0; | |
709 | for (i = 0; i < num; i++) { | |
710 | LogotypeImage *image = sk_LogotypeImage_value(data->image, i); | |
711 | i2r_LogotypeImage(image, out, indent + 4); | |
712 | } | |
713 | ||
714 | num = data->audio ? sk_LogotypeAudio_num(data->audio) : 0; | |
715 | for (i = 0; i < num; i++) { | |
716 | BIO_printf(out, "%*saudio: TODO\n", indent, ""); | |
717 | } | |
718 | } | |
719 | ||
720 | static void i2r_LogotypeReference(LogotypeReference *ref, const char *title, | |
721 | BIO *out, int indent) | |
722 | { | |
723 | int i, hash_num, uri_num; | |
724 | ||
725 | BIO_printf(out, "%*s%s - LogotypeReference\n", indent, "", title); | |
726 | ||
727 | hash_num = ref->refStructHash ? | |
728 | sk_HashAlgAndValue_num(ref->refStructHash) : 0; | |
729 | uri_num = ref->refStructURI ? | |
730 | sk_ASN1_IA5STRING_num(ref->refStructURI) : 0; | |
731 | if (hash_num != uri_num) { | |
732 | BIO_printf(out, "%*sUnexpected LogotypeReference array size difference %d != %d\n", | |
733 | indent, "", hash_num, uri_num); | |
734 | return; | |
735 | } | |
736 | ||
737 | for (i = 0; i < hash_num; i++) { | |
738 | HashAlgAndValue *hash; | |
739 | ASN1_IA5STRING *uri; | |
740 | ||
741 | hash = sk_HashAlgAndValue_value(ref->refStructHash, i); | |
742 | i2r_HashAlgAndValue(hash, out, indent); | |
743 | ||
744 | uri = sk_ASN1_IA5STRING_value(ref->refStructURI, i); | |
745 | BIO_printf(out, "%*srefStructURI: ", indent, ""); | |
746 | ASN1_STRING_print(out, uri); | |
747 | BIO_printf(out, "\n"); | |
748 | } | |
749 | } | |
750 | ||
751 | static void i2r_LogotypeInfo(LogotypeInfo *info, const char *title, BIO *out, | |
752 | int indent) | |
753 | { | |
754 | switch (info->type) { | |
755 | case 0: | |
756 | i2r_LogotypeData(info->d.direct, title, out, indent); | |
757 | break; | |
758 | case 1: | |
759 | i2r_LogotypeReference(info->d.indirect, title, out, indent); | |
760 | break; | |
761 | } | |
762 | } | |
763 | ||
764 | static void debug_print_logotypeext(LogotypeExtn *logo) | |
765 | { | |
766 | BIO *out; | |
767 | int i, num; | |
768 | int indent = 0; | |
769 | ||
770 | out = BIO_new_fp(stdout, BIO_NOCLOSE); | |
771 | if (out == NULL) | |
772 | return; | |
773 | ||
774 | if (logo->communityLogos) { | |
775 | num = sk_LogotypeInfo_num(logo->communityLogos); | |
776 | for (i = 0; i < num; i++) { | |
777 | LogotypeInfo *info; | |
778 | info = sk_LogotypeInfo_value(logo->communityLogos, i); | |
779 | i2r_LogotypeInfo(info, "communityLogo", out, indent); | |
780 | } | |
781 | } | |
782 | ||
783 | if (logo->issuerLogo) { | |
784 | i2r_LogotypeInfo(logo->issuerLogo, "issuerLogo", out, indent ); | |
785 | } | |
786 | ||
787 | if (logo->subjectLogo) { | |
788 | i2r_LogotypeInfo(logo->subjectLogo, "subjectLogo", out, indent); | |
789 | } | |
790 | ||
791 | if (logo->otherLogos) { | |
792 | BIO_printf(out, "%*sotherLogos - TODO\n", indent, ""); | |
793 | } | |
794 | ||
795 | BIO_free(out); | |
796 | } | |
797 | ||
798 | ||
1cb3eda9 JM |
799 | static void add_logotype_ext(struct http_ctx *ctx, struct http_cert *hcert, |
800 | X509 *cert) | |
801 | { | |
802 | ASN1_OBJECT *obj; | |
803 | int pos; | |
804 | X509_EXTENSION *ext; | |
805 | ASN1_OCTET_STRING *os; | |
806 | LogotypeExtn *logo; | |
807 | const unsigned char *data; | |
808 | int i, num; | |
809 | ||
810 | obj = OBJ_txt2obj("1.3.6.1.5.5.7.1.12", 0); | |
811 | if (obj == NULL) | |
812 | return; | |
813 | ||
814 | pos = X509_get_ext_by_OBJ(cert, obj, -1); | |
815 | if (pos < 0) { | |
816 | wpa_printf(MSG_INFO, "No logotype extension included"); | |
817 | return; | |
818 | } | |
819 | ||
820 | wpa_printf(MSG_INFO, "Parsing logotype extension"); | |
821 | ext = X509_get_ext(cert, pos); | |
822 | if (!ext) { | |
823 | wpa_printf(MSG_INFO, "Could not get logotype extension"); | |
824 | return; | |
825 | } | |
826 | ||
827 | os = X509_EXTENSION_get_data(ext); | |
828 | if (os == NULL) { | |
829 | wpa_printf(MSG_INFO, "Could not get logotype extension data"); | |
830 | return; | |
831 | } | |
832 | ||
833 | wpa_hexdump(MSG_DEBUG, "logotypeExtn", | |
4b970ee5 | 834 | ASN1_STRING_get0_data(os), ASN1_STRING_length(os)); |
1cb3eda9 | 835 | |
4b970ee5 | 836 | data = ASN1_STRING_get0_data(os); |
1cb3eda9 JM |
837 | logo = d2i_LogotypeExtn(NULL, &data, ASN1_STRING_length(os)); |
838 | if (logo == NULL) { | |
839 | wpa_printf(MSG_INFO, "Failed to parse logotypeExtn"); | |
840 | return; | |
841 | } | |
842 | ||
39b420f7 JM |
843 | if (wpa_debug_level < MSG_INFO) |
844 | debug_print_logotypeext(logo); | |
845 | ||
1cb3eda9 JM |
846 | if (!logo->communityLogos) { |
847 | wpa_printf(MSG_INFO, "No communityLogos included"); | |
848 | LogotypeExtn_free(logo); | |
849 | return; | |
850 | } | |
851 | ||
852 | num = sk_LogotypeInfo_num(logo->communityLogos); | |
853 | for (i = 0; i < num; i++) { | |
854 | LogotypeInfo *info; | |
855 | info = sk_LogotypeInfo_value(logo->communityLogos, i); | |
856 | switch (info->type) { | |
857 | case 0: | |
858 | add_logo_direct(ctx, hcert, info->d.direct); | |
859 | break; | |
860 | case 1: | |
861 | add_logo_indirect(ctx, hcert, info->d.indirect); | |
862 | break; | |
863 | } | |
864 | } | |
865 | ||
866 | LogotypeExtn_free(logo); | |
867 | } | |
868 | ||
869 | ||
39b420f7 JM |
870 | static void parse_cert(struct http_ctx *ctx, struct http_cert *hcert, |
871 | X509 *cert, GENERAL_NAMES **names) | |
872 | { | |
873 | os_memset(hcert, 0, sizeof(*hcert)); | |
874 | ||
875 | *names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); | |
04c18fa0 | 876 | if (*names) |
39b420f7 JM |
877 | add_alt_names(ctx, hcert, *names); |
878 | ||
879 | add_logotype_ext(ctx, hcert, cert); | |
880 | } | |
881 | ||
882 | ||
883 | static void parse_cert_free(struct http_cert *hcert, GENERAL_NAMES *names) | |
884 | { | |
885 | unsigned int i; | |
886 | ||
887 | for (i = 0; i < hcert->num_dnsname; i++) | |
888 | OPENSSL_free(hcert->dnsname[i]); | |
889 | os_free(hcert->dnsname); | |
890 | ||
891 | for (i = 0; i < hcert->num_othername; i++) | |
892 | os_free(hcert->othername[i].oid); | |
893 | os_free(hcert->othername); | |
894 | ||
895 | for (i = 0; i < hcert->num_logo; i++) { | |
896 | os_free(hcert->logo[i].alg_oid); | |
897 | os_free(hcert->logo[i].hash); | |
898 | os_free(hcert->logo[i].uri); | |
899 | } | |
900 | os_free(hcert->logo); | |
901 | ||
902 | sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); | |
903 | } | |
904 | ||
905 | ||
1cb3eda9 JM |
906 | static int validate_server_cert(struct http_ctx *ctx, X509 *cert) |
907 | { | |
908 | GENERAL_NAMES *names; | |
909 | struct http_cert hcert; | |
1cb3eda9 JM |
910 | int ret; |
911 | ||
23dd15a9 BG |
912 | if (ctx->cert_cb == NULL) { |
913 | wpa_printf(MSG_DEBUG, "%s: no cert_cb configured", __func__); | |
1cb3eda9 | 914 | return 0; |
23dd15a9 | 915 | } |
1cb3eda9 | 916 | |
1cb3eda9 JM |
917 | if (0) { |
918 | BIO *out; | |
919 | out = BIO_new_fp(stdout, BIO_NOCLOSE); | |
920 | X509_print_ex(out, cert, XN_FLAG_COMPAT, X509_FLAG_COMPAT); | |
921 | BIO_free(out); | |
922 | } | |
923 | ||
39b420f7 JM |
924 | parse_cert(ctx, &hcert, cert, &names); |
925 | ret = ctx->cert_cb(ctx->cert_cb_ctx, &hcert); | |
926 | parse_cert_free(&hcert, names); | |
1cb3eda9 | 927 | |
39b420f7 JM |
928 | return ret; |
929 | } | |
1cb3eda9 | 930 | |
1cb3eda9 | 931 | |
39b420f7 JM |
932 | void http_parse_x509_certificate(struct http_ctx *ctx, const char *fname) |
933 | { | |
934 | BIO *in, *out; | |
935 | X509 *cert; | |
936 | GENERAL_NAMES *names; | |
937 | struct http_cert hcert; | |
938 | unsigned int i; | |
939 | ||
940 | in = BIO_new_file(fname, "r"); | |
941 | if (in == NULL) { | |
942 | wpa_printf(MSG_ERROR, "Could not read '%s'", fname); | |
943 | return; | |
944 | } | |
1cb3eda9 | 945 | |
39b420f7 JM |
946 | cert = d2i_X509_bio(in, NULL); |
947 | BIO_free(in); | |
1cb3eda9 | 948 | |
39b420f7 JM |
949 | if (cert == NULL) { |
950 | wpa_printf(MSG_ERROR, "Could not parse certificate"); | |
951 | return; | |
1cb3eda9 | 952 | } |
1cb3eda9 | 953 | |
39b420f7 JM |
954 | out = BIO_new_fp(stdout, BIO_NOCLOSE); |
955 | if (out) { | |
956 | X509_print_ex(out, cert, XN_FLAG_COMPAT, | |
957 | X509_FLAG_COMPAT); | |
958 | BIO_free(out); | |
959 | } | |
1cb3eda9 | 960 | |
39b420f7 JM |
961 | wpa_printf(MSG_INFO, "Additional parsing information:"); |
962 | parse_cert(ctx, &hcert, cert, &names); | |
963 | for (i = 0; i < hcert.num_othername; i++) { | |
964 | if (os_strcmp(hcert.othername[i].oid, | |
965 | "1.3.6.1.4.1.40808.1.1.1") == 0) { | |
966 | char *name = os_zalloc(hcert.othername[i].len + 1); | |
967 | if (name) { | |
968 | os_memcpy(name, hcert.othername[i].data, | |
969 | hcert.othername[i].len); | |
970 | wpa_printf(MSG_INFO, | |
971 | "id-wfa-hotspot-friendlyName: %s", | |
972 | name); | |
973 | os_free(name); | |
974 | } | |
975 | wpa_hexdump_ascii(MSG_INFO, | |
976 | "id-wfa-hotspot-friendlyName", | |
977 | hcert.othername[i].data, | |
978 | hcert.othername[i].len); | |
979 | } else { | |
980 | wpa_printf(MSG_INFO, "subjAltName[othername]: oid=%s", | |
981 | hcert.othername[i].oid); | |
982 | wpa_hexdump_ascii(MSG_INFO, "unknown othername", | |
983 | hcert.othername[i].data, | |
984 | hcert.othername[i].len); | |
985 | } | |
986 | } | |
987 | parse_cert_free(&hcert, names); | |
988 | ||
989 | X509_free(cert); | |
1cb3eda9 JM |
990 | } |
991 | ||
992 | ||
993 | static int curl_cb_ssl_verify(int preverify_ok, X509_STORE_CTX *x509_ctx) | |
994 | { | |
995 | struct http_ctx *ctx; | |
996 | X509 *cert; | |
997 | int err, depth; | |
998 | char buf[256]; | |
999 | X509_NAME *name; | |
1000 | const char *err_str; | |
1001 | SSL *ssl; | |
1002 | SSL_CTX *ssl_ctx; | |
1003 | ||
1004 | ssl = X509_STORE_CTX_get_ex_data(x509_ctx, | |
1005 | SSL_get_ex_data_X509_STORE_CTX_idx()); | |
a28675da | 1006 | ssl_ctx = SSL_get_SSL_CTX(ssl); |
1cb3eda9 JM |
1007 | ctx = SSL_CTX_get_app_data(ssl_ctx); |
1008 | ||
23dd15a9 BG |
1009 | wpa_printf(MSG_DEBUG, "curl_cb_ssl_verify, preverify_ok: %d", |
1010 | preverify_ok); | |
1cb3eda9 JM |
1011 | |
1012 | err = X509_STORE_CTX_get_error(x509_ctx); | |
1013 | err_str = X509_verify_cert_error_string(err); | |
1014 | depth = X509_STORE_CTX_get_error_depth(x509_ctx); | |
1015 | cert = X509_STORE_CTX_get_current_cert(x509_ctx); | |
1016 | if (!cert) { | |
1017 | wpa_printf(MSG_INFO, "No server certificate available"); | |
1018 | ctx->last_err = "No server certificate available"; | |
1019 | return 0; | |
1020 | } | |
1021 | ||
1022 | if (depth == 0) | |
1023 | ctx->peer_cert = cert; | |
1024 | else if (depth == 1) | |
1025 | ctx->peer_issuer = cert; | |
1026 | else if (depth == 2) | |
1027 | ctx->peer_issuer_issuer = cert; | |
1028 | ||
1029 | name = X509_get_subject_name(cert); | |
1030 | X509_NAME_oneline(name, buf, sizeof(buf)); | |
1031 | wpa_printf(MSG_INFO, "Server certificate chain - depth=%d err=%d (%s) subject=%s", | |
1032 | depth, err, err_str, buf); | |
1033 | debug_dump_cert("Server certificate chain - certificate", cert); | |
1034 | ||
1035 | if (depth == 0 && preverify_ok && validate_server_cert(ctx, cert) < 0) | |
1036 | return 0; | |
1037 | ||
8d27efa8 JM |
1038 | #ifdef OPENSSL_IS_BORINGSSL |
1039 | if (depth == 0 && ctx->ocsp != NO_OCSP && preverify_ok) { | |
1040 | enum ocsp_result res; | |
1041 | ||
1042 | res = check_ocsp_resp(ssl_ctx, ssl, cert, ctx->peer_issuer, | |
1043 | ctx->peer_issuer_issuer); | |
1044 | if (res == OCSP_REVOKED) { | |
1045 | preverify_ok = 0; | |
1046 | wpa_printf(MSG_INFO, "OCSP: certificate revoked"); | |
1047 | if (err == X509_V_OK) | |
1048 | X509_STORE_CTX_set_error( | |
1049 | x509_ctx, X509_V_ERR_CERT_REVOKED); | |
1050 | } else if (res != OCSP_GOOD && (ctx->ocsp == MANDATORY_OCSP)) { | |
1051 | preverify_ok = 0; | |
1052 | wpa_printf(MSG_INFO, | |
1053 | "OCSP: bad certificate status response"); | |
1054 | } | |
1055 | } | |
1056 | #endif /* OPENSSL_IS_BORINGSSL */ | |
1057 | ||
1cb3eda9 JM |
1058 | if (!preverify_ok) |
1059 | ctx->last_err = "TLS validation failed"; | |
1060 | ||
1061 | return preverify_ok; | |
1062 | } | |
1063 | ||
1064 | ||
1065 | #ifdef HAVE_OCSP | |
1066 | ||
1067 | static void ocsp_debug_print_resp(OCSP_RESPONSE *rsp) | |
1068 | { | |
1069 | BIO *out; | |
1070 | size_t rlen; | |
1071 | char *txt; | |
1072 | int res; | |
1073 | ||
1074 | out = BIO_new(BIO_s_mem()); | |
1075 | if (!out) | |
1076 | return; | |
1077 | ||
1078 | OCSP_RESPONSE_print(out, rsp, 0); | |
1079 | rlen = BIO_ctrl_pending(out); | |
1080 | txt = os_malloc(rlen + 1); | |
1081 | if (!txt) { | |
1082 | BIO_free(out); | |
1083 | return; | |
1084 | } | |
1085 | ||
1086 | res = BIO_read(out, txt, rlen); | |
1087 | if (res > 0) { | |
1088 | txt[res] = '\0'; | |
1089 | wpa_printf(MSG_MSGDUMP, "OpenSSL: OCSP Response\n%s", txt); | |
1090 | } | |
1091 | os_free(txt); | |
1092 | BIO_free(out); | |
1093 | } | |
1094 | ||
1095 | ||
1096 | static void tls_show_errors(const char *func, const char *txt) | |
1097 | { | |
1098 | unsigned long err; | |
1099 | ||
1100 | wpa_printf(MSG_DEBUG, "OpenSSL: %s - %s %s", | |
1101 | func, txt, ERR_error_string(ERR_get_error(), NULL)); | |
1102 | ||
1103 | while ((err = ERR_get_error())) { | |
1104 | wpa_printf(MSG_DEBUG, "OpenSSL: pending error: %s", | |
1105 | ERR_error_string(err, NULL)); | |
1106 | } | |
1107 | } | |
1108 | ||
1109 | ||
1110 | static int ocsp_resp_cb(SSL *s, void *arg) | |
1111 | { | |
1112 | struct http_ctx *ctx = arg; | |
1113 | const unsigned char *p; | |
d264c2e3 | 1114 | int len, status, reason, res; |
1cb3eda9 JM |
1115 | OCSP_RESPONSE *rsp; |
1116 | OCSP_BASICRESP *basic; | |
1117 | OCSP_CERTID *id; | |
1118 | ASN1_GENERALIZEDTIME *produced_at, *this_update, *next_update; | |
1119 | X509_STORE *store; | |
1120 | STACK_OF(X509) *certs = NULL; | |
1121 | ||
1122 | len = SSL_get_tlsext_status_ocsp_resp(s, &p); | |
1123 | if (!p) { | |
1124 | wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received"); | |
1125 | if (ctx->ocsp == MANDATORY_OCSP) | |
1126 | ctx->last_err = "No OCSP response received"; | |
1127 | return (ctx->ocsp == MANDATORY_OCSP) ? 0 : 1; | |
1128 | } | |
1129 | ||
1130 | wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", p, len); | |
1131 | ||
1132 | rsp = d2i_OCSP_RESPONSE(NULL, &p, len); | |
1133 | if (!rsp) { | |
1134 | wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSP response"); | |
1135 | ctx->last_err = "Failed to parse OCSP response"; | |
1136 | return 0; | |
1137 | } | |
1138 | ||
1139 | ocsp_debug_print_resp(rsp); | |
1140 | ||
1141 | status = OCSP_response_status(rsp); | |
1142 | if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { | |
1143 | wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d (%s)", | |
1144 | status, OCSP_response_status_str(status)); | |
1145 | ctx->last_err = "OCSP responder error"; | |
1146 | return 0; | |
1147 | } | |
1148 | ||
1149 | basic = OCSP_response_get1_basic(rsp); | |
1150 | if (!basic) { | |
1151 | wpa_printf(MSG_INFO, "OpenSSL: Could not find BasicOCSPResponse"); | |
1152 | ctx->last_err = "Could not find BasicOCSPResponse"; | |
1153 | return 0; | |
1154 | } | |
1155 | ||
9805bb1b | 1156 | store = SSL_CTX_get_cert_store(SSL_get_SSL_CTX(s)); |
1cb3eda9 JM |
1157 | if (ctx->peer_issuer) { |
1158 | wpa_printf(MSG_DEBUG, "OpenSSL: Add issuer"); | |
1159 | debug_dump_cert("OpenSSL: Issuer certificate", | |
1160 | ctx->peer_issuer); | |
1161 | ||
1162 | if (X509_STORE_add_cert(store, ctx->peer_issuer) != 1) { | |
1163 | tls_show_errors(__func__, | |
58d405fc | 1164 | "OpenSSL: Could not add issuer to certificate store"); |
1cb3eda9 JM |
1165 | } |
1166 | certs = sk_X509_new_null(); | |
1167 | if (certs) { | |
1168 | X509 *cert; | |
1169 | cert = X509_dup(ctx->peer_issuer); | |
1170 | if (cert && !sk_X509_push(certs, cert)) { | |
1171 | tls_show_errors( | |
1172 | __func__, | |
58d405fc | 1173 | "OpenSSL: Could not add issuer to OCSP responder trust store"); |
1cb3eda9 JM |
1174 | X509_free(cert); |
1175 | sk_X509_free(certs); | |
1176 | certs = NULL; | |
1177 | } | |
bd7bb437 | 1178 | if (certs && ctx->peer_issuer_issuer) { |
1cb3eda9 JM |
1179 | cert = X509_dup(ctx->peer_issuer_issuer); |
1180 | if (cert && !sk_X509_push(certs, cert)) { | |
1181 | tls_show_errors( | |
1182 | __func__, | |
58d405fc | 1183 | "OpenSSL: Could not add issuer's issuer to OCSP responder trust store"); |
1cb3eda9 JM |
1184 | X509_free(cert); |
1185 | } | |
1186 | } | |
1187 | } | |
1188 | } | |
1189 | ||
1190 | status = OCSP_basic_verify(basic, certs, store, OCSP_TRUSTOTHER); | |
1191 | sk_X509_pop_free(certs, X509_free); | |
1192 | if (status <= 0) { | |
1193 | tls_show_errors(__func__, | |
1194 | "OpenSSL: OCSP response failed verification"); | |
1195 | OCSP_BASICRESP_free(basic); | |
1196 | OCSP_RESPONSE_free(rsp); | |
1197 | ctx->last_err = "OCSP response failed verification"; | |
1198 | return 0; | |
1199 | } | |
1200 | ||
1201 | wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response verification succeeded"); | |
1202 | ||
1203 | if (!ctx->peer_cert) { | |
1204 | wpa_printf(MSG_DEBUG, "OpenSSL: Peer certificate not available for OCSP status check"); | |
1205 | OCSP_BASICRESP_free(basic); | |
1206 | OCSP_RESPONSE_free(rsp); | |
1207 | ctx->last_err = "Peer certificate not available for OCSP status check"; | |
1208 | return 0; | |
1209 | } | |
1210 | ||
1211 | if (!ctx->peer_issuer) { | |
1212 | wpa_printf(MSG_DEBUG, "OpenSSL: Peer issuer certificate not available for OCSP status check"); | |
1213 | OCSP_BASICRESP_free(basic); | |
1214 | OCSP_RESPONSE_free(rsp); | |
1215 | ctx->last_err = "Peer issuer certificate not available for OCSP status check"; | |
1216 | return 0; | |
1217 | } | |
1218 | ||
d264c2e3 | 1219 | id = OCSP_cert_to_id(EVP_sha256(), ctx->peer_cert, ctx->peer_issuer); |
1cb3eda9 | 1220 | if (!id) { |
d264c2e3 JM |
1221 | wpa_printf(MSG_DEBUG, |
1222 | "OpenSSL: Could not create OCSP certificate identifier (SHA256)"); | |
1cb3eda9 JM |
1223 | OCSP_BASICRESP_free(basic); |
1224 | OCSP_RESPONSE_free(rsp); | |
1225 | ctx->last_err = "Could not create OCSP certificate identifier"; | |
1226 | return 0; | |
1227 | } | |
1228 | ||
d264c2e3 JM |
1229 | res = OCSP_resp_find_status(basic, id, &status, &reason, &produced_at, |
1230 | &this_update, &next_update); | |
1231 | if (!res) { | |
1232 | id = OCSP_cert_to_id(NULL, ctx->peer_cert, ctx->peer_issuer); | |
1233 | if (!id) { | |
1234 | wpa_printf(MSG_DEBUG, | |
1235 | "OpenSSL: Could not create OCSP certificate identifier (SHA1)"); | |
1236 | OCSP_BASICRESP_free(basic); | |
1237 | OCSP_RESPONSE_free(rsp); | |
1238 | ctx->last_err = | |
1239 | "Could not create OCSP certificate identifier"; | |
1240 | return 0; | |
1241 | } | |
1242 | ||
1243 | res = OCSP_resp_find_status(basic, id, &status, &reason, | |
1244 | &produced_at, &this_update, | |
1245 | &next_update); | |
1246 | } | |
1247 | ||
1248 | if (!res) { | |
1cb3eda9 JM |
1249 | wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s", |
1250 | (ctx->ocsp == MANDATORY_OCSP) ? "" : | |
1251 | " (OCSP not required)"); | |
d9a0f697 | 1252 | OCSP_CERTID_free(id); |
1cb3eda9 JM |
1253 | OCSP_BASICRESP_free(basic); |
1254 | OCSP_RESPONSE_free(rsp); | |
1255 | if (ctx->ocsp == MANDATORY_OCSP) | |
1256 | ||
1257 | ctx->last_err = "Could not find current server certificate from OCSP response"; | |
1258 | return (ctx->ocsp == MANDATORY_OCSP) ? 0 : 1; | |
1259 | } | |
d9a0f697 | 1260 | OCSP_CERTID_free(id); |
1cb3eda9 JM |
1261 | |
1262 | if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) { | |
1263 | tls_show_errors(__func__, "OpenSSL: OCSP status times invalid"); | |
1264 | OCSP_BASICRESP_free(basic); | |
1265 | OCSP_RESPONSE_free(rsp); | |
1266 | ctx->last_err = "OCSP status times invalid"; | |
1267 | return 0; | |
1268 | } | |
1269 | ||
1270 | OCSP_BASICRESP_free(basic); | |
1271 | OCSP_RESPONSE_free(rsp); | |
1272 | ||
1273 | wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status for server certificate: %s", | |
1274 | OCSP_cert_status_str(status)); | |
1275 | ||
1276 | if (status == V_OCSP_CERTSTATUS_GOOD) | |
1277 | return 1; | |
9c196f77 | 1278 | if (status == V_OCSP_CERTSTATUS_REVOKED) { |
1cb3eda9 JM |
1279 | ctx->last_err = "Server certificate has been revoked"; |
1280 | return 0; | |
9c196f77 | 1281 | } |
1cb3eda9 JM |
1282 | if (ctx->ocsp == MANDATORY_OCSP) { |
1283 | wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP required"); | |
1284 | ctx->last_err = "OCSP status unknown"; | |
1285 | return 0; | |
1286 | } | |
1287 | wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue"); | |
1288 | return 1; | |
1289 | } | |
1290 | ||
1291 | ||
22d1b2ec | 1292 | #if OPENSSL_VERSION_NUMBER < 0x10100000L |
1cb3eda9 JM |
1293 | static SSL_METHOD patch_ssl_method; |
1294 | static const SSL_METHOD *real_ssl_method; | |
1295 | ||
1296 | static int curl_patch_ssl_new(SSL *s) | |
1297 | { | |
9805bb1b | 1298 | SSL_CTX *ssl = SSL_get_SSL_CTX(s); |
1cb3eda9 JM |
1299 | int ret; |
1300 | ||
1301 | ssl->method = real_ssl_method; | |
1302 | s->method = real_ssl_method; | |
1303 | ||
1304 | ret = s->method->ssl_new(s); | |
1305 | SSL_set_tlsext_status_type(s, TLSEXT_STATUSTYPE_ocsp); | |
1306 | ||
1307 | return ret; | |
1308 | } | |
22d1b2ec | 1309 | #endif /* OpenSSL < 1.1.0 */ |
1cb3eda9 JM |
1310 | |
1311 | #endif /* HAVE_OCSP */ | |
1312 | ||
1313 | ||
1314 | static CURLcode curl_cb_ssl(CURL *curl, void *sslctx, void *parm) | |
1315 | { | |
1316 | struct http_ctx *ctx = parm; | |
1317 | SSL_CTX *ssl = sslctx; | |
1318 | ||
1319 | wpa_printf(MSG_DEBUG, "curl_cb_ssl"); | |
1320 | SSL_CTX_set_app_data(ssl, ctx); | |
1321 | SSL_CTX_set_verify(ssl, SSL_VERIFY_PEER, curl_cb_ssl_verify); | |
1322 | ||
1323 | #ifdef HAVE_OCSP | |
1324 | if (ctx->ocsp != NO_OCSP) { | |
1325 | SSL_CTX_set_tlsext_status_cb(ssl, ocsp_resp_cb); | |
1326 | SSL_CTX_set_tlsext_status_arg(ssl, ctx); | |
1327 | ||
22d1b2ec | 1328 | #if OPENSSL_VERSION_NUMBER < 0x10100000L |
1cb3eda9 JM |
1329 | /* |
1330 | * Use a temporary SSL_METHOD to get a callback on SSL_new() | |
1331 | * from libcurl since there is no proper callback registration | |
1332 | * available for this. | |
1333 | */ | |
1334 | os_memset(&patch_ssl_method, 0, sizeof(patch_ssl_method)); | |
1335 | patch_ssl_method.ssl_new = curl_patch_ssl_new; | |
1336 | real_ssl_method = ssl->method; | |
1337 | ssl->method = &patch_ssl_method; | |
22d1b2ec | 1338 | #endif /* OpenSSL < 1.1.0 */ |
1cb3eda9 JM |
1339 | } |
1340 | #endif /* HAVE_OCSP */ | |
1341 | ||
1342 | return CURLE_OK; | |
1343 | } | |
1344 | ||
1345 | #endif /* EAP_TLS_OPENSSL */ | |
1346 | ||
1347 | ||
1348 | static CURL * setup_curl_post(struct http_ctx *ctx, const char *address, | |
1349 | const char *ca_fname, const char *username, | |
1350 | const char *password, const char *client_cert, | |
1351 | const char *client_key) | |
1352 | { | |
1353 | CURL *curl; | |
23dd15a9 BG |
1354 | #ifdef EAP_TLS_OPENSSL |
1355 | const char *extra = " tls=openssl"; | |
1356 | #else /* EAP_TLS_OPENSSL */ | |
1357 | const char *extra = ""; | |
1358 | #endif /* EAP_TLS_OPENSSL */ | |
1cb3eda9 JM |
1359 | |
1360 | wpa_printf(MSG_DEBUG, "Start HTTP client: address=%s ca_fname=%s " | |
23dd15a9 | 1361 | "username=%s%s", address, ca_fname, username, extra); |
1cb3eda9 JM |
1362 | |
1363 | curl = curl_easy_init(); | |
1364 | if (curl == NULL) | |
1365 | return NULL; | |
1366 | ||
1367 | curl_easy_setopt(curl, CURLOPT_URL, address); | |
1368 | curl_easy_setopt(curl, CURLOPT_POST, 1L); | |
1369 | if (ca_fname) { | |
1370 | curl_easy_setopt(curl, CURLOPT_CAINFO, ca_fname); | |
1371 | curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); | |
1372 | #ifdef EAP_TLS_OPENSSL | |
1373 | curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, curl_cb_ssl); | |
1374 | curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, ctx); | |
22d1b2ec | 1375 | #if defined(OPENSSL_IS_BORINGSSL) || (OPENSSL_VERSION_NUMBER >= 0x10100000L) |
8d27efa8 JM |
1376 | /* For now, using the CURLOPT_SSL_VERIFYSTATUS option only |
1377 | * with BoringSSL since the OpenSSL specific callback hack to | |
1378 | * enable OCSP is not available with BoringSSL. The OCSP | |
1379 | * implementation within libcurl is not sufficient for the | |
1380 | * Hotspot 2.0 OSU needs, so cannot use this with OpenSSL. | |
1381 | */ | |
1382 | if (ctx->ocsp != NO_OCSP) | |
1383 | curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1L); | |
1384 | #endif /* OPENSSL_IS_BORINGSSL */ | |
1cb3eda9 JM |
1385 | #endif /* EAP_TLS_OPENSSL */ |
1386 | } else { | |
1387 | curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); | |
1388 | } | |
1389 | if (client_cert && client_key) { | |
1390 | curl_easy_setopt(curl, CURLOPT_SSLCERT, client_cert); | |
1391 | curl_easy_setopt(curl, CURLOPT_SSLKEY, client_key); | |
1392 | } | |
1393 | /* TODO: use curl_easy_getinfo() with CURLINFO_CERTINFO to fetch | |
1394 | * information about the server certificate */ | |
1395 | curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L); | |
1396 | curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, curl_cb_debug); | |
1397 | curl_easy_setopt(curl, CURLOPT_DEBUGDATA, ctx); | |
1cb3eda9 JM |
1398 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_cb_write); |
1399 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctx); | |
1400 | curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); | |
1401 | if (username) { | |
1402 | curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE); | |
1403 | curl_easy_setopt(curl, CURLOPT_USERNAME, username); | |
1404 | curl_easy_setopt(curl, CURLOPT_PASSWORD, password); | |
1405 | } | |
1406 | ||
1407 | return curl; | |
1408 | } | |
1409 | ||
1410 | ||
1411 | static int post_init_client(struct http_ctx *ctx, const char *address, | |
1412 | const char *ca_fname, const char *username, | |
1413 | const char *password, const char *client_cert, | |
1414 | const char *client_key) | |
1415 | { | |
1416 | char *pos; | |
1417 | int count; | |
1418 | ||
1419 | clone_str(&ctx->svc_address, address); | |
1420 | clone_str(&ctx->svc_ca_fname, ca_fname); | |
1421 | clone_str(&ctx->svc_username, username); | |
1422 | clone_str(&ctx->svc_password, password); | |
1423 | clone_str(&ctx->svc_client_cert, client_cert); | |
1424 | clone_str(&ctx->svc_client_key, client_key); | |
1425 | ||
1426 | /* | |
1427 | * Workaround for Apache "Hostname 'FOO' provided via SNI and hostname | |
1428 | * 'foo' provided via HTTP are different. | |
1429 | */ | |
1430 | for (count = 0, pos = ctx->svc_address; count < 3 && pos && *pos; | |
1431 | pos++) { | |
1432 | if (*pos == '/') | |
1433 | count++; | |
1434 | *pos = tolower(*pos); | |
1435 | } | |
1436 | ||
1437 | ctx->curl = setup_curl_post(ctx, ctx->svc_address, ca_fname, username, | |
1438 | password, client_cert, client_key); | |
1439 | if (ctx->curl == NULL) | |
1440 | return -1; | |
1441 | ||
1442 | return 0; | |
1443 | } | |
1444 | ||
1445 | ||
1446 | int soap_init_client(struct http_ctx *ctx, const char *address, | |
1447 | const char *ca_fname, const char *username, | |
1448 | const char *password, const char *client_cert, | |
1449 | const char *client_key) | |
1450 | { | |
1451 | if (post_init_client(ctx, address, ca_fname, username, password, | |
1452 | client_cert, client_key) < 0) | |
1453 | return -1; | |
1454 | ||
1455 | ctx->curl_hdr = curl_slist_append(ctx->curl_hdr, | |
1456 | "Content-Type: application/soap+xml"); | |
1457 | ctx->curl_hdr = curl_slist_append(ctx->curl_hdr, "SOAPAction: "); | |
1458 | ctx->curl_hdr = curl_slist_append(ctx->curl_hdr, "Expect:"); | |
1459 | curl_easy_setopt(ctx->curl, CURLOPT_HTTPHEADER, ctx->curl_hdr); | |
1460 | ||
1461 | return 0; | |
1462 | } | |
1463 | ||
1464 | ||
1465 | int soap_reinit_client(struct http_ctx *ctx) | |
1466 | { | |
1467 | char *address = NULL; | |
1468 | char *ca_fname = NULL; | |
1469 | char *username = NULL; | |
1470 | char *password = NULL; | |
1471 | char *client_cert = NULL; | |
1472 | char *client_key = NULL; | |
1473 | int ret; | |
1474 | ||
1475 | clear_curl(ctx); | |
1476 | ||
1477 | clone_str(&address, ctx->svc_address); | |
1478 | clone_str(&ca_fname, ctx->svc_ca_fname); | |
1479 | clone_str(&username, ctx->svc_username); | |
1480 | clone_str(&password, ctx->svc_password); | |
1481 | clone_str(&client_cert, ctx->svc_client_cert); | |
1482 | clone_str(&client_key, ctx->svc_client_key); | |
1483 | ||
1484 | ret = soap_init_client(ctx, address, ca_fname, username, password, | |
1485 | client_cert, client_key); | |
1486 | os_free(address); | |
1487 | os_free(ca_fname); | |
d1ecca6c JM |
1488 | str_clear_free(username); |
1489 | str_clear_free(password); | |
1cb3eda9 JM |
1490 | os_free(client_cert); |
1491 | os_free(client_key); | |
1492 | return ret; | |
1493 | } | |
1494 | ||
1495 | ||
1496 | static void free_curl_buf(struct http_ctx *ctx) | |
1497 | { | |
1498 | os_free(ctx->curl_buf); | |
1499 | ctx->curl_buf = NULL; | |
1500 | ctx->curl_buf_len = 0; | |
1501 | } | |
1502 | ||
1503 | ||
1504 | xml_node_t * soap_send_receive(struct http_ctx *ctx, xml_node_t *node) | |
1505 | { | |
1506 | char *str; | |
1507 | xml_node_t *envelope, *ret, *resp, *n; | |
1508 | CURLcode res; | |
1509 | long http = 0; | |
1510 | ||
1511 | ctx->last_err = NULL; | |
1512 | ||
1513 | wpa_printf(MSG_DEBUG, "SOAP: Sending message"); | |
1514 | envelope = soap_build_envelope(ctx->xml, node); | |
1515 | str = xml_node_to_str(ctx->xml, envelope); | |
1516 | xml_node_free(ctx->xml, envelope); | |
1517 | wpa_printf(MSG_MSGDUMP, "SOAP[%s]", str); | |
1518 | ||
1519 | curl_easy_setopt(ctx->curl, CURLOPT_POSTFIELDS, str); | |
1520 | free_curl_buf(ctx); | |
1521 | ||
1522 | res = curl_easy_perform(ctx->curl); | |
1523 | if (res != CURLE_OK) { | |
1524 | if (!ctx->last_err) | |
1525 | ctx->last_err = curl_easy_strerror(res); | |
1526 | wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s", | |
1527 | ctx->last_err); | |
1528 | os_free(str); | |
1529 | free_curl_buf(ctx); | |
1530 | return NULL; | |
1531 | } | |
1532 | os_free(str); | |
1533 | ||
1534 | curl_easy_getinfo(ctx->curl, CURLINFO_RESPONSE_CODE, &http); | |
1535 | wpa_printf(MSG_DEBUG, "SOAP: Server response code %ld", http); | |
1536 | if (http != 200) { | |
1537 | ctx->last_err = "HTTP download failed"; | |
1538 | wpa_printf(MSG_INFO, "HTTP download failed - code %ld", http); | |
1539 | free_curl_buf(ctx); | |
1540 | return NULL; | |
1541 | } | |
1542 | ||
1543 | if (ctx->curl_buf == NULL) | |
1544 | return NULL; | |
1545 | ||
1546 | wpa_printf(MSG_MSGDUMP, "Server response:\n%s", ctx->curl_buf); | |
1547 | resp = xml_node_from_buf(ctx->xml, ctx->curl_buf); | |
1548 | free_curl_buf(ctx); | |
1549 | if (resp == NULL) { | |
1550 | wpa_printf(MSG_INFO, "Could not parse SOAP response"); | |
1551 | ctx->last_err = "Could not parse SOAP response"; | |
1552 | return NULL; | |
1553 | } | |
1554 | ||
1555 | ret = soap_get_body(ctx->xml, resp); | |
1556 | if (ret == NULL) { | |
1557 | wpa_printf(MSG_INFO, "Could not get SOAP body"); | |
1558 | ctx->last_err = "Could not get SOAP body"; | |
1559 | return NULL; | |
1560 | } | |
1561 | ||
1562 | wpa_printf(MSG_DEBUG, "SOAP body localname: '%s'", | |
1563 | xml_node_get_localname(ctx->xml, ret)); | |
1564 | n = xml_node_copy(ctx->xml, ret); | |
1565 | xml_node_free(ctx->xml, resp); | |
1566 | ||
1567 | return n; | |
1568 | } | |
1569 | ||
1570 | ||
1571 | struct http_ctx * http_init_ctx(void *upper_ctx, struct xml_node_ctx *xml_ctx) | |
1572 | { | |
1573 | struct http_ctx *ctx; | |
1574 | ||
1575 | ctx = os_zalloc(sizeof(*ctx)); | |
1576 | if (ctx == NULL) | |
1577 | return NULL; | |
1578 | ctx->ctx = upper_ctx; | |
1579 | ctx->xml = xml_ctx; | |
1580 | ctx->ocsp = OPTIONAL_OCSP; | |
1581 | ||
1582 | curl_global_init(CURL_GLOBAL_ALL); | |
1583 | ||
1584 | return ctx; | |
1585 | } | |
1586 | ||
1587 | ||
1588 | void http_ocsp_set(struct http_ctx *ctx, int val) | |
1589 | { | |
1590 | if (val == 0) | |
1591 | ctx->ocsp = NO_OCSP; | |
1592 | else if (val == 1) | |
1593 | ctx->ocsp = OPTIONAL_OCSP; | |
1594 | if (val == 2) | |
1595 | ctx->ocsp = MANDATORY_OCSP; | |
1596 | } | |
1597 | ||
1598 | ||
1599 | void http_deinit_ctx(struct http_ctx *ctx) | |
1600 | { | |
1601 | clear_curl(ctx); | |
1602 | os_free(ctx->curl_buf); | |
1603 | curl_global_cleanup(); | |
1604 | ||
1605 | os_free(ctx->svc_address); | |
1606 | os_free(ctx->svc_ca_fname); | |
d1ecca6c JM |
1607 | str_clear_free(ctx->svc_username); |
1608 | str_clear_free(ctx->svc_password); | |
1cb3eda9 JM |
1609 | os_free(ctx->svc_client_cert); |
1610 | os_free(ctx->svc_client_key); | |
1611 | ||
1612 | os_free(ctx); | |
1613 | } | |
1614 | ||
1615 | ||
1616 | int http_download_file(struct http_ctx *ctx, const char *url, | |
1617 | const char *fname, const char *ca_fname) | |
1618 | { | |
1619 | CURL *curl; | |
1620 | FILE *f; | |
1621 | CURLcode res; | |
1622 | long http = 0; | |
1623 | ||
1624 | ctx->last_err = NULL; | |
1625 | ||
1626 | wpa_printf(MSG_DEBUG, "curl: Download file from %s to %s (ca=%s)", | |
1627 | url, fname, ca_fname); | |
1628 | curl = curl_easy_init(); | |
1629 | if (curl == NULL) | |
1630 | return -1; | |
1631 | ||
1632 | f = fopen(fname, "wb"); | |
1633 | if (f == NULL) { | |
1634 | curl_easy_cleanup(curl); | |
1635 | return -1; | |
1636 | } | |
1637 | ||
1638 | curl_easy_setopt(curl, CURLOPT_URL, url); | |
1639 | if (ca_fname) { | |
1640 | curl_easy_setopt(curl, CURLOPT_CAINFO, ca_fname); | |
1641 | curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); | |
1642 | curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L); | |
1643 | } else { | |
1644 | curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); | |
1645 | } | |
1646 | curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, curl_cb_debug); | |
1647 | curl_easy_setopt(curl, CURLOPT_DEBUGDATA, ctx); | |
1cb3eda9 JM |
1648 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); |
1649 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, f); | |
1650 | curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); | |
1651 | ||
1652 | res = curl_easy_perform(curl); | |
1653 | if (res != CURLE_OK) { | |
1654 | if (!ctx->last_err) | |
1655 | ctx->last_err = curl_easy_strerror(res); | |
1656 | wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s", | |
1657 | ctx->last_err); | |
1658 | curl_easy_cleanup(curl); | |
1659 | fclose(f); | |
1660 | return -1; | |
1661 | } | |
1662 | ||
1663 | curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http); | |
1664 | wpa_printf(MSG_DEBUG, "curl: Server response code %ld", http); | |
1665 | if (http != 200) { | |
1666 | ctx->last_err = "HTTP download failed"; | |
1667 | wpa_printf(MSG_INFO, "HTTP download failed - code %ld", http); | |
1668 | curl_easy_cleanup(curl); | |
1669 | fclose(f); | |
1670 | return -1; | |
1671 | } | |
1672 | ||
1673 | curl_easy_cleanup(curl); | |
1674 | fclose(f); | |
1675 | ||
1676 | return 0; | |
1677 | } | |
1678 | ||
1679 | ||
1680 | char * http_post(struct http_ctx *ctx, const char *url, const char *data, | |
1681 | const char *content_type, const char *ext_hdr, | |
1682 | const char *ca_fname, | |
1683 | const char *username, const char *password, | |
1684 | const char *client_cert, const char *client_key, | |
1685 | size_t *resp_len) | |
1686 | { | |
1687 | long http = 0; | |
1688 | CURLcode res; | |
1689 | char *ret; | |
1690 | CURL *curl; | |
1691 | struct curl_slist *curl_hdr = NULL; | |
1692 | ||
1693 | ctx->last_err = NULL; | |
1694 | wpa_printf(MSG_DEBUG, "curl: HTTP POST to %s", url); | |
1695 | curl = setup_curl_post(ctx, url, ca_fname, username, password, | |
1696 | client_cert, client_key); | |
1697 | if (curl == NULL) | |
1698 | return NULL; | |
1699 | ||
1700 | if (content_type) { | |
1701 | char ct[200]; | |
1702 | snprintf(ct, sizeof(ct), "Content-Type: %s", content_type); | |
1703 | curl_hdr = curl_slist_append(curl_hdr, ct); | |
1704 | } | |
1705 | if (ext_hdr) | |
1706 | curl_hdr = curl_slist_append(curl_hdr, ext_hdr); | |
1707 | curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_hdr); | |
1708 | ||
1709 | curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); | |
1710 | free_curl_buf(ctx); | |
1711 | ||
1712 | res = curl_easy_perform(curl); | |
1713 | if (res != CURLE_OK) { | |
1714 | if (!ctx->last_err) | |
1715 | ctx->last_err = curl_easy_strerror(res); | |
1716 | wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s", | |
1717 | ctx->last_err); | |
1718 | free_curl_buf(ctx); | |
1719 | return NULL; | |
1720 | } | |
1721 | ||
1722 | curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http); | |
1723 | wpa_printf(MSG_DEBUG, "curl: Server response code %ld", http); | |
1724 | if (http != 200) { | |
1725 | ctx->last_err = "HTTP POST failed"; | |
1726 | wpa_printf(MSG_INFO, "HTTP POST failed - code %ld", http); | |
1727 | free_curl_buf(ctx); | |
1728 | return NULL; | |
1729 | } | |
1730 | ||
1731 | if (ctx->curl_buf == NULL) | |
1732 | return NULL; | |
1733 | ||
1734 | ret = ctx->curl_buf; | |
1735 | if (resp_len) | |
1736 | *resp_len = ctx->curl_buf_len; | |
1737 | ctx->curl_buf = NULL; | |
1738 | ctx->curl_buf_len = 0; | |
1739 | ||
1740 | wpa_printf(MSG_MSGDUMP, "Server response:\n%s", ret); | |
1741 | ||
1742 | return ret; | |
1743 | } | |
1744 | ||
1745 | ||
1746 | void http_set_cert_cb(struct http_ctx *ctx, | |
1747 | int (*cb)(void *ctx, struct http_cert *cert), | |
1748 | void *cb_ctx) | |
1749 | { | |
1750 | ctx->cert_cb = cb; | |
1751 | ctx->cert_cb_ctx = cb_ctx; | |
1752 | } | |
1753 | ||
1754 | ||
1755 | const char * http_get_err(struct http_ctx *ctx) | |
1756 | { | |
1757 | return ctx->last_err; | |
1758 | } |