/* Licensed to Stichting The Commons Conservancy (TCC) under one or more * contributor license agreements. See the AUTHORS file distributed with * this work for additional information regarding copyright ownership. * TCC licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Parse and return responses to RFC3161 Time Stamp Protocol requests. * * Author: Graham Leggett * */ #include #include #include #include #include #include #include #include #include #include "httpd.h" #include "http_config.h" #include "http_core.h" #include "http_log.h" #include "http_protocol.h" #include "http_request.h" #include "util_script.h" #include "mod_ca.h" #ifndef sk_EVP_MD_free /* Recent versions of OpenSSL seem to no longer define * a stack of EVP_MD's. */ #include DEFINE_STACK_OF(EVP_MD) #endif #define DEFAULT_TIMESTAMP_SIZE 128*1024 module AP_MODULE_DECLARE_DATA timestamp_module; typedef struct { X509 *signer; int signer_set; EVP_PKEY *key; int key_set; STACK_OF(X509) *chain; int chain_set; apr_off_t size; int size_set; const char *location; int location_set; ASN1_OBJECT *default_policy; int default_policy_set; STACK_OF(ASN1_OBJECT) *policies; int policies_set; STACK_OF(EVP_MD) *digests; int digests_set; int include_chain; int include_chain_set; int ordering; int ordering_set; unsigned int precision; int precision_set; int tsa_name; int tsa_name_set; } timestamp_config_rec; static apr_status_t timestamp_BIO_cleanup(void *data) { BIO_free((BIO *) data); return APR_SUCCESS; } static apr_status_t timestamp_TS_RESP_CTX_cleanup(void *data) { TS_RESP_CTX_free((TS_RESP_CTX *) data); return APR_SUCCESS; } static apr_status_t timestamp_TS_RESP_cleanup(void *data) { TS_RESP_free((TS_RESP *) data); return APR_SUCCESS; } static apr_status_t timestamp_X509_cleanup(void *data) { X509_free((X509 *) data); return APR_SUCCESS; } static apr_status_t timestamp_EVP_PKEY_cleanup(void *data) { EVP_PKEY_free((EVP_PKEY *) data); return APR_SUCCESS; } static apr_status_t timestamp_ASN1_OBJECT_cleanup(void *data) { ASN1_OBJECT_free((ASN1_OBJECT *) data); return APR_SUCCESS; } static apr_status_t timestamp_sk_ASN1_OBJECT_cleanup(void *data) { sk_ASN1_OBJECT_free((STACK_OF(ASN1_OBJECT) *) data); return APR_SUCCESS; } static apr_status_t timestamp_sk_EVP_MD_cleanup(void *data) { sk_EVP_MD_free((STACK_OF(EVP_MD) *) data); return APR_SUCCESS; } static apr_status_t timestamp_sk_X509_cleanup(void *data) { sk_X509_free((STACK_OF(X509) *) data); return APR_SUCCESS; } static void *create_timestamp_dir_config(apr_pool_t *p, char *d) { timestamp_config_rec *conf = apr_pcalloc(p, sizeof(timestamp_config_rec)); conf->size = DEFAULT_TIMESTAMP_SIZE; return conf; } static void *merge_timestamp_dir_config(apr_pool_t *p, void *basev, void *addv) { timestamp_config_rec *new = (timestamp_config_rec *) apr_pcalloc(p, sizeof(timestamp_config_rec)); timestamp_config_rec *add = (timestamp_config_rec *) addv; timestamp_config_rec *base = (timestamp_config_rec *) basev; new->signer = (add->signer_set == 0) ? base->signer : add->signer; new->signer_set = add->signer_set || base->signer_set; new->key = (add->key_set == 0) ? base->key : add->key; new->key_set = add->key_set || base->key_set; new->chain = (add->chain_set == 0) ? base->chain : add->chain; new->chain_set = add->chain_set || base->chain_set; new->size = (add->size_set == 0) ? base->size : add->size; new->size_set = add->size_set || base->size_set; new->location = (add->location_set == 0) ? base->location : add->location; new->location_set = add->location_set || base->location_set; new->default_policy = (add->default_policy_set == 0) ? base->default_policy : add->default_policy; new->default_policy_set = add->default_policy_set || base->default_policy_set; new->policies = (add->policies_set == 0) ? base->policies : add->policies; new->policies_set = add->policies_set || base->policies_set; new->digests = (add->digests_set == 0) ? base->digests : add->digests; new->digests_set = add->digests_set || base->digests_set; new->include_chain = (add->include_chain_set == 0) ? base->include_chain : add->include_chain; new->include_chain_set = add->include_chain_set || base->include_chain_set; new->ordering = (add->ordering_set == 0) ? base->ordering : add->ordering; new->ordering_set = add->ordering_set || base->ordering_set; new->precision = (add->precision_set == 0) ? base->precision : add->precision; new->precision_set = add->precision_set || base->precision_set; new->tsa_name = (add->tsa_name_set == 0) ? base->tsa_name : add->tsa_name; new->tsa_name_set = add->tsa_name_set || base->tsa_name_set; return new; } static const char *set_tsa_certificate(cmd_parms *cmd, void *dconf, const char *arg) { timestamp_config_rec *conf = dconf; BIO *in; /* set_signing_certificate() will be called twice. Don't bother * going through all of the initialization on the first call * because it will just be thrown away.*/ if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) { return NULL; } arg = ap_server_root_relative(cmd->pool, arg); in = BIO_new(BIO_s_file()); if (BIO_read_filename(in, arg) <= 0) { return apr_psprintf(cmd->pool, "Could not load certificate from: %s", arg); } conf->signer = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL); if (!conf->signer) { BIO_free(in); return apr_psprintf(cmd->pool, "Could not parse certificate from: %s", arg); } conf->signer_set = 1; apr_pool_cleanup_register(cmd->pool, conf->signer, timestamp_X509_cleanup, apr_pool_cleanup_null); BIO_free(in); return NULL; } static const char *set_tsa_key(cmd_parms *cmd, void *dconf, const char *arg) { timestamp_config_rec *conf = dconf; BIO *in; /* set_signing_key() will be called twice. Don't bother * going through all of the initialization on the first call * because it will just be thrown away.*/ if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) { return NULL; } arg = ap_server_root_relative(cmd->pool, arg); in = BIO_new(BIO_s_file()); if (BIO_read_filename(in, arg) <= 0) { return apr_psprintf(cmd->pool, "Could not load key from: %s", arg); } conf->key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL); if (!conf->key) { BIO_free(in); return apr_psprintf(cmd->pool, "Could not parse key from: %s", arg); } conf->key_set = 1; apr_pool_cleanup_register(cmd->pool, conf->key, timestamp_EVP_PKEY_cleanup, apr_pool_cleanup_null); BIO_free(in); return NULL; } static const char *set_tsa_certificate_chain(cmd_parms *cmd, void *dconf, const char *arg) { timestamp_config_rec *conf = dconf; BIO *in; STACK_OF(X509_INFO) *xis; X509_INFO *xi; int i; /* set_tsa_certificate_chain() will be called twice. Don't bother * going through all of the initialization on the first call * because it will just be thrown away.*/ if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) { return NULL; } arg = ap_server_root_relative(cmd->pool, arg); in = BIO_new(BIO_s_file()); if (BIO_read_filename(in, arg) <= 0) { return apr_psprintf(cmd->pool, "Could not load certificate from: %s", arg); } if (!conf->chain) { conf->chain = sk_X509_new_null(); apr_pool_cleanup_register(cmd->pool, conf->chain, timestamp_sk_X509_cleanup, apr_pool_cleanup_null); } xis = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL); if (!xis) { BIO_free(in); return apr_psprintf(cmd->pool, "Could not parse certificate(s) from: %s", arg); } for (i = 0; i < sk_X509_INFO_num(xis); i++) { xi = sk_X509_INFO_value(xis, i); if (xi->x509) { if (!sk_X509_push(conf->chain, xi->x509)) { sk_X509_INFO_pop_free(xis, X509_INFO_free); BIO_free(in); return apr_psprintf(cmd->pool, "Could not push certificate(s) from: %s", arg); } xi->x509 = NULL; } } conf->chain_set = 1; sk_X509_INFO_pop_free(xis, X509_INFO_free); BIO_free(in); return NULL; } static const char *add_timestamp_policy(cmd_parms *cmd, void *dconf, const char *arg) { timestamp_config_rec *conf = dconf; ASN1_OBJECT *obj; if (!conf->policies) { conf->policies = sk_ASN1_OBJECT_new_null(); apr_pool_cleanup_register(cmd->pool, conf->policies, timestamp_sk_ASN1_OBJECT_cleanup, apr_pool_cleanup_null); } obj = OBJ_txt2obj(arg, 0); if (!obj) { return apr_psprintf(cmd->pool, "'%s' could not be recognised as a valid policy.", arg); } sk_ASN1_OBJECT_push(conf->policies, obj); conf->policies_set = 1; return NULL; } static const char *set_timestamp_default_policy(cmd_parms *cmd, void *dconf, const char *arg) { timestamp_config_rec *conf = dconf; conf->default_policy = OBJ_txt2obj(arg, 0); if (!conf->default_policy) { return apr_psprintf(cmd->pool, "'%s' could not be recognised as a valid policy.", arg); } apr_pool_cleanup_register(cmd->pool, conf->default_policy, timestamp_ASN1_OBJECT_cleanup, apr_pool_cleanup_null); conf->default_policy_set = 1; return NULL; } static const char *add_timestamp_digest(cmd_parms *cmd, void *dconf, const char *arg) { timestamp_config_rec *conf = dconf; const EVP_MD *digest; if (!conf->digests) { conf->digests = sk_EVP_MD_new_null(); apr_pool_cleanup_register(cmd->pool, conf->digests, timestamp_sk_EVP_MD_cleanup, apr_pool_cleanup_null); } digest = EVP_get_digestbyname(arg); if (!digest) { return apr_psprintf(cmd->pool, "'%s' could not be recognised as a valid digest.", arg); } if (!sk_EVP_MD_push(conf->digests, (EVP_MD *)digest)) { return apr_psprintf(cmd->pool, "'%s' could not be added as a valid digest.", arg); } conf->digests_set = 1; return NULL; } static const char *set_timestamp_include_chain(cmd_parms *cmd, void *dconf, int flag) { timestamp_config_rec *conf = dconf; conf->include_chain = flag; conf->include_chain_set = 1; return NULL; } static const char *set_timestamp_ordering(cmd_parms *cmd, void *dconf, int flag) { timestamp_config_rec *conf = dconf; conf->ordering = flag; conf->ordering_set = 1; return NULL; } static const char *set_timestamp_tsa_name(cmd_parms *cmd, void *dconf, int flag) { timestamp_config_rec *conf = dconf; conf->tsa_name = flag; conf->tsa_name_set = 1; return NULL; } static const char *set_timestamp_precision(cmd_parms *cmd, void *dconf, const char *arg) { timestamp_config_rec *conf = dconf; apr_off_t offset; if (apr_strtoff(&offset, arg, NULL, 10) != APR_SUCCESS || offset < 0|| offset > TS_MAX_CLOCK_PRECISION_DIGITS) { return apr_psprintf(cmd->pool, "TimestampClockPrecisionDigits argument must be a positive integer from 0 to %d.", TS_MAX_CLOCK_PRECISION_DIGITS); } conf->precision = (unsigned int) offset; conf->precision_set = 1; return NULL; } static const char *set_timestamp_size(cmd_parms *cmd, void *dconf, const char *arg) { timestamp_config_rec *conf = dconf; if (apr_strtoff(&conf->size, arg, NULL, 10) != APR_SUCCESS || conf->size < 4096) { return "TimestampSize argument must be an integer representing the max size of an RFC3161 request, at least 4096"; } conf->size_set = 1; return NULL; } static const char *set_location(cmd_parms *cmd, void *dconf, const char *arg) { timestamp_config_rec *conf = dconf; conf->location = arg; conf->location_set = 1; return NULL; } static const command_rec timestamp_cmds[] = { AP_INIT_TAKE1("TimestampSigningCertificate", set_tsa_certificate, NULL, RSRC_CONF | ACCESS_CONF, "Set to the name of the signing certificate."), AP_INIT_TAKE1("TimestampSigningKey", set_tsa_key, NULL, RSRC_CONF | ACCESS_CONF, "Set to the name of the signing key."), AP_INIT_TAKE1("TimestampCertificateChain", set_tsa_certificate_chain, NULL, RSRC_CONF | ACCESS_CONF, "Set to the name of a file containing the rest of the certificate chain."), AP_INIT_TAKE1("TimestampSize", set_timestamp_size, NULL, RSRC_CONF | ACCESS_CONF, "Set to the maximum size of the timestamp request from the client."), AP_INIT_TAKE1("TimestampLocation", set_location, NULL, RSRC_CONF | ACCESS_CONF, "Set to the location of the timestamp service."), AP_INIT_ITERATE("TimestampPolicy", add_timestamp_policy, NULL, RSRC_CONF | ACCESS_CONF, "Add the given policy to the timestamp."), AP_INIT_ITERATE("TimestampDefaultPolicy", set_timestamp_default_policy, NULL, RSRC_CONF | ACCESS_CONF, "Set the given policy as the default timestamp policy."), AP_INIT_ITERATE("TimestampDigest", add_timestamp_digest, NULL, RSRC_CONF | ACCESS_CONF, "Add the given digest to the timestamp."), AP_INIT_FLAG("TimestampIncludeChain", set_timestamp_include_chain, NULL, RSRC_CONF | ACCESS_CONF, "Indicate whether the certificate chain should be included in the ESS signing certificate attribute within the response."), AP_INIT_FLAG("TimestampOrdering", set_timestamp_ordering, NULL, RSRC_CONF | ACCESS_CONF, "Set ordering to true in the response."), AP_INIT_FLAG("TimestampTsaName", set_timestamp_tsa_name, NULL, RSRC_CONF | ACCESS_CONF, "Set to include the TSA name in the response."), AP_INIT_TAKE1("TimestampClockPrecisionDigits", set_timestamp_precision, NULL, RSRC_CONF | ACCESS_CONF, "Set the number of clock precision digits."), { NULL } }; static void log_message(request_rec *r, apr_status_t status, const char *message) { int len; BIO *mem = BIO_new(BIO_s_mem()); char *err = apr_palloc(r->pool, HUGE_STRING_LEN); ERR_print_errors(mem); len = BIO_gets(mem, err, HUGE_STRING_LEN - 1); if (len > -1) { err[len] = 0; } apr_table_setn(r->notes, "error-notes", apr_pstrcat(r->pool, "Timestamp response could not be returned: ", ap_escape_html( r->pool, message), NULL)); /* Allow "error-notes" string to be printed by ap_send_error_response() */ apr_table_setn(r->notes, "verbose-error-to", "*"); if (len > 0) { ap_log_rerror( APLOG_MARK, APLOG_ERR, status, r, "%s (%s)", message, err); } else { ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, "%s", message); } BIO_free(mem); } ASN1_INTEGER *timestamp_serial_cb(TS_RESP_CTX *ctx, void *data) { request_rec *r = data; int rv; const unsigned char *buffer; apr_size_t len; ASN1_INTEGER *sno = NULL; /* read in the serial number */ rv = ap_run_ca_makeserial(r, NULL, &buffer, &len); if (rv == DECLINED) { TS_RESP_CTX_set_status_info(ctx, TS_STATUS_REJECTION, "No module configured to generate the serial number (ca_makeserial)"); TS_RESP_CTX_add_failure_info(ctx, TS_INFO_ADD_INFO_NOT_AVAILABLE); return NULL; } if (rv != OK) { TS_RESP_CTX_set_status_info(ctx, TS_STATUS_REJECTION, "Unable to generate the serial number (ca_makeserial)"); TS_RESP_CTX_add_failure_info(ctx, TS_INFO_ADD_INFO_NOT_AVAILABLE); return NULL; } if (!d2i_ASN1_INTEGER(&sno, &buffer, len)) { TS_RESP_CTX_set_status_info(ctx, TS_STATUS_REJECTION, "Could not DER decode the serial number (ca_makeserial)"); TS_RESP_CTX_add_failure_info(ctx, TS_INFO_ADD_INFO_NOT_AVAILABLE); return NULL; } return sno; } static int timestamp_time_cb(TS_RESP_CTX *ctx, void *data, long *sec, long *usec) { request_rec *r = data; int rv; apr_time_t time = 0; apr_interval_time_t as = 0, ams = 0, amicro = 0; /* read in the time */ rv = ap_run_ca_gettime(r, &time, &as, &ams, &amicro); if (rv == DECLINED) { TS_RESP_CTX_set_status_info(ctx, TS_STATUS_REJECTION, "No module configured to generate the time (ca_gettime)"); TS_RESP_CTX_add_failure_info(ctx, TS_INFO_TIME_NOT_AVAILABLE); return 0; } if (rv != OK) { TS_RESP_CTX_set_status_info(ctx, TS_STATUS_REJECTION, "Unable to generate the time (ca_gettime)"); TS_RESP_CTX_add_failure_info(ctx, TS_INFO_TIME_NOT_AVAILABLE); return 0; } /* set the accuracy */ if (!TS_RESP_CTX_set_accuracy(ctx, (int) as, (int) ams, (int) amicro)) { TS_RESP_CTX_set_status_info(ctx, TS_STATUS_REJECTION, "Unable to set the accuracy"); TS_RESP_CTX_add_failure_info(ctx, TS_INFO_TIME_NOT_AVAILABLE); return 0; } /* return the time we retrieved */ *sec = (long) apr_time_sec(time); *usec = (long) apr_time_usec(time); return 1; } static int post_timestamp(request_rec *r, timestamp_config_rec *conf) { apr_size_t len; const unsigned char *buf; unsigned char *tmp; apr_bucket_brigade *bb; apr_bucket *e; int rv, seen_eos; apr_status_t status; apr_size_t total = 0; const char *type; TS_RESP_CTX *ctx; TS_RESP *resp; TS_STATUS_INFO *si; BIO *in = BIO_new(BIO_s_mem()); apr_pool_cleanup_register(r->pool, in, timestamp_BIO_cleanup, apr_pool_cleanup_null); /* is this an RFC3161 request? */ type = apr_table_get(r->headers_in, "Content-Type"); if (!type || strcmp(type, "application/timestamp-query")) { return HTTP_UNSUPPORTED_MEDIA_TYPE; } bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); seen_eos = 0; do { apr_bucket *bucket; rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, APR_BLOCK_READ, HUGE_STRING_LEN); if (rv != APR_SUCCESS) { apr_brigade_destroy(bb); return HTTP_BAD_REQUEST; } for (bucket = APR_BRIGADE_FIRST(bb); bucket != APR_BRIGADE_SENTINEL(bb); bucket = APR_BUCKET_NEXT(bucket)) { const char *data; apr_size_t len; if (APR_BUCKET_IS_EOS(bucket)) { seen_eos = 1; break; } /* These are metadata buckets. */ if (bucket->length == 0) { continue; } rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ); if (rv != APR_SUCCESS) { return HTTP_BAD_REQUEST; } if (!BIO_write(in, data, (int) len)) { return HTTP_BAD_REQUEST; } total += len; if (total > conf->size) { return HTTP_REQUEST_ENTITY_TOO_LARGE; } } apr_brigade_cleanup(bb); } while (!seen_eos); /* create the TS context */ ctx = TS_RESP_CTX_new(); if (!ctx) { log_message(r, APR_SUCCESS, "Timestamp context could not be created"); return HTTP_INTERNAL_SERVER_ERROR; } apr_pool_cleanup_register(r->pool, ctx, timestamp_TS_RESP_CTX_cleanup, apr_pool_cleanup_null); TS_RESP_CTX_set_serial_cb(ctx, timestamp_serial_cb, r); TS_RESP_CTX_set_time_cb(ctx, timestamp_time_cb, r); if (!conf->signer || !TS_RESP_CTX_set_signer_cert(ctx, conf->signer)) { log_message(r, APR_SUCCESS, "Signer certificate could not be added, and is required (TimestampSigningCertificate). Check that this certificate has a critical extendedKeyUsage of 'timeStamping'."); return HTTP_INTERNAL_SERVER_ERROR; } if (!conf->key || !TS_RESP_CTX_set_signer_key(ctx, conf->key)) { log_message(r, APR_SUCCESS, "Signer key could not be added, and is required (TimestampSigningKey)"); return HTTP_INTERNAL_SERVER_ERROR; } if (conf->chain && !TS_RESP_CTX_set_certs(ctx, conf->chain)) { log_message(r, APR_SUCCESS, "Certificate chain was specified, but could not be added"); return HTTP_INTERNAL_SERVER_ERROR; } if (!conf->default_policy || !TS_RESP_CTX_set_def_policy(ctx, conf->default_policy)) { log_message(r, APR_SUCCESS, "Default policy could not be set, and is required (TimestampDefaultPolicy)"); return HTTP_INTERNAL_SERVER_ERROR; } if (conf->policies) { int i; for (i = 0; i < sk_ASN1_OBJECT_num(conf->policies); i++) { ASN1_OBJECT *obj = sk_ASN1_OBJECT_value(conf->policies, i); if (obj) { if (!TS_RESP_CTX_add_policy(ctx, obj)) { log_message(r, APR_SUCCESS, "Timestamp policy could not be added"); return HTTP_INTERNAL_SERVER_ERROR; } } } } if (conf->digests) { int i; for (i = 0; i < sk_EVP_MD_num(conf->digests); i++) { const EVP_MD *digest = sk_EVP_MD_value(conf->digests, i); if (digest) { if (!TS_RESP_CTX_add_md(ctx, digest)) { log_message(r, APR_SUCCESS, "Timestamp digest could not be added"); return HTTP_INTERNAL_SERVER_ERROR; } } } } else { log_message(r, APR_SUCCESS, "At least one timestamp digest must be specified (TimestampDigest)"); return HTTP_INTERNAL_SERVER_ERROR; } if (conf->precision_set) { if (!TS_RESP_CTX_set_clock_precision_digits(ctx, conf->precision)) { log_message(r, APR_SUCCESS, "Timestamp clock precision digits could not be set"); return HTTP_INTERNAL_SERVER_ERROR; } } if (conf->include_chain) { TS_RESP_CTX_add_flags(ctx, TS_ESS_CERT_ID_CHAIN); } if (conf->ordering) { TS_RESP_CTX_add_flags(ctx, TS_ORDERING); } if (conf->tsa_name) { TS_RESP_CTX_add_flags(ctx, TS_TSA_NAME); } /* parse the request, create the response */ resp = TS_RESP_create_response(ctx, in); if (!resp) { log_message(r, APR_SUCCESS, "Timestamp request could not be parsed"); return HTTP_BAD_REQUEST; } apr_pool_cleanup_register(r->pool, resp, timestamp_TS_RESP_cleanup, apr_pool_cleanup_null); /* sanity check - any errors during the generation process? */ si = TS_RESP_get_status_info(resp); // Introduced around 1.1.0a #if OPENSSL_VERSION_NUMBER > 0x1010000fL const ASN1_INTEGER * sisp = TS_STATUS_INFO_get0_status(si); #else ASN1_INTEGER * sisp = si->status; #endif if (ASN1_INTEGER_get(sisp) != TS_STATUS_GRANTED) { log_message(r, APR_SUCCESS, "Timestamp not granted"); } /* serialise the response */ len = i2d_TS_RESP(resp, NULL); if (!len) { log_message(r, APR_SUCCESS, "Timestamp response could not be written"); return HTTP_BAD_REQUEST; } buf = tmp = apr_palloc(r->pool, len); i2d_TS_RESP(resp, &tmp); e = apr_bucket_pool_create((const char *) buf, len, r->pool, r->connection->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, e); /* content type */ ap_set_content_type(r, "application/timestamp-reply"); ap_set_content_length(r, len); e = apr_bucket_eos_create(r->connection->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, e); status = ap_pass_brigade(r->output_filters, bb); if (status == APR_SUCCESS || r->status != HTTP_OK || r->connection->aborted) { return OK; } else { /* no way to know what type of error occurred */ ap_log_rerror( APLOG_MARK, APLOG_DEBUG, status, r, "timestamp_handler: ap_pass_brigade returned %i", status); return HTTP_INTERNAL_SERVER_ERROR; } /* ready to leave */ return OK; } static int options_wadl(request_rec *r, timestamp_config_rec *conf) { int rv; /* discard the request body */ if ((rv = ap_discard_request_body(r)) != OK) { return rv; } ap_set_content_type(r, "application/vnd.sun.wadl+xml"); ap_rprintf(r, "\n" "\n" " \n" " \n" " \n" " \n" " \n" " The body of the request is expected to contain an ASN.1 DER encoded\n" " Time-Stamp Request message.\n" " \n" " \n" " \n" " \n" " If an internal configuration error occurred, 500\n" " Internal Server Error will be returned, and the server error log will contain\n" " full details of the error.\n" " \n" " \n" " \n" " \n" " If request lacks key information, 400 Bad Request\n" " will be returned.\n" " \n" " \n" " \n" " \n" " After a successful timestamp, 200 OK will be returned with the body\n" " containing the ASN.1 DER-encoded Time-Stamp Response message.\n" " \n" " \n" " \n" " \n" " \n" "\n", conf->location ? conf->location : apr_pstrcat(r->pool, ap_http_scheme(r), "://", r->server->server_hostname, r->uri, NULL)); return OK; } static int timestamp_handler(request_rec *r) { timestamp_config_rec *conf = ap_get_module_config(r->per_dir_config, ×tamp_module); if (!conf) { return DECLINED; } if (strcmp(r->handler, "timestamp")) { return DECLINED; } /* POST handles RFC3161, while OPTIONS returns WADL for the service */ ap_allow_methods(r, 1, "POST", "OPTIONS", NULL); if (!strcmp(r->method, "POST")) { return post_timestamp(r, conf); } else if (!strcmp(r->method, "OPTIONS")) { return options_wadl(r, conf); } else { return HTTP_METHOD_NOT_ALLOWED; } } static apr_status_t timestamp_cleanup(void *data) { ERR_free_strings(); EVP_cleanup(); return APR_SUCCESS; } static int timestamp_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp) { OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); apr_pool_cleanup_register(pconf, NULL, timestamp_cleanup, apr_pool_cleanup_null); return APR_SUCCESS; } static void register_hooks(apr_pool_t *p) { ap_hook_pre_config(timestamp_pre_config, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_handler(timestamp_handler, NULL, NULL, APR_HOOK_MIDDLE); } AP_DECLARE_MODULE(timestamp) = { STANDARD20_MODULE_STUFF, create_timestamp_dir_config, /* dir config creater */ merge_timestamp_dir_config, /* dir merger --- default is to override */ NULL, /* server config */ NULL, /* merge server config */ timestamp_cmds, /* command apr_table_t */ register_hooks /* register hooks */ };