You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by tr...@apache.org on 2014/04/21 23:14:22 UTC

svn commit: r1588987 [3/3] - in /httpd/httpd/trunk: ./ docs/manual/mod/ docs/manual/programs/ modules/ssl/ support/

Added: httpd/httpd/trunk/modules/ssl/ssl_ct_log_config.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/ssl_ct_log_config.c?rev=1588987&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/ssl/ssl_ct_log_config.c (added)
+++ httpd/httpd/trunk/modules/ssl/ssl_ct_log_config.c Mon Apr 21 21:14:21 2014
@@ -0,0 +1,419 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+#include "apr_dbd.h"
+#include "apr_escape.h"
+#include "apr_strings.h"
+
+#include "httpd.h"
+#include "http_log.h"
+#include "http_main.h"
+
+#include "ssl_ct_sct.h"
+#include "ssl_ct_log_config.h"
+#include "ssl_ct_util.h"
+
+APLOG_USE_MODULE(ssl_ct);
+
+int log_config_readable(apr_pool_t *p, const char *logconfig,
+                        const char **msg)
+{
+    const apr_dbd_driver_t *driver;
+    apr_dbd_t *handle;
+    apr_status_t rv;
+    apr_dbd_results_t *res;
+    int rc;
+
+    rv = apr_dbd_get_driver(p, "sqlite3", &driver);
+    if (rv != APR_SUCCESS) {
+        if (msg) {
+            *msg = "SQLite3 driver cannot be loaded";
+        }
+        return 0;
+    }
+
+    rv = apr_dbd_open(driver, p, logconfig, &handle);
+    if (rv != APR_SUCCESS) {
+        return 0;
+    }
+
+    /* is there a cheaper way? */
+    res = NULL;
+    rc = apr_dbd_select(driver, p, handle, &res,
+                        "SELECT * FROM loginfo WHERE id = 0", 0);
+
+    apr_dbd_close(driver, handle);
+
+    if (rc != 0) {
+        return 0;
+    }
+
+    return 1;
+}
+
+static apr_status_t public_key_cleanup(void *data)
+{
+    EVP_PKEY *pubkey = data;
+
+    EVP_PKEY_free(pubkey);
+    return APR_SUCCESS;
+}
+
+static apr_status_t read_public_key(apr_pool_t *p, const char *pubkey_fname,
+                                    EVP_PKEY **ppkey)
+{
+    apr_status_t rv;
+    EVP_PKEY *pubkey;
+    FILE *pubkeyf;
+
+    *ppkey = NULL;
+
+    rv = ctutil_fopen(pubkey_fname, "r", &pubkeyf);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
+                     "could not open log public key file %s",
+                     pubkey_fname);
+        return rv;
+    }
+
+    pubkey = PEM_read_PUBKEY(pubkeyf, NULL, NULL, NULL);
+    if (!pubkey) {
+        fclose(pubkeyf);
+        rv = APR_EINVAL;
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
+                     "PEM_read_PUBKEY() failed to process public key file %s",
+                     pubkey_fname);
+        return rv;
+    }
+
+    fclose(pubkeyf);
+
+    *ppkey = pubkey;
+
+    apr_pool_cleanup_register(p, (void *)pubkey, public_key_cleanup,
+                              apr_pool_cleanup_null);
+
+    return APR_SUCCESS;
+}
+
+static void digest_public_key(EVP_PKEY *pubkey, unsigned char digest[LOG_ID_SIZE])
+{
+    int len = i2d_PUBKEY(pubkey, NULL);
+    unsigned char *val = malloc(len);
+    unsigned char *tmp = val;
+    SHA256_CTX sha256ctx;
+
+    ap_assert(LOG_ID_SIZE == SHA256_DIGEST_LENGTH);
+
+    i2d_PUBKEY(pubkey, &tmp);
+    SHA256_Init(&sha256ctx);
+    SHA256_Update(&sha256ctx, (unsigned char *)val, len);
+    SHA256_Final(digest, &sha256ctx);
+    free(val);
+}
+
+static apr_status_t parse_log_url(apr_pool_t *p, const char *lu, apr_uri_t *puri)
+{
+    apr_status_t rv;
+    apr_uri_t uri;
+
+    rv = apr_uri_parse(p, lu, &uri);
+    if (rv == APR_SUCCESS) {
+        if (!uri.scheme
+            || !uri.hostname
+            || !uri.path) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
+                         "Error in log url \"%s\": URL can't be parsed or is missing required "
+                         "elements", lu);
+            rv = APR_EINVAL;
+        }
+        if (strcmp(uri.scheme, "http")) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
+                         "Error in log url \"%s\": Only scheme \"http\" (instead of \"%s\") "
+                         "is currently accepted",
+                         lu, uri.scheme);
+            rv = APR_EINVAL;
+        }
+        if (strcmp(uri.path, "/")) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
+                         "Error in log url \"%s\": Only path \"/\" (instead of \"%s\") "
+                         "is currently accepted",
+                         lu, uri.path);
+            rv = APR_EINVAL;
+        }
+    }
+    if (rv == APR_SUCCESS) {
+        *puri = uri;
+    }
+    return rv;
+}
+
+static apr_status_t parse_time_str(apr_pool_t *p, const char *time_str,
+                                   apr_time_t *time)
+{
+    apr_int64_t val;
+    const char *end;
+
+    errno = 0;
+    val = apr_strtoi64(time_str, (char **)&end, 10);
+    if (errno || *end != '\0') {
+        return APR_EINVAL;
+    }
+
+    *time = apr_time_from_msec(val);
+    return APR_SUCCESS;
+}
+
+/* The log_config array should have already been allocated from p. */
+apr_status_t save_log_config_entry(apr_array_header_t *log_config,
+                                   apr_pool_t *p,
+                                   const char *log_id,
+                                   const char *pubkey_fname,
+                                   const char *distrusted_str,
+                                   const char *min_time_str,
+                                   const char *max_time_str,
+                                   const char *url)
+{
+    apr_size_t len;
+    apr_status_t rv;
+    apr_time_t min_time, max_time;
+    apr_uri_t uri;
+    char *computed_log_id = NULL, *log_id_bin = NULL;
+    ct_log_config *newconf, **pnewconf;
+    int distrusted;
+    EVP_PKEY *public_key;
+
+    if (!distrusted_str) {
+        distrusted = DISTRUSTED_UNSET;
+    }
+    else if (!strcasecmp(distrusted_str, "1")) {
+        distrusted = DISTRUSTED;
+    }
+    else if (!strcasecmp(distrusted_str, "0")) {
+        distrusted = DISTRUSTED;
+    }
+    else {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
+                     "Trusted status \"%s\" not valid", distrusted_str);
+        return APR_EINVAL;
+    }
+
+    if (log_id) {
+        rv = apr_unescape_hex(NULL, log_id, strlen(log_id), 0, &len);
+        if (rv != 0 || len != 32) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
+                         "Log id \"%s\" not valid", log_id);
+            log_id_bin = apr_palloc(p, len);
+            apr_unescape_hex(log_id_bin, log_id, strlen(log_id), 0, NULL);
+        }
+    }
+
+    if (pubkey_fname) {
+        rv = read_public_key(p, pubkey_fname, &public_key);
+        if (rv != APR_SUCCESS) {
+            return rv;
+        }
+    }
+    else {
+        public_key = NULL;
+    }
+
+    if (min_time_str) {
+        rv = parse_time_str(p, min_time_str, &min_time);
+        if (rv) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
+                         "Invalid min time \"%s\"", min_time_str);
+            return rv;
+        }
+    }
+    else {
+        min_time = 0;
+    }
+
+    if (max_time_str) {
+        rv = parse_time_str(p, max_time_str, &max_time);
+        if (rv) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
+                         "Invalid max time \"%s\"", max_time_str);
+            return rv;
+        }
+    }
+    else {
+        max_time = 0;
+    }
+
+    if (url) {
+        rv = parse_log_url(p, url, &uri);
+        if (rv != APR_SUCCESS) {
+            return rv;
+        }
+    }
+
+    newconf = apr_pcalloc(p, sizeof(ct_log_config));
+    pnewconf = (ct_log_config **)apr_array_push(log_config);
+    *pnewconf = newconf;
+
+    newconf->distrusted = distrusted;
+    newconf->public_key = public_key;
+
+    if (newconf->public_key) {
+        computed_log_id = apr_palloc(p, LOG_ID_SIZE);
+        digest_public_key(newconf->public_key,
+                          (unsigned char *)computed_log_id);
+    }
+
+    if (computed_log_id && log_id_bin) {
+        if (memcmp(computed_log_id, log_id_bin, LOG_ID_SIZE)) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf,
+                         "Provided log id doesn't match digest of public key");
+            return APR_EINVAL;
+        }
+    }
+
+    newconf->log_id = log_id_bin ? log_id_bin : computed_log_id;
+
+    newconf->min_valid_time = min_time;
+    newconf->max_valid_time = max_time;
+
+    newconf->url = url;
+    if (url) {
+        newconf->uri = uri;
+        newconf->uri_str = apr_uri_unparse(p, &uri, 0);
+    }
+    newconf->public_key_pem = pubkey_fname;
+
+    return APR_SUCCESS;
+}
+
+apr_status_t read_config_db(apr_pool_t *p, server_rec *s_main,
+                            const char *log_config_fname,
+                            apr_array_header_t *log_config)
+{
+    apr_status_t rv;
+    const apr_dbd_driver_t *driver;
+    apr_dbd_t *handle;
+    apr_dbd_results_t *res;
+    apr_dbd_row_t *row;
+    int rc;
+
+    ap_assert(log_config);
+
+    rv = apr_dbd_get_driver(p, "sqlite3", &driver);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s_main,
+                     "APR SQLite3 driver can't be loaded");
+        return rv;
+    }
+
+    rv = apr_dbd_open(driver, p, log_config_fname, &handle);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s_main,
+                     "Can't open SQLite3 db %s", log_config_fname);
+        return rv;
+    }
+
+    res = NULL;
+    rc = apr_dbd_select(driver, p, handle, &res,
+                        "SELECT * FROM loginfo", 0);
+
+    if (rc != 0) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s_main,
+                     "SELECT of loginfo records failed");
+        apr_dbd_close(driver, handle);
+        return APR_EINVAL;
+    }
+
+    rc = apr_dbd_num_tuples(driver, res);
+    switch (rc) {
+    case -1:
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s_main,
+                     "Unexpected asynchronous result reading %s",
+                     log_config_fname);
+        apr_dbd_close(driver, handle);
+        return APR_EINVAL;
+    case 0:
+        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s_main,
+                     "Log configuration in %s is empty",
+                     log_config_fname);
+        apr_dbd_close(driver, handle);
+        return APR_SUCCESS;
+    default:
+        /* quiet some lints */
+        break;
+    }
+        
+    for (rv = apr_dbd_get_row(driver, p, res, &row, -1);
+         rv == APR_SUCCESS;
+         rv = apr_dbd_get_row(driver, p, res, &row, -1)) {
+        int cur = 0;
+        const char *id = apr_dbd_get_entry(driver, row, cur++);
+        const char *log_id = apr_dbd_get_entry(driver, row, cur++);
+        const char *public_key = apr_dbd_get_entry(driver, row, cur++);
+        const char *distrusted = apr_dbd_get_entry(driver, row, cur++);
+        const char *min_timestamp = apr_dbd_get_entry(driver, row, cur++);
+        const char *max_timestamp = apr_dbd_get_entry(driver, row, cur++);
+        const char *url = apr_dbd_get_entry(driver, row, cur++);
+
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s_main,
+                     "Log config: Record %s, log id %s, public key file %s, distrusted %s, URL %s, time %s->%s",
+                     id,
+                     log_id ? log_id : "(unset)",
+                     public_key ? public_key : "(unset)",
+                     distrusted ? distrusted : "(unset, defaults to trusted)",
+                     url ? url : "(unset)",
+                     min_timestamp ? min_timestamp : "-INF",
+                     max_timestamp ? max_timestamp : "+INF");
+
+        rv = save_log_config_entry(log_config, p, log_id,
+                                   public_key, distrusted, 
+                                   min_timestamp, max_timestamp, url);
+        if (rv != APR_SUCCESS) {
+            apr_dbd_close(driver, handle);
+            return rv;
+        }
+    }
+
+    apr_dbd_close(driver, handle);
+
+    return APR_SUCCESS;
+}
+
+int log_valid_for_received_sct(const ct_log_config *l, apr_time_t to_check)
+{
+    if (l->distrusted == DISTRUSTED) {
+        return 0;
+    }
+
+    if (l->max_valid_time && l->max_valid_time < to_check) {
+        return 0;
+    }
+
+    if (l->min_valid_time && l->min_valid_time < to_check) {
+        return 0;
+    }
+
+    return 1;
+}
+
+int log_valid_for_sent_sct(const ct_log_config *l)
+{
+    /* The log could return us an SCT with an older timestamp which
+     * is within the trusted time interval for the log, but for
+     * simplicity let's just assume that if the log isn't still
+     * within a trusted interval we won't send SCTs from the log.
+     */
+    return log_valid_for_received_sct(l, apr_time_now());
+}

