curlx.c
Go to the documentation of this file.
00001 /*
00002   curlx.c  Authors: Peter Sylvester, Jean-Paul Merlin
00003 
00004   This is a little program to demonstrate the usage of
00005 
00006   - an ssl initialisation callback setting a user key and trustbases
00007   coming from a pkcs12 file
00008   - using an ssl application callback to find a URI in the
00009   certificate presented during ssl session establishment.
00010 
00011 */
00012 /* <DESC>
00013  * demonstrates use of SSL context callback, requires OpenSSL
00014  * </DESC>
00015  */
00016 
00017 /*
00018  * Copyright (c) 2003 The OpenEvidence Project.  All rights reserved.
00019  *
00020  * Redistribution and use in source and binary forms, with or without
00021  * modification, are permitted provided that the following conditions
00022  * are met:
00023  *
00024  * 1. Redistributions of source code must retain the above copyright
00025  *    notice, this list of conditions, the following disclaimer,
00026  *    and the original OpenSSL and SSLeay Licences below.
00027  *
00028  * 2. Redistributions in binary form must reproduce the above copyright
00029  *    notice, this list of conditions, the following disclaimer
00030  *    and the original OpenSSL and SSLeay Licences below in
00031  *    the documentation and/or other materials provided with the
00032  *    distribution.
00033  *
00034  * 3. All advertising materials mentioning features or use of this
00035  *    software must display the following acknowledgments:
00036  *    "This product includes software developed by the Openevidence Project
00037  *    for use in the OpenEvidence Toolkit. (http://www.openevidence.org/)"
00038  *    This product includes software developed by the OpenSSL Project
00039  *    for use in the OpenSSL Toolkit (https://www.openssl.org/)"
00040  *    This product includes cryptographic software written by Eric Young
00041  *    (eay@cryptsoft.com).  This product includes software written by Tim
00042  *    Hudson (tjh@cryptsoft.com)."
00043  *
00044  * 4. The names "OpenEvidence Toolkit" and "OpenEvidence Project" must not be
00045  *    used to endorse or promote products derived from this software without
00046  *    prior written permission. For written permission, please contact
00047  *    openevidence-core@openevidence.org.
00048  *
00049  * 5. Products derived from this software may not be called "OpenEvidence"
00050  *    nor may "OpenEvidence" appear in their names without prior written
00051  *    permission of the OpenEvidence Project.
00052  *
00053  * 6. Redistributions of any form whatsoever must retain the following
00054  *    acknowledgments:
00055  *    "This product includes software developed by the OpenEvidence Project
00056  *    for use in the OpenEvidence Toolkit (http://www.openevidence.org/)
00057  *    This product includes software developed by the OpenSSL Project
00058  *    for use in the OpenSSL Toolkit (https://www.openssl.org/)"
00059  *    This product includes cryptographic software written by Eric Young
00060  *    (eay@cryptsoft.com).  This product includes software written by Tim
00061  *    Hudson (tjh@cryptsoft.com)."
00062  *
00063  * THIS SOFTWARE IS PROVIDED BY THE OpenEvidence PROJECT ``AS IS'' AND ANY
00064  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00065  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00066  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenEvidence PROJECT OR
00067  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00068  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00069  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00070  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00071  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
00072  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00073  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
00074  * OF THE POSSIBILITY OF SUCH DAMAGE.
00075  * ====================================================================
00076  *
00077  * This product includes software developed by the OpenSSL Project
00078  * for use in the OpenSSL Toolkit (https://www.openssl.org/)
00079  * This product includes cryptographic software written by Eric Young
00080  * (eay@cryptsoft.com).  This product includes software written by Tim
00081  * Hudson (tjh@cryptsoft.com).
00082  *
00083  */
00084 
00085 #include <stdio.h>
00086 #include <stdlib.h>
00087 #include <string.h>
00088 #include <curl/curl.h>
00089 #include <openssl/x509v3.h>
00090 #include <openssl/x509_vfy.h>
00091 #include <openssl/crypto.h>
00092 #include <openssl/lhash.h>
00093 #include <openssl/objects.h>
00094 #include <openssl/err.h>
00095 #include <openssl/evp.h>
00096 #include <openssl/x509.h>
00097 #include <openssl/pkcs12.h>
00098 #include <openssl/bio.h>
00099 #include <openssl/ssl.h>
00100 
00101 static const char *curlx_usage[]={
00102   "usage: curlx args\n",
00103   " -p12 arg         - tia  file ",
00104   " -envpass arg     - environement variable which content the tia private"
00105   " key password",
00106   " -out arg         - output file (response)- default stdout",
00107   " -in arg          - input file (request)- default stdin",
00108   " -connect arg     - URL of the server for the connection ex:"
00109   " www.openevidence.org",
00110   " -mimetype arg    - MIME type for data in ex : application/timestamp-query"
00111   " or application/dvcs -default application/timestamp-query",
00112   " -acceptmime arg  - MIME type acceptable for the response ex : "
00113   "application/timestamp-response or application/dvcs -default none",
00114   " -accesstype arg  - an Object identifier in an AIA/SIA method, e.g."
00115   " AD_DVCS or ad_timestamping",
00116   NULL
00117 };
00118 
00119 /*
00120 
00121 ./curlx -p12 psy.p12 -envpass XX -in request -verbose -accesstype AD_DVCS
00122 -mimetype application/dvcs -acceptmime application/dvcs -out response
00123 
00124 */
00125 
00126 /*
00127  * We use this ZERO_NULL to avoid picky compiler warnings,
00128  * when assigning a NULL pointer to a function pointer var.
00129  */
00130 
00131 #define ZERO_NULL 0
00132 
00133 /* This is a context that we pass to all callbacks */
00134 
00135 typedef struct sslctxparm_st {
00136   unsigned char *p12file;
00137   const char *pst;
00138   PKCS12 *p12;
00139   EVP_PKEY *pkey;
00140   X509 *usercert;
00141   STACK_OF(X509) * ca;
00142   CURL *curl;
00143   BIO *errorbio;
00144   int accesstype;
00145   int verbose;
00146 
00147 } sslctxparm;
00148 
00149 /* some helper function. */
00150 
00151 static char *ia5string(ASN1_IA5STRING *ia5)
00152 {
00153   char *tmp;
00154   if(!ia5 || !ia5->length)
00155     return NULL;
00156   tmp = OPENSSL_malloc(ia5->length + 1);
00157   memcpy(tmp, ia5->data, ia5->length);
00158   tmp[ia5->length] = 0;
00159   return tmp;
00160 }
00161 
00162 /* A conveniance routine to get an access URI. */
00163 static unsigned char *my_get_ext(X509 *cert, const int type,
00164                                  int extensiontype)
00165 {
00166   int i;
00167   STACK_OF(ACCESS_DESCRIPTION) * accessinfo;
00168   accessinfo =  X509_get_ext_d2i(cert, extensiontype, NULL, NULL);
00169 
00170   if(!sk_ACCESS_DESCRIPTION_num(accessinfo))
00171     return NULL;
00172   for(i = 0; i < sk_ACCESS_DESCRIPTION_num(accessinfo); i++) {
00173     ACCESS_DESCRIPTION * ad = sk_ACCESS_DESCRIPTION_value(accessinfo, i);
00174     if(OBJ_obj2nid(ad->method) == type) {
00175       if(ad->location->type == GEN_URI) {
00176         return ia5string(ad->location->d.ia5);
00177       }
00178       return NULL;
00179     }
00180   }
00181   return NULL;
00182 }
00183 
00184 /* This is an application verification call back, it does not
00185    perform any addition verification but tries to find a URL
00186    in the presented certificat. If found, this will become
00187    the URL to be used in the POST.
00188 */
00189 
00190 static int ssl_app_verify_callback(X509_STORE_CTX *ctx, void *arg)
00191 {
00192   sslctxparm * p = (sslctxparm *) arg;
00193   int ok;
00194 
00195   if(p->verbose > 2)
00196     BIO_printf(p->errorbio, "entering ssl_app_verify_callback\n");
00197 
00198   if((ok= X509_verify_cert(ctx)) && ctx->cert) {
00199     unsigned char *accessinfo;
00200     if(p->verbose > 1)
00201       X509_print_ex(p->errorbio, ctx->cert, 0, 0);
00202 
00203     accessinfo = my_get_ext(ctx->cert, p->accesstype, NID_sinfo_access);
00204     if(accessinfo) {
00205       if(p->verbose)
00206         BIO_printf(p->errorbio, "Setting URL from SIA to: %s\n", accessinfo);
00207 
00208       curl_easy_setopt(p->curl, CURLOPT_URL, accessinfo);
00209     }
00210     else if(accessinfo = my_get_ext(ctx->cert, p->accesstype,
00211                                     NID_info_access)) {
00212       if(p->verbose)
00213         BIO_printf(p->errorbio, "Setting URL from AIA to: %s\n", accessinfo);
00214 
00215       curl_easy_setopt(p->curl, CURLOPT_URL, accessinfo);
00216     }
00217   }
00218   if(p->verbose > 2)
00219     BIO_printf(p->errorbio, "leaving ssl_app_verify_callback with %d\n", ok);
00220 
00221   return ok;
00222 }
00223 
00224 
00225 /* The SSL initialisation callback. The callback sets:
00226    - a private key and certificate
00227    - a trusted ca certificate
00228    - a preferred cipherlist
00229    - an application verification callback (the function above)
00230 */
00231 
00232 static CURLcode sslctxfun(CURL *curl, void *sslctx, void *parm)
00233 {
00234   sslctxparm *p = (sslctxparm *) parm;
00235   SSL_CTX *ctx = (SSL_CTX *) sslctx;
00236 
00237   if(!SSL_CTX_use_certificate(ctx, p->usercert)) {
00238     BIO_printf(p->errorbio, "SSL_CTX_use_certificate problem\n");
00239     goto err;
00240   }
00241   if(!SSL_CTX_use_PrivateKey(ctx, p->pkey)) {
00242     BIO_printf(p->errorbio, "SSL_CTX_use_PrivateKey\n");
00243     goto err;
00244   }
00245 
00246   if(!SSL_CTX_check_private_key(ctx)) {
00247     BIO_printf(p->errorbio, "SSL_CTX_check_private_key\n");
00248     goto err;
00249   }
00250 
00251   SSL_CTX_set_quiet_shutdown(ctx, 1);
00252   SSL_CTX_set_cipher_list(ctx, "RC4-MD5");
00253   SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
00254 
00255   X509_STORE_add_cert(SSL_CTX_get_cert_store(ctx),
00256                       sk_X509_value(p->ca, sk_X509_num(p->ca)-1));
00257 
00258   SSL_CTX_set_verify_depth(ctx, 2);
00259   SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, ZERO_NULL);
00260   SSL_CTX_set_cert_verify_callback(ctx, ssl_app_verify_callback, parm);
00261 
00262   return CURLE_OK;
00263   err:
00264   ERR_print_errors(p->errorbio);
00265   return CURLE_SSL_CERTPROBLEM;
00266 
00267 }
00268 
00269 int main(int argc, char **argv)
00270 {
00271   BIO* in=NULL;
00272   BIO* out=NULL;
00273 
00274   char *outfile = NULL;
00275   char *infile = NULL;
00276 
00277   int tabLength=100;
00278   char *binaryptr;
00279   char *mimetype;
00280   char *mimetypeaccept=NULL;
00281   char *contenttype;
00282   const char **pp;
00283   unsigned char *hostporturl = NULL;
00284   BIO *p12bio;
00285   char **args = argv + 1;
00286   unsigned char *serverurl;
00287   sslctxparm p;
00288   char *response;
00289 
00290   CURLcode res;
00291   struct curl_slist *headers=NULL;
00292   int badarg=0;
00293 
00294   binaryptr = malloc(tabLength);
00295 
00296   p.verbose = 0;
00297   p.errorbio = BIO_new_fp(stderr, BIO_NOCLOSE);
00298 
00299   curl_global_init(CURL_GLOBAL_DEFAULT);
00300 
00301   /* we need some more for the P12 decoding */
00302 
00303   OpenSSL_add_all_ciphers();
00304   OpenSSL_add_all_digests();
00305   ERR_load_crypto_strings();
00306 
00307   while(*args && *args[0] == '-') {
00308     if(!strcmp (*args, "-in")) {
00309       if(args[1]) {
00310         infile=*(++args);
00311       }
00312       else
00313         badarg=1;
00314     }
00315     else if(!strcmp (*args, "-out")) {
00316       if(args[1]) {
00317         outfile=*(++args);
00318       }
00319       else
00320         badarg=1;
00321     }
00322     else if(!strcmp (*args, "-p12")) {
00323       if(args[1]) {
00324         p.p12file = *(++args);
00325       }
00326       else
00327         badarg=1;
00328     }
00329     else if(strcmp(*args, "-envpass") == 0) {
00330       if(args[1]) {
00331         p.pst = getenv(*(++args));
00332       }
00333       else
00334         badarg=1;
00335     }
00336     else if(strcmp(*args, "-connect") == 0) {
00337       if(args[1]) {
00338         hostporturl = *(++args);
00339       }
00340       else
00341         badarg=1;
00342     }
00343     else if(strcmp(*args, "-mimetype") == 0) {
00344       if(args[1]) {
00345         mimetype = *(++args);
00346       }
00347       else
00348         badarg=1;
00349     }
00350     else if(strcmp(*args, "-acceptmime") == 0) {
00351       if(args[1]) {
00352         mimetypeaccept = *(++args);
00353       }
00354       else
00355         badarg=1;
00356     }
00357     else if(strcmp(*args, "-accesstype") == 0) {
00358       if(args[1]) {
00359         p.accesstype = OBJ_obj2nid(OBJ_txt2obj(*++args, 0));
00360         if(p.accesstype == 0)
00361           badarg=1;
00362       }
00363       else
00364         badarg=1;
00365     }
00366     else if(strcmp(*args, "-verbose") == 0) {
00367       p.verbose++;
00368     }
00369     else
00370       badarg=1;
00371     args++;
00372   }
00373 
00374   if(mimetype==NULL || mimetypeaccept == NULL)
00375     badarg = 1;
00376 
00377   if(badarg) {
00378     for(pp=curlx_usage; (*pp != NULL); pp++)
00379       BIO_printf(p.errorbio, "%s\n", *pp);
00380     BIO_printf(p.errorbio, "\n");
00381     goto err;
00382   }
00383 
00384   /* set input */
00385 
00386   if((in=BIO_new(BIO_s_file())) == NULL) {
00387     BIO_printf(p.errorbio, "Error setting input bio\n");
00388     goto err;
00389   }
00390   else if(infile == NULL)
00391     BIO_set_fp(in, stdin, BIO_NOCLOSE|BIO_FP_TEXT);
00392   else if(BIO_read_filename(in, infile) <= 0) {
00393     BIO_printf(p.errorbio, "Error opening input file %s\n", infile);
00394     BIO_free(in);
00395     goto err;
00396   }
00397 
00398   /* set output  */
00399 
00400   if((out=BIO_new(BIO_s_file())) == NULL) {
00401     BIO_printf(p.errorbio, "Error setting output bio.\n");
00402     goto err;
00403   }
00404   else if(outfile == NULL)
00405     BIO_set_fp(out, stdout, BIO_NOCLOSE|BIO_FP_TEXT);
00406   else if(BIO_write_filename(out, outfile) <= 0) {
00407     BIO_printf(p.errorbio, "Error opening output file %s\n", outfile);
00408     BIO_free(out);
00409     goto err;
00410   }
00411 
00412 
00413   p.errorbio = BIO_new_fp(stderr, BIO_NOCLOSE);
00414 
00415   p.curl = curl_easy_init();
00416   if(!p.curl) {
00417     BIO_printf(p.errorbio, "Cannot init curl lib\n");
00418     goto err;
00419   }
00420 
00421   p12bio = BIO_new_file(p.p12file, "rb");
00422   if(!p12bio) {
00423     BIO_printf(p.errorbio, "Error opening P12 file %s\n", p.p12file);
00424     goto err;
00425   }
00426   p.p12 = d2i_PKCS12_bio(p12bio, NULL);
00427   if(!p.p12) {
00428     BIO_printf(p.errorbio, "Cannot decode P12 structure %s\n", p.p12file);
00429     goto err;
00430   }
00431 
00432   p.ca= NULL;
00433   if(!(PKCS12_parse (p.p12, p.pst, &(p.pkey), &(p.usercert), &(p.ca) ) )) {
00434     BIO_printf(p.errorbio, "Invalid P12 structure in %s\n", p.p12file);
00435     goto err;
00436   }
00437 
00438   if(sk_X509_num(p.ca) <= 0) {
00439     BIO_printf(p.errorbio, "No trustworthy CA given.%s\n", p.p12file);
00440     goto err;
00441   }
00442 
00443   if(p.verbose > 1)
00444     X509_print_ex(p.errorbio, p.usercert, 0, 0);
00445 
00446   /* determine URL to go */
00447 
00448   if(hostporturl) {
00449     size_t len = strlen(hostporturl) + 9;
00450     serverurl = malloc(len);
00451     snprintf(serverurl, len, "https://%s", hostporturl);
00452   }
00453   else if(p.accesstype != 0) { /* see whether we can find an AIA or SIA for a
00454                                   given access type */
00455     serverurl = my_get_ext(p.usercert, p.accesstype, NID_info_access);
00456     if(!serverurl) {
00457       int j=0;
00458       BIO_printf(p.errorbio, "no service URL in user cert "
00459                  "cherching in others certificats\n");
00460       for(j=0; j<sk_X509_num(p.ca); j++) {
00461         serverurl = my_get_ext(sk_X509_value(p.ca, j), p.accesstype,
00462                                NID_info_access);
00463         if(serverurl)
00464           break;
00465         serverurl = my_get_ext(sk_X509_value(p.ca, j), p.accesstype,
00466                                NID_sinfo_access);
00467         if(serverurl)
00468           break;
00469       }
00470     }
00471   }
00472 
00473   if(!serverurl) {
00474     BIO_printf(p.errorbio, "no service URL in certificats,"
00475                " check '-accesstype (AD_DVCS | ad_timestamping)'"
00476                " or use '-connect'\n");
00477     goto err;
00478   }
00479 
00480   if(p.verbose)
00481     BIO_printf(p.errorbio, "Service URL: <%s>\n", serverurl);
00482 
00483   curl_easy_setopt(p.curl, CURLOPT_URL, serverurl);
00484 
00485   /* Now specify the POST binary data */
00486 
00487   curl_easy_setopt(p.curl, CURLOPT_POSTFIELDS, binaryptr);
00488   curl_easy_setopt(p.curl, CURLOPT_POSTFIELDSIZE, (long)tabLength);
00489 
00490   /* pass our list of custom made headers */
00491 
00492   contenttype = malloc(15+strlen(mimetype));
00493   snprintf(contenttype, 15+strlen(mimetype), "Content-type: %s", mimetype);
00494   headers = curl_slist_append(headers, contenttype);
00495   curl_easy_setopt(p.curl, CURLOPT_HTTPHEADER, headers);
00496 
00497   if(p.verbose)
00498     BIO_printf(p.errorbio, "Service URL: <%s>\n", serverurl);
00499 
00500   {
00501     FILE *outfp;
00502     BIO_get_fp(out, &outfp);
00503     curl_easy_setopt(p.curl, CURLOPT_WRITEDATA, outfp);
00504   }
00505 
00506   res = curl_easy_setopt(p.curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun);
00507 
00508   if(res != CURLE_OK)
00509     BIO_printf(p.errorbio, "%d %s=%d %d\n", __LINE__,
00510                "CURLOPT_SSL_CTX_FUNCTION", CURLOPT_SSL_CTX_FUNCTION, res);
00511 
00512   curl_easy_setopt(p.curl, CURLOPT_SSL_CTX_DATA, &p);
00513 
00514   {
00515     int lu; int i=0;
00516     while((lu = BIO_read(in, &binaryptr[i], tabLength-i)) >0) {
00517       i+=lu;
00518       if(i== tabLength) {
00519         tabLength+=100;
00520         binaryptr=realloc(binaryptr, tabLength); /* should be more careful */
00521       }
00522     }
00523     tabLength = i;
00524   }
00525   /* Now specify the POST binary data */
00526 
00527   curl_easy_setopt(p.curl, CURLOPT_POSTFIELDS, binaryptr);
00528   curl_easy_setopt(p.curl, CURLOPT_POSTFIELDSIZE, (long)tabLength);
00529 
00530 
00531   /* Perform the request, res will get the return code */
00532 
00533   BIO_printf(p.errorbio, "%d %s %d\n", __LINE__, "curl_easy_perform",
00534              res = curl_easy_perform(p.curl));
00535   {
00536     int result =curl_easy_getinfo(p.curl, CURLINFO_CONTENT_TYPE, &response);
00537     if(mimetypeaccept && p.verbose)
00538       if(!strcmp(mimetypeaccept, response))
00539         BIO_printf(p.errorbio, "the response has a correct mimetype : %s\n",
00540                    response);
00541       else
00542         BIO_printf(p.errorbio, "the response doesn\'t have an acceptable "
00543                    "mime type, it is %s instead of %s\n",
00544                    response, mimetypeaccept);
00545   }
00546 
00547   /*** code d'erreur si accept mime ***, egalement code return HTTP != 200 ***/
00548 
00549 /* free the header list*/
00550 
00551   curl_slist_free_all(headers);
00552 
00553   /* always cleanup */
00554   curl_easy_cleanup(p.curl);
00555 
00556   BIO_free(in);
00557   BIO_free(out);
00558   return (EXIT_SUCCESS);
00559 
00560   err: BIO_printf(p.errorbio, "error");
00561   exit(1);
00562 }


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:02