[rs-dev] TransactionIDs on SCEP and filenames
Dirk-Willem van Gulik
dirkx at webweaving.org
Mon Aug 7 23:18:07 CEST 2023
After looking into this some more - it seems that below patch / more protection is actually needed - it prevents the 1 in 20 fails on a CISCO SCEP all (when there are things like dots and /-esh in the KeyID).
So at the least below safe_filename() is needed (and not just wise) for the client controled KeyID. Looking at the serial files - those should be safe as they are essentially numbers (assuming a sensible length).
Not yet committed.
With kind regards,
Dw
> On 7 Aug 2023, at 17:37, Dirk-Willem van Gulik <dirkx at webweaving.org> wrote:
>
> It appears that we're sailing fairly close to the wind when using the transaction ID 'raw' as the start of the filename; relying on just
> apr_filepath_merge(... APR_FILEPATH_SECUREROOT | APR_FILEPATH_NOTRELATIVE ..) to trap shenanigans:
>
> [Mon Aug 07 15:21:12.278390 2023] [ca_disk:error] [pid 15425] (20023)The given path was above the root path: [client 127.0.1.11:42999]
> mod_ca_disk: The CADiskCertificateByTransactionPath could not be merged with: /tmp/ATNBEIl2XHzyFxwAp++Rv/uyBsQ=.cert
>
> Would it make sense to simply SHA256 it; and use that instead as the file name (mod_ca_disk.c - near line 356).
>
> It also prevents 'confusing' names. E.g. a transaction ID such as 'ca' or 'foo' will yield a file foo.cert or ca.cert 'near' the real ones.
>
> Dw
>
Index: mod_ca_disk.c
===================================================================
--- mod_ca_disk.c (revision 432)
+++ mod_ca_disk.c (working copy)
@@ -22,6 +22,7 @@
*/
#include <apr_strings.h>
#include <apr_hash.h>
+#include <apr_lib.h>
#include <openssl/err.h>
#include <openssl/evp.h>
@@ -294,6 +295,38 @@
return n;
}
+/* Prevent anything untowards in a filename; by replacing
+ * any non alpanums by a _<hex> construct.
+ */
+static const char * safe_filename(apr_pool_t *pool, const char * fname)
+{
+ const char hex[16] = "0123456789ABCDEF";
+ char * out = NULL, * o;
+
+ if (fname == NULL)
+ return "_";
+
+ if (!*fname)
+ return "_00";
+
+ // worst case lenght; including trailing \0.
+ out = apr_palloc(pool, 1 + strlen(fname) * 3);
+
+ o = out;
+ for(const char *p = fname; *p; p++)
+ {
+ if (apr_isalnum(*p))
+ {
+ *o++ = *p;
+ continue;
+ };
+ *o++ = '_';
+ *o++ = hex[ ( *p >> 4) & 0xF ];
+ *o++ = hex[ ( *p >> 0) & 0xF ];
+ };
+ return out;
+}
+
static int ca_sign_disk(request_rec *r, apr_hash_t *params,
const unsigned char **buffer, apr_size_t *len)
{
@@ -377,7 +410,7 @@
/* try write away the CSR as a PEM file */
status = apr_filepath_merge(&fname, conf->csr_path,
- apr_pstrcat(r->pool, transaction_id, ".csr", NULL),
+ apr_pstrcat(r->pool, safe_filename(r->pool, transaction_id), ".csr", NULL),
APR_FILEPATH_SECUREROOT | APR_FILEPATH_NOTRELATIVE, r->pool);
if (APR_SUCCESS != status) {
log_message(r, status, "The CSR path must be a valid path");
@@ -531,28 +564,27 @@
APR_HASH_KEY_STRING) :
NULL;
if (transaction_id) {
+ const char * kid;
ASN1_STRING *s = parse_ASN1_STRING(r->pool, transaction_id);
- if (s) {
+ if (!s) {
+ log_message(r, status, "The transactionID could not be parsed");
+
+ return HTTP_BAD_REQUEST;
+ }
+ if (conf->serial_path)
+ ap_log_rerror( APLOG_MARK, APLOG_WARNING, APR_SUCCESS, r,
+ "Both serial and transaction path defined; using the latter.");
+
#if HAVE_ASN1_STRING_GET0_DATA
- key = apr_pstrcat(r->pool,
- apr_pstrndup(r->pool,
- (const char *) ASN1_STRING_get0_data(s),
- ASN1_STRING_length(s)), ".",
- conf->transaction_path_suffix, NULL);
+ kid = (const char *) ASN1_STRING_get0_data(s);
#else
- key = apr_pstrcat(r->pool,
- apr_pstrndup(r->pool,
- (const char *) ASN1_STRING_data(s),
+ kid = (const char *) ASN1_STRING_data(s);
+#endif
+ key = apr_pstrcat(r->pool,
+ apr_pstrndup(r->pool, safe_filename(r->pool,kid),
ASN1_STRING_length(s)), ".",
conf->transaction_path_suffix, NULL);
-#endif
- }
- else {
- log_message(r, status, "The transactionID could not be parsed");
- return HTTP_BAD_REQUEST;
- }
-
/* set up the path */
status = apr_filepath_merge(path ? &link : &path,
conf->transaction_path, key,
@@ -740,7 +772,7 @@
}
/* does the file exist? */
- status = apr_filepath_merge(&fname, path, key,
+ status = apr_filepath_merge(&fname, path, safe_filename(r->pool, key),
APR_FILEPATH_SECUREROOT | APR_FILEPATH_NOTRELATIVE, r->pool);
if (APR_SUCCESS != status) {
log_message(r, status, "The certificate was not found");
More information about the rs-dev
mailing list