Propchange: httpd/httpd/trunk/modules/ssl/ssl_ct_log_config.c
------------------------------------------------------------------------------
    eol-style:native = mod_ssl_ct.c

Added: httpd/httpd/trunk/modules/ssl/ssl_ct_log_config.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/ssl_ct_log_config.h?rev=1588987&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/ssl/ssl_ct_log_config.h (added)
+++ httpd/httpd/trunk/modules/ssl/ssl_ct_log_config.h Mon Apr 21 21:14:21 2014
@@ -0,0 +1,57 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+#ifndef SSL_CT_LOG_CONFIG_H
+#define SSL_CT_LOG_CONFIG_H
+
+#include "httpd.h"
+#include "mod_ssl_openssl.h" /* cheap way to get OpenSSL headers */
+
+typedef struct ct_log_config {
+    const char *log_id; /* binary form */
+    const char *public_key_pem;
+    EVP_PKEY *public_key;
+#define DISTRUSTED_UNSET -1
+#define TRUSTED           0
+#define DISTRUSTED        1
+    int distrusted;
+    apr_time_t min_valid_time, max_valid_time;
+    const char *url;
+    const char *uri_str;
+    apr_uri_t uri;
+} ct_log_config;
+
+int log_config_readable(apr_pool_t *p, const char *logconfig,
+                        const char **msg);
+
+apr_status_t read_config_db(apr_pool_t *p, server_rec *s_main,
+                            const char *log_config_fname,
+                            apr_array_header_t *log_config);
+
+apr_status_t save_log_config_entry(apr_array_header_t *log_config,
+                                   apr_pool_t *p,
+                                   const char *log_id,
+                                   const char *pubkey_fname,
+                                   const char *distrusted,
+                                   const char *min_time,
+                                   const char *max_time,
+                                   const char *url);
+
+int log_valid_for_sent_sct(const ct_log_config *l);
+
+int log_valid_for_received_sct(const ct_log_config *l, apr_time_t to_check);
+
+#endif /* SSL_CT_LOG_CONFIG_H */

Propchange: httpd/httpd/trunk/modules/ssl/ssl_ct_log_config.h
------------------------------------------------------------------------------
    eol-style:native = mod_ssl_ct.c

