XrdCryptosslAux.cc

Go to the documentation of this file.
00001 // $Id: XrdCryptosslAux.cc 35287 2010-09-14 21:19:35Z ganis $
00002 
00003 const char *XrdCryptosslAuxCVSID = "$Id: XrdCryptosslAux.cc 35287 2010-09-14 21:19:35Z ganis $";
00004 /******************************************************************************/
00005 /*                                                                            */
00006 /*                  X r d C r y p t o S s l A u x . h h                       */
00007 /*                                                                            */
00008 /* (c) 2005 G. Ganis, CERN                                                    */
00009 /*                                                                            */
00010 /******************************************************************************/
00011 
00012 /* ************************************************************************** */
00013 /*                                                                            */
00014 /* OpenSSL utility functions                                                  */
00015 /*                                                                            */
00016 /* ************************************************************************** */
00017 #include <time.h>
00018 #include <errno.h>
00019 #include <stdlib.h>
00020 #include <sys/types.h>
00021 #include <sys/stat.h>
00022 
00023 #include <XrdCrypto/XrdCryptoX509Chain.hh>
00024 #include <XrdCrypto/XrdCryptosslAux.hh>
00025 #include <XrdCrypto/XrdCryptosslRSA.hh>
00026 #include <XrdCrypto/XrdCryptosslX509.hh>
00027 #include <XrdCrypto/XrdCryptosslTrace.hh>
00028 #include <openssl/pem.h>
00029 
00030 // Error code from verification set by verify callback function
00031 static int gErrVerifyChain = 0;
00032 //____________________________________________________________________________
00033 int XrdCryptosslX509VerifyCB(int ok, X509_STORE_CTX *ctx)
00034 {
00035    // Verify callback function
00036 
00037    // Reset global error
00038    gErrVerifyChain = 0;
00039 
00040    if (ok != 0) {
00041 
00042       // Error analysis
00043       gErrVerifyChain = 1;
00044    }
00045 
00046    // We are done
00047    return ok;
00048 }
00049 
00050 //____________________________________________________________________________
00051 int XrdCryptosslKDFunLen()
00052 {
00053    // default buffer length
00054    return kSslKDFunDefLen;
00055 }
00056 
00057 //____________________________________________________________________________
00058 int XrdCryptosslKDFun(const char *pass, int plen, const char *salt, int slen,
00059                       char *key, int klen)
00060 {
00061    // Password-Based Key Derivation Function 2, specified in PKCS #5
00062    // Following (J.Viega, M.Messier, "Secure programming Cookbook", p.141),
00063    // the default number of iterations is set to 10000 .
00064    // It can be specified at the beginning of the salt using a construct
00065    // like this: salt = "$$<number_of_iterations>$<effective_salt>"
00066   
00067    klen = (klen <= 0) ? 24 : klen;
00068 
00069    // Defaults
00070    char *realsalt = (char *)salt;
00071    int realslen = slen;
00072    int it = 10000;
00073    //
00074    // Extract iteration number from salt, if any
00075    char *ibeg = (char *)memchr(salt+1,'$',slen-1);
00076    if (ibeg) {
00077       char *del = 0;
00078       int newit = strtol(ibeg+1, &del, 10);
00079       if (newit > 0 && del[0] == '$' && errno != ERANGE) {
00080          // found iteration number
00081          it = newit;
00082          realsalt = del+1;
00083          realslen = slen - (int)(realsalt-salt);
00084       }
00085    }
00086 
00087    PKCS5_PBKDF2_HMAC_SHA1(pass, plen,
00088                          (unsigned char *)realsalt, realslen, it,
00089                           klen, (unsigned char *)key);
00090    return klen;
00091 }
00092 
00093 //____________________________________________________________________________
00094 bool XrdCryptosslX509VerifyCert(XrdCryptoX509 *cert, XrdCryptoX509 *ref)
00095 {
00096    // Verify signature of cert using public key of ref
00097 
00098    // Input must make sense
00099    X509 *c = cert ? (X509 *)(cert->Opaque()) : 0;
00100    X509 *r = ref ? (X509 *)(ref->Opaque()) : 0;
00101    EVP_PKEY *rk = r ? X509_get_pubkey(r) : 0;
00102    if (!c || !rk) return 0;
00103 
00104    // Ok: we can verify
00105    return (X509_verify(c, rk) > 0);
00106 }
00107 
00108 //____________________________________________________________________________
00109 bool XrdCryptosslX509VerifyChain(XrdCryptoX509Chain *chain, int &errcode)
00110 {
00111    // Verifies crossed signatures of X509 certificate 'chain'
00112    // In case of failure, and error code is returned in errcode.
00113 
00114    // Make sure we got a potentially meaningful chain
00115    if (!chain || chain->Size() <= 1)
00116       return 0;
00117 
00118    // Create a store
00119    X509_STORE *store = X509_STORE_new();
00120    if (!store)
00121       return 0;
00122 
00123    // Set the verify callback function
00124    X509_STORE_set_verify_cb_func(store,0);
00125 
00126    // Add the first (the CA) certificate
00127    XrdCryptoX509 *cert = chain->Begin();
00128    if (cert->type != XrdCryptoX509::kCA && cert->Opaque())
00129       return 0;
00130    X509_STORE_add_cert(store, (X509 *)(cert->Opaque()));
00131 
00132    // Create a stack
00133    STACK_OF(X509) *stk = sk_X509_new_null();
00134    if (!stk)
00135       return 0;
00136 
00137    // Fill it with chain we have
00138    X509 *cref = 0;
00139    while ((cert = chain->Next()) && cert->Opaque()) {
00140       if (!cref)
00141          cref = (X509 *)(cert->Opaque());
00142       sk_X509_push(stk, (X509 *)(cert->Opaque()));
00143    }
00144 
00145    // Make sure all the certificates have been inserted
00146 #if OPENSSL_VERSION_NUMBER >= 0x10000000L
00147    if (sk_X509_num(stk) != chain->Size() - 1)
00148 #else /* OPENSSL */
00149    if (sk_num(stk) != chain->Size() - 1)
00150 #endif /* OPENSSL */
00151       return 0;
00152 
00153    // Create a store ctx ...
00154    X509_STORE_CTX *ctx = X509_STORE_CTX_new();
00155    if (!ctx)
00156       return 0;
00157 
00158    // ... and initialize it
00159    X509_STORE_CTX_init(ctx, store, cref, stk);
00160 
00161    // verify ?
00162    bool verify_ok = (X509_verify_cert(ctx) == 1);
00163 
00164    // Fill error code, if any
00165    errcode = 0;
00166    if (!verify_ok)
00167       errcode = gErrVerifyChain;
00168 
00169    return verify_ok;
00170 }
00171 
00172 //____________________________________________________________________________
00173 XrdSutBucket *XrdCryptosslX509ExportChain(XrdCryptoX509Chain *chain,
00174                                           bool withprivatekey)
00175 {
00176    // Export non-CA content of 'chain' into a bucket for transfer.
00177    EPNAME("X509ExportChain");
00178    XrdSutBucket *bck = 0;
00179 
00180    // Make sure we got something to export
00181    if (!chain || chain->Size() <= 0) {
00182       DEBUG("chain undefined or empty: nothing to export");
00183       return bck;
00184    }
00185 
00186    // Do not export CA selfsigned certificates
00187    if (chain->Size() == 1 && chain->Begin()->type == XrdCryptoX509::kCA &&
00188        !strcmp(chain->Begin()->IssuerHash(),chain->Begin()->SubjectHash())) {
00189       DEBUG("chain contains only a CA certificate: nothing to export");
00190       return bck;
00191    }
00192 
00193    // Now we create a bio_mem to serialize the certificates
00194    BIO *bmem = BIO_new(BIO_s_mem());
00195    if (!bmem) {
00196       DEBUG("unable to create BIO for memory operations");
00197       return bck;
00198    }
00199 
00200    // Reorder the chain
00201    chain->Reorder();
00202 
00203    // Write the last cert first
00204    XrdCryptoX509 *c = chain->End();
00205    if (!PEM_write_bio_X509(bmem, (X509 *)c->Opaque())) {
00206       DEBUG("error while writing proxy certificate"); 
00207       BIO_free(bmem);
00208       return bck;
00209    }
00210    // Write its private key, if any and if asked
00211    if (withprivatekey) {
00212       XrdCryptoRSA *k = c->PKI();
00213       if (k->status == XrdCryptoRSA::kComplete) {
00214          if (!PEM_write_bio_PrivateKey(bmem, (EVP_PKEY *)(k->Opaque()),
00215                                   0, 0, 0, 0, 0)) {
00216             DEBUG("error while writing proxy private key"); 
00217             BIO_free(bmem);
00218             return bck;
00219          }
00220       }
00221    }
00222    // Now write all other certificates (except selfsigned CAs ...)
00223    while ((c = chain->SearchBySubject(c->Issuer()))) {
00224       if (c->type == XrdCryptoX509::kCA) {
00225          DEBUG("Encountered CA in chain; breaking.  Subject: " << c->Subject());
00226          break;
00227       }
00228       if (strcmp(c->IssuerHash(), c->SubjectHash())) {
00229          // Write to bucket
00230          if (!PEM_write_bio_X509(bmem, (X509 *)c->Opaque())) {
00231             DEBUG("error while writing proxy certificate"); 
00232             BIO_free(bmem);
00233             return bck;
00234          }
00235       } else {
00236          DEBUG("Encountered self-signed CA in chain; breaking.  Subject: " << c->Subject());
00237          break;
00238       }
00239    }
00240 
00241    // Extract pointer to BIO data and length of segment
00242    char *bdata = 0;  
00243    int blen = BIO_get_mem_data(bmem, &bdata);
00244    DEBUG("BIO data: "<<blen<<" bytes at 0x"<<(int *)bdata);
00245 
00246    // create the bucket now
00247    bck = new XrdSutBucket(0, 0, kXRS_x509);
00248    if (bck) {
00249       // Fill bucket
00250       bck->SetBuf(bdata, blen);
00251       DEBUG("result of serialization: "<<bck->size<<" bytes");
00252    } else {
00253       DEBUG("unable to create bucket for serialized format");
00254       BIO_free(bmem);
00255       return bck;
00256    }
00257    //
00258    // Free BIO
00259    BIO_free(bmem);
00260    //
00261    // We are done
00262    return bck;
00263 }
00264 
00265 //____________________________________________________________________________
00266 int XrdCryptosslX509ChainToFile(XrdCryptoX509Chain *ch, const char *fn)
00267 {
00268    // Dump non-CA content of chain 'c' into file 'fn'
00269    EPNAME("X509ChainToFile");
00270 
00271    // Check inputs
00272    if (!ch || !fn) {
00273       DEBUG("Invalid inputs");
00274       return -1;
00275    }
00276 
00277    // We proceed only if we can lock for write
00278    FILE *fp = fopen(fn,"w");
00279    if (!fp) {
00280       DEBUG("cannot open file to save chain (file: "<<fn<<")"); 
00281       return -1;
00282    }
00283    int ifp = fileno(fp);
00284    if (ifp == -1) {
00285       DEBUG("got invalid file descriptor (file: "<<fn<<")"); 
00286       fclose(fp);
00287       return -1;
00288    }
00289 
00290    // We need to lock from now on
00291    {  XrdSutFileLocker fl(ifp,XrdSutFileLocker::kExcl);
00292 
00293       // If not successful, return
00294       if (!fl.IsValid()) { 
00295          DEBUG("could not lock file: "<<fn<<")"); 
00296          fclose(fp);
00297          return -1;
00298       }
00299 
00300       // Set permissions to 0600
00301       if (fchmod(ifp, 0600) == -1) {
00302          DEBUG("cannot set permissions on file: "<<fn<<" (errno: "<<errno<<")"); 
00303          fclose(fp);
00304          return -1;
00305       }
00306 
00307       // Reorder the chain
00308       ch->Reorder();
00309 
00310       // Write the last cert first
00311       XrdCryptoX509 *c = ch->End();
00312       if (PEM_write_X509(fp, (X509 *)c->Opaque()) != 1) {
00313          DEBUG("error while writing proxy certificate"); 
00314          fclose(fp);
00315          return -1;
00316       }
00317       // Write its private key, if any
00318       XrdCryptoRSA *k = c->PKI();
00319       if (k->status == XrdCryptoRSA::kComplete) {
00320          if (PEM_write_PrivateKey(fp, (EVP_PKEY *)(k->Opaque()),
00321                                   0, 0, 0, 0, 0) != 1) {
00322             DEBUG("error while writing proxy private key"); 
00323             fclose(fp);
00324             return -1;
00325          }
00326       }
00327       // Now write all other certificates
00328       while ((c = ch->SearchBySubject(c->Issuer())) && c->type != XrdCryptoX509::kCA) {
00329          // Write to file
00330          if (PEM_write_X509(fp, (X509 *)c->Opaque()) != 1) {
00331             DEBUG("error while writing proxy certificate"); 
00332             fclose(fp);
00333             return -1;
00334          }
00335       }
00336    } // Unlocks the file
00337 
00338    // CLose the file
00339    fclose(fp);
00340    //
00341    // We are done
00342    return 0;
00343 }
00344 
00345 //____________________________________________________________________________
00346 int XrdCryptosslX509ParseFile(const char *fname,
00347                               XrdCryptoX509Chain *chain)
00348 {
00349    // Parse content of file 'fname' and add X509 certificates to
00350    // chain (which must be initialized by the caller).
00351    // If a private key matching the public key of one of the certificates
00352    // is found in teh file, the certificate key is completed.
00353    EPNAME("X509ParseFile");
00354    int nci = 0;
00355 
00356    // Make sure we got a file to import
00357    if (!fname) {
00358       DEBUG("file name undefined: can do nothing");
00359       return nci;
00360    }
00361 
00362    // Make sure we got a chain where to add the certificates
00363    if (!chain) {
00364       DEBUG("chain undefined: can do nothing");
00365       return nci;
00366    }
00367 
00368    //
00369    // Open file and read the content:
00370    // it should contain blocks on information in PEM form
00371    FILE *fcer = fopen(fname, "r");
00372    if (!fcer) {
00373       DEBUG("unable to open file (errno: "<<errno<<")");
00374       return nci;
00375    }
00376 
00377    // Now read out certificates and add them to the chain
00378    X509 *xcer = 0;
00379    while (PEM_read_X509(fcer, &xcer, 0, 0)) {
00380       // Add it to the chain
00381       XrdCryptoX509 *c = new XrdCryptosslX509(xcer);
00382       if (c) {
00383          chain->PushBack(c);
00384          nci++;
00385          DEBUG("certificate added to the chain - ord: "<<chain->Size());
00386       } else {
00387          DEBUG("could not create certificate: memory exhausted?");
00388          fclose(fcer);
00389          return nci;
00390       }
00391       xcer = 0;
00392    }
00393 
00394    // If we found something, and we are asked to extract a key,
00395    // rewind and look for it
00396    if (nci) {
00397       rewind(fcer);
00398       RSA  *rsap = 0;
00399       if (!PEM_read_RSAPrivateKey(fcer, &rsap, 0, 0)) {
00400          DEBUG("no RSA private key found in file "<<fname);
00401       } else {
00402          DEBUG("found a RSA private key in file "<<fname);
00403          // We need to complete the key: we save it temporarly
00404          // to a bio and check all the private keys of the
00405          // loaded certificates 
00406          bool ok = 1;
00407          BIO *bkey = BIO_new(BIO_s_mem());
00408          if (!bkey) {
00409             DEBUG("unable to create BIO for key completion");
00410             ok = 0;
00411          }
00412          if (ok) {
00413             // Write the private key
00414             if (!PEM_write_bio_RSAPrivateKey(bkey,rsap,0,0,0,0,0)) {
00415                DEBUG("unable to write RSA private key to bio");
00416                ok = 0;
00417             }
00418          }
00419          RSA_free(rsap);
00420          if (ok) {
00421             // Loop over the chain certificates
00422             XrdCryptoX509 *cert = chain->Begin();
00423             while (cert->Opaque()) {
00424                if (cert->type != XrdCryptoX509::kCA) {
00425                   // Get the public key
00426                   EVP_PKEY *evpp = X509_get_pubkey((X509 *)(cert->Opaque()));
00427                   if (evpp) {
00428 #if OPENSSL_VERSION_NUMBER >= 0x10000000L
00429                      // evpp gets reset by the other call on >=1.0.0; to be investigated
00430                      if (PEM_read_bio_RSAPrivateKey(bkey,&(evpp->pkey.rsa),0,0)) {
00431 #else
00432                      if (PEM_read_bio_PrivateKey(bkey,&evpp,0,0)) {
00433 #endif
00434                         DEBUG("RSA key completed ");
00435                         // Test consistency
00436                         int rc = RSA_check_key(evpp->pkey.rsa);
00437                         if (rc != 0) {
00438                            // Update PKI in certificate
00439                            cert->SetPKI((XrdCryptoX509data)evpp);
00440                            // Update status
00441                            cert->PKI()->status = XrdCryptoRSA::kComplete;
00442                            break;
00443                         }
00444                      }
00445                   }
00446                }
00447                // Get next
00448                cert = chain->Next();
00449             }
00450          }
00451          // Cleanup
00452          BIO_free(bkey);
00453       }
00454    }
00455 
00456    // We can close the file now
00457    fclose(fcer);
00458 
00459    // We are done
00460    return nci;
00461 }
00462 
00463 //____________________________________________________________________________
00464 int XrdCryptosslX509ParseBucket(XrdSutBucket *b, XrdCryptoX509Chain *chain)
00465 {
00466    // Import certificate(s) from bucket b adding them to 'chain'
00467    // (which must be initialized by the caller).
00468    EPNAME("X509ParseBucket");
00469    int nci = 0;
00470 
00471    // Make sure we got something to import
00472    if (!b || b->size <= 0) {
00473       DEBUG("bucket undefined or empty: can do nothing");
00474       return nci;
00475    }
00476 
00477    // Make sure we got a chain where to add the certificates
00478    if (!chain) {
00479       DEBUG("chain undefined: can do nothing");
00480       return nci;
00481    }
00482    //
00483    // Now we create a bio_mem to store the certificates
00484    BIO *bmem = BIO_new(BIO_s_mem());
00485    if (!bmem) {
00486       DEBUG("unable to create BIO to import certificates");
00487       return nci;
00488    }
00489 
00490    // Write data to BIO
00491    if (BIO_write(bmem,(const void *)(b->buffer),b->size) != b->size) {
00492       DEBUG("problems writing data to BIO");
00493       BIO_free(bmem);
00494       return nci;
00495    }
00496 
00497    // Get certificates from BIO
00498    X509 *xcer = 0;
00499    while (PEM_read_bio_X509(bmem,&xcer,0,0)) {
00500       //
00501       // Create container and add to the list
00502       XrdCryptoX509 *c = new XrdCryptosslX509(xcer);
00503       if (c) {
00504          chain->PushBack(c);
00505          nci++;
00506          DEBUG("certificate added to the chain - ord: "<<chain->Size());
00507       } else {
00508          DEBUG("could not create certificate: memory exhausted?");
00509          BIO_free(bmem);
00510          return nci;
00511       }
00512       // reset cert otherwise the next one is not fetched
00513       xcer = 0;
00514    }
00515 
00516    // If we found something, and we are asked to extract a key,
00517    // refill the BIO and search again for the key (this is mandatory
00518    // as read operations modify the BIO contents; a read-only BIO
00519    // may be more efficient)
00520    if (nci && BIO_write(bmem,(const void *)(b->buffer),b->size) == b->size) {
00521       RSA  *rsap = 0;
00522       if (!PEM_read_bio_RSAPrivateKey(bmem, &rsap, 0, 0)) {
00523          DEBUG("no RSA private key found in bucket ");
00524       } else {
00525          DEBUG("found a RSA private key in bucket ");
00526          // We need to complete the key: we save it temporarly
00527          // to a bio and check all the private keys of the
00528          // loaded certificates 
00529          bool ok = 1;
00530          BIO *bkey = BIO_new(BIO_s_mem());
00531          if (!bkey) {
00532             DEBUG("unable to create BIO for key completion");
00533             ok = 0;
00534          }
00535          if (ok) {
00536             // Write the private key
00537             if (!PEM_write_bio_RSAPrivateKey(bkey,rsap,0,0,0,0,0)) {
00538                DEBUG("unable to write RSA private key to bio");
00539                ok = 0;
00540             }
00541          }
00542          RSA_free(rsap);
00543          if (ok) {
00544             // Loop over the chain certificates
00545             XrdCryptoX509 *cert = chain->Begin();
00546             while (cert->Opaque()) {
00547                if (cert->type != XrdCryptoX509::kCA) {
00548                   // Get the public key
00549                   EVP_PKEY *evpp = X509_get_pubkey((X509 *)(cert->Opaque()));
00550                   if (evpp) {
00551                      if (PEM_read_bio_PrivateKey(bkey,&evpp,0,0)) {
00552                         DEBUG("RSA key completed ");
00553                         // Test consistency
00554                         int rc = RSA_check_key(evpp->pkey.rsa);
00555                         if (rc != 0) {
00556                            // Update PKI in certificate
00557                            cert->SetPKI((XrdCryptoX509data)evpp);
00558                            // Update status
00559                            cert->PKI()->status = XrdCryptoRSA::kComplete;
00560                            break;
00561                         }
00562                      }
00563                   }
00564                }
00565                // Get next
00566                cert = chain->Next();
00567             }
00568          }
00569          // Cleanup
00570          BIO_free(bkey);
00571       }
00572    }
00573 
00574    // Cleanup
00575    BIO_free(bmem);
00576 
00577    // We are done
00578    return nci;
00579 }
00580 
00581 //____________________________________________________________________________
00582 int XrdCryptosslASN1toUTC(ASN1_TIME *tsn1)
00583 {
00584    // Function to convert from ASN1 time format into UTC
00585    // since Epoch (Jan 1, 1970) 
00586    // Return -1 if something went wrong
00587    int etime = -1;
00588 
00589    //
00590    // Make sure there is something to convert
00591    if (!tsn1) return etime;
00592 
00593    //
00594    // Parse the input string: here we basically cut&paste from GRIDSITE
00595    // They finally use timegm to convert to UTC seconds, which is less
00596    // standard and seems to give an offset of 3600 secs.
00597    // Our result is in agreement with 'date +%s`. 
00598    struct tm ltm;
00599    char zz;
00600    if ((sscanf((const char *)(tsn1->data),
00601        "%02d%02d%02d%02d%02d%02d%c", 
00602        &(ltm.tm_year), &(ltm.tm_mon), &(ltm.tm_mday),
00603        &(ltm.tm_hour), &(ltm.tm_min), &(ltm.tm_sec),
00604                                       &zz) != 7) || (zz != 'Z')) {
00605        return -1;
00606    }
00607    // Init also the ones not used by mktime
00608    ltm.tm_wday  = 0;        // day of the week 
00609    ltm.tm_yday  = 0;        // day in the year
00610    ltm.tm_isdst = -1;       // daylight saving time
00611    //
00612    // Renormalize some values: year should be modulo 1900
00613    if (ltm.tm_year < 90)
00614       ltm.tm_year += 100;
00615    //
00616    // month should in [0, 11]
00617    (ltm.tm_mon)--;
00618    //
00619    // calculate UTC
00620    etime = mktime(&ltm);
00621    //
00622    // If GMT we need a correction because mktime use local time zone
00623    time_t now = time(0);
00624    struct tm ltn, gtn;
00625    if (!localtime_r(&now, &ltn)) return etime;
00626    if (!gmtime_r(&now, &gtn)) return etime;
00627    //
00628    // Calculate correction
00629    int tzcor = (int) difftime(mktime(&ltn), mktime(&gtn));
00630    //
00631    // Apply correction
00632    etime += tzcor;
00633    //
00634    // We are done
00635    return etime;
00636 } 

Generated on Tue Jul 5 14:46:33 2011 for ROOT_528-00b_version by  doxygen 1.5.1