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