Added: httpd/httpd/trunk/modules/ssl/ssl_ct_sct.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/ssl_ct_sct.c?rev=1588987&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/ssl/ssl_ct_sct.c (added)
+++ httpd/httpd/trunk/modules/ssl/ssl_ct_sct.c Mon Apr 21 21:14:21 2014
@@ -0,0 +1,300 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+
+#include "ssl_ct_sct.h"
+#include "ssl_ct_util.h"
+
+#include "http_log.h"
+
+APLOG_USE_MODULE(ssl_ct);
+
+static apr_status_t verify_signature(sct_fields_t *sctf,
+                                     EVP_PKEY *pkey)
+{
+    EVP_MD_CTX ctx;
+    int rc;
+
+    if (sctf->signed_data == NULL) {
+        return APR_EINVAL;
+    }
+
+    EVP_MD_CTX_init(&ctx);
+    ap_assert(1 == EVP_VerifyInit(&ctx, EVP_sha256()));
+    ap_assert(1 == EVP_VerifyUpdate(&ctx, sctf->signed_data,
+                                    sctf->signed_data_len));
+    rc = EVP_VerifyFinal(&ctx, sctf->sig, sctf->siglen, pkey);
+    EVP_MD_CTX_cleanup(&ctx);
+
+    return rc == 1 ? APR_SUCCESS : APR_EINVAL;
+}
+
+apr_status_t sct_verify_signature(conn_rec *c, sct_fields_t *sctf,
+                                  apr_array_header_t *log_config)
+{
+    apr_status_t rv = APR_EINVAL;
+    int i;
+    ct_log_config **config_elts;
+    int nelts = log_config->nelts;
+
+    ap_assert(sctf->signed_data != NULL);
+
+    config_elts = (ct_log_config **)log_config->elts;
+
+    for (i = 0; i < nelts; i++) {
+        EVP_PKEY *pubkey = config_elts[i]->public_key;
+        const char *logid = config_elts[i]->log_id;
+
+        if (!pubkey || !logid) {
+            continue;
+        }
+
+        if (!memcmp(logid, sctf->logid, LOG_ID_SIZE)) {
+            if (!log_valid_for_received_sct(config_elts[i], sctf->time)) {
+                ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c,
+                              "Got SCT from distrusted log, or out of trusted time interval");
+                return APR_EINVAL;
+            }
+            rv = verify_signature(sctf, pubkey);
+            if (rv != APR_SUCCESS) {
+                ap_log_cerror(APLOG_MARK, 
+                              APLOG_ERR,
+                              rv, c,
+                              "verify_signature failed");
+            }
+            else {
+                ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
+                              "verify_signature succeeded");
+            }
+            return rv;
+        }
+    }
+
+    return APR_NOTFOUND;
+}
+
+apr_status_t sct_parse(const char *source,
+                       server_rec *s, const unsigned char *sct,
+                       apr_size_t len, cert_chain *cc,
+                       sct_fields_t *fields)
+{
+    const unsigned char *cur;
+    apr_size_t orig_len = len;
+    apr_status_t rv;
+
+    memset(fields, 0, sizeof *fields);
+
+    if (len < 1 + LOG_ID_SIZE + 8) {
+        /* no room for header */
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+                     "SCT size %" APR_SIZE_T_FMT " is too small",
+                     len);
+        return APR_EINVAL;
+    }
+
+    cur = sct;
+
+    fields->version = *cur;
+    cur++;
+    len -= 1;
+    memcpy(fields->logid, cur, LOG_ID_SIZE);
+    cur += LOG_ID_SIZE;
+    len -= LOG_ID_SIZE;
+    rv = ctutil_deserialize_uint64(&cur, &len, &fields->timestamp);
+    ap_assert(rv == APR_SUCCESS);
+
+    fields->time = apr_time_from_msec(fields->timestamp);
+
+    /* XXX maybe do this only if log level is such that we'll
+     *     use it later?
+     */
+    apr_rfc822_date(fields->timestr, fields->time);
+
+
+    if (len < 2) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+                     "SCT size %" APR_SIZE_T_FMT " has no space for extension "
+                     "len", orig_len);
+        return APR_EINVAL;
+    }
+
+    rv = ctutil_deserialize_uint16(&cur, &len, &fields->extlen);
+    ap_assert(rv == APR_SUCCESS);
+
+    if (fields->extlen != 0) {
+        if (fields->extlen < len) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+                         "SCT size %" APR_SIZE_T_FMT " has no space for "
+                         "%hu bytes of extensions",
+                         orig_len, fields->extlen);
+            return APR_EINVAL;
+        }
+
+        fields->extensions = cur;
+        cur += fields->extlen;
+        len -= fields->extlen;
+    }
+    else {
+        fields->extensions = 0;
+    }
+
+    if (len < 4) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+                     "SCT size %" APR_SIZE_T_FMT " has no space for "
+                     "hash algorithm, signature algorithm, and signature len",
+                     orig_len);
+        return APR_EINVAL;
+    }
+
+    fields->hash_alg = *cur;
+    cur += 1;
+    len -= 1;
+    fields->sig_alg = *cur;
+    cur += 1;
+    len -= 1;
+    rv = ctutil_deserialize_uint16(&cur, &len, &fields->siglen);
+    ap_assert(rv == APR_SUCCESS);
+
+    if (fields->siglen < len) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+                     "SCT has no space for signature");
+        return APR_EINVAL;
+    }
+
+    fields->sig = cur;
+    cur += fields->siglen;
+    len -= fields->siglen;
+
+    if (cc) {
+        /* If we have the server certificate, we can construct the
+         * data over which the signature is computed.
+         */
+
+        /* XXX Which part is signed? */
+        /* See certificate-transparency/src/proto/serializer.cc,
+         * method Serializer::SerializeV1CertSCTSignatureInput()
+         */
+
+        apr_size_t orig_len;
+        apr_size_t avail;
+        int der_length;
+        unsigned char *mem;
+        unsigned char *orig_mem;
+
+        der_length = i2d_X509(cc->leaf, NULL);
+        if (der_length < 0) {
+            rv = APR_EINVAL;
+        }
+
+        if (rv == APR_SUCCESS) {
+            orig_len = 0
+                + 1 /* version 1 */
+                + 1 /* CERTIFICATE_TIMESTAMP */
+                + 8 /* timestamp */
+                + 2 /* X509_ENTRY */
+                + 3 + der_length /* 24-bit length + X509 */
+                + 2 + fields->extlen /* 16-bit length + extensions */
+                ;
+            avail = orig_len;
+            mem = malloc(avail);
+            orig_mem = mem;
+            
+            rv = ctutil_serialize_uint8(&mem, &avail, 0); /* version 1 */
+            if (rv == APR_SUCCESS) {
+                rv = ctutil_serialize_uint8(&mem, &avail, 0); /* CERTIFICATE_TIMESTAMP */
+            }
+            if (rv == APR_SUCCESS) {
+                rv = ctutil_serialize_uint64(&mem, &avail, fields->timestamp);
+            }
+            if (rv == APR_SUCCESS) {
+                rv = ctutil_serialize_uint16(&mem, &avail, 0); /* X509_ENTRY */
+            }
+            if (rv == APR_SUCCESS) {
+                /* Get DER encoding of leaf certificate */
+                unsigned char *der_buf
+                    /* get OpenSSL to allocate: */
+                    = NULL;
+
+                der_length = i2d_X509(cc->leaf, &der_buf);
+                if (der_length < 0) {
+                    rv = APR_EINVAL;
+                }
+                else {
+                    rv = ctutil_write_var24_bytes(&mem, &avail,
+                                                  der_buf, der_length);
+                    OPENSSL_free(der_buf);
+                }
+            }
+            if (rv == APR_SUCCESS) {
+                rv = ctutil_write_var16_bytes(&mem, &avail, fields->extensions,
+                                              fields->extlen);
+            }
+        }
+
+        if (rv != APR_SUCCESS) {
+            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s,
+                         "Failed to reconstruct signed data for SCT");
+            free(orig_mem);
+        }
+        else {
+            if (avail != 0) {
+                ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s,
+                             "length miscalculation for signed data (%" APR_SIZE_T_FMT
+                             " vs. %" APR_SIZE_T_FMT ")",
+                             orig_len, avail);
+            }
+            fields->signed_data_len = orig_len - avail;
+            fields->signed_data = orig_mem;
+            /* Force invalid signature error: orig_mem[0] = orig_mem[0] + 1; */
+        }
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+                 "SCT from %s: version %d timestamp %s hash alg %d sig alg %d",
+                 source, fields->version, fields->timestr,
+                 fields->hash_alg, fields->sig_alg);
+#if AP_MODULE_MAGIC_AT_LEAST(20130702,2)
+    ap_log_data(APLOG_MARK, APLOG_DEBUG, s, "Log Id",
+                fields->logid, sizeof(fields->logid),
+                AP_LOG_DATA_SHOW_OFFSET);
+    ap_log_data(APLOG_MARK, APLOG_DEBUG, s, "Signature",
+                fields->sig, fields->siglen,
+                AP_LOG_DATA_SHOW_OFFSET);
+#endif /* httpd has ap_log_*data() */
+
+    ap_assert(!(fields->signed_data && rv != APR_SUCCESS));
+
+    return rv;
+}
+
+void sct_release(sct_fields_t *sctf)
+{
+    if (sctf->signed_data) {
+        free((void *)sctf->signed_data);
+        sctf->signed_data = NULL;
+    }
+}
+
+apr_status_t sct_verify_timestamp(conn_rec *c, sct_fields_t *sctf)
+{
+    if (sctf->time > apr_time_now()) {
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c,
+                      "Server sent SCT not yet valid (timestamp %s)",
+                      sctf->timestr);
+        return APR_EINVAL;
+    }
+    return APR_SUCCESS;
+}

Propchange: httpd/httpd/trunk/modules/ssl/ssl_ct_sct.c
------------------------------------------------------------------------------
    eol-style:native = mod_ssl_ct.c

Added: httpd/httpd/trunk/modules/ssl/ssl_ct_sct.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/ssl_ct_sct.h?rev=1588987&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/ssl/ssl_ct_sct.h (added)
+++ httpd/httpd/trunk/modules/ssl/ssl_ct_sct.h Mon Apr 21 21:14:21 2014
@@ -0,0 +1,64 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+#ifndef SSL_CT_SCT_H
+#define SSL_CT_SCT_H
+
+#include "apr_pools.h"
+#include "apr_tables.h"
+
+#include "httpd.h"
+#include "mod_ssl.h"
+
+#include "ssl_ct_log_config.h"
+
+#define LOG_ID_SIZE 32
+
+typedef struct cert_chain {
+    apr_pool_t *p;
+    apr_array_header_t *cert_arr; /* array of X509 * */
+    X509 *leaf;
+} cert_chain;
+
+typedef struct {
+    unsigned char version;
+    unsigned char logid[LOG_ID_SIZE];
+    apr_uint64_t timestamp;
+    apr_time_t time;
+    char timestr[APR_RFC822_DATE_LEN];
+    const unsigned char *extensions;
+    apr_uint16_t extlen;
+    unsigned char hash_alg;
+    unsigned char sig_alg;
+    apr_uint16_t siglen;
+    const unsigned char *sig;
+    const unsigned char *signed_data;
+    apr_size_t signed_data_len;
+} sct_fields_t;
+
+apr_status_t sct_parse(const char *source,
+                       server_rec *s, const unsigned char *sct,
+                       apr_size_t len, cert_chain *cc,
+                       sct_fields_t *fields);
+
+void sct_release(sct_fields_t *sctf);
+
+apr_status_t sct_verify_signature(conn_rec *c, sct_fields_t *sctf,
+                                  apr_array_header_t *log_config);
+
+apr_status_t sct_verify_timestamp(conn_rec *c, sct_fields_t *sctf);
+
+#endif /* SSL_CT_SCT_H */

