Loading CHANGES +7 −0 Original line number Diff line number Diff line Loading @@ -12,6 +12,13 @@ *) applies to 0.9.6a/0.9.6b and 0.9.7 +) applies to 0.9.7 only +) Add initial OCSP responder support to ocsp application. The revocation information is handled using the text based index use by the ca application. The responder can either handle requests generated internally, supplied in files (for example via a CGI script) or using an internal minimal server. [Steve Henson] +) Add configuration choices to get zlib compression for TLS. [Richard Levitte] Loading apps/apps.h +6 −0 Original line number Diff line number Diff line Loading @@ -68,6 +68,7 @@ #include <openssl/lhash.h> #include <openssl/conf.h> #include <openssl/engine.h> #include <openssl/txt_db.h> int app_RAND_load_file(const char *file, BIO *bio_e, int dont_warn); int app_RAND_write_file(const char *file, BIO *bio_e); Loading Loading @@ -187,6 +188,11 @@ STACK_OF(X509) *load_certs(BIO *err, const char *file, int format, X509_STORE *setup_verify(BIO *bp, char *CAfile, char *CApath); ENGINE *setup_engine(BIO *err, const char *engine, int debug); /* Functions defined in ca.c and also used in ocsp.c */ int unpack_revinfo(ASN1_TIME **prevtm, int *preason, ASN1_OBJECT **phold, ASN1_GENERALIZEDTIME **pinvtm, char *str); int make_serial_index(TXT_DB *db); #define FORMAT_UNDEF 0 #define FORMAT_ASN1 1 #define FORMAT_TEXT 2 Loading apps/ca.c +123 −88 Original line number Diff line number Diff line Loading @@ -658,15 +658,8 @@ bad: db=TXT_DB_read(in,DB_NUMBER); if (db == NULL) goto err; if (!TXT_DB_create_index(db, DB_serial, NULL, LHASH_HASH_FN(index_serial_hash), LHASH_COMP_FN(index_serial_cmp))) { BIO_printf(bio_err, "error creating serial number index:(%ld,%ld,%ld)\n", db->error,db->arg1,db->arg2); if (!make_serial_index(db)) goto err; } if (get_certificate_status(ser_status,db) != 1) BIO_printf(bio_err,"Error verifying serial %s!\n", Loading Loading @@ -891,13 +884,8 @@ bad: BIO_printf(bio_err,"generating index\n"); } if (!TXT_DB_create_index(db, DB_serial, NULL, LHASH_HASH_FN(index_serial_hash), LHASH_COMP_FN(index_serial_cmp))) { BIO_printf(bio_err,"error creating serial number index:(%ld,%ld,%ld)\n",db->error,db->arg1,db->arg2); if (!make_serial_index(db)) goto err; } if (!TXT_DB_create_index(db, DB_name, index_name_qual, LHASH_HASH_FN(index_name_hash), Loading Loading @@ -2889,92 +2877,21 @@ char *make_revocation_str(int rev_type, char *rev_arg) * 2 OK and some extensions added (i.e. V2 CRL) */ int make_revoked(X509_REVOKED *rev, char *str) { char *tmp = NULL; char *rtime_str, *reason_str = NULL, *arg_str = NULL, *p; int reason_code = -1; int i, ret = 0; ASN1_OBJECT *hold = NULL; ASN1_GENERALIZEDTIME *comp_time = NULL; ASN1_ENUMERATED *rtmp = NULL; tmp = BUF_strdup(str); p = strchr(tmp, ','); rtime_str = tmp; i = unpack_revinfo(&rev->revocationDate, &reason_code, &hold, &comp_time, str); if (p) { *p = '\0'; p++; reason_str = p; p = strchr(p, ','); if (p) { *p = '\0'; arg_str = p + 1; } } if (rev && !ASN1_UTCTIME_set_string(rev->revocationDate, rtime_str)) { BIO_printf(bio_err, "invalid revocation date %s\n", rtime_str); goto err; } if (reason_str) { for (i = 0; i < NUM_REASONS; i++) { if(!strcasecmp(reason_str, crl_reasons[i])) { reason_code = i; break; } } if (reason_code == OCSP_REVOKED_STATUS_NOSTATUS) { BIO_printf(bio_err, "invalid reason code %s\n", reason_str); goto err; } if (reason_code == 7) reason_code = OCSP_REVOKED_STATUS_REMOVEFROMCRL; else if (reason_code == 8) /* Hold instruction */ { if (!arg_str) { BIO_printf(bio_err, "missing hold instruction\n"); goto err; } reason_code = OCSP_REVOKED_STATUS_CERTIFICATEHOLD; hold = OBJ_txt2obj(arg_str, 0); if (!hold) { BIO_printf(bio_err, "invalid object identifier %s\n", arg_str); goto err; } } else if ((reason_code == 9) || (reason_code == 10)) { if (!arg_str) { BIO_printf(bio_err, "missing compromised time\n"); goto err; } comp_time = ASN1_GENERALIZEDTIME_new(); if (!ASN1_GENERALIZEDTIME_set_string(comp_time, arg_str)) { BIO_printf(bio_err, "invalid compromised time %s\n", arg_str); if (i == 0) goto err; } if (reason_code == 9) reason_code = OCSP_REVOKED_STATUS_KEYCOMPROMISE; else reason_code = OCSP_REVOKED_STATUS_CACOMPROMISE; } } if (rev && (reason_code != OCSP_REVOKED_STATUS_NOSTATUS)) { Loading Loading @@ -3108,3 +3025,121 @@ int old_entry_print(BIO *bp, ASN1_OBJECT *obj, ASN1_STRING *str) BIO_printf(bp,"'\n"); return 1; } int unpack_revinfo(ASN1_TIME **prevtm, int *preason, ASN1_OBJECT **phold, ASN1_GENERALIZEDTIME **pinvtm, char *str) { char *tmp = NULL; char *rtime_str, *reason_str = NULL, *arg_str = NULL, *p; int reason_code = -1; int i, ret = 0; ASN1_OBJECT *hold = NULL; ASN1_GENERALIZEDTIME *comp_time = NULL; tmp = BUF_strdup(str); p = strchr(tmp, ','); rtime_str = tmp; if (p) { *p = '\0'; p++; reason_str = p; p = strchr(p, ','); if (p) { *p = '\0'; arg_str = p + 1; } } if (prevtm) { *prevtm = ASN1_UTCTIME_new(); if (!ASN1_UTCTIME_set_string(*prevtm, rtime_str)) { BIO_printf(bio_err, "invalid revocation date %s\n", rtime_str); goto err; } } if (reason_str) { for (i = 0; i < NUM_REASONS; i++) { if(!strcasecmp(reason_str, crl_reasons[i])) { reason_code = i; break; } } if (reason_code == OCSP_REVOKED_STATUS_NOSTATUS) { BIO_printf(bio_err, "invalid reason code %s\n", reason_str); goto err; } if (reason_code == 7) reason_code = OCSP_REVOKED_STATUS_REMOVEFROMCRL; else if (reason_code == 8) /* Hold instruction */ { if (!arg_str) { BIO_printf(bio_err, "missing hold instruction\n"); goto err; } reason_code = OCSP_REVOKED_STATUS_CERTIFICATEHOLD; hold = OBJ_txt2obj(arg_str, 0); if (!hold) { BIO_printf(bio_err, "invalid object identifier %s\n", arg_str); goto err; } if (phold) *phold = hold; } else if ((reason_code == 9) || (reason_code == 10)) { if (!arg_str) { BIO_printf(bio_err, "missing compromised time\n"); goto err; } comp_time = ASN1_GENERALIZEDTIME_new(); if (!ASN1_GENERALIZEDTIME_set_string(comp_time, arg_str)) { BIO_printf(bio_err, "invalid compromised time %s\n", arg_str); goto err; } if (reason_code == 9) reason_code = OCSP_REVOKED_STATUS_KEYCOMPROMISE; else reason_code = OCSP_REVOKED_STATUS_CACOMPROMISE; } } if (preason) *preason = reason_code; if (pinvtm) *pinvtm = comp_time; else ASN1_GENERALIZEDTIME_free(comp_time); err: if (tmp) OPENSSL_free(tmp); if (!phold) ASN1_OBJECT_free(hold); if (!pinvtm) ASN1_GENERALIZEDTIME_free(comp_time); return ret; } int make_serial_index(TXT_DB *db) { if (!TXT_DB_create_index(db, DB_serial, NULL, LHASH_HASH_FN(index_serial_hash), LHASH_COMP_FN(index_serial_cmp))) { BIO_printf(bio_err, "error creating serial number index:(%ld,%ld,%ld)\n", db->error,db->arg1,db->arg2); return 0; } return 1; } apps/ocsp.c +404 −20 Original line number Diff line number Diff line Loading @@ -67,6 +67,19 @@ /* Maximum leeway in validity period: default 5 minutes */ #define MAX_VALIDITY_PERIOD (5 * 60) /* CA index.txt definitions */ #define DB_type 0 #define DB_exp_date 1 #define DB_rev_date 2 #define DB_serial 3 /* index - unique */ #define DB_file 4 #define DB_name 5 /* index - unique for active */ #define DB_NUMBER 6 #define DB_TYPE_REV 'R' #define DB_TYPE_EXP 'E' #define DB_TYPE_VAL 'V' static int add_ocsp_cert(OCSP_REQUEST **req, X509 *cert, X509 *issuer, STACK_OF(OCSP_CERTID) *ids); static int add_ocsp_serial(OCSP_REQUEST **req, char *serial, X509 *issuer, Loading @@ -75,6 +88,15 @@ static int print_ocsp_summary(BIO *out, OCSP_BASICRESP *bs, OCSP_REQUEST *req, STACK *names, STACK_OF(OCSP_CERTID) *ids, long nsec, long maxage); static int make_ocsp_response(OCSP_RESPONSE **resp, OCSP_REQUEST *req, TXT_DB *db, X509 *ca, X509 *rcert, EVP_PKEY *rkey, STACK_OF(X509) *rother, unsigned long flags, int nmin, int ndays); static char **lookup_serial(TXT_DB *db, ASN1_INTEGER *ser); static OCSP_REQUEST *do_responder(BIO **cbio, char *port); static int send_ocsp_response(BIO *cbio, OCSP_RESPONSE *resp); #undef PROG #define PROG ocsp_main Loading @@ -88,14 +110,15 @@ int MAIN(int argc, char **argv) char *reqin = NULL, *respin = NULL; char *reqout = NULL, *respout = NULL; char *signfile = NULL, *keyfile = NULL; char *rsignfile = NULL, *rkeyfile = NULL; char *outfile = NULL; int add_nonce = 1, noverify = 0, use_ssl = -1; OCSP_REQUEST *req = NULL; OCSP_RESPONSE *resp = NULL; OCSP_BASICRESP *bs = NULL; X509 *issuer = NULL, *cert = NULL; X509 *signer = NULL; EVP_PKEY *key = NULL; X509 *signer = NULL, *rsigner = NULL; EVP_PKEY *key = NULL, *rkey = NULL; BIO *cbio = NULL, *derbio = NULL; BIO *out = NULL; int req_text = 0, resp_text = 0; Loading @@ -103,14 +126,21 @@ int MAIN(int argc, char **argv) char *CAfile = NULL, *CApath = NULL; X509_STORE *store = NULL; SSL_CTX *ctx = NULL; STACK_OF(X509) *sign_other = NULL, *verify_other = NULL; char *sign_certfile = NULL, *verify_certfile = NULL; unsigned long sign_flags = 0, verify_flags = 0; STACK_OF(X509) *sign_other = NULL, *verify_other = NULL, *rother = NULL; char *sign_certfile = NULL, *verify_certfile = NULL, *rcertfile = NULL; unsigned long sign_flags = 0, verify_flags = 0, rflags = 0; int ret = 1; int badarg = 0; int i; STACK *reqnames = NULL; STACK_OF(OCSP_CERTID) *ids = NULL; X509 *rca_cert = NULL; char *ridx_filename = NULL; char *rca_filename = NULL; TXT_DB *rdb = NULL; int nmin = 0, ndays = -1; if (bio_err == NULL) bio_err = BIO_new_fp(stderr, BIO_NOCLOSE); SSL_load_error_strings(); args = argv + 1; Loading Loading @@ -149,12 +179,27 @@ int MAIN(int argc, char **argv) } else badarg = 1; } else if (!strcmp(*args, "-port")) { if (args[1]) { args++; port = *args; } else badarg = 1; } else if (!strcmp(*args, "-noverify")) noverify = 1; else if (!strcmp(*args, "-nonce")) add_nonce = 2; else if (!strcmp(*args, "-no_nonce")) add_nonce = 0; else if (!strcmp(*args, "-responder_no_certs")) rflags |= OCSP_NOCERTS; else if (!strcmp(*args, "-responder_no_time")) rflags |= OCSP_NOTIME; else if (!strcmp(*args, "-responder_key_id")) rflags |= OCSP_RESPID_KEY; else if (!strcmp(*args, "-no_certs")) sign_flags |= OCSP_NOCERTS; else if (!strcmp(*args, "-no_signature_verify")) Loading Loading @@ -361,12 +406,91 @@ int MAIN(int argc, char **argv) } else badarg = 1; } else if (!strcmp(*args, "-index")) { if (args[1]) { args++; ridx_filename = *args; } else badarg = 1; } else if (!strcmp(*args, "-CA")) { if (args[1]) { args++; rca_filename = *args; } else badarg = 1; } else if (!strcmp (*args, "-nmin")) { if (args[1]) { args++; nmin = atol(*args); if (nmin < 0) { BIO_printf(bio_err, "Illegal update period %s\n", *args); badarg = 1; } } if (ndays == -1) ndays = 0; else badarg = 1; } else if (!strcmp (*args, "-ndays")) { if (args[1]) { args++; ndays = atol(*args); if (ndays < 0) { BIO_printf(bio_err, "Illegal update period %s\n", *args); badarg = 1; } } else badarg = 1; } else if (!strcmp(*args, "-rsigner")) { if (args[1]) { args++; rsignfile = *args; } else badarg = 1; } else if (!strcmp(*args, "-rkey")) { if (args[1]) { args++; rkeyfile = *args; } else badarg = 1; } else if (!strcmp(*args, "-rother")) { if (args[1]) { args++; rcertfile = *args; } else badarg = 1; } else badarg = 1; args++; } /* Have we anything to do? */ if (!req && !reqin && !respin) badarg = 1; if (!req && !reqin && !respin && !(port && ridx_filename)) badarg = 1; if (badarg) { Loading Loading @@ -437,7 +561,20 @@ int MAIN(int argc, char **argv) } } if (!req && (signfile || reqout || host || add_nonce)) if (!req && port) { req = do_responder(&cbio, port); if (!req && !cbio) goto end; if (!req && cbio) { resp = OCSP_response_create(OCSP_RESPONSE_STATUS_MALFORMEDREQUEST, NULL); send_ocsp_response(cbio, resp); goto done_resp; } } if (!req && (signfile || reqout || host || add_nonce || ridx_filename)) { BIO_printf(bio_err, "Need an OCSP request for this operation!\n"); goto end; Loading @@ -464,12 +601,7 @@ int MAIN(int argc, char **argv) key = load_key(bio_err, keyfile, FORMAT_PEM, NULL, NULL, "signer private key"); if (!key) { #if 0 /* An appropriate message has already been printed */ BIO_printf(bio_err, "Error loading signer private key\n"); #endif goto end; } if (!OCSP_request_sign(req, signer, key, EVP_sha1(), sign_other, sign_flags)) { BIO_printf(bio_err, "Error signing OCSP request\n"); Loading @@ -477,21 +609,61 @@ int MAIN(int argc, char **argv) } } if (reqout) if (req_text && req) OCSP_REQUEST_print(out, req, 0); if (rsignfile) { derbio = BIO_new_file(reqout, "wb"); if (!derbio) if (!rkeyfile) rkeyfile = rsignfile; rsigner = load_cert(bio_err, rsignfile, FORMAT_PEM, NULL, e, "responder certificate"); if (!rsigner) { BIO_printf(bio_err, "Error opening file %s\n", reqout); BIO_printf(bio_err, "Error loading responder certificate\n"); goto end; } i2d_OCSP_REQUEST_bio(derbio, req); BIO_free(derbio); rca_cert = load_cert(bio_err, rca_filename, FORMAT_PEM, NULL, e, "CA certificate"); if (rcertfile) { rother = load_certs(bio_err, sign_certfile, FORMAT_PEM, NULL, e, "responder other certificates"); if (!sign_other) goto end; } rkey = load_key(bio_err, rkeyfile, FORMAT_PEM, NULL, NULL, "responder private key"); if (!rkey) goto end; } if (req_text && req) OCSP_REQUEST_print(out, req, 0); if (ridx_filename && (!rkey || !rsigner || !rca_cert)) { BIO_printf(bio_err, "Need a responder certificate, key and CA for this operation!\n"); goto end; } if (host) if (ridx_filename) { BIO *db_bio = NULL; db_bio = BIO_new_file(ridx_filename, "r"); if (!db_bio) { BIO_printf(bio_err, "Error opening index file %s\n", ridx_filename); goto end; } rdb = TXT_DB_read(db_bio, DB_NUMBER); BIO_free(db_bio); if (!rdb) { BIO_printf(bio_err, "Error reading index file %s\n", ridx_filename); goto end; } if (!make_serial_index(rdb)) goto end; i = make_ocsp_response(&resp, req, rdb, rca_cert, rsigner, rkey, rother, rflags, nmin, ndays); if (cbio) send_ocsp_response(cbio, resp); } else if (host) { cbio = BIO_new_connect(host); if (!cbio) Loading Loading @@ -545,6 +717,8 @@ int MAIN(int argc, char **argv) goto end; } done_resp: if (respout) { derbio = BIO_new_file(respout, "wb"); Loading @@ -569,6 +743,10 @@ int MAIN(int argc, char **argv) if (resp_text) OCSP_RESPONSE_print(out, resp, 0); /* If running as responder don't verify our own response */ if (cbio) goto end; store = setup_verify(bio_err, CAfile, CApath); if(!store) goto end; if (verify_certfile) Loading Loading @@ -622,8 +800,12 @@ end: X509_free(signer); X509_STORE_free(store); EVP_PKEY_free(key); EVP_PKEY_free(rkey); X509_free(issuer); X509_free(cert); X509_free(rsigner); X509_free(rca_cert); TXT_DB_free(rdb); BIO_free_all(cbio); BIO_free(out); OCSP_REQUEST_free(req); Loading Loading @@ -763,3 +945,205 @@ static int print_ocsp_summary(BIO *out, OCSP_BASICRESP *bs, OCSP_REQUEST *req, return 1; } static int make_ocsp_response(OCSP_RESPONSE **resp, OCSP_REQUEST *req, TXT_DB *db, X509 *ca, X509 *rcert, EVP_PKEY *rkey, STACK_OF(X509) *rother, unsigned long flags, int nmin, int ndays) { ASN1_TIME *thisupd = NULL, *nextupd = NULL; OCSP_CERTID *cid, *ca_id = NULL; OCSP_BASICRESP *bs = NULL; int i, id_count, ret = 1; id_count = OCSP_request_onereq_count(req); if (id_count <= 0) { *resp = OCSP_response_create(OCSP_RESPONSE_STATUS_MALFORMEDREQUEST, NULL); goto end; } ca_id = OCSP_cert_to_id(EVP_sha1(), NULL, ca); bs = OCSP_BASICRESP_new(); thisupd = X509_gmtime_adj(NULL, 0); if (ndays != -1) nextupd = X509_gmtime_adj(NULL, nmin * 60 + ndays * 3600 * 24 ); /* Examine each certificate id in the request */ for (i = 0; i < id_count; i++) { OCSP_ONEREQ *one; ASN1_INTEGER *serial; char **inf; one = OCSP_request_onereq_get0(req, i); cid = OCSP_onereq_get0_id(one); /* Is this request about our CA? */ if (OCSP_id_issuer_cmp(ca_id, cid)) { OCSP_basic_add1_status(bs, cid, V_OCSP_CERTSTATUS_UNKNOWN, 0, NULL, thisupd, nextupd); continue; } OCSP_id_get0_info(NULL, NULL, NULL, &serial, cid); inf = lookup_serial(db, serial); if (!inf) OCSP_basic_add1_status(bs, cid, V_OCSP_CERTSTATUS_UNKNOWN, 0, NULL, thisupd, nextupd); else if (inf[DB_type][0] == DB_TYPE_VAL) OCSP_basic_add1_status(bs, cid, V_OCSP_CERTSTATUS_GOOD, 0, NULL, thisupd, nextupd); else if (inf[DB_type][0] == DB_TYPE_REV) { ASN1_OBJECT *inst = NULL; ASN1_TIME *revtm = NULL; ASN1_GENERALIZEDTIME *invtm = NULL; OCSP_SINGLERESP *single; int reason = -1; unpack_revinfo(&revtm, &reason, &inst, &invtm, inf[DB_rev_date]); single = OCSP_basic_add1_status(bs, cid, V_OCSP_CERTSTATUS_REVOKED, reason, revtm, thisupd, nextupd); if (invtm) OCSP_SINGLERESP_add1_ext_i2d(single, NID_invalidity_date, invtm, 0, 0); else if (inst) OCSP_SINGLERESP_add1_ext_i2d(single, NID_hold_instruction_code, inst, 0, 0); ASN1_OBJECT_free(inst); ASN1_TIME_free(revtm); ASN1_GENERALIZEDTIME_free(invtm); } } OCSP_copy_nonce(bs, req); OCSP_basic_sign(bs, rcert, rkey, EVP_sha1(), rother, flags); *resp = OCSP_response_create(OCSP_RESPONSE_STATUS_SUCCESSFUL, bs); end: ASN1_TIME_free(thisupd); ASN1_TIME_free(nextupd); OCSP_CERTID_free(ca_id); OCSP_BASICRESP_free(bs); return ret; } static char **lookup_serial(TXT_DB *db, ASN1_INTEGER *ser) { int i; BIGNUM *bn = NULL; char *itmp, *row[DB_NUMBER],**rrow; for (i = 0; i < DB_NUMBER; i++) row[i] = NULL; bn = ASN1_INTEGER_to_BN(ser,NULL); itmp = BN_bn2hex(bn); row[DB_serial] = itmp; BN_free(bn); rrow=TXT_DB_get_by_index(db,DB_serial,row); OPENSSL_free(itmp); return rrow; } /* Quick and dirty OCSP server: read in and parse input request */ static OCSP_REQUEST *do_responder(BIO **pcbio, char *port) { int have_post = 0, len; OCSP_REQUEST *req = NULL; char inbuf[1024]; BIO *acbio = NULL, *bufbio = NULL, *cbio = NULL; bufbio = BIO_new(BIO_f_buffer()); if (!bufbio) goto err; acbio = BIO_new_accept(port); if (!acbio) goto err; BIO_set_accept_bios(acbio, bufbio); bufbio = NULL; if (BIO_do_accept(acbio) <= 0) { BIO_printf(bio_err, "Error setting up accept BIO\n"); ERR_print_errors(bio_err); goto err; } BIO_printf(bio_err, "Waiting for OCSP client connection...\n"); if (BIO_do_accept(acbio) <= 0) { BIO_printf(bio_err, "Error accepting connection\n"); ERR_print_errors(bio_err); goto err; } BIO_printf(bio_err, "Connection Established\n"); cbio = BIO_pop(acbio); BIO_free_all(acbio); acbio = NULL; for(;;) { len = BIO_gets(cbio, inbuf, 1024); if (len <= 0) goto err; /* Look for "POST" signalling start of query */ if (!have_post) { if(strncmp(inbuf, "POST", 4)) { BIO_printf(bio_err, "Invalid request\n"); goto err; } have_post = 1; } /* Look for end of headers */ if ((inbuf[0] == '\r') || (inbuf[0] == '\n')) break; } /* Try to read OCSP request */ req = d2i_OCSP_REQUEST_bio(cbio, NULL); *pcbio = cbio; if (!req) { BIO_printf(bio_err, "Error parsing OCSP request\n"); ERR_print_errors(bio_err); } return req; err: BIO_free_all(acbio); BIO_free_all(cbio); BIO_free(bufbio); OCSP_REQUEST_free(req); return NULL; } static int send_ocsp_response(BIO *cbio, OCSP_RESPONSE *resp) { char http_resp[] = "HTTP/1.0 200 OK\r\nContent-type: application/ocsp-response\r\n" "Content-Length: %d\r\n\r\n"; if (!cbio) return 0; BIO_printf(cbio, http_resp, i2d_OCSP_RESPONSE(resp, NULL)); i2d_OCSP_RESPONSE_bio(cbio, resp); BIO_flush(cbio); return 1; } Loading
CHANGES +7 −0 Original line number Diff line number Diff line Loading @@ -12,6 +12,13 @@ *) applies to 0.9.6a/0.9.6b and 0.9.7 +) applies to 0.9.7 only +) Add initial OCSP responder support to ocsp application. The revocation information is handled using the text based index use by the ca application. The responder can either handle requests generated internally, supplied in files (for example via a CGI script) or using an internal minimal server. [Steve Henson] +) Add configuration choices to get zlib compression for TLS. [Richard Levitte] Loading
apps/apps.h +6 −0 Original line number Diff line number Diff line Loading @@ -68,6 +68,7 @@ #include <openssl/lhash.h> #include <openssl/conf.h> #include <openssl/engine.h> #include <openssl/txt_db.h> int app_RAND_load_file(const char *file, BIO *bio_e, int dont_warn); int app_RAND_write_file(const char *file, BIO *bio_e); Loading Loading @@ -187,6 +188,11 @@ STACK_OF(X509) *load_certs(BIO *err, const char *file, int format, X509_STORE *setup_verify(BIO *bp, char *CAfile, char *CApath); ENGINE *setup_engine(BIO *err, const char *engine, int debug); /* Functions defined in ca.c and also used in ocsp.c */ int unpack_revinfo(ASN1_TIME **prevtm, int *preason, ASN1_OBJECT **phold, ASN1_GENERALIZEDTIME **pinvtm, char *str); int make_serial_index(TXT_DB *db); #define FORMAT_UNDEF 0 #define FORMAT_ASN1 1 #define FORMAT_TEXT 2 Loading
apps/ca.c +123 −88 Original line number Diff line number Diff line Loading @@ -658,15 +658,8 @@ bad: db=TXT_DB_read(in,DB_NUMBER); if (db == NULL) goto err; if (!TXT_DB_create_index(db, DB_serial, NULL, LHASH_HASH_FN(index_serial_hash), LHASH_COMP_FN(index_serial_cmp))) { BIO_printf(bio_err, "error creating serial number index:(%ld,%ld,%ld)\n", db->error,db->arg1,db->arg2); if (!make_serial_index(db)) goto err; } if (get_certificate_status(ser_status,db) != 1) BIO_printf(bio_err,"Error verifying serial %s!\n", Loading Loading @@ -891,13 +884,8 @@ bad: BIO_printf(bio_err,"generating index\n"); } if (!TXT_DB_create_index(db, DB_serial, NULL, LHASH_HASH_FN(index_serial_hash), LHASH_COMP_FN(index_serial_cmp))) { BIO_printf(bio_err,"error creating serial number index:(%ld,%ld,%ld)\n",db->error,db->arg1,db->arg2); if (!make_serial_index(db)) goto err; } if (!TXT_DB_create_index(db, DB_name, index_name_qual, LHASH_HASH_FN(index_name_hash), Loading Loading @@ -2889,92 +2877,21 @@ char *make_revocation_str(int rev_type, char *rev_arg) * 2 OK and some extensions added (i.e. V2 CRL) */ int make_revoked(X509_REVOKED *rev, char *str) { char *tmp = NULL; char *rtime_str, *reason_str = NULL, *arg_str = NULL, *p; int reason_code = -1; int i, ret = 0; ASN1_OBJECT *hold = NULL; ASN1_GENERALIZEDTIME *comp_time = NULL; ASN1_ENUMERATED *rtmp = NULL; tmp = BUF_strdup(str); p = strchr(tmp, ','); rtime_str = tmp; i = unpack_revinfo(&rev->revocationDate, &reason_code, &hold, &comp_time, str); if (p) { *p = '\0'; p++; reason_str = p; p = strchr(p, ','); if (p) { *p = '\0'; arg_str = p + 1; } } if (rev && !ASN1_UTCTIME_set_string(rev->revocationDate, rtime_str)) { BIO_printf(bio_err, "invalid revocation date %s\n", rtime_str); goto err; } if (reason_str) { for (i = 0; i < NUM_REASONS; i++) { if(!strcasecmp(reason_str, crl_reasons[i])) { reason_code = i; break; } } if (reason_code == OCSP_REVOKED_STATUS_NOSTATUS) { BIO_printf(bio_err, "invalid reason code %s\n", reason_str); goto err; } if (reason_code == 7) reason_code = OCSP_REVOKED_STATUS_REMOVEFROMCRL; else if (reason_code == 8) /* Hold instruction */ { if (!arg_str) { BIO_printf(bio_err, "missing hold instruction\n"); goto err; } reason_code = OCSP_REVOKED_STATUS_CERTIFICATEHOLD; hold = OBJ_txt2obj(arg_str, 0); if (!hold) { BIO_printf(bio_err, "invalid object identifier %s\n", arg_str); goto err; } } else if ((reason_code == 9) || (reason_code == 10)) { if (!arg_str) { BIO_printf(bio_err, "missing compromised time\n"); goto err; } comp_time = ASN1_GENERALIZEDTIME_new(); if (!ASN1_GENERALIZEDTIME_set_string(comp_time, arg_str)) { BIO_printf(bio_err, "invalid compromised time %s\n", arg_str); if (i == 0) goto err; } if (reason_code == 9) reason_code = OCSP_REVOKED_STATUS_KEYCOMPROMISE; else reason_code = OCSP_REVOKED_STATUS_CACOMPROMISE; } } if (rev && (reason_code != OCSP_REVOKED_STATUS_NOSTATUS)) { Loading Loading @@ -3108,3 +3025,121 @@ int old_entry_print(BIO *bp, ASN1_OBJECT *obj, ASN1_STRING *str) BIO_printf(bp,"'\n"); return 1; } int unpack_revinfo(ASN1_TIME **prevtm, int *preason, ASN1_OBJECT **phold, ASN1_GENERALIZEDTIME **pinvtm, char *str) { char *tmp = NULL; char *rtime_str, *reason_str = NULL, *arg_str = NULL, *p; int reason_code = -1; int i, ret = 0; ASN1_OBJECT *hold = NULL; ASN1_GENERALIZEDTIME *comp_time = NULL; tmp = BUF_strdup(str); p = strchr(tmp, ','); rtime_str = tmp; if (p) { *p = '\0'; p++; reason_str = p; p = strchr(p, ','); if (p) { *p = '\0'; arg_str = p + 1; } } if (prevtm) { *prevtm = ASN1_UTCTIME_new(); if (!ASN1_UTCTIME_set_string(*prevtm, rtime_str)) { BIO_printf(bio_err, "invalid revocation date %s\n", rtime_str); goto err; } } if (reason_str) { for (i = 0; i < NUM_REASONS; i++) { if(!strcasecmp(reason_str, crl_reasons[i])) { reason_code = i; break; } } if (reason_code == OCSP_REVOKED_STATUS_NOSTATUS) { BIO_printf(bio_err, "invalid reason code %s\n", reason_str); goto err; } if (reason_code == 7) reason_code = OCSP_REVOKED_STATUS_REMOVEFROMCRL; else if (reason_code == 8) /* Hold instruction */ { if (!arg_str) { BIO_printf(bio_err, "missing hold instruction\n"); goto err; } reason_code = OCSP_REVOKED_STATUS_CERTIFICATEHOLD; hold = OBJ_txt2obj(arg_str, 0); if (!hold) { BIO_printf(bio_err, "invalid object identifier %s\n", arg_str); goto err; } if (phold) *phold = hold; } else if ((reason_code == 9) || (reason_code == 10)) { if (!arg_str) { BIO_printf(bio_err, "missing compromised time\n"); goto err; } comp_time = ASN1_GENERALIZEDTIME_new(); if (!ASN1_GENERALIZEDTIME_set_string(comp_time, arg_str)) { BIO_printf(bio_err, "invalid compromised time %s\n", arg_str); goto err; } if (reason_code == 9) reason_code = OCSP_REVOKED_STATUS_KEYCOMPROMISE; else reason_code = OCSP_REVOKED_STATUS_CACOMPROMISE; } } if (preason) *preason = reason_code; if (pinvtm) *pinvtm = comp_time; else ASN1_GENERALIZEDTIME_free(comp_time); err: if (tmp) OPENSSL_free(tmp); if (!phold) ASN1_OBJECT_free(hold); if (!pinvtm) ASN1_GENERALIZEDTIME_free(comp_time); return ret; } int make_serial_index(TXT_DB *db) { if (!TXT_DB_create_index(db, DB_serial, NULL, LHASH_HASH_FN(index_serial_hash), LHASH_COMP_FN(index_serial_cmp))) { BIO_printf(bio_err, "error creating serial number index:(%ld,%ld,%ld)\n", db->error,db->arg1,db->arg2); return 0; } return 1; }
apps/ocsp.c +404 −20 Original line number Diff line number Diff line Loading @@ -67,6 +67,19 @@ /* Maximum leeway in validity period: default 5 minutes */ #define MAX_VALIDITY_PERIOD (5 * 60) /* CA index.txt definitions */ #define DB_type 0 #define DB_exp_date 1 #define DB_rev_date 2 #define DB_serial 3 /* index - unique */ #define DB_file 4 #define DB_name 5 /* index - unique for active */ #define DB_NUMBER 6 #define DB_TYPE_REV 'R' #define DB_TYPE_EXP 'E' #define DB_TYPE_VAL 'V' static int add_ocsp_cert(OCSP_REQUEST **req, X509 *cert, X509 *issuer, STACK_OF(OCSP_CERTID) *ids); static int add_ocsp_serial(OCSP_REQUEST **req, char *serial, X509 *issuer, Loading @@ -75,6 +88,15 @@ static int print_ocsp_summary(BIO *out, OCSP_BASICRESP *bs, OCSP_REQUEST *req, STACK *names, STACK_OF(OCSP_CERTID) *ids, long nsec, long maxage); static int make_ocsp_response(OCSP_RESPONSE **resp, OCSP_REQUEST *req, TXT_DB *db, X509 *ca, X509 *rcert, EVP_PKEY *rkey, STACK_OF(X509) *rother, unsigned long flags, int nmin, int ndays); static char **lookup_serial(TXT_DB *db, ASN1_INTEGER *ser); static OCSP_REQUEST *do_responder(BIO **cbio, char *port); static int send_ocsp_response(BIO *cbio, OCSP_RESPONSE *resp); #undef PROG #define PROG ocsp_main Loading @@ -88,14 +110,15 @@ int MAIN(int argc, char **argv) char *reqin = NULL, *respin = NULL; char *reqout = NULL, *respout = NULL; char *signfile = NULL, *keyfile = NULL; char *rsignfile = NULL, *rkeyfile = NULL; char *outfile = NULL; int add_nonce = 1, noverify = 0, use_ssl = -1; OCSP_REQUEST *req = NULL; OCSP_RESPONSE *resp = NULL; OCSP_BASICRESP *bs = NULL; X509 *issuer = NULL, *cert = NULL; X509 *signer = NULL; EVP_PKEY *key = NULL; X509 *signer = NULL, *rsigner = NULL; EVP_PKEY *key = NULL, *rkey = NULL; BIO *cbio = NULL, *derbio = NULL; BIO *out = NULL; int req_text = 0, resp_text = 0; Loading @@ -103,14 +126,21 @@ int MAIN(int argc, char **argv) char *CAfile = NULL, *CApath = NULL; X509_STORE *store = NULL; SSL_CTX *ctx = NULL; STACK_OF(X509) *sign_other = NULL, *verify_other = NULL; char *sign_certfile = NULL, *verify_certfile = NULL; unsigned long sign_flags = 0, verify_flags = 0; STACK_OF(X509) *sign_other = NULL, *verify_other = NULL, *rother = NULL; char *sign_certfile = NULL, *verify_certfile = NULL, *rcertfile = NULL; unsigned long sign_flags = 0, verify_flags = 0, rflags = 0; int ret = 1; int badarg = 0; int i; STACK *reqnames = NULL; STACK_OF(OCSP_CERTID) *ids = NULL; X509 *rca_cert = NULL; char *ridx_filename = NULL; char *rca_filename = NULL; TXT_DB *rdb = NULL; int nmin = 0, ndays = -1; if (bio_err == NULL) bio_err = BIO_new_fp(stderr, BIO_NOCLOSE); SSL_load_error_strings(); args = argv + 1; Loading Loading @@ -149,12 +179,27 @@ int MAIN(int argc, char **argv) } else badarg = 1; } else if (!strcmp(*args, "-port")) { if (args[1]) { args++; port = *args; } else badarg = 1; } else if (!strcmp(*args, "-noverify")) noverify = 1; else if (!strcmp(*args, "-nonce")) add_nonce = 2; else if (!strcmp(*args, "-no_nonce")) add_nonce = 0; else if (!strcmp(*args, "-responder_no_certs")) rflags |= OCSP_NOCERTS; else if (!strcmp(*args, "-responder_no_time")) rflags |= OCSP_NOTIME; else if (!strcmp(*args, "-responder_key_id")) rflags |= OCSP_RESPID_KEY; else if (!strcmp(*args, "-no_certs")) sign_flags |= OCSP_NOCERTS; else if (!strcmp(*args, "-no_signature_verify")) Loading Loading @@ -361,12 +406,91 @@ int MAIN(int argc, char **argv) } else badarg = 1; } else if (!strcmp(*args, "-index")) { if (args[1]) { args++; ridx_filename = *args; } else badarg = 1; } else if (!strcmp(*args, "-CA")) { if (args[1]) { args++; rca_filename = *args; } else badarg = 1; } else if (!strcmp (*args, "-nmin")) { if (args[1]) { args++; nmin = atol(*args); if (nmin < 0) { BIO_printf(bio_err, "Illegal update period %s\n", *args); badarg = 1; } } if (ndays == -1) ndays = 0; else badarg = 1; } else if (!strcmp (*args, "-ndays")) { if (args[1]) { args++; ndays = atol(*args); if (ndays < 0) { BIO_printf(bio_err, "Illegal update period %s\n", *args); badarg = 1; } } else badarg = 1; } else if (!strcmp(*args, "-rsigner")) { if (args[1]) { args++; rsignfile = *args; } else badarg = 1; } else if (!strcmp(*args, "-rkey")) { if (args[1]) { args++; rkeyfile = *args; } else badarg = 1; } else if (!strcmp(*args, "-rother")) { if (args[1]) { args++; rcertfile = *args; } else badarg = 1; } else badarg = 1; args++; } /* Have we anything to do? */ if (!req && !reqin && !respin) badarg = 1; if (!req && !reqin && !respin && !(port && ridx_filename)) badarg = 1; if (badarg) { Loading Loading @@ -437,7 +561,20 @@ int MAIN(int argc, char **argv) } } if (!req && (signfile || reqout || host || add_nonce)) if (!req && port) { req = do_responder(&cbio, port); if (!req && !cbio) goto end; if (!req && cbio) { resp = OCSP_response_create(OCSP_RESPONSE_STATUS_MALFORMEDREQUEST, NULL); send_ocsp_response(cbio, resp); goto done_resp; } } if (!req && (signfile || reqout || host || add_nonce || ridx_filename)) { BIO_printf(bio_err, "Need an OCSP request for this operation!\n"); goto end; Loading @@ -464,12 +601,7 @@ int MAIN(int argc, char **argv) key = load_key(bio_err, keyfile, FORMAT_PEM, NULL, NULL, "signer private key"); if (!key) { #if 0 /* An appropriate message has already been printed */ BIO_printf(bio_err, "Error loading signer private key\n"); #endif goto end; } if (!OCSP_request_sign(req, signer, key, EVP_sha1(), sign_other, sign_flags)) { BIO_printf(bio_err, "Error signing OCSP request\n"); Loading @@ -477,21 +609,61 @@ int MAIN(int argc, char **argv) } } if (reqout) if (req_text && req) OCSP_REQUEST_print(out, req, 0); if (rsignfile) { derbio = BIO_new_file(reqout, "wb"); if (!derbio) if (!rkeyfile) rkeyfile = rsignfile; rsigner = load_cert(bio_err, rsignfile, FORMAT_PEM, NULL, e, "responder certificate"); if (!rsigner) { BIO_printf(bio_err, "Error opening file %s\n", reqout); BIO_printf(bio_err, "Error loading responder certificate\n"); goto end; } i2d_OCSP_REQUEST_bio(derbio, req); BIO_free(derbio); rca_cert = load_cert(bio_err, rca_filename, FORMAT_PEM, NULL, e, "CA certificate"); if (rcertfile) { rother = load_certs(bio_err, sign_certfile, FORMAT_PEM, NULL, e, "responder other certificates"); if (!sign_other) goto end; } rkey = load_key(bio_err, rkeyfile, FORMAT_PEM, NULL, NULL, "responder private key"); if (!rkey) goto end; } if (req_text && req) OCSP_REQUEST_print(out, req, 0); if (ridx_filename && (!rkey || !rsigner || !rca_cert)) { BIO_printf(bio_err, "Need a responder certificate, key and CA for this operation!\n"); goto end; } if (host) if (ridx_filename) { BIO *db_bio = NULL; db_bio = BIO_new_file(ridx_filename, "r"); if (!db_bio) { BIO_printf(bio_err, "Error opening index file %s\n", ridx_filename); goto end; } rdb = TXT_DB_read(db_bio, DB_NUMBER); BIO_free(db_bio); if (!rdb) { BIO_printf(bio_err, "Error reading index file %s\n", ridx_filename); goto end; } if (!make_serial_index(rdb)) goto end; i = make_ocsp_response(&resp, req, rdb, rca_cert, rsigner, rkey, rother, rflags, nmin, ndays); if (cbio) send_ocsp_response(cbio, resp); } else if (host) { cbio = BIO_new_connect(host); if (!cbio) Loading Loading @@ -545,6 +717,8 @@ int MAIN(int argc, char **argv) goto end; } done_resp: if (respout) { derbio = BIO_new_file(respout, "wb"); Loading @@ -569,6 +743,10 @@ int MAIN(int argc, char **argv) if (resp_text) OCSP_RESPONSE_print(out, resp, 0); /* If running as responder don't verify our own response */ if (cbio) goto end; store = setup_verify(bio_err, CAfile, CApath); if(!store) goto end; if (verify_certfile) Loading Loading @@ -622,8 +800,12 @@ end: X509_free(signer); X509_STORE_free(store); EVP_PKEY_free(key); EVP_PKEY_free(rkey); X509_free(issuer); X509_free(cert); X509_free(rsigner); X509_free(rca_cert); TXT_DB_free(rdb); BIO_free_all(cbio); BIO_free(out); OCSP_REQUEST_free(req); Loading Loading @@ -763,3 +945,205 @@ static int print_ocsp_summary(BIO *out, OCSP_BASICRESP *bs, OCSP_REQUEST *req, return 1; } static int make_ocsp_response(OCSP_RESPONSE **resp, OCSP_REQUEST *req, TXT_DB *db, X509 *ca, X509 *rcert, EVP_PKEY *rkey, STACK_OF(X509) *rother, unsigned long flags, int nmin, int ndays) { ASN1_TIME *thisupd = NULL, *nextupd = NULL; OCSP_CERTID *cid, *ca_id = NULL; OCSP_BASICRESP *bs = NULL; int i, id_count, ret = 1; id_count = OCSP_request_onereq_count(req); if (id_count <= 0) { *resp = OCSP_response_create(OCSP_RESPONSE_STATUS_MALFORMEDREQUEST, NULL); goto end; } ca_id = OCSP_cert_to_id(EVP_sha1(), NULL, ca); bs = OCSP_BASICRESP_new(); thisupd = X509_gmtime_adj(NULL, 0); if (ndays != -1) nextupd = X509_gmtime_adj(NULL, nmin * 60 + ndays * 3600 * 24 ); /* Examine each certificate id in the request */ for (i = 0; i < id_count; i++) { OCSP_ONEREQ *one; ASN1_INTEGER *serial; char **inf; one = OCSP_request_onereq_get0(req, i); cid = OCSP_onereq_get0_id(one); /* Is this request about our CA? */ if (OCSP_id_issuer_cmp(ca_id, cid)) { OCSP_basic_add1_status(bs, cid, V_OCSP_CERTSTATUS_UNKNOWN, 0, NULL, thisupd, nextupd); continue; } OCSP_id_get0_info(NULL, NULL, NULL, &serial, cid); inf = lookup_serial(db, serial); if (!inf) OCSP_basic_add1_status(bs, cid, V_OCSP_CERTSTATUS_UNKNOWN, 0, NULL, thisupd, nextupd); else if (inf[DB_type][0] == DB_TYPE_VAL) OCSP_basic_add1_status(bs, cid, V_OCSP_CERTSTATUS_GOOD, 0, NULL, thisupd, nextupd); else if (inf[DB_type][0] == DB_TYPE_REV) { ASN1_OBJECT *inst = NULL; ASN1_TIME *revtm = NULL; ASN1_GENERALIZEDTIME *invtm = NULL; OCSP_SINGLERESP *single; int reason = -1; unpack_revinfo(&revtm, &reason, &inst, &invtm, inf[DB_rev_date]); single = OCSP_basic_add1_status(bs, cid, V_OCSP_CERTSTATUS_REVOKED, reason, revtm, thisupd, nextupd); if (invtm) OCSP_SINGLERESP_add1_ext_i2d(single, NID_invalidity_date, invtm, 0, 0); else if (inst) OCSP_SINGLERESP_add1_ext_i2d(single, NID_hold_instruction_code, inst, 0, 0); ASN1_OBJECT_free(inst); ASN1_TIME_free(revtm); ASN1_GENERALIZEDTIME_free(invtm); } } OCSP_copy_nonce(bs, req); OCSP_basic_sign(bs, rcert, rkey, EVP_sha1(), rother, flags); *resp = OCSP_response_create(OCSP_RESPONSE_STATUS_SUCCESSFUL, bs); end: ASN1_TIME_free(thisupd); ASN1_TIME_free(nextupd); OCSP_CERTID_free(ca_id); OCSP_BASICRESP_free(bs); return ret; } static char **lookup_serial(TXT_DB *db, ASN1_INTEGER *ser) { int i; BIGNUM *bn = NULL; char *itmp, *row[DB_NUMBER],**rrow; for (i = 0; i < DB_NUMBER; i++) row[i] = NULL; bn = ASN1_INTEGER_to_BN(ser,NULL); itmp = BN_bn2hex(bn); row[DB_serial] = itmp; BN_free(bn); rrow=TXT_DB_get_by_index(db,DB_serial,row); OPENSSL_free(itmp); return rrow; } /* Quick and dirty OCSP server: read in and parse input request */ static OCSP_REQUEST *do_responder(BIO **pcbio, char *port) { int have_post = 0, len; OCSP_REQUEST *req = NULL; char inbuf[1024]; BIO *acbio = NULL, *bufbio = NULL, *cbio = NULL; bufbio = BIO_new(BIO_f_buffer()); if (!bufbio) goto err; acbio = BIO_new_accept(port); if (!acbio) goto err; BIO_set_accept_bios(acbio, bufbio); bufbio = NULL; if (BIO_do_accept(acbio) <= 0) { BIO_printf(bio_err, "Error setting up accept BIO\n"); ERR_print_errors(bio_err); goto err; } BIO_printf(bio_err, "Waiting for OCSP client connection...\n"); if (BIO_do_accept(acbio) <= 0) { BIO_printf(bio_err, "Error accepting connection\n"); ERR_print_errors(bio_err); goto err; } BIO_printf(bio_err, "Connection Established\n"); cbio = BIO_pop(acbio); BIO_free_all(acbio); acbio = NULL; for(;;) { len = BIO_gets(cbio, inbuf, 1024); if (len <= 0) goto err; /* Look for "POST" signalling start of query */ if (!have_post) { if(strncmp(inbuf, "POST", 4)) { BIO_printf(bio_err, "Invalid request\n"); goto err; } have_post = 1; } /* Look for end of headers */ if ((inbuf[0] == '\r') || (inbuf[0] == '\n')) break; } /* Try to read OCSP request */ req = d2i_OCSP_REQUEST_bio(cbio, NULL); *pcbio = cbio; if (!req) { BIO_printf(bio_err, "Error parsing OCSP request\n"); ERR_print_errors(bio_err); } return req; err: BIO_free_all(acbio); BIO_free_all(cbio); BIO_free(bufbio); OCSP_REQUEST_free(req); return NULL; } static int send_ocsp_response(BIO *cbio, OCSP_RESPONSE *resp) { char http_resp[] = "HTTP/1.0 200 OK\r\nContent-type: application/ocsp-response\r\n" "Content-Length: %d\r\n\r\n"; if (!cbio) return 0; BIO_printf(cbio, http_resp, i2d_OCSP_RESPONSE(resp, NULL)); i2d_OCSP_RESPONSE_bio(cbio, resp); BIO_flush(cbio); return 1; }