From: Jonathan McDowell Date: Sun, 18 Aug 2019 20:55:45 +0000 (+0100) Subject: Add support for a key fingerprint blacklist X-Git-Tag: onak-0.6.0~29 X-Git-Url: https://the.earth.li/gitweb/?p=onak.git;a=commitdiff_plain;h=a94e29c9a3763f96b1c773f0818b97e4fd0777b0 Add support for a key fingerprint blacklist There are various keys in the wild, such as Evil32 (https://evil32.com/) which a keyserver is unlikely to want to carry in the general case. Introduce the capability to have a blacklist of fingerprints which will not be accepted into the keyserver. --- diff --git a/cgi/add.c b/cgi/add.c index c3a54a7..02eb3eb 100644 --- a/cgi/add.c +++ b/cgi/add.c @@ -92,7 +92,8 @@ int main(int argc, char *argv[]) logthing(LOGTHING_INFO, "%d keys cleaned.", count); - count = dbctx->update_keys(dbctx, &keys, true); + count = dbctx->update_keys(dbctx, &keys, + &config.blacklist, true); logthing(LOGTHING_NOTICE, "Got %d new keys.", count); diff --git a/keyarray.c b/keyarray.c index f869802..8548555 100644 --- a/keyarray.c +++ b/keyarray.c @@ -16,6 +16,8 @@ * this program. If not, see . */ +#include +#include #include #include #include @@ -128,3 +130,70 @@ void array_free(struct keyarray *array) return; } + +static uint8_t hex2bin(char c) +{ + if (c >= '0' && c <= '9') { + return (c - '0'); + } else if (c >= 'a' && c <= 'f') { + return (c - 'a' + 10); + } else if (c >= 'A' && c <= 'F') { + return (c - 'A' + 10); + } + + return 255; +} + +bool array_load(struct keyarray *array, const char *file) +{ + struct openpgp_fingerprint fp; + char curline[1024]; + FILE *fpfile; + int i; + + fpfile = fopen(file, "r"); + + if (fpfile != NULL) { + if (!fgets(curline, sizeof(curline) - 1, fpfile)) { + fclose(fpfile); + return false; + } + + while (!feof(fpfile)) { + /* Strip any trailing white space */ + for (i = strlen(curline) - 1; + i >= 0 && isspace(curline[i]); i--) { + curline[i] = 0; + } + i++; + //if ((i % 2) != 0) { + // break; + //} + i >>= 1; + if (curline[0] == '#') { + // Comment line, ignore + } else if (i == FINGERPRINT_V3_LEN || + i == FINGERPRINT_V4_LEN || + i == FINGERPRINT_V5_LEN) { + fp.length = i; + for (i = 0; i < fp.length; i++) { + fp.fp[i] = hex2bin(curline[i * 2]); + fp.fp[i] <<= 4; + fp.fp[i] |= + hex2bin(curline[i * 2 + 1]); + } + array_add(array, &fp); + } else { + printf("Bad line.\n"); + } + + if (!fgets(curline, sizeof(curline) - 1, fpfile)) { + break; + } + } + + fclose(fpfile); + } + + return (array->count != 0); +} diff --git a/keyarray.h b/keyarray.h index e50a030..1ac13ba 100644 --- a/keyarray.h +++ b/keyarray.h @@ -64,6 +64,18 @@ void array_free(struct keyarray *array); */ bool array_add(struct keyarray *array, struct openpgp_fingerprint *fp); +/** + * @brief Load a file into a keyarray + * @param array Pointer to the key array + * @param file The full path to the file to load + * + * Loads fingerprints from the supplied file into the provided keyarray. Does + * not re-initialise the array so can be called repeatedly to add multiple + * files. The file does not need to be sorted; array_add() is called for each + * key to ensure the array is suitable for binary searching with array_find() + */ +bool array_load(struct keyarray *array, const char *file); + /** * @brief Compare two OpenPGP fingerprints * @param a Fingerprint 1 diff --git a/keydb.c b/keydb.c index 30a77ca..f362b1a 100644 --- a/keydb.c +++ b/keydb.c @@ -168,7 +168,9 @@ struct ll *generic_cached_getkeysigs(struct onak_dbctx *dbctx, uint64_t keyid) * the DB). Returns the number of entirely new keys added. */ int generic_update_keys(struct onak_dbctx *dbctx, - struct openpgp_publickey **keys, bool sendsync) + struct openpgp_publickey **keys, + struct keyarray *blacklist, + bool sendsync) { struct openpgp_publickey **curkey, *tmp = NULL; struct openpgp_publickey *oldkey = NULL; @@ -179,6 +181,14 @@ int generic_update_keys(struct onak_dbctx *dbctx, curkey = keys; while (*curkey != NULL) { get_fingerprint((*curkey)->publickey, &fp); + if (blacklist && array_find(blacklist, &fp)) { + logthing(LOGTHING_INFO, "Ignoring blacklisted key."); + tmp = *curkey; + *curkey = (*curkey)->next; + tmp->next = NULL; + free_publickey(tmp); + continue; + } intrans = dbctx->starttrans(dbctx); diff --git a/keydb.h b/keydb.h index 5dcb436..a1078c7 100644 --- a/keydb.h +++ b/keydb.h @@ -23,6 +23,7 @@ #include #include +#include "keyarray.h" #include "keystructs.h" #include "ll.h" @@ -139,6 +140,7 @@ struct onak_dbctx { /** * @brief Takes a list of public keys and updates them in the DB. * @param keys The keys to update in the DB. + * @param blacklist A keyarray of fingerprints that shouldn't be added. * @param sendsync If we should send a keysync mail. * * Takes a list of keys and adds them to the database, merging them with @@ -151,7 +153,9 @@ struct onak_dbctx { * with the update. */ int (*update_keys)(struct onak_dbctx *, - struct openpgp_publickey **keys, bool sendsync); + struct openpgp_publickey **keys, + struct keyarray *blacklist, + bool sendsync); /** * @brief Takes a keyid and returns the primary UID for it. diff --git a/keydb_dynamic.c b/keydb_dynamic.c index 52a4af9..cf9d87f 100644 --- a/keydb_dynamic.c +++ b/keydb_dynamic.c @@ -120,13 +120,15 @@ static int dynamic_delete_key(struct onak_dbctx *dbctx, } static int dynamic_update_keys(struct onak_dbctx *dbctx, - struct openpgp_publickey **keys, bool sendsync) + struct openpgp_publickey **keys, + struct keyarray *blacklist, + bool sendsync) { struct onak_dynamic_dbctx *privctx = (struct onak_dynamic_dbctx *) dbctx->priv; return privctx->loadeddbctx->update_keys(privctx->loadeddbctx, - keys, sendsync); + keys, blacklist, sendsync); } static struct ll *dynamic_getkeysigs(struct onak_dbctx *dbctx, diff --git a/keydb_keyring.c b/keydb_keyring.c index 0b865b5..e3f8584 100644 --- a/keydb_keyring.c +++ b/keydb_keyring.c @@ -222,7 +222,9 @@ static int keyring_iterate_keys(struct onak_dbctx *dbctx, } static int keyring_update_keys(struct onak_dbctx *dbctx, - struct openpgp_publickey **keys, bool sendsync) + struct openpgp_publickey **keys, + struct keyarray *blacklist, + bool sendsync) { return 0; } diff --git a/keydb_stacked.c b/keydb_stacked.c index ee1c5cf..b702c84 100644 --- a/keydb_stacked.c +++ b/keydb_stacked.c @@ -85,14 +85,16 @@ static int stacked_delete_key(struct onak_dbctx *dbctx, } static int stacked_update_keys(struct onak_dbctx *dbctx, - struct openpgp_publickey **keys, bool sendsync) + struct openpgp_publickey **keys, + struct keyarray *blacklist, + bool sendsync) { struct onak_stacked_dbctx *privctx = (struct onak_stacked_dbctx *) dbctx->priv; struct onak_dbctx *backend = (struct onak_dbctx *) privctx->backends->object; - return backend->update_keys(backend, keys, sendsync); + return backend->update_keys(backend, keys, blacklist, sendsync); } static int stacked_iterate_keys(struct onak_dbctx *dbctx, diff --git a/keystructs.h b/keystructs.h index af909f7..18a9ba4 100644 --- a/keystructs.h +++ b/keystructs.h @@ -27,11 +27,16 @@ #include "ll.h" /* + * Fingerprint lengths + * * v3 MD5 fingerprint is 16 bytes * v4 SHA-1 fingerprint is 20 * v5 SHA2-256 fingerprint is 32 */ -#define MAX_FINGERPRINT_LEN 32 +#define FINGERPRINT_V3_LEN 16 +#define FINGERPRINT_V4_LEN 20 +#define FINGERPRINT_V5_LEN 32 +#define MAX_FINGERPRINT_LEN 32 /** * @brief Stores the fingerprint of an OpenPGP key diff --git a/onak-conf.c b/onak-conf.c index 7b8a2e3..f0768a0 100644 --- a/onak-conf.c +++ b/onak-conf.c @@ -284,6 +284,8 @@ static bool parseconfigline(char *line) config.syncsites = lladd(config.syncsites, strdup(value)); /* [verification] section */ + } else if (MATCH("verification", "blacklist")) { + array_load(&config.blacklist, value); } else if (MATCH("verification", "drop_v3")) { if (parsebool(value, config.clean_policies & ONAK_CLEAN_DROP_V3_KEYS)) { @@ -602,4 +604,7 @@ void cleanupconfig(void) { free(config.mail_dir); config.mail_dir = NULL; } + if (config.blacklist.count != 0) { + array_free(&config.blacklist); + } } diff --git a/onak-conf.h b/onak-conf.h index 3e02b7a..5d39f6a 100644 --- a/onak-conf.h +++ b/onak-conf.h @@ -23,6 +23,7 @@ #include #include +#include "keyarray.h" #include "ll.h" /** @@ -89,6 +90,9 @@ struct onak_config { /** Pointer to the initialisation function for our loaded DB backend */ struct onak_dbctx *(*dbinit)(struct onak_db_config *, bool); + /** Blacklist of fingerprints to reject */ + struct keyarray blacklist; + /** What policies should we use for cleaning keys? */ uint64_t clean_policies; diff --git a/onak.c b/onak.c index 4c319cd..70f5434 100644 --- a/onak.c +++ b/onak.c @@ -241,7 +241,8 @@ int main(int argc, char *argv[]) dbctx = config.dbinit(config.backend, false); logthing(LOGTHING_NOTICE, "Got %d new keys.", dbctx->update_keys(dbctx, &keys, - false)); + &config.blacklist, + false)); if (keys != NULL && update) { flatten_publickey(keys, &packets, diff --git a/onak.ini.in b/onak.ini.in index 8989f07..a9745cd 100644 --- a/onak.ini.in +++ b/onak.ini.in @@ -16,6 +16,9 @@ max_reply_keys=128 ; Settings related to key verification options available. [verification] +; Blacklist certain fingerprints (e.g. EVIL32). One fingerprint per line, +; comment lines start with # +;blacklist=blacklist.txt ; Verify signature hashes - verify that the hash a signature claims to be ; over matches the hash of the data. Does not actually verify the signature. check_sighash=true diff --git a/runtests b/runtests index 60c0222..6b5b830 100755 --- a/runtests +++ b/runtests @@ -37,6 +37,7 @@ for t in libkeydb_*.so; do sed -e "s;BUILDDIR;${BUILDDIR};" -e "s;WORKDIR;${WORKDIR};" \ -e "s;DB;${backend};" \ ${TESTSDIR}/test-in.ini > ${WORKDIR}/test.ini + touch ${WORKDIR}/blacklist for t in ${TESTSDIR}/$backend-*.t ${TESTSDIR}/all-*.t; do total=`expr $total + 1` mkdir ${WORKDIR}/db/ diff --git a/t/test-in.ini b/t/test-in.ini index b741401..e40c0e2 100644 --- a/t/test-in.ini +++ b/t/test-in.ini @@ -13,3 +13,6 @@ location=WORKDIR/db/ [mail] bin_dir=BUILDDIR this_site=pgp-public-keys@localhost + +[verification] +blacklist=WORKDIR/blacklist