Propchange: httpd/httpd/trunk/modules/ssl/ssl_ct_sct.h
------------------------------------------------------------------------------
    eol-style:native = mod_ssl_ct.c

Added: httpd/httpd/trunk/modules/ssl/ssl_ct_util.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/ssl_ct_util.c?rev=1588987&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/ssl/ssl_ct_util.c (added)
+++ httpd/httpd/trunk/modules/ssl/ssl_ct_util.c Mon Apr 21 21:14:21 2014
@@ -0,0 +1,781 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+#include "apr_fnmatch.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+
+#include "httpd.h"
+#include "http_log.h"
+
+#include "ssl_ct_util.h"
+
+APLOG_USE_MODULE(ssl_ct);
+
+apr_status_t ctutil_path_join(char **out, const char *dirname, const char *basename,
+                              apr_pool_t *p, server_rec *s)
+{
+    apr_status_t rv;
+
+    rv = apr_filepath_merge(out, dirname, basename, 0, p);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+                     "can't build filename from %s and %s",
+                     dirname, basename);
+    }
+
+    return rv;
+}
+
+int ctutil_dir_exists(apr_pool_t *p, const char *dirname)
+{
+    apr_finfo_t finfo;
+    apr_status_t rv = apr_stat(&finfo, dirname, APR_FINFO_TYPE, p);
+
+    return rv == APR_SUCCESS && finfo.filetype == APR_DIR;
+}
+
+int ctutil_file_exists(apr_pool_t *p, const char *filename)
+{
+    apr_finfo_t finfo;
+    apr_status_t rv = apr_stat(&finfo, filename, APR_FINFO_TYPE, p);
+
+    return rv == APR_SUCCESS && finfo.filetype == APR_REG;
+}
+
+void ctutil_buffer_to_array(apr_pool_t *p, const char *b,
+                            apr_size_t b_size, apr_array_header_t **out)
+{
+    apr_array_header_t *arr = apr_array_make(p, 10, sizeof(char *));
+    const char *ch, *last;
+
+    ch = b;
+    last = b + b_size - 1;
+    while (ch < last) {
+        const char *end = memchr(ch, '\n', last - ch);
+        const char *line;
+
+        if (!end) {
+            end = last + 1;
+        }
+        while (apr_isspace(*ch) && ch < end) {
+            ch++;
+        }
+        if (ch < end) {
+            const char *tmpend = end - 1;
+
+            while (tmpend > ch
+                   && isspace(*tmpend)) {
+                --tmpend;
+            }
+            
+            line = apr_pstrndup(p, ch, 1 + tmpend - ch);
+            *(const char **)apr_array_push(arr) = line;
+        }
+        ch = end + 1;
+    }
+
+    *out = arr;
+}
+
+int ctutil_in_array(const char *needle, const apr_array_header_t *haystack)
+{
+    const char * const *elts;
+    int i;
+
+    elts = (const char * const *)haystack->elts;
+    for (i = 0; i < haystack->nelts; i++) {
+        if (!strcmp(needle, elts[i])) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+apr_status_t ctutil_fopen(const char *fn, const char *mode, FILE **f)
+{
+    apr_status_t rv;
+
+    *f = fopen(fn, mode);
+    if (*f == NULL) {
+        rv = errno; /* XXX Windows equivalent -- CreateFile + fdopen? */
+    }
+    else {
+        rv = APR_SUCCESS;
+    }
+
+    return rv;
+}
+
+/* read_dir() is remarkably like apr_match_glob(), which could
+ * probably use some processing flags to indicate variations on
+ * the basic behavior (and implement better error checking).
+ */
+apr_status_t ctutil_read_dir(apr_pool_t *p,
+                             server_rec *s,
+                             const char *dirname,
+                             const char *pattern,
+                             apr_array_header_t **outarr)
+{
+    apr_array_header_t *arr;
+    apr_dir_t *d;
+    apr_finfo_t finfo;
+    apr_status_t rv;
+    int reported = 0;
+
+    /* add to existing array if it already exists */
+    arr = *outarr;
+    if (arr == NULL) {
+        arr = apr_array_make(p, 4, sizeof(char *));
+    }
+
+    rv = apr_dir_open(&d, dirname, p);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "couldn't read dir %s",
+                     dirname);
+        return rv;
+    }
+
+    while ((rv = apr_dir_read(&finfo, APR_FINFO_NAME, d)) == APR_SUCCESS) {
+        const char *fn;
+
+        if (APR_SUCCESS == apr_fnmatch(pattern, finfo.name, APR_FNM_CASE_BLIND)) {
+            rv = ctutil_path_join((char **)&fn, dirname, finfo.name, p, s);
+            if (rv != APR_SUCCESS) {
+                reported = 1;
+                break;
+            }
+
+            *(char **)apr_array_push(arr) = apr_pstrdup(p, fn);
+        }
+    }
+
+    if (APR_STATUS_IS_ENOENT(rv)) {
+        rv = APR_SUCCESS;
+    }
+    else if (rv != APR_SUCCESS && !reported) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+                     "couldn't read entry from dir %s", dirname);
+    }
+
+    apr_dir_close(d);
+
+    if (rv == APR_SUCCESS) {
+        *outarr = arr;
+    }
+
+    return rv;
+}
+
+apr_status_t ctutil_read_file(apr_pool_t *p,
+                              server_rec *s,
+                              const char *fn,
+                              apr_off_t limit,
+                              char **contents,
+                              apr_size_t *contents_size)
+{
+    apr_file_t *f;
+    apr_finfo_t finfo;
+    apr_status_t rv;
+    apr_size_t nbytes;
+
+    *contents = NULL;
+    *contents_size = 0;
+
+    rv = apr_file_open(&f, fn, APR_READ | APR_BINARY, APR_FPROT_OS_DEFAULT, p);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+                     "couldn't read %s", fn);
+        return rv;
+    }
+    
+    rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, f);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+                     "couldn't retrieve size of %s", fn);
+        apr_file_close(f);
+        return rv;
+    }
+
+    if (finfo.size > limit) {
+        rv = APR_ENOSPC;
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+                     "size %" APR_OFF_T_FMT " of %s exceeds limit (%"
+                     APR_SIZE_T_FMT ")", finfo.size, fn, limit);
+        apr_file_close(f);
+        return rv;
+    }
+
+    nbytes = (apr_size_t)finfo.size;
+    *contents = apr_palloc(p, nbytes);
+    rv = apr_file_read_full(f, *contents, nbytes, contents_size);
+    if (rv != APR_SUCCESS) { /* shouldn't get APR_EOF since we know
+                              * how big the file is
+                              */
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+                         "apr_file_read_full");
+    }
+    apr_file_close(f);
+
+    return rv;
+}
+
+#if APR_FILES_AS_SOCKETS
+static void io_loop(apr_pool_t *p, server_rec *s, apr_proc_t *proc,
+                    const char *desc_for_log)
+{
+    apr_status_t rv;
+    apr_pollfd_t pfd = {0};
+    apr_pollset_t *pollset;
+    int fds_waiting;
+
+    rv = apr_pollset_create(&pollset, 2, p, 0);
+    ap_assert(rv == APR_SUCCESS);
+
+    fds_waiting = 0;
+
+    pfd.p = p;
+    pfd.desc_type = APR_POLL_FILE;
+    pfd.reqevents = APR_POLLIN;
+    pfd.desc.f = proc->err;
+    rv = apr_pollset_add(pollset, &pfd);
+    ap_assert(rv == APR_SUCCESS);
+    ++fds_waiting;
+
+    pfd.desc.f = proc->out;
+    rv = apr_pollset_add(pollset, &pfd);
+    ap_assert(rv == APR_SUCCESS);
+    ++fds_waiting;
+
+    while (fds_waiting) {
+        int i, num_events;
+        const apr_pollfd_t *pdesc;
+        char buf[4096];
+        apr_size_t len;
+
+        rv = apr_pollset_poll(pollset, apr_time_from_sec(10),
+                              &num_events, &pdesc);
+        if (rv != APR_SUCCESS && !APR_STATUS_IS_EINTR(rv)) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+                         "apr_pollset_poll");
+            break;
+        }
+
+        for (i = 0; i < num_events; i++) {
+            len = sizeof buf;
+            rv = apr_file_read(pdesc[i].desc.f, buf, &len);
+            if (APR_STATUS_IS_EOF(rv)) {
+                apr_file_close(pdesc[i].desc.f);
+                apr_pollset_remove(pollset, &pdesc[i]);
+                --fds_waiting;
+            }
+            else if (rv != APR_SUCCESS) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+                             "apr_file_read");
+            }
+            else {
+                ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, s,
+                             "%s: %.*s", desc_for_log, (int)len, buf);
+            }
+        }
+    }
+}
+#else /* APR_FILES_AS_SOCKETS */
+static void io_loop(apr_pool_t *p, server_rec *s, apr_proc_t *proc,
+                    const char *desc_for_log)
+{
+    apr_status_t rv;
+    apr_file_t *fds[2] = {proc->out, proc->err};
+    apr_size_t len;
+    char buf[4096];
+    int fds_waiting = 2;
+
+    while (fds_waiting) {
+        int i;
+        int read = 0;
+
+        for (i = 0; i < sizeof fds / sizeof fds[0]; i++) {
+            if (!fds[i]) {
+                continue;
+            }
+            len = sizeof buf;
+            rv = apr_file_read(fds[i], buf, &len);
+            if (APR_STATUS_IS_EOF(rv)) {
+                apr_file_close(fds[i]);
+                fds[i] = NULL;
+                --fds_waiting;
+            }
+            else if (APR_STATUS_IS_EAGAIN(rv)) {
+                /* we don't actually know if data is ready before reading, so
+                 * this isn't an error
+                 */
+            }
+            else if (rv != APR_SUCCESS) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+                             "apr_file_read");
+            }
+            else {
+                ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, s,
+                             "%s: %.*s", desc_for_log, (int)len, buf);
+                ++read;
+            }
+        }
+        if (fds_waiting && !read) {
+            /* no tight loop */
+            apr_sleep(apr_time_from_msec(100));
+        }
+    }
+}
+#endif /* APR_FILES_AS_SOCKETS */
+
+apr_status_t ctutil_run_to_log(apr_pool_t *p,
+                               server_rec *s,
+                               const char *args[8],
+                               const char *desc_for_log)
+{
+    apr_exit_why_e exitwhy;
+    apr_proc_t proc = {0};
+    apr_procattr_t *attr;
+    apr_status_t rv;
+    int exitcode;
+
+    rv = apr_procattr_create(&attr, p);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "apr_procattr_create failed");
+        return rv;
+    }
+
+    rv = apr_procattr_io_set(attr,
+                             APR_NO_PIPE,
+                             APR_CHILD_BLOCK,
+                             APR_CHILD_BLOCK);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "apr_procattr_io_set failed");
+        return rv;
+    }
+
+    rv = apr_proc_create(&proc, args[0], args, NULL, attr, p);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "apr_proc_create failed");
+        return rv;
+    }
+
+    io_loop(p, s, &proc, desc_for_log);
+
+    rv = apr_proc_wait(&proc, &exitcode, &exitwhy, APR_WAIT);
+    rv = rv == APR_CHILD_DONE ? APR_SUCCESS : rv;
+
+    ap_log_error(APLOG_MARK,
+                 rv != APR_SUCCESS || exitcode ? APLOG_ERR : APLOG_DEBUG,
+                 rv, s,
+                 "exit code from %s: %d (%s)", 
+                 desc_for_log, exitcode,
+                 exitwhy == APR_PROC_EXIT ? "exited normally" : "exited due to a signal");
+
+    if (rv == APR_SUCCESS && exitcode) {
+        rv = APR_EGENERAL;
+    }
+
+    return rv;
+}
+
+void ctutil_thread_mutex_lock(apr_thread_mutex_t *m)
+{
+    apr_status_t rv = apr_thread_mutex_lock(m);
+    ap_assert(rv == APR_SUCCESS);
+}
+
+void ctutil_thread_mutex_unlock(apr_thread_mutex_t *m)
+{
+    apr_status_t rv = apr_thread_mutex_unlock(m);
+    ap_assert(rv == APR_SUCCESS);
+}
+
+apr_status_t ctutil_file_write_uint16(server_rec *s,
+                                      apr_file_t *f,
+                                      apr_uint16_t in_val)
+{
+    apr_size_t nbytes;
+    apr_status_t rv;
+    char vals[2];
+
+    vals[0] = (in_val & 0xFF00) >> 8;
+    vals[1] = (in_val & 0x00FF);
+    nbytes = sizeof(vals);
+    rv = apr_file_write(f, vals, &nbytes);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+                     "can't write 2-byte length to file");
+    }
+    return rv;
+}
+
+apr_status_t ctutil_file_write_uint24(server_rec *s,
+                                      apr_file_t *f,
+                                      apr_uint32_t in_val)
+{
+    apr_size_t nbytes;
+    apr_status_t rv;
+    char vals[3];
+
+    vals[0] = (in_val & 0xFF0000) >> 16;
+    vals[1] = (in_val & 0x00FF00) >> 8;
+    vals[2] = (in_val & 0x0000FF) >> 0;
+    nbytes = sizeof(vals);
+    rv = apr_file_write(f, vals, &nbytes);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+                     "can't write 3-byte length to file");
+    }
+    return rv;
+}
+
+void ctutil_log_array(const char *file, int line, int module_index,
+                      int level, server_rec *s, const char *desc,
+                      apr_array_header_t *arr)
+{
+    const char **elts = (const char **)arr->elts;
+    int i;
+
+    ap_log_error(file, line, module_index, level,
+                 0, s, "%s", desc);
+    for (i = 0; i < arr->nelts; i++) {
+        ap_log_error(file, line, module_index, level,
+                     0, s, ">>%s", elts[i]);
+    }
+}
+
+static apr_status_t deserialize_uint(const unsigned char **mem,
+                                     apr_size_t *avail,
+                                     apr_byte_t num_bits, apr_uint64_t *pval)
+{
+    apr_byte_t num_bytes = num_bits / 8;
+    apr_uint64_t val = 0;
+    int i;
+
+    if (*avail < num_bytes || num_bits > 64) {
+        return APR_EINVAL;
+    }
+
+    for (i = 0; i < num_bytes; i++) {
+        val = (val << 8) | **mem;
+        *mem += 1;
+        *avail -= 1;
+    }
+
+    *pval = val;
+    return APR_SUCCESS;
+}
+
+apr_status_t ctutil_deserialize_uint64(const unsigned char **mem,
+                                       apr_size_t *avail,
+                                       apr_uint64_t *pval)
+{
+    return deserialize_uint(mem, avail, 64, pval);
+}
+
+apr_status_t ctutil_deserialize_uint16(const unsigned char **mem,
+                                       apr_size_t *avail,
+                                       apr_uint16_t *pval)
+{
+    apr_status_t rv;
+    apr_uint64_t val64;
+
+    rv = deserialize_uint(mem, avail, 16, &val64);
+    *pval = (apr_uint16_t)val64;
+    return rv;
+}
+
+static apr_status_t serialize_uint(unsigned char **mem, apr_size_t *avail,
+                                   apr_byte_t num_bits, apr_uint64_t val)
+{
+    apr_byte_t num_bytes = num_bits / 8;
+    int i;
+    apr_uint64_t mask;
+    apr_byte_t shift;
+
+    if (*avail < num_bytes || num_bits > 64) {
+        return APR_EINVAL;
+    }
+
+    mask = (apr_uint64_t)0xFF << (num_bits - 8);
+    shift = num_bits - 8;
+    for (i = 0; i < num_bytes; i++) {
+        **mem = (unsigned char)((val & mask) >> shift);
+        *mem += 1;
+        *avail -= 1;
+        mask = mask >> 8;
+        shift -= 8;
+    }
+
+    return APR_SUCCESS;
+}
+
+apr_status_t ctutil_serialize_uint64(unsigned char **mem, apr_size_t *avail,
+                                     apr_uint64_t val)
+{
+    return serialize_uint(mem, avail, 64, val);
+}
+
+apr_status_t ctutil_serialize_uint24(unsigned char **mem, apr_size_t *avail,
+                                     apr_uint32_t val)
+{
+    return serialize_uint(mem, avail, 24, val);
+}
+
+apr_status_t ctutil_serialize_uint16(unsigned char **mem, apr_size_t *avail,
+                                     apr_uint16_t val)
+{
+    return serialize_uint(mem, avail, 16, val);
+}
+
+apr_status_t ctutil_serialize_uint8(unsigned char **mem, apr_size_t *avail,
+                                    unsigned char val)
+{
+    return serialize_uint(mem, avail, 8, val);
+}
+
+apr_status_t ctutil_write_var16_bytes(unsigned char **mem, apr_size_t *avail,
+                                      const unsigned char *val,
+                                      apr_uint16_t len)
+{
+    apr_status_t rv;
+
+    if (*avail < (sizeof(apr_uint16_t) + len)) {
+        return APR_EINVAL;
+    }
+
+    rv = ctutil_serialize_uint16(mem, avail, len);
+    if (rv != APR_SUCCESS) { /* should not occur */
+        return rv;
+    }
+
+    memcpy(*mem, val, len);
+    *mem += len;
+    *avail -= len;
+    return APR_SUCCESS;
+}
+
+apr_status_t ctutil_write_var24_bytes(unsigned char **mem, apr_size_t *avail,
+                                      const unsigned char *val,
+                                      apr_uint32_t len)
+{
+    apr_status_t rv;
+
+    if (*avail < (3 + len)) {
+        return APR_EINVAL;
+    }
+
+    rv = ctutil_serialize_uint24(mem, avail, len);
+    if (rv != APR_SUCCESS) { /* should not occur */
+        return rv;
+    }
+
+    memcpy(*mem, val, len);
+    *mem += len;
+    *avail -= len;
+    return APR_SUCCESS;
+}
+
+/* all this deserialization crap is of course from
+ * c-t/src/proto/serializer.cc
+ */
+static apr_status_t read_length_prefix(const unsigned char **mem, apr_size_t *avail,
+                                       apr_size_t *result)
+{
+    apr_status_t rv;
+    apr_uint16_t val;
+
+    rv = ctutil_deserialize_uint16(mem, avail, &val);
+    if (rv == APR_SUCCESS) {
+        *result = val;
+    }
+
+    return rv;
+}
+
+static apr_status_t read_fixed_bytes(const unsigned char **mem, apr_size_t *avail,
+                                     apr_size_t len,
+                                     const unsigned char **start)
+{
+    if (*avail < len) {
+        return APR_EINVAL;
+    }
+
+    *start = *mem;
+    *avail -= len;
+    *mem += len;
+
+    return APR_SUCCESS;
+}
+
+apr_status_t ctutil_read_var_bytes(const unsigned char **mem, apr_size_t *avail,
+                                   const unsigned char **start, apr_size_t *len)
+{
+    apr_status_t rv;
+
+    rv = read_length_prefix(mem, avail, len);
+    if (rv != APR_SUCCESS) {
+        return rv;
+    }
+
+    rv = read_fixed_bytes(mem, avail, *len, start);
+    return rv;
+}
+
+#define TESTURL1 "http://127.0.0.1:8888"
+#define TESTURL2 "http://127.0.0.1:9999"
+#define TESTURL3 "http://127.0.0.1:10000"
+
+void ctutil_run_internal_tests(apr_pool_t *p)
+{
+    apr_array_header_t *arr;
+    const char *filecontents =
+      " " TESTURL1 " \r\n" TESTURL2 "\n"
+      TESTURL3 /* no "\n" */ ;
+    unsigned char buf[8], *ch;
+    const unsigned char *const_ch;
+    apr_size_t avail;
+    apr_status_t rv;
+    apr_uint16_t val16;
+    apr_uint64_t val64;
+
+    ctutil_buffer_to_array(p, filecontents, strlen(filecontents), &arr);
+    
+    ap_assert(ctutil_in_array(TESTURL1, arr));
+    ap_assert(ctutil_in_array(TESTURL2, arr));
+    ap_assert(ctutil_in_array(TESTURL3, arr));
+    ap_assert(!ctutil_in_array(TESTURL1 "x", arr));
+
+    ch = buf;
+    avail = 8;
+    rv = ctutil_serialize_uint64(&ch, &avail, 0xDEADBEEFCAFEBABE);
+    ap_assert(rv == APR_SUCCESS);
+    ap_assert(avail == 0);
+    ap_assert(ch == buf + 8);
+    ap_assert(buf[0] == 0xDE);
+    ap_assert(buf[1] == 0xAD);
+    ap_assert(buf[2] == 0xBE);
+    ap_assert(buf[3] == 0xEF);
+    ap_assert(buf[4] == 0xCA);
+    ap_assert(buf[5] == 0xFE);
+    ap_assert(buf[6] == 0xBA);
+    ap_assert(buf[7] == 0xBE);
+
+    const_ch = buf;
+    avail = 8;
+    rv = ctutil_deserialize_uint64(&const_ch, &avail, &val64);
+    ap_assert(rv == APR_SUCCESS);
+    ap_assert(avail == 0);
+    ap_assert(const_ch == buf + 8);
+    ap_assert(val64 == 0xDEADBEEFCAFEBABE);
+
+    ch = buf;
+    avail = 7;
+    ap_assert(ctutil_serialize_uint64(&ch, &avail, 0xDEADBEEFCAFEBABE)
+              == APR_EINVAL);
+
+    ch = buf;
+    avail = 3;
+    rv = ctutil_serialize_uint24(&ch, &avail, 0xDEADBE);
+    ap_assert(rv == APR_SUCCESS);
+    ap_assert(avail == 0);
+    ap_assert(ch == buf + 3);
+    ap_assert(buf[0] == 0xDE);
+    ap_assert(buf[1] == 0xAD);
+    ap_assert(buf[2] == 0xBE);
+
+    ch = buf;
+    avail = 1;
+    ap_assert(ctutil_serialize_uint16(&ch, &avail, 0xDEAD)
+              == APR_EINVAL);
+
+    ch = buf;
+    avail = 2;
+    rv = ctutil_serialize_uint16(&ch, &avail, 0xDEAD);
+    ap_assert(rv == APR_SUCCESS);
+    ap_assert(avail == 0);
+    ap_assert(ch == buf + 2);
+    ap_assert(buf[0] == 0xDE);
+    ap_assert(buf[1] == 0xAD);
+
+    const_ch = buf;
+    avail = 2;
+    rv = ctutil_deserialize_uint16(&const_ch, &avail, &val16);
+    ap_assert(rv == APR_SUCCESS);
+    ap_assert(avail == 0);
+    ap_assert(val16 == 0xDEAD);
+
+    ch = buf;
+    avail = 1;
+    ap_assert(ctutil_serialize_uint16(&ch, &avail, 0xDEAD)
+              == APR_EINVAL);
+
+    ch = buf;
+    avail = 1;
+    rv = ctutil_serialize_uint8(&ch, &avail, 0xDE);
+    ap_assert(rv == APR_SUCCESS);
+    ap_assert(avail == 0);
+    ap_assert(ch == buf + 1);
+    ap_assert(buf[0] == 0xDE);
+
+    ch = buf;
+    avail = 0;
+    ap_assert(ctutil_serialize_uint8(&ch, &avail, 0xDE)
+              == APR_EINVAL);
+
+    ch = buf;
+    avail = 8;
+    rv = ctutil_write_var16_bytes(&ch, &avail, 
+                                  (unsigned char *)"\x01""\x02""\x03""\x04", 4);
+    ap_assert(rv == APR_SUCCESS);
+    ap_assert(avail == 2);
+    ap_assert(ch == buf + 6);
+    ap_assert(buf[0] == 0);
+    ap_assert(buf[1] == 4);
+    ap_assert(buf[2] == 0x01);
+    ap_assert(buf[3] == 0x02);
+    ap_assert(buf[4] == 0x03);
+    ap_assert(buf[5] == 0x04);
+
+    ch = buf;
+    avail = 3;
+    rv = ctutil_write_var16_bytes(&ch, &avail, 
+                                  (unsigned char *)"\x01""\x02""\x03""\x04", 4);
+    ap_assert(rv == APR_EINVAL);
+
+    ch = buf;
+    avail = 8;
+    rv = ctutil_write_var24_bytes(&ch, &avail, 
+                                  (unsigned char *)"\x01""\x02""\x03""\x04", 4);
+    ap_assert(rv == APR_SUCCESS);
+    ap_assert(avail == 1);
+    ap_assert(ch == buf + 7);
+    ap_assert(buf[0] == 0);
+    ap_assert(buf[1] == 0);
+    ap_assert(buf[2] == 4);
+    ap_assert(buf[3] == 0x01);
+    ap_assert(buf[4] == 0x02);
+    ap_assert(buf[5] == 0x03);
+    ap_assert(buf[6] == 0x04);
+
+    ch = buf;
+    avail = 4;
+    rv = ctutil_write_var24_bytes(&ch, &avail, 
+                                  (unsigned char *)"\x01""\x02""\x03""\x04", 4);
+    ap_assert(rv == APR_EINVAL);
+}

