Return to curlx.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / docs / examples |
1.1 ! misho 1: /* ! 2: curlx.c Authors: Peter Sylvester, Jean-Paul Merlin ! 3: ! 4: This is a little program to demonstrate the usage of ! 5: ! 6: - an ssl initialisation callback setting a user key and trustbases ! 7: coming from a pkcs12 file ! 8: - using an ssl application callback to find a URI in the ! 9: certificate presented during ssl session establishment. ! 10: ! 11: */ ! 12: /* <DESC> ! 13: * demonstrates use of SSL context callback, requires OpenSSL ! 14: * </DESC> ! 15: */ ! 16: ! 17: /* ! 18: * Copyright (c) 2003 - 2019 The OpenEvidence Project. All rights reserved. ! 19: * ! 20: * Redistribution and use in source and binary forms, with or without ! 21: * modification, are permitted provided that the following conditions ! 22: * are met: ! 23: * ! 24: * 1. Redistributions of source code must retain the above copyright ! 25: * notice, this list of conditions, the following disclaimer, ! 26: * and the original OpenSSL and SSLeay Licences below. ! 27: * ! 28: * 2. Redistributions in binary form must reproduce the above copyright ! 29: * notice, this list of conditions, the following disclaimer ! 30: * and the original OpenSSL and SSLeay Licences below in ! 31: * the documentation and/or other materials provided with the ! 32: * distribution. ! 33: * ! 34: * 3. All advertising materials mentioning features or use of this ! 35: * software must display the following acknowledgments: ! 36: * "This product includes software developed by the Openevidence Project ! 37: * for use in the OpenEvidence Toolkit. (http://www.openevidence.org/)" ! 38: * This product includes software developed by the OpenSSL Project ! 39: * for use in the OpenSSL Toolkit (https://www.openssl.org/)" ! 40: * This product includes cryptographic software written by Eric Young ! 41: * (eay@cryptsoft.com). This product includes software written by Tim ! 42: * Hudson (tjh@cryptsoft.com)." ! 43: * ! 44: * 4. The names "OpenEvidence Toolkit" and "OpenEvidence Project" must not be ! 45: * used to endorse or promote products derived from this software without ! 46: * prior written permission. For written permission, please contact ! 47: * openevidence-core@openevidence.org. ! 48: * ! 49: * 5. Products derived from this software may not be called "OpenEvidence" ! 50: * nor may "OpenEvidence" appear in their names without prior written ! 51: * permission of the OpenEvidence Project. ! 52: * ! 53: * 6. Redistributions of any form whatsoever must retain the following ! 54: * acknowledgments: ! 55: * "This product includes software developed by the OpenEvidence Project ! 56: * for use in the OpenEvidence Toolkit (http://www.openevidence.org/) ! 57: * This product includes software developed by the OpenSSL Project ! 58: * for use in the OpenSSL Toolkit (https://www.openssl.org/)" ! 59: * This product includes cryptographic software written by Eric Young ! 60: * (eay@cryptsoft.com). This product includes software written by Tim ! 61: * Hudson (tjh@cryptsoft.com)." ! 62: * ! 63: * THIS SOFTWARE IS PROVIDED BY THE OpenEvidence PROJECT ``AS IS'' AND ANY ! 64: * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ! 65: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ! 66: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenEvidence PROJECT OR ! 67: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ! 68: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ! 69: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; ! 70: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ! 71: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, ! 72: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ! 73: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED ! 74: * OF THE POSSIBILITY OF SUCH DAMAGE. ! 75: * ==================================================================== ! 76: * ! 77: * This product includes software developed by the OpenSSL Project ! 78: * for use in the OpenSSL Toolkit (https://www.openssl.org/) ! 79: * This product includes cryptographic software written by Eric Young ! 80: * (eay@cryptsoft.com). This product includes software written by Tim ! 81: * Hudson (tjh@cryptsoft.com). ! 82: * ! 83: */ ! 84: ! 85: #include <stdio.h> ! 86: #include <stdlib.h> ! 87: #include <string.h> ! 88: #include <curl/curl.h> ! 89: #include <openssl/x509v3.h> ! 90: #include <openssl/x509_vfy.h> ! 91: #include <openssl/crypto.h> ! 92: #include <openssl/lhash.h> ! 93: #include <openssl/objects.h> ! 94: #include <openssl/err.h> ! 95: #include <openssl/evp.h> ! 96: #include <openssl/x509.h> ! 97: #include <openssl/pkcs12.h> ! 98: #include <openssl/bio.h> ! 99: #include <openssl/ssl.h> ! 100: ! 101: static const char *curlx_usage[]={ ! 102: "usage: curlx args\n", ! 103: " -p12 arg - tia file ", ! 104: " -envpass arg - environment variable which content the tia private" ! 105: " key password", ! 106: " -out arg - output file (response)- default stdout", ! 107: " -in arg - input file (request)- default stdin", ! 108: " -connect arg - URL of the server for the connection ex:" ! 109: " www.openevidence.org", ! 110: " -mimetype arg - MIME type for data in ex : application/timestamp-query" ! 111: " or application/dvcs -default application/timestamp-query", ! 112: " -acceptmime arg - MIME type acceptable for the response ex : " ! 113: "application/timestamp-response or application/dvcs -default none", ! 114: " -accesstype arg - an Object identifier in an AIA/SIA method, e.g." ! 115: " AD_DVCS or ad_timestamping", ! 116: NULL ! 117: }; ! 118: ! 119: /* ! 120: ! 121: ./curlx -p12 psy.p12 -envpass XX -in request -verbose -accesstype AD_DVCS ! 122: -mimetype application/dvcs -acceptmime application/dvcs -out response ! 123: ! 124: */ ! 125: ! 126: /* ! 127: * We use this ZERO_NULL to avoid picky compiler warnings, ! 128: * when assigning a NULL pointer to a function pointer var. ! 129: */ ! 130: ! 131: #define ZERO_NULL 0 ! 132: ! 133: /* This is a context that we pass to all callbacks */ ! 134: ! 135: typedef struct sslctxparm_st { ! 136: unsigned char *p12file; ! 137: const char *pst; ! 138: PKCS12 *p12; ! 139: EVP_PKEY *pkey; ! 140: X509 *usercert; ! 141: STACK_OF(X509) * ca; ! 142: CURL *curl; ! 143: BIO *errorbio; ! 144: int accesstype; ! 145: int verbose; ! 146: ! 147: } sslctxparm; ! 148: ! 149: /* some helper function. */ ! 150: ! 151: static char *ia5string(ASN1_IA5STRING *ia5) ! 152: { ! 153: char *tmp; ! 154: if(!ia5 || !ia5->length) ! 155: return NULL; ! 156: tmp = OPENSSL_malloc(ia5->length + 1); ! 157: memcpy(tmp, ia5->data, ia5->length); ! 158: tmp[ia5->length] = 0; ! 159: return tmp; ! 160: } ! 161: ! 162: /* A convenience routine to get an access URI. */ ! 163: static unsigned char *my_get_ext(X509 *cert, const int type, ! 164: int extensiontype) ! 165: { ! 166: int i; ! 167: STACK_OF(ACCESS_DESCRIPTION) * accessinfo; ! 168: accessinfo = X509_get_ext_d2i(cert, extensiontype, NULL, NULL); ! 169: ! 170: if(!sk_ACCESS_DESCRIPTION_num(accessinfo)) ! 171: return NULL; ! 172: for(i = 0; i < sk_ACCESS_DESCRIPTION_num(accessinfo); i++) { ! 173: ACCESS_DESCRIPTION * ad = sk_ACCESS_DESCRIPTION_value(accessinfo, i); ! 174: if(OBJ_obj2nid(ad->method) == type) { ! 175: if(ad->location->type == GEN_URI) { ! 176: return ia5string(ad->location->d.ia5); ! 177: } ! 178: return NULL; ! 179: } ! 180: } ! 181: return NULL; ! 182: } ! 183: ! 184: /* This is an application verification call back, it does not ! 185: perform any addition verification but tries to find a URL ! 186: in the presented certificate. If found, this will become ! 187: the URL to be used in the POST. ! 188: */ ! 189: ! 190: static int ssl_app_verify_callback(X509_STORE_CTX *ctx, void *arg) ! 191: { ! 192: sslctxparm * p = (sslctxparm *) arg; ! 193: int ok; ! 194: ! 195: if(p->verbose > 2) ! 196: BIO_printf(p->errorbio, "entering ssl_app_verify_callback\n"); ! 197: ! 198: ok = X509_verify_cert(ctx); ! 199: if(ok && ctx->cert) { ! 200: unsigned char *accessinfo; ! 201: if(p->verbose > 1) ! 202: X509_print_ex(p->errorbio, ctx->cert, 0, 0); ! 203: ! 204: accessinfo = my_get_ext(ctx->cert, p->accesstype, NID_sinfo_access); ! 205: if(accessinfo) { ! 206: if(p->verbose) ! 207: BIO_printf(p->errorbio, "Setting URL from SIA to: %s\n", accessinfo); ! 208: ! 209: curl_easy_setopt(p->curl, CURLOPT_URL, accessinfo); ! 210: } ! 211: else if(accessinfo = my_get_ext(ctx->cert, p->accesstype, ! 212: NID_info_access)) { ! 213: if(p->verbose) ! 214: BIO_printf(p->errorbio, "Setting URL from AIA to: %s\n", accessinfo); ! 215: ! 216: curl_easy_setopt(p->curl, CURLOPT_URL, accessinfo); ! 217: } ! 218: } ! 219: if(p->verbose > 2) ! 220: BIO_printf(p->errorbio, "leaving ssl_app_verify_callback with %d\n", ok); ! 221: ! 222: return ok; ! 223: } ! 224: ! 225: ! 226: /* The SSL initialisation callback. The callback sets: ! 227: - a private key and certificate ! 228: - a trusted ca certificate ! 229: - a preferred cipherlist ! 230: - an application verification callback (the function above) ! 231: */ ! 232: ! 233: static CURLcode sslctxfun(CURL *curl, void *sslctx, void *parm) ! 234: { ! 235: sslctxparm *p = (sslctxparm *) parm; ! 236: SSL_CTX *ctx = (SSL_CTX *) sslctx; ! 237: ! 238: if(!SSL_CTX_use_certificate(ctx, p->usercert)) { ! 239: BIO_printf(p->errorbio, "SSL_CTX_use_certificate problem\n"); ! 240: goto err; ! 241: } ! 242: if(!SSL_CTX_use_PrivateKey(ctx, p->pkey)) { ! 243: BIO_printf(p->errorbio, "SSL_CTX_use_PrivateKey\n"); ! 244: goto err; ! 245: } ! 246: ! 247: if(!SSL_CTX_check_private_key(ctx)) { ! 248: BIO_printf(p->errorbio, "SSL_CTX_check_private_key\n"); ! 249: goto err; ! 250: } ! 251: ! 252: SSL_CTX_set_quiet_shutdown(ctx, 1); ! 253: SSL_CTX_set_cipher_list(ctx, "RC4-MD5"); ! 254: SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); ! 255: ! 256: X509_STORE_add_cert(SSL_CTX_get_cert_store(ctx), ! 257: sk_X509_value(p->ca, sk_X509_num(p->ca)-1)); ! 258: ! 259: SSL_CTX_set_verify_depth(ctx, 2); ! 260: SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, ZERO_NULL); ! 261: SSL_CTX_set_cert_verify_callback(ctx, ssl_app_verify_callback, parm); ! 262: ! 263: return CURLE_OK; ! 264: err: ! 265: ERR_print_errors(p->errorbio); ! 266: return CURLE_SSL_CERTPROBLEM; ! 267: ! 268: } ! 269: ! 270: int main(int argc, char **argv) ! 271: { ! 272: BIO* in = NULL; ! 273: BIO* out = NULL; ! 274: ! 275: char *outfile = NULL; ! 276: char *infile = NULL; ! 277: ! 278: int tabLength = 100; ! 279: char *binaryptr; ! 280: char *mimetype = NULL; ! 281: char *mimetypeaccept = NULL; ! 282: char *contenttype; ! 283: const char **pp; ! 284: unsigned char *hostporturl = NULL; ! 285: BIO *p12bio; ! 286: char **args = argv + 1; ! 287: unsigned char *serverurl; ! 288: sslctxparm p; ! 289: char *response; ! 290: ! 291: CURLcode res; ! 292: struct curl_slist *headers = NULL; ! 293: int badarg = 0; ! 294: ! 295: binaryptr = malloc(tabLength); ! 296: ! 297: memset(&p, '\0', sizeof(p)); ! 298: p.errorbio = BIO_new_fp(stderr, BIO_NOCLOSE); ! 299: ! 300: curl_global_init(CURL_GLOBAL_DEFAULT); ! 301: ! 302: /* we need some more for the P12 decoding */ ! 303: ! 304: OpenSSL_add_all_ciphers(); ! 305: OpenSSL_add_all_digests(); ! 306: ERR_load_crypto_strings(); ! 307: ! 308: while(*args && *args[0] == '-') { ! 309: if(!strcmp (*args, "-in")) { ! 310: if(args[1]) { ! 311: infile = *(++args); ! 312: } ! 313: else ! 314: badarg = 1; ! 315: } ! 316: else if(!strcmp (*args, "-out")) { ! 317: if(args[1]) { ! 318: outfile = *(++args); ! 319: } ! 320: else ! 321: badarg = 1; ! 322: } ! 323: else if(!strcmp (*args, "-p12")) { ! 324: if(args[1]) { ! 325: p.p12file = *(++args); ! 326: } ! 327: else ! 328: badarg = 1; ! 329: } ! 330: else if(strcmp(*args, "-envpass") == 0) { ! 331: if(args[1]) { ! 332: p.pst = getenv(*(++args)); ! 333: } ! 334: else ! 335: badarg = 1; ! 336: } ! 337: else if(strcmp(*args, "-connect") == 0) { ! 338: if(args[1]) { ! 339: hostporturl = *(++args); ! 340: } ! 341: else ! 342: badarg = 1; ! 343: } ! 344: else if(strcmp(*args, "-mimetype") == 0) { ! 345: if(args[1]) { ! 346: mimetype = *(++args); ! 347: } ! 348: else ! 349: badarg = 1; ! 350: } ! 351: else if(strcmp(*args, "-acceptmime") == 0) { ! 352: if(args[1]) { ! 353: mimetypeaccept = *(++args); ! 354: } ! 355: else ! 356: badarg = 1; ! 357: } ! 358: else if(strcmp(*args, "-accesstype") == 0) { ! 359: if(args[1]) { ! 360: p.accesstype = OBJ_obj2nid(OBJ_txt2obj(*++args, 0)); ! 361: if(p.accesstype == 0) ! 362: badarg = 1; ! 363: } ! 364: else ! 365: badarg = 1; ! 366: } ! 367: else if(strcmp(*args, "-verbose") == 0) { ! 368: p.verbose++; ! 369: } ! 370: else ! 371: badarg = 1; ! 372: args++; ! 373: } ! 374: ! 375: if(mimetype == NULL || mimetypeaccept == NULL || p.p12file == NULL) ! 376: badarg = 1; ! 377: ! 378: if(badarg) { ! 379: for(pp = curlx_usage; (*pp != NULL); pp++) ! 380: BIO_printf(p.errorbio, "%s\n", *pp); ! 381: BIO_printf(p.errorbio, "\n"); ! 382: goto err; ! 383: } ! 384: ! 385: /* set input */ ! 386: ! 387: in = BIO_new(BIO_s_file()); ! 388: if(in == NULL) { ! 389: BIO_printf(p.errorbio, "Error setting input bio\n"); ! 390: goto err; ! 391: } ! 392: else if(infile == NULL) ! 393: BIO_set_fp(in, stdin, BIO_NOCLOSE|BIO_FP_TEXT); ! 394: else if(BIO_read_filename(in, infile) <= 0) { ! 395: BIO_printf(p.errorbio, "Error opening input file %s\n", infile); ! 396: BIO_free(in); ! 397: goto err; ! 398: } ! 399: ! 400: /* set output */ ! 401: ! 402: out = BIO_new(BIO_s_file()); ! 403: if(out == NULL) { ! 404: BIO_printf(p.errorbio, "Error setting output bio.\n"); ! 405: goto err; ! 406: } ! 407: else if(outfile == NULL) ! 408: BIO_set_fp(out, stdout, BIO_NOCLOSE|BIO_FP_TEXT); ! 409: else if(BIO_write_filename(out, outfile) <= 0) { ! 410: BIO_printf(p.errorbio, "Error opening output file %s\n", outfile); ! 411: BIO_free(out); ! 412: goto err; ! 413: } ! 414: ! 415: ! 416: p.errorbio = BIO_new_fp(stderr, BIO_NOCLOSE); ! 417: ! 418: p.curl = curl_easy_init(); ! 419: if(!p.curl) { ! 420: BIO_printf(p.errorbio, "Cannot init curl lib\n"); ! 421: goto err; ! 422: } ! 423: ! 424: p12bio = BIO_new_file(p.p12file, "rb"); ! 425: if(!p12bio) { ! 426: BIO_printf(p.errorbio, "Error opening P12 file %s\n", p.p12file); ! 427: goto err; ! 428: } ! 429: p.p12 = d2i_PKCS12_bio(p12bio, NULL); ! 430: if(!p.p12) { ! 431: BIO_printf(p.errorbio, "Cannot decode P12 structure %s\n", p.p12file); ! 432: goto err; ! 433: } ! 434: ! 435: p.ca = NULL; ! 436: if(!(PKCS12_parse (p.p12, p.pst, &(p.pkey), &(p.usercert), &(p.ca) ) )) { ! 437: BIO_printf(p.errorbio, "Invalid P12 structure in %s\n", p.p12file); ! 438: goto err; ! 439: } ! 440: ! 441: if(sk_X509_num(p.ca) <= 0) { ! 442: BIO_printf(p.errorbio, "No trustworthy CA given.%s\n", p.p12file); ! 443: goto err; ! 444: } ! 445: ! 446: if(p.verbose > 1) ! 447: X509_print_ex(p.errorbio, p.usercert, 0, 0); ! 448: ! 449: /* determine URL to go */ ! 450: ! 451: if(hostporturl) { ! 452: size_t len = strlen(hostporturl) + 9; ! 453: serverurl = malloc(len); ! 454: snprintf(serverurl, len, "https://%s", hostporturl); ! 455: } ! 456: else if(p.accesstype != 0) { /* see whether we can find an AIA or SIA for a ! 457: given access type */ ! 458: serverurl = my_get_ext(p.usercert, p.accesstype, NID_info_access); ! 459: if(!serverurl) { ! 460: int j = 0; ! 461: BIO_printf(p.errorbio, "no service URL in user cert " ! 462: "cherching in others certificats\n"); ! 463: for(j = 0; j<sk_X509_num(p.ca); j++) { ! 464: serverurl = my_get_ext(sk_X509_value(p.ca, j), p.accesstype, ! 465: NID_info_access); ! 466: if(serverurl) ! 467: break; ! 468: serverurl = my_get_ext(sk_X509_value(p.ca, j), p.accesstype, ! 469: NID_sinfo_access); ! 470: if(serverurl) ! 471: break; ! 472: } ! 473: } ! 474: } ! 475: ! 476: if(!serverurl) { ! 477: BIO_printf(p.errorbio, "no service URL in certificats," ! 478: " check '-accesstype (AD_DVCS | ad_timestamping)'" ! 479: " or use '-connect'\n"); ! 480: goto err; ! 481: } ! 482: ! 483: if(p.verbose) ! 484: BIO_printf(p.errorbio, "Service URL: <%s>\n", serverurl); ! 485: ! 486: curl_easy_setopt(p.curl, CURLOPT_URL, serverurl); ! 487: ! 488: /* Now specify the POST binary data */ ! 489: ! 490: curl_easy_setopt(p.curl, CURLOPT_POSTFIELDS, binaryptr); ! 491: curl_easy_setopt(p.curl, CURLOPT_POSTFIELDSIZE, (long)tabLength); ! 492: ! 493: /* pass our list of custom made headers */ ! 494: ! 495: contenttype = malloc(15 + strlen(mimetype)); ! 496: snprintf(contenttype, 15 + strlen(mimetype), "Content-type: %s", mimetype); ! 497: headers = curl_slist_append(headers, contenttype); ! 498: curl_easy_setopt(p.curl, CURLOPT_HTTPHEADER, headers); ! 499: ! 500: if(p.verbose) ! 501: BIO_printf(p.errorbio, "Service URL: <%s>\n", serverurl); ! 502: ! 503: { ! 504: FILE *outfp; ! 505: BIO_get_fp(out, &outfp); ! 506: curl_easy_setopt(p.curl, CURLOPT_WRITEDATA, outfp); ! 507: } ! 508: ! 509: res = curl_easy_setopt(p.curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun); ! 510: ! 511: if(res != CURLE_OK) ! 512: BIO_printf(p.errorbio, "%d %s=%d %d\n", __LINE__, ! 513: "CURLOPT_SSL_CTX_FUNCTION", CURLOPT_SSL_CTX_FUNCTION, res); ! 514: ! 515: curl_easy_setopt(p.curl, CURLOPT_SSL_CTX_DATA, &p); ! 516: ! 517: { ! 518: char *ptr; ! 519: int lu; int i = 0; ! 520: while((lu = BIO_read(in, &binaryptr[i], tabLength-i)) >0) { ! 521: i += lu; ! 522: if(i == tabLength) { ! 523: tabLength += 100; ! 524: ptr = realloc(binaryptr, tabLength); /* should be more careful */ ! 525: if(!ptr) { ! 526: /* out of memory */ ! 527: BIO_printf(p.errorbio, "out of memory (realloc returned NULL)\n"); ! 528: goto fail; ! 529: } ! 530: binaryptr = ptr; ! 531: ptr = NULL; ! 532: } ! 533: } ! 534: tabLength = i; ! 535: } ! 536: /* Now specify the POST binary data */ ! 537: ! 538: curl_easy_setopt(p.curl, CURLOPT_POSTFIELDS, binaryptr); ! 539: curl_easy_setopt(p.curl, CURLOPT_POSTFIELDSIZE, (long)tabLength); ! 540: ! 541: ! 542: /* Perform the request, res will get the return code */ ! 543: ! 544: BIO_printf(p.errorbio, "%d %s %d\n", __LINE__, "curl_easy_perform", ! 545: res = curl_easy_perform(p.curl)); ! 546: { ! 547: curl_easy_getinfo(p.curl, CURLINFO_CONTENT_TYPE, &response); ! 548: if(mimetypeaccept && p.verbose) { ! 549: if(!strcmp(mimetypeaccept, response)) ! 550: BIO_printf(p.errorbio, "the response has a correct mimetype : %s\n", ! 551: response); ! 552: else ! 553: BIO_printf(p.errorbio, "the response doesn\'t have an acceptable " ! 554: "mime type, it is %s instead of %s\n", ! 555: response, mimetypeaccept); ! 556: } ! 557: } ! 558: ! 559: /*** code d'erreur si accept mime ***, egalement code return HTTP != 200 ***/ ! 560: ! 561: /* free the header list*/ ! 562: fail: ! 563: curl_slist_free_all(headers); ! 564: ! 565: /* always cleanup */ ! 566: curl_easy_cleanup(p.curl); ! 567: ! 568: BIO_free(in); ! 569: BIO_free(out); ! 570: return (EXIT_SUCCESS); ! 571: ! 572: err: BIO_printf(p.errorbio, "error"); ! 573: exit(1); ! 574: }