/* 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); }