Propchange: httpd/httpd/trunk/modules/ssl/ssl_ct_util.c
------------------------------------------------------------------------------
    eol-style:native = mod_ssl_ct.c

Added: httpd/httpd/trunk/modules/ssl/ssl_ct_util.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/ssl/ssl_ct_util.h?rev=1588987&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/ssl/ssl_ct_util.h (added)
+++ httpd/httpd/trunk/modules/ssl/ssl_ct_util.h Mon Apr 21 21:14:21 2014
@@ -0,0 +1,102 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.
+ */
+
+#ifndef SSL_CT_UTIL_H
+#define SSL_CT_UTIL_H
+
+#include "httpd.h"
+
+apr_status_t ctutil_path_join(char **out, const char *dirname, const char *basename,
+                              apr_pool_t *p, server_rec *s);
+
+int ctutil_dir_exists(apr_pool_t *p, const char *dirname);
+
+int ctutil_file_exists(apr_pool_t *p, const char *filename);
+
+void ctutil_buffer_to_array(apr_pool_t *p, const char *b,
+                            apr_size_t b_size, apr_array_header_t **out);
+
+int ctutil_in_array(const char *needle, const apr_array_header_t *haystack);
+
+apr_status_t ctutil_fopen(const char *fn, const char *mode, FILE **f);
+
+apr_status_t ctutil_read_dir(apr_pool_t *p,
+                             server_rec *s,
+                             const char *dirname,
+                             const char *pattern,
+                             apr_array_header_t **outarr);
+
+apr_status_t ctutil_read_file(apr_pool_t *p,
+                              server_rec *s,
+                              const char *fn,
+                              apr_off_t limit,
+                              char **contents,
+                              apr_size_t *contents_size);
+
+apr_status_t ctutil_run_to_log(apr_pool_t *p,
+                               server_rec *s,
+                               const char *args[8],
+                               const char *desc_for_log);
+
+void ctutil_thread_mutex_lock(apr_thread_mutex_t *m);
+void ctutil_thread_mutex_unlock(apr_thread_mutex_t *m);
+
+apr_status_t ctutil_file_write_uint16(server_rec *s,
+                                      apr_file_t *f,
+                                      apr_uint16_t val);
+
+apr_status_t ctutil_file_write_uint24(server_rec *s,
+                                      apr_file_t *f,
+                                      apr_uint32_t val);
+
+void ctutil_log_array(const char *file, int line, int module_index,
+                      int level, server_rec *s, const char *desc,
+                      apr_array_header_t *arr);
+
+apr_status_t ctutil_read_var_bytes(const unsigned char **mem,
+                                   apr_size_t *avail,
+                                   const unsigned char **start,
+                                   apr_size_t *len);
+
+apr_status_t ctutil_deserialize_uint64(const unsigned char **mem,
+                                       apr_size_t *avail, apr_uint64_t *pval);
+apr_status_t ctutil_deserialize_uint16(const unsigned char **mem,
+                                       apr_size_t *avail,
+                                       apr_uint16_t *pval);
+
+apr_status_t ctutil_serialize_uint64(unsigned char **mem, apr_size_t *avail,
+                                     apr_uint64_t val);
+
+apr_status_t ctutil_serialize_uint24(unsigned char **mem, apr_size_t *avail,
+                                     apr_uint32_t val);
+
+apr_status_t ctutil_serialize_uint16(unsigned char **mem, apr_size_t *avail,
+                                     apr_uint16_t val);
+
+apr_status_t ctutil_serialize_uint8(unsigned char **mem, apr_size_t *avail,
+                                    unsigned char val);
+
+apr_status_t ctutil_write_var16_bytes(unsigned char **mem, apr_size_t *avail,
+                                      const unsigned char *val,
+                                      apr_uint16_t len);
+
+apr_status_t ctutil_write_var24_bytes(unsigned char **mem, apr_size_t *avail,
+                                      const unsigned char *val,
+                                      apr_uint32_t len);
+
+void ctutil_run_internal_tests(apr_pool_t *p);
+
+#endif /* SSL_CT_UTIL_H */

