/* 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 #include #include #include #include #include #include #include #include "tls_proto.h" #include "tls_util.h" extern module AP_MODULE_DECLARE_DATA tls_module; APLOG_USE_MODULE(tls); tls_data_t tls_data_from_str(const char *s) { tls_data_t d; d.data = (const unsigned char*)s; d.len = s? strlen(s) : 0; return d; } tls_data_t tls_data_assign_copy(apr_pool_t *p, const tls_data_t *d) { tls_data_t copy; copy.data = apr_pmemdup(p, d->data, d->len); copy.len = d->len; return copy; } tls_data_t *tls_data_copy(apr_pool_t *p, const tls_data_t *d) { tls_data_t *copy; copy = apr_pcalloc(p, sizeof(*copy)); *copy = tls_data_assign_copy(p, d); return copy; } const char *tls_data_to_str(apr_pool_t *p, const tls_data_t *d) { char *s = apr_pcalloc(p, d->len+1); memcpy(s, d->data, d->len); return s; } apr_status_t tls_util_rustls_error( apr_pool_t *p, rustls_result rr, const char **perr_descr) { if (perr_descr) { char buffer[HUGE_STRING_LEN]; apr_size_t len = 0; rustls_error(rr, buffer, sizeof(buffer), &len); *perr_descr = apr_pstrndup(p, buffer, len); } return APR_EGENERAL; } int tls_util_is_file( apr_pool_t *p, const char *fpath) { apr_finfo_t finfo; return (fpath != NULL && apr_stat(&finfo, fpath, APR_FINFO_TYPE|APR_FINFO_SIZE, p) == 0 && finfo.filetype == APR_REG); } apr_status_t tls_util_file_load( apr_pool_t *p, const char *fpath, apr_size_t min_len, apr_size_t max_len, tls_data_t *data) { apr_finfo_t finfo; apr_status_t rv; apr_file_t *f = NULL; unsigned char *buffer; apr_size_t len; const char *err = NULL; tls_data_t *d; rv = apr_stat(&finfo, fpath, APR_FINFO_TYPE|APR_FINFO_SIZE, p); if (APR_SUCCESS != rv) { err = "cannot stat"; goto cleanup; } if (finfo.filetype != APR_REG) { err = "not a plain file"; rv = APR_EINVAL; goto cleanup; } if (finfo.size > LONG_MAX) { err = "file is too large"; rv = APR_EINVAL; goto cleanup; } len = (apr_size_t)finfo.size; if (len < min_len || len > max_len) { err = "file size not in allowed range"; rv = APR_EINVAL; goto cleanup; } d = apr_pcalloc(p, sizeof(*d)); buffer = apr_pcalloc(p, len+1); /* keep it NUL terminated in any case */ rv = apr_file_open(&f, fpath, APR_FOPEN_READ, 0, p); if (APR_SUCCESS != rv) { err = "error opening"; goto cleanup; } rv = apr_file_read(f, buffer, &len); if (APR_SUCCESS != rv) { err = "error reading"; goto cleanup; } cleanup: if (f) apr_file_close(f); if (APR_SUCCESS == rv) { data->data = buffer; data->len = len; } else { memset(data, 0, sizeof(*data)); ap_log_perror(APLOG_MARK, APLOG_ERR, rv, p, APLOGNO(10361) "Failed to load file %s: %s", fpath, err? err: "-"); } return rv; } int tls_util_array_uint16_contains(const apr_array_header_t* a, apr_uint16_t n) { int i; for (i = 0; i < a->nelts; ++i) { if (APR_ARRAY_IDX(a, i, apr_uint16_t) == n) return 1; } return 0; } const apr_array_header_t *tls_util_array_uint16_remove( apr_pool_t *pool, const apr_array_header_t* from, const apr_array_header_t* others) { apr_array_header_t *na = NULL; apr_uint16_t id; int i, j; for (i = 0; i < from->nelts; ++i) { id = APR_ARRAY_IDX(from, i, apr_uint16_t); if (tls_util_array_uint16_contains(others, id)) { if (na == NULL) { /* first removal, make a new result array, copy elements before */ na = apr_array_make(pool, from->nelts, sizeof(apr_uint16_t)); for (j = 0; j < i; ++j) { APR_ARRAY_PUSH(na, apr_uint16_t) = APR_ARRAY_IDX(from, j, apr_uint16_t); } } } else if (na) { APR_ARRAY_PUSH(na, apr_uint16_t) = id; } } return na? na : from; } apr_status_t tls_util_brigade_transfer( apr_bucket_brigade *dest, apr_bucket_brigade *src, apr_off_t length, apr_off_t *pnout) { apr_bucket *b; apr_off_t remain = length; apr_status_t rv = APR_SUCCESS; const char *ign; apr_size_t ilen; *pnout = 0; while (!APR_BRIGADE_EMPTY(src)) { b = APR_BRIGADE_FIRST(src); if (APR_BUCKET_IS_METADATA(b)) { APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(dest, b); } else { if (remain == (apr_off_t)b->length) { /* fall through */ } else if (remain <= 0) { goto cleanup; } else { if (b->length == ((apr_size_t)-1)) { rv= apr_bucket_read(b, &ign, &ilen, APR_BLOCK_READ); if (APR_SUCCESS != rv) goto cleanup; } if (remain < (apr_off_t)b->length) { apr_bucket_split(b, (apr_size_t)remain); } } APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(dest, b); remain -= (apr_off_t)b->length; *pnout += (apr_off_t)b->length; } } cleanup: return rv; } apr_status_t tls_util_brigade_copy( apr_bucket_brigade *dest, apr_bucket_brigade *src, apr_off_t length, apr_off_t *pnout) { apr_bucket *b, *next; apr_off_t remain = length; apr_status_t rv = APR_SUCCESS; const char *ign; apr_size_t ilen; *pnout = 0; for (b = APR_BRIGADE_FIRST(src); b != APR_BRIGADE_SENTINEL(src); b = next) { next = APR_BUCKET_NEXT(b); if (APR_BUCKET_IS_METADATA(b)) { /* fall through */ } else { if (remain == (apr_off_t)b->length) { /* fall through */ } else if (remain <= 0) { goto cleanup; } else { if (b->length == ((apr_size_t)-1)) { rv = apr_bucket_read(b, &ign, &ilen, APR_BLOCK_READ); if (APR_SUCCESS != rv) goto cleanup; } if (remain < (apr_off_t)b->length) { apr_bucket_split(b, (apr_size_t)remain); } } } rv = apr_bucket_copy(b, &b); if (APR_SUCCESS != rv) goto cleanup; APR_BRIGADE_INSERT_TAIL(dest, b); remain -= (apr_off_t)b->length; *pnout += (apr_off_t)b->length; } cleanup: return rv; } apr_status_t tls_util_brigade_split_line( apr_bucket_brigade *dest, apr_bucket_brigade *src, apr_read_type_e block, apr_off_t length, apr_off_t *pnout) { apr_off_t nstart, nend; apr_status_t rv; apr_brigade_length(dest, 0, &nstart); rv = apr_brigade_split_line(dest, src, block, length); if (APR_SUCCESS != rv) goto cleanup; apr_brigade_length(dest, 0, &nend); /* apr_brigade_split_line() has the nasty habit of leaving a 0-length bucket * at the start of the brigade when it transferred the whole content. Get rid of it. */ if (!APR_BRIGADE_EMPTY(src)) { apr_bucket *b = APR_BRIGADE_FIRST(src); if (!APR_BUCKET_IS_METADATA(b) && 0 == b->length) { APR_BUCKET_REMOVE(b); apr_bucket_delete(b); } } cleanup: *pnout = (APR_SUCCESS == rv)? (nend - nstart) : 0; return rv; } int tls_util_name_matches_server(const char *name, server_rec *s) { apr_array_header_t *names; char **alias; int i; if (!s || !s->server_hostname) return 0; if (!strcasecmp(name, s->server_hostname)) return 1; /* first the fast equality match, then the pattern wild_name matches */ names = s->names; if (!names) return 0; alias = (char **)names->elts; for (i = 0; i < names->nelts; ++i) { if (alias[i] && !strcasecmp(name, alias[i])) return 1; } names = s->wild_names; if (!names) return 0; alias = (char **)names->elts; for (i = 0; i < names->nelts; ++i) { if (alias[i] && !ap_strcasecmp_match(name, alias[i])) return 1; } return 0; } apr_size_t tls_util_bucket_print(char *buffer, apr_size_t bmax, apr_bucket *b, const char *sep) { apr_size_t off = 0; if (sep && *sep) { off += (size_t)apr_snprintf(buffer+off, bmax-off, "%s", sep); } if (bmax <= off) { return off; } else if (APR_BUCKET_IS_METADATA(b)) { off += (size_t)apr_snprintf(buffer+off, bmax-off, "%s", b->type->name); } else if (bmax > off) { off += (size_t)apr_snprintf(buffer+off, bmax-off, "%s[%ld]", b->type->name, (long)(b->length == ((apr_size_t)-1)? -1 : (int)b->length)); } return off; } apr_size_t tls_util_bb_print(char *buffer, apr_size_t bmax, const char *tag, const char *sep, apr_bucket_brigade *bb) { apr_size_t off = 0; const char *sp = ""; apr_bucket *b; if (bmax > 1) { if (bb) { memset(buffer, 0, bmax--); off += (size_t)apr_snprintf(buffer+off, bmax-off, "%s(", tag); for (b = APR_BRIGADE_FIRST(bb); (bmax > off) && (b != APR_BRIGADE_SENTINEL(bb)); b = APR_BUCKET_NEXT(b)) { off += tls_util_bucket_print(buffer+off, bmax-off, b, sp); sp = " "; } if (bmax > off) { off += (size_t)apr_snprintf(buffer+off, bmax-off, ")%s", sep); } } else { off += (size_t)apr_snprintf(buffer+off, bmax-off, "%s(null)%s", tag, sep); } } return off; }