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