Propchange: httpd/httpd/trunk/modules/ssl/ssl_ct_util.h
------------------------------------------------------------------------------
    eol-style:native = mod_ssl_ct.c

Modified: httpd/httpd/trunk/support/Makefile.in
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/support/Makefile.in?rev=1588987&r1=1588986&r2=1588987&view=diff
==============================================================================
--- httpd/httpd/trunk/support/Makefile.in (original)
+++ httpd/httpd/trunk/support/Makefile.in Mon Apr 21 21:14:21 2014
@@ -23,7 +23,7 @@ install:
 	        chmod 755 $(DESTDIR)$(bindir)/$$i; \
 	    fi ; \
 	done
-	@for i in apachectl; do \
+	@for i in apachectl ctlogconfig; do \
 	    if test -f "$(builddir)/$$i"; then \
 	        cp -p $$i $(DESTDIR)$(sbindir); \
 	        chmod 755 $(DESTDIR)$(sbindir)/$$i; \

Added: httpd/httpd/trunk/support/ctauditscts
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/support/ctauditscts?rev=1588987&view=auto
==============================================================================
--- httpd/httpd/trunk/support/ctauditscts (added)
+++ httpd/httpd/trunk/support/ctauditscts Mon Apr 21 21:14:21 2014
@@ -0,0 +1,160 @@
+#!/usr/bin/env python
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF 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.
+
+import binascii
+import os
+import sqlite3
+import ssl
+import struct
+import sys
+import tempfile
+
+from contextlib import closing
+
+SERVER_START = 1
+KEY_START = 2
+CERT_START = 3
+SCT_START = 4
+
+
+def usage():
+    print >> sys.stderr, ('Usage: %s /path/to/audit/files ' +
+                          '[/path/to/log-config-db]') % sys.argv[0]
+    sys.exit(1)
+
+
+def audit(fn, tmp, already_checked, cur):
+    print 'Auditing %s...' % fn
+
+    # First, parse the audit file into a series of related
+    #
+    #   1. PEM file with certificate chain
+    #   2. Individual SCT files
+    #
+    # Next,  for each SCT, invoke verify_single_proof to verify.
+    log_bytes = open(fn, 'rb').read()
+    offset = 0
+    while offset < len(log_bytes):
+        print 'Got package from server...'
+        val = struct.unpack_from('>H', log_bytes, offset)
+        assert val[0] == SERVER_START
+        offset += 2
+
+        assert struct.unpack_from('>H', log_bytes, offset)[0] == KEY_START
+        offset += 2
+
+        key_size = struct.unpack_from('>H', log_bytes, offset)[0]
+        assert key_size > 0
+        offset += 2
+
+        key = log_bytes[offset:offset + key_size]
+        offset += key_size
+
+        # at least one certificate
+        assert struct.unpack_from('>H', log_bytes, offset)[0] == CERT_START
+
+        # for each certificate:
+        leaf = None
+        while struct.unpack_from('>H', log_bytes, offset)[0] == CERT_START:
+            offset += 2
+            val = struct.unpack_from('BBB', log_bytes, offset)
+            offset += 3
+            der_size = (val[0] << 16) | (val[1] << 8) | (val[2] << 0)
+            print '  Certificate size:', hex(der_size)
+            if not leaf:
+                leaf = (offset, der_size)
+            offset += der_size
+
+        pem = ssl.DER_cert_to_PEM_cert(log_bytes[leaf[0]:leaf[0] + leaf[1]])
+
+        tmp_leaf_pem = tempfile.mkstemp(text=True)
+        with closing(os.fdopen(tmp_leaf_pem[0], 'w')) as f:
+            f.write(pem)
+
+        # at least one SCT
+        assert struct.unpack_from('>H', log_bytes, offset)[0] == SCT_START
+
+        # for each SCT:
+        while offset < len(log_bytes) and \
+                struct.unpack_from('>H', log_bytes, offset)[0] == SCT_START:
+            offset += 2
+            len_offset = offset
+            sct_size = struct.unpack_from('>H', log_bytes, len_offset)[0]
+            offset += 2
+            print '  SCT size:', hex(sct_size)
+            log_id = log_bytes[offset + 1:offset + 1 + 32]
+            log_id_hex = binascii.hexlify(log_id).upper()
+            print '    Log id: %s' % log_id_hex
+            timestamp_ms = struct.unpack_from('>Q', log_bytes, offset + 33)[0]
+            print '    Timestamp: %s' % timestamp_ms
+
+            #  If we ever need the full SCT: sct = (offset, sct_size)
+            offset += sct_size
+
+            if key in already_checked:
+                print '  (SCTs already checked)'
+                continue
+
+            already_checked[key] = True
+
+            log_url_arg = ''
+            if cur:
+                stmt = 'SELECT * FROM loginfo WHERE log_id = ?'
+                cur.execute(stmt, [log_id_hex])
+                recs = list(cur.fetchall())
+                if len(recs) > 0 and recs[0][6] is not None:
+                    log_url = recs[0][6]
+
+                    # verify_single_proof doesn't accept <scheme>://
+                    if '://' in log_url:
+                        log_url = log_url.split('://')[1]
+                    log_url_arg = '--log_url %s' % log_url
+
+                    print '    Log URL: ' + log_url
+
+            cmd = 'verify_single_proof.py --cert %s --timestamp %s %s' % \
+                  (tmp_leaf_pem[1], timestamp_ms, log_url_arg)
+            print '>%s<' % cmd
+            os.system(cmd)
+
+        os.unlink(tmp_leaf_pem[1])
+
+
+def main():
+    if len(sys.argv) != 2 and len(sys.argv) != 3:
+        usage()
+
+    top = sys.argv[1]
+    tmp = '/tmp'
+
+    if len(sys.argv) == 3:
+        cxn = sqlite3.connect(sys.argv[2])
+        cur = cxn.cursor()
+    else:
+        cur = None
+
+    # could serialize this between runs to further limit duplicate checking
+    already_checked = dict()
+
+    for dirpath, dnames, fnames in os.walk(top):
+        fnames = [fn for fn in fnames if fn[-4:] == '.out']
+        for fn in fnames:
+            audit(os.path.join(dirpath, fn), tmp, already_checked, cur)
+
+
+if __name__ == "__main__":
+    main()

