Back to index

nordugrid-arc-nox  1.1.0~rc6
PayloadTLSMCC.cpp
Go to the documentation of this file.
00001 #ifdef HAVE_CONFIG_H
00002 #include <config.h>
00003 #endif
00004 
00005 #include <fstream>
00006 
00007 #include "GlobusSigningPolicy.h"
00008 
00009 #include "PayloadTLSMCC.h"
00010 #include <openssl/err.h>
00011 #include <glibmm/miscutils.h>
00012 #include <arc/DateTime.h>
00013 
00014 namespace Arc {
00015 
00016 static void* ex_data_id = (void*)"ARC_MCC_Payload_TLS";
00017 int PayloadTLSMCC::ex_data_index_ = -1;
00018 
00019 #ifndef HAVE_OPENSSL_X509_VERIFY_PARAM
00020 #define FLAG_CRL_DISABLED (0x1)
00021 
00022 static unsigned long get_flag_STORE_CTX(X509_STORE_CTX* container) {
00023   PayloadTLSMCC* it = PayloadTLSMCC::RetrieveInstance(container);
00024   if(!it) return 0;
00025   return it->Flags();
00026 }
00027 
00028 static void set_flag_STORE_CTX(X509_STORE_CTX* container,unsigned long flags) {
00029   PayloadTLSMCC* it = PayloadTLSMCC::RetrieveInstance(container);
00030   if(!it) return;
00031   it->Flags(flags);
00032 }
00033 #endif
00034 
00035 Time asn1_to_utctime(const ASN1_UTCTIME *s) {
00036   std::string t_str;
00037   if(!s) return Time();
00038   if(s->type == V_ASN1_UTCTIME) {
00039     t_str.append("20");
00040     t_str.append((char*)(s->data));
00041   }
00042   else {//V_ASN1_GENERALIZEDTIME
00043     t_str.append((char*)(s->data));
00044   }
00045   return Time(t_str);
00046 }
00047 
00048 // This callback implements additional verification
00049 // algorithms not present in OpenSSL
00050 static int verify_callback(int ok,X509_STORE_CTX *sctx) {
00051 /*
00052 #ifdef HAVE_OPENSSL_X509_VERIFY_PARAM
00053   unsigned long flag = get_flag_STORE_CTX(sctx);
00054   if(!(flag & FLAG_CRL_DISABLED)) {
00055     // Not sure if this will work
00056 #if OPENSSL_VERSION_NUMBER < 0x00908000
00057     if(!(sctx->flags & X509_V_FLAG_CRL_CHECK)) {
00058       sctx->flags |= X509_V_FLAG_CRL_CHECK;
00059 #else
00060     if(!(sctx->param->flags & X509_V_FLAG_CRL_CHECK)) {
00061       sctx->param->flags |= X509_V_FLAG_CRL_CHECK;
00062 #endif
00063       ok=X509_verify_cert(sctx);
00064       return ok;
00065     };
00066   };
00067 #endif
00068 */
00069   if (ok != 1) {
00070     int err = X509_STORE_CTX_get_error(sctx);
00071     switch(err) {
00072 #ifdef HAVE_OPENSSL_PROXY
00073       case X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED: {
00074         // This shouldn't happen here because flags are already set
00075         // to allow proxy. But one can never know because used flag
00076         // setting method is undocumented.
00077         X509_STORE_CTX_set_flags(sctx,X509_V_FLAG_ALLOW_PROXY_CERTS);
00078         // Calling X509_verify_cert will cause a recursive call to verify_callback.
00079         // But there should be no loop because PROXY_CERTIFICATES_NOT_ALLOWED error
00080         // can't happen anymore.
00081         //ok=X509_verify_cert(sctx);
00082         ok=1;
00083         if(ok == 1) X509_STORE_CTX_set_error(sctx,X509_V_OK);
00084       }; break;
00085 #endif
00086       case X509_V_ERR_UNABLE_TO_GET_CRL: {
00087         // Missing CRL is not an error (TODO: make it configurable)
00088         // Consider that to be a policy of site like Globus does
00089         // Not sure of there is need for recursive X509_verify_cert() here.
00090         // It looks like openssl runs tests sequentially for whole chain.
00091         // But not sure if behavior is same in all versions.
00092 #ifdef HAVE_OPENSSL_X509_VERIFY_PARAM
00093         if(sctx->param) {
00094           X509_VERIFY_PARAM_clear_flags(sctx->param,X509_V_FLAG_CRL_CHECK);
00095           //ok=X509_verify_cert(sctx);
00096           //X509_VERIFY_PARAM_set_flags(sctx->param,X509_V_FLAG_CRL_CHECK);
00097           ok=1;
00098           if(ok == 1) X509_STORE_CTX_set_error(sctx,X509_V_OK);
00099         };
00100 #else
00101 
00102 #if OPENSSL_VERSION_NUMBER < 0x00908000
00103         sctx->flags &= ~X509_V_FLAG_CRL_CHECK;
00104 #else
00105         sctx->param->flags &= ~X509_V_FLAG_CRL_CHECK;
00106 #endif
00107 
00108         set_flag_STORE_CTX(sctx,get_flag_STORE_CTX(sctx) | FLAG_CRL_DISABLED);
00109         //ok=X509_verify_cert(sctx);
00110         //sctx->flags |= X509_V_FLAG_CRL_CHECK;
00111         //set_flag_STORE_CTX(sctx,get_flag_STORE_CTX(sctx) & (~FLAG_CRL_DISABLED));
00112         ok=1;
00113         if(ok == 1) X509_STORE_CTX_set_error(sctx,X509_V_OK);
00114 #endif
00115       }; break;
00116       default: {
00117         PayloadTLSMCC::HandleError(Logger::getRootLogger(),err);
00118       }; break;
00119     };
00120   };
00121   if(ok == 1) {
00122     // Do additional verification here.
00123     PayloadTLSMCC* it = PayloadTLSMCC::RetrieveInstance(sctx);
00124     X509* cert = X509_STORE_CTX_get_current_cert(sctx);
00125     char* subject_name = X509_NAME_oneline(X509_get_subject_name(cert),NULL,0);
00126     if(it == NULL) {
00127       Logger::getRootLogger().msg(WARNING,"Failed to retrieve link to TLS stream. Additional policy matching is skipped.");
00128     } else {
00129       // Globus signing policy
00130       // Do not apply to proxies and self-signed CAs.
00131       if((it->Config().GlobusPolicy()) && (!(it->Config().CADir().empty()))) {
00132 #ifdef HAVE_OPENSSL_PROXY
00133         int pos = X509_get_ext_by_NID(cert,NID_proxyCertInfo,-1);
00134         if(pos < 0) {
00135 #else
00136         {
00137 #endif
00138           std::istream* in = open_globus_policy(X509_get_issuer_name(cert),it->Config().CADir());
00139           if(in) {
00140             if(!match_globus_policy(*in,X509_get_issuer_name(cert),X509_get_subject_name(cert))) {
00141               Logger::getRootLogger().msg(ERROR,"Certificate %s failed Globus signing policy",subject_name);
00142               ok=0;
00143               X509_STORE_CTX_set_error(sctx,X509_V_ERR_SUBJECT_ISSUER_MISMATCH);            };
00144             delete in;
00145           };
00146         };
00147       };
00148     };
00149     //Check the left validity time of the peer certificate;
00150     //Give warning if the certificate is going to be expired
00151     //in a while of time
00152     Time exptime = asn1_to_utctime(X509_get_notAfter(cert));
00153     if(exptime <= Time()) {
00154       Logger::getRootLogger().msg(WARNING,"Certificate %s already expired",subject_name);
00155     } else {
00156       Arc::Period timeleft = asn1_to_utctime(X509_get_notAfter(cert)) - Time();
00157 #ifdef HAVE_OPENSSL_PROXY
00158       int pos = X509_get_ext_by_NID(cert,NID_proxyCertInfo,-1);
00159 #else
00160       int pos = -1;
00161 #endif
00162       //for proxy certificate, give warning 1 hour in advance
00163       //for EEC certificate, give warning 5 days in advance
00164       if(((pos < 0) && (timeleft <= 5*24*3600)) ||
00165          (timeleft <= 3600)) {
00166         Logger::getRootLogger().msg(WARNING,"Certificate %s will expire in %s", subject_name, timeleft.istr());
00167       }
00168     }
00169     OPENSSL_free(subject_name);
00170   };
00171   return ok;
00172 }
00173 
00174 // This callback is just a placeholder. We do not expect
00175 // encrypted private keys here.
00176 static int no_passphrase_callback(char*, int, int, void*) {
00177    return -1;
00178 }
00179 
00180 bool PayloadTLSMCC::StoreInstance(void) {
00181    if(ex_data_index_ == -1) {
00182       // In case of race condition we will have 2 indices assigned - harmless
00183       ex_data_index_=SSL_CTX_get_ex_new_index(0,ex_data_id,NULL,NULL,NULL);
00184    };
00185    if(ex_data_index_ == -1) {
00186       Logger::getRootLogger().msg(ERROR,"Failed to store application data");
00187       return false;
00188    };
00189    SSL_CTX_set_ex_data(sslctx_,ex_data_index_,this);
00190    return true;
00191 }
00192 
00193 PayloadTLSMCC* PayloadTLSMCC::RetrieveInstance(X509_STORE_CTX* container) {
00194   PayloadTLSMCC* it = NULL;
00195   if(ex_data_index_ != -1) {
00196     SSL* ssl = (SSL*)X509_STORE_CTX_get_ex_data(container,SSL_get_ex_data_X509_STORE_CTX_idx());
00197     if(ssl != NULL) {
00198       SSL_CTX* ssl_ctx = SSL_get_SSL_CTX(ssl);
00199       if(ssl_ctx != NULL) {
00200         it = (PayloadTLSMCC*)SSL_CTX_get_ex_data(ssl_ctx,ex_data_index_);
00201       }
00202     };
00203   };
00204   if(it == NULL) {
00205     Logger::getRootLogger().msg(ERROR,"Failed to retrieve application data from OpenSSL");
00206   };
00207   return it;
00208 }
00209 
00210 PayloadTLSMCC::PayloadTLSMCC(MCCInterface* mcc, const ConfigTLSMCC& cfg, Logger& logger):PayloadTLSStream(logger),sslctx_(NULL),config_(cfg) {
00211    // Client mode
00212    int err = SSL_ERROR_NONE;
00213    master_=true;
00214    // Creating BIO for communication through stream which it will
00215    // extract from provided MCC
00216    BIO* bio = BIO_new_MCC(mcc);
00217    // Initialize the SSL Context object
00218    if(cfg.IfTLSHandshake()) {
00219      sslctx_=SSL_CTX_new(SSLv23_client_method());
00220    } else {
00221      sslctx_=SSL_CTX_new(SSLv3_client_method());
00222    };
00223    if(sslctx_==NULL){
00224       logger.msg(ERROR, "Can not create the SSL Context object");
00225       goto error;
00226    };
00227    SSL_CTX_set_mode(sslctx_,SSL_MODE_ENABLE_PARTIAL_WRITE);
00228    SSL_CTX_set_session_cache_mode(sslctx_,SSL_SESS_CACHE_OFF);
00229    if(!config_.Set(sslctx_,logger_)) goto error;
00230    SSL_CTX_set_verify(sslctx_, SSL_VERIFY_PEER |  SSL_VERIFY_FAIL_IF_NO_PEER_CERT, &verify_callback);
00231 
00232    // Allow proxies, request CRL check
00233 #ifdef HAVE_OPENSSL_X509_VERIFY_PARAM
00234    if(sslctx_->param == NULL) {
00235       logger.msg(ERROR,"Can't set OpenSSL verify flags");
00236       goto error;
00237    } else {
00238 #ifdef HAVE_OPENSSL_PROXY
00239       if(sslctx_->param) X509_VERIFY_PARAM_set_flags(sslctx_->param,X509_V_FLAG_CRL_CHECK | X509_V_FLAG_ALLOW_PROXY_CERTS);
00240 #else
00241       if(sslctx_->param) X509_VERIFY_PARAM_set_flags(sslctx_->param,X509_V_FLAG_CRL_CHECK);
00242 #endif
00243    };
00244 #endif
00245    StoreInstance();
00246 #ifdef SSL_OP_NO_TICKET
00247    SSL_CTX_set_options(sslctx_, SSL_OP_SINGLE_DH_USE | SSL_OP_NO_SSLv2 | SSL_OP_ALL | SSL_OP_NO_TICKET);
00248 #else
00249    SSL_CTX_set_options(sslctx_, SSL_OP_SINGLE_DH_USE | SSL_OP_NO_SSLv2 | SSL_OP_ALL);
00250 #endif
00251 
00252    SSL_CTX_set_default_passwd_cb(sslctx_, no_passphrase_callback);
00253    /* Get DN from certificate, and put it into message's attribute */
00254 
00255    // Creating SSL object for handling connection
00256    ssl_ = SSL_new(sslctx_);
00257    if (ssl_ == NULL){
00258       logger.msg(ERROR, "Can not create the SSL object");
00259       goto error;
00260    };
00261    SSL_set_bio(ssl_,bio,bio); bio=NULL;
00262    //SSL_set_connect_state(ssl_);
00263    if((err=SSL_connect(ssl_)) != 1) {
00264       logger.msg(ERROR, "Failed to establish SSL connection");
00265       goto error;
00266    };
00267    // if(SSL_in_init(ssl_)){
00268    //handle error
00269    // }
00270    return;
00271 error:
00272    HandleError(err);
00273    if(bio) BIO_free(bio);
00274    if(ssl_) SSL_free(ssl_); ssl_=NULL;
00275    if(sslctx_) SSL_CTX_free(sslctx_); sslctx_=NULL;
00276    return;
00277 }
00278 
00279 PayloadTLSMCC::PayloadTLSMCC(PayloadStreamInterface* stream, const ConfigTLSMCC& cfg, Logger& logger):PayloadTLSStream(logger),sslctx_(NULL),config_(cfg) {
00280    // Server mode
00281    int err = SSL_ERROR_NONE;
00282    master_=true;
00283    // Creating BIO for communication through provided stream
00284    BIO* bio = BIO_new_MCC(stream);
00285    // Initialize the SSL Context object
00286    if(cfg.IfTLSHandshake()) {
00287      sslctx_=SSL_CTX_new(SSLv23_server_method());
00288    } else {
00289      sslctx_=SSL_CTX_new(SSLv3_server_method());
00290    };
00291    if(sslctx_==NULL){
00292       logger.msg(ERROR, "Can not create the SSL Context object");
00293       goto error;
00294    };
00295    SSL_CTX_set_mode(sslctx_,SSL_MODE_ENABLE_PARTIAL_WRITE);
00296    SSL_CTX_set_session_cache_mode(sslctx_,SSL_SESS_CACHE_OFF);
00297    if(config_.IfClientAuthn()) {
00298      SSL_CTX_set_verify(sslctx_, SSL_VERIFY_PEER |  SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE, &verify_callback);
00299    }
00300    else {
00301      SSL_CTX_set_verify(sslctx_, SSL_VERIFY_NONE, NULL);
00302    }
00303    if(!config_.Set(sslctx_,logger_)) goto error;
00304 
00305    // Allow proxies, request CRL check
00306 #ifdef HAVE_OPENSSL_X509_VERIFY_PARAM
00307    if(sslctx_->param == NULL) {
00308       logger.msg(ERROR,"Can't set OpenSSL verify flags");
00309       goto error;
00310    } else {
00311 #ifdef HAVE_OPENSSL_PROXY
00312       if(sslctx_->param) X509_VERIFY_PARAM_set_flags(sslctx_->param,X509_V_FLAG_CRL_CHECK | X509_V_FLAG_ALLOW_PROXY_CERTS);
00313 #else
00314       if(sslctx_->param) X509_VERIFY_PARAM_set_flags(sslctx_->param,X509_V_FLAG_CRL_CHECK);
00315 #endif
00316    };
00317 #endif
00318    StoreInstance();
00319    SSL_CTX_set_options(sslctx_, SSL_OP_SINGLE_DH_USE | SSL_OP_NO_SSLv2 | SSL_OP_ALL);
00320    SSL_CTX_set_default_passwd_cb(sslctx_, no_passphrase_callback);
00321 
00322    // Creating SSL object for handling connection
00323    ssl_ = SSL_new(sslctx_);
00324    if (ssl_ == NULL){
00325       logger.msg(ERROR, "Can not create the SSL object");
00326       goto error;
00327    };
00328    SSL_set_bio(ssl_,bio,bio); bio=NULL;
00329    //SSL_set_accept_state(ssl_);
00330    if((err=SSL_accept(ssl_)) != 1) {
00331       logger.msg(ERROR, "Failed to accept SSL connection");
00332       goto error;
00333    };
00334    //handle error
00335    // if(SSL_in_init(ssl_)){
00336    //handle error
00337    // }
00338    return;
00339 error:
00340    HandleError(err);
00341    if(bio) BIO_free(bio);
00342    if(ssl_) SSL_free(ssl_); ssl_=NULL;
00343    if(sslctx_) SSL_CTX_free(sslctx_); sslctx_=NULL;
00344    return;
00345 }
00346 
00347 PayloadTLSMCC::PayloadTLSMCC(PayloadTLSMCC& stream):
00348   PayloadTLSStream(stream), config_(stream.config_) {
00349    master_=false;
00350    sslctx_=stream.sslctx_;
00351    ssl_=stream.ssl_;
00352 }
00353 
00354 
00355 PayloadTLSMCC::~PayloadTLSMCC(void) {
00356   if (!master_)
00357     return;
00358   if (ssl_) {
00359     if (SSL_shutdown(ssl_) < 0)
00360       logger_.msg(ERROR, "Failed to shut down SSL");
00361     SSL_free(ssl_);
00362     ssl_ = NULL;
00363   }
00364   if(sslctx_) {
00365     SSL_CTX_free(sslctx_);
00366     sslctx_ = NULL;
00367   }
00368 }
00369 
00370 } // namespace Arc