Propchange: httpd/httpd/trunk/support/ctauditscts
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpd/httpd/trunk/support/ctauditscts
------------------------------------------------------------------------------
    svn:executable = *

Added: httpd/httpd/trunk/support/ctlogconfig
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/support/ctlogconfig?rev=1588987&view=auto
==============================================================================
--- httpd/httpd/trunk/support/ctlogconfig (added)
+++ httpd/httpd/trunk/support/ctlogconfig Mon Apr 21 21:14:21 2014
@@ -0,0 +1,313 @@
+#!/usr/bin/env python
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF 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.
+
+import os
+import re
+import sqlite3
+import sys
+
+
+def create_tables(db_name):
+    cxn = sqlite3.connect(db_name)
+    cur = cxn.cursor()
+
+    cur.execute(
+        'CREATE TABLE loginfo('
+        + 'id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, '
+        + 'log_id TEXT, '
+        + 'public_key TEXT, '  # path to PEM-encoded file
+        + 'distrusted INTEGER, '  # non-zero if not trusted
+        + 'min_valid_timestamp INTEGER, '
+        + 'max_valid_timestamp INTEGER, '
+        + 'url TEXT)'
+    )
+    cur.close()
+    cxn.commit()
+    cxn.close()
+
+
+def record_id_arg(cur, args, required=False):
+    if len(args) < 1 or args[0][0] != '#' or len(args[0]) < 2:
+        if required:
+            print >> sys.stderr, 'A record id was not provided'
+            sys.exit(1)
+        return None
+    record_id = args.pop(0)[1:]
+    stmt = 'SELECT * FROM loginfo WHERE id = ?'
+    cur.execute(stmt, [record_id])
+    recs = list(cur.fetchall())
+    assert len(recs) < 2
+    if len(recs) == 0:
+        print >> sys.stderr, 'Record #%s was not found' % record_id
+        sys.exit(1)
+    return record_id
+
+
+def log_id_arg(cur, args, required=True):
+    if len(args) < 1 or len(args[0]) != 64:
+        if not required:
+            return None
+        print >> sys.stderr, 'A log id was not provided'
+        sys.exit(1)
+    log_id = args.pop(0).upper()
+    if len(re.compile(r'[A-Z0-9]').findall(log_id)) != len(log_id):
+        print >> sys.stderr, 'The log id is not formatted properly'
+        sys.exit(1)
+    return log_id
+
+
+def public_key_arg(args):
+    if len(args) < 1:
+        print >> sys.stderr, 'A public key file was not provided'
+        sys.exit(1)
+    pubkey = args.pop(0)
+    if not os.path.exists(pubkey):
+        print >> sys.stderr, 'Public key file %s could not be read' % pubkey
+        sys.exit(1)
+    return pubkey
+
+
+def time_arg(args):
+    if len(args) < 1:
+        print >> sys.stderr, 'A timestamp was not provided'
+        sys.exit(1)
+    t = args.pop(0)
+    if t == '-':
+        return None
+    try:
+        return int(t)
+    except ValueError:
+        print >> sys.stderr, 'The timestamp "%s" is invalid' % t
+        sys.exit(1)
+
+
+def configure_public_key(cur, args):
+    record_id = record_id_arg(cur, args, False)
+    public_key = public_key_arg(args)
+    if len(args) != 0:
+        usage()
+    if not record_id:
+        stmt = 'INSERT INTO loginfo (public_key) VALUES(?)'
+        cur.execute(stmt, [public_key])
+    else:
+        stmt = 'UPDATE loginfo SET public_key = ? WHERE id = ?'
+        cur.execute(stmt, [public_key, record_id])
+
+
+def configure_url(cur, args):
+    # can't specify more than one of record-id and log-id
+    log_id = None
+    record_id = record_id_arg(cur, args, False)
+    if not record_id:
+        log_id = log_id_arg(cur, args, False)
+    if len(args) != 1:
+        usage()
+    url = args.pop(0)
+
+    if record_id:
+        stmt = 'UPDATE loginfo SET url = ? WHERE id = ?'
+        args = [url, record_id]
+    elif log_id:
+        stmt = 'INSERT INTO loginfo (log_id, url) VALUES(?, ?)'
+        args = [log_id, url]
+    else:
+        stmt = 'INSERT INTO loginfo (url) VALUES(?)'
+        args = [url]
+
+    cur.execute(stmt, args)
+
+
+def forget_log(cur, args):
+    record_id = record_id_arg(cur, args, False)
+    log_id = None
+    if not record_id:
+        log_id = log_id_arg(cur, args, True)
+    if len(args) != 0:
+        usage()
+    if record_id:
+        stmt = 'DELETE FROM loginfo WHERE id = ?'
+        args = [record_id]
+    else:
+        stmt = 'DELETE FROM loginfo WHERE log_id = ?'
+        args = [log_id]
+    cur.execute(stmt, args)
+
+
+def trust_distrust_log(cur, args):
+    # could take a record id or a log id
+    record_id = record_id_arg(cur, args, False)
+    if record_id:
+        log_id = None
+    else:
+        log_id = log_id_arg(cur, args)
+
+    if len(args) != 1:
+        usage()
+    flag = args.pop(0)
+
+    if not record_id:
+        stmt = 'INSERT INTO loginfo (log_id, distrusted) VALUES(?, ?)'
+        cur.execute(stmt, [log_id, flag])
+    else:
+        stmt = 'UPDATE loginfo SET distrusted = ? WHERE id = ?'
+        cur.execute(stmt, [flag, record_id])
+
+
+def trust_log(cur, args):
+    trust_distrust_log(cur, args + [0])
+
+
+def distrust_log(cur, args):
+    trust_distrust_log(cur, args + [1])
+
+
+def time_range(cur, args):
+    # could take a record id or a log id
+    record_id = record_id_arg(cur, args, False)
+    if record_id:
+        log_id = None
+    else:
+        log_id = log_id_arg(cur, args)
+
+    min_valid_time = time_arg(args)
+    max_valid_time = time_arg(args)
+    if len(args) != 0:
+        usage()
+    if not record_id:
+        stmt = 'INSERT INTO loginfo ' + \
+               '(log_id, min_valid_timestamp, max_valid_timestamp) ' + \
+               'VALUES(?, ?, ?)'
+        cur.execute(stmt, [log_id, min_valid_time, max_valid_time])
+    else:
+        stmt = 'UPDATE loginfo SET min_valid_timestamp = ?, ' + \
+               'max_valid_timestamp = ? WHERE id = ?'
+        cur.execute(stmt, [min_valid_time, max_valid_time, record_id])
+
+
+class ConfigEntry:
+
+    pass
+
+
+def dump_ll(cur):
+    stmt = 'SELECT * FROM loginfo'
+    cur.execute(stmt)
+    recs = []
+    for row in cur.fetchall():
+        obj = ConfigEntry()
+        obj.id = row[0]
+        obj.log_id = row[1]
+        obj.public_key = row[2]
+        obj.distrusted = row[3]
+        obj.min_valid_timestamp = row[4]
+        obj.max_valid_timestamp = row[5]
+        obj.url = row[6]
+        recs += [obj]
+    return recs
+
+
+def dump(cur, args):
+    if len(args) != 0:
+        usage()
+    recs = dump_ll(cur)
+    for rec in recs:
+        not_conf = '(not configured)'
+
+        mint = \
+            str(rec.min_valid_timestamp) if rec.min_valid_timestamp else '-INF'
+        maxt = \
+            str(rec.max_valid_timestamp) if rec.max_valid_timestamp else '+INF'
+        print 'Log entry:'
+        print '  Record ' + str(rec.id) + \
+            (' (DISTRUSTED)' if rec.distrusted else '')
+        print '  Log id         : ' + (rec.log_id if rec.log_id else not_conf)
+        print '  Public key file: ' + \
+            (rec.public_key if rec.public_key else not_conf)
+        print '  URL            : ' + (rec.url if rec.url else not_conf)
+        print '  Time range     : ' + mint + ' to ' + maxt
+        print ''
+
+
+def usage():
+    help = """Usage: %s /path/to/log-config-db command args
+
+Commands:
+  display config-db contents:
+    dump
+  configure public key:
+    configure-public-key [log-id|record-id] /path/log-pub-key.pem
+  configure URL:
+    configure-url [log-id|record-id] http://www.example.com/path/
+  configure min and/or max valid timestamps:
+    valid-time-range log-id|record-id min-range max-range
+  mark log as trusted (default):
+    trust log-id|record-id
+  mark log as untrusted:
+    distrust log-id|record-id
+  remove log config from config-db:
+    forget log-id|record-id
+
+log-id is a 64-character hex string representation of a log id
+
+record-id references an existing entry and is in the form:
+  #<record-number>
+  (displayable with the dump command)
+""" % sys.argv[0]
+    print >> sys.stderr, help
+    sys.exit(1)
+
+
+def main(argv):
+    if len(argv) < 3:
+        usage()
+
+    db_name = argv[1]
+    cmd = argv[2]
+    args = argv[3:]
+
+    cmds = {'configure-public-key': configure_public_key,
+            'configure-url': configure_url,
+            'distrust': distrust_log,
+            'trust': trust_log,
+            'forget': forget_log,
+            'valid-time-range': time_range,
+            'dump': dump,
+            }
+
+    cmds_requiring_db = ['dump', 'forget']  # db must already exist
+
+    if not cmd in cmds:
+        usage()
+
+    if not os.path.exists(db_name):
+        if not cmd in cmds_requiring_db:
+            create_tables(db_name)
+        else:
+            print >> sys.stderr, 'Database "%s" does not exist' % db_name
+            sys.exit(1)
+
+    cxn = sqlite3.connect(db_name)
+    cur = cxn.cursor()
+
+    cmds[cmd](cur, args)
+
+    cur.close()
+    cxn.commit()
+    cxn.close()
+
+if __name__ == "__main__":
+    main(sys.argv)

Propchange: httpd/httpd/trunk/support/ctlogconfig
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpd/httpd/trunk/support/ctlogconfig
------------------------------------------------------------------------------
    svn:executable = *