]> the.earth.li Git - onak.git/blobdiff - cleankey.c
Remove dead store in generic_fetch_key
[onak.git] / cleankey.c
index afe5a06a82b7cb2aacf53c18e4d06cfebc03f7e0..3a74098c4683e114ef37421099fc46f67a0ca3a2 100644 (file)
  */
 
 #include <stdbool.h>
-#include <stdio.h>
 #include <stdlib.h>
 
+#include "build-config.h"
 #include "cleankey.h"
+#include "decodekey.h"
 #include "keyid.h"
 #include "keystructs.h"
 #include "log.h"
 #include "mem.h"
 #include "merge.h"
-#include "onak-conf.h"
+#include "openpgp.h"
 #include "sigcheck.h"
 
 /**
@@ -132,15 +133,104 @@ int dedupsubkeys(struct openpgp_publickey *key)
  *     hash cannot be checked (eg we don't support that hash type) we err
  *     on the side of caution and keep it.
  */
-int clean_sighashes(struct openpgp_publickey *key,
+int clean_sighashes(struct onak_dbctx *dbctx,
+               struct openpgp_publickey *key,
                struct openpgp_packet *sigdata,
-               struct openpgp_packet_list **sigs)
+               struct openpgp_packet_list **sigs,
+               bool fullverify,
+               bool *selfsig, bool *othersig)
 {
        struct openpgp_packet_list *tmpsig;
+       struct openpgp_publickey *sigkeys = NULL, *curkey;
+       onak_status_t ret;
+       uint8_t hashtype;
+       uint8_t hash[64];
+       uint8_t *sighash;
        int removed = 0;
+       uint64_t keyid, sigid;
+       bool remove;
 
+       get_keyid(key, &keyid);
+       if (othersig != NULL) {
+               *othersig = false;
+       }
+       if (selfsig != NULL) {
+               *selfsig = false;
+       }
        while (*sigs != NULL) {
-               if (check_packet_sighash(key, sigdata, (*sigs)->packet) == 0) {
+               remove = false;
+               ret = calculate_packet_sighash(key, sigdata, (*sigs)->packet,
+                               &hashtype, hash, &sighash);
+
+               if (ret == ONAK_E_UNSUPPORTED_FEATURE) {
+                       get_keyid(key, &keyid);
+                       logthing(LOGTHING_ERROR,
+                               "Unsupported signature hash type %d on 0x%016"
+                               PRIX64,
+                               hashtype,
+                               keyid);
+                       if (fullverify) {
+                               remove = true;
+                       }
+               } else if (ret != ONAK_E_OK || (!fullverify &&
+                               !(hash[0] == sighash[0] &&
+                                       hash[1] == sighash[1]))) {
+                       remove = true;
+               }
+
+#if HAVE_CRYPTO
+               if (fullverify && !remove) {
+                       sig_info((*sigs)->packet, &sigid, NULL);
+
+                       /* Start by assuming it's a bad sig */
+
+                       remove = true;
+                       if (sigid == keyid) {
+                               ret = onak_check_hash_sig(key, (*sigs)->packet,
+                                               hash, hashtype);
+
+                               /* We have a valid self signature */
+                               if (ret == ONAK_E_OK) {
+                                       remove = false;
+                                       if (selfsig != NULL) {
+                                               *selfsig = true;
+                                       }
+                               }
+                       }
+
+                       if (remove) {
+                               dbctx->fetch_key_id(dbctx, sigid,
+                                               &sigkeys, false);
+                       }
+
+                       /*
+                        * A 64 bit collision is probably a sign of something
+                        * sneaky happening, but if the signature verifies we
+                        * should keep it.
+                        */
+                       for (curkey = sigkeys; curkey != NULL;
+                                       curkey = curkey->next) {
+
+                               ret = onak_check_hash_sig(curkey,
+                                               (*sigs)->packet,
+                                               hash, hashtype);
+
+                               /* Got a valid signature */
+                               if (ret == ONAK_E_OK) {
+                                       remove = false;
+                                       if (othersig != NULL) {
+                                               *othersig = true;
+                                       }
+                                       break;
+                               }
+                       }
+
+                       free_publickey(sigkeys);
+                       sigkeys = NULL;
+               }
+#endif
+
+               if (remove) {
                        tmpsig = *sigs;
                        *sigs = (*sigs)->next;
                        tmpsig->next = NULL;
@@ -154,53 +244,153 @@ int clean_sighashes(struct openpgp_publickey *key,
        return removed;
 }
 
-int clean_list_sighashes(struct openpgp_publickey *key,
-                       struct openpgp_signedpacket_list *siglist)
+int clean_list_sighashes(struct onak_dbctx *dbctx,
+                       struct openpgp_publickey *key,
+                       struct openpgp_signedpacket_list **siglist,
+                       bool fullverify, bool needother)
 {
+       struct openpgp_signedpacket_list *tmp = NULL;
+       bool selfsig, othersig;
        int removed = 0;
 
-       while (siglist != NULL) {
-               removed += clean_sighashes(key, siglist->packet,
-                       &siglist->sigs);
-               siglist = siglist->next;
+       while (siglist != NULL && *siglist != NULL) {
+               selfsig = othersig = false;
+
+               removed += clean_sighashes(dbctx, key, (*siglist)->packet,
+                       &(*siglist)->sigs, fullverify, &selfsig, &othersig);
+
+               if (fullverify && (!selfsig || (needother && !othersig))) {
+                       /* Remove the UID/subkey if there's no selfsig */
+                       tmp = *siglist;
+                       *siglist = (*siglist)->next;
+                       tmp->next = NULL;
+                       free_signedpacket_list(tmp);
+               } else {
+                       siglist = &(*siglist)->next;
+               }
        }
 
        return removed;
 }
 
-int clean_key_sighashes(struct openpgp_publickey *key)
+int clean_key_signatures(struct onak_dbctx *dbctx,
+               struct openpgp_publickey *key, bool fullverify, bool needother)
 {
        int removed;
 
-       removed = clean_sighashes(key, NULL, &key->sigs);
-       removed += clean_list_sighashes(key, key->uids);
-       removed += clean_list_sighashes(key, key->subkeys);
+       removed = clean_sighashes(dbctx, key, NULL, &key->sigs, fullverify,
+                       NULL, NULL);
+       removed += clean_list_sighashes(dbctx, key, &key->uids, fullverify,
+                       needother);
+       removed += clean_list_sighashes(dbctx, key, &key->subkeys, fullverify,
+                       false);
 
        return removed;
 }
 
+#define UAT_LIMIT      0xFFFF
+#define UID_LIMIT      1024
+#define PACKET_LIMIT   8383            /* Fits in 2 byte packet length */
+int clean_large_packets(struct openpgp_publickey *key)
+{
+       struct openpgp_signedpacket_list **curuid = NULL;
+       struct openpgp_signedpacket_list *tmp = NULL;
+       bool                              drop;
+       int                               dropped = 0;
+
+       log_assert(key != NULL);
+       curuid = &key->uids;
+       while (*curuid != NULL) {
+               drop = false;
+               switch ((*curuid)->packet->tag) {
+               case OPENPGP_PACKET_UID:
+                       if ((*curuid)->packet->length > UID_LIMIT)
+                               drop = true;
+                       break;
+               case OPENPGP_PACKET_UAT:
+                       if ((*curuid)->packet->length > UAT_LIMIT)
+                               drop = true;
+                       break;
+               default:
+                       if ((*curuid)->packet->length > PACKET_LIMIT)
+                               drop = true;
+                       break;
+               }
+
+               if (drop) {
+                       logthing(LOGTHING_INFO,
+                                       "Dropping large (%d) packet, type %d",
+                                       (*curuid)->packet->length,
+                                       (*curuid)->packet->tag);
+                       /* Remove the entire large signed packet list */
+                       tmp = *curuid;
+                       *curuid = (*curuid)->next;
+                       tmp->next = NULL;
+                       free_signedpacket_list(tmp);
+                       dropped++;
+               } else {
+                       curuid = &(*curuid)->next;
+               }
+       }
+
+       return dropped;
+}
+
 /**
  *     cleankeys - Apply all available cleaning options on a list of keys.
- *     @keys: The list of keys to clean.
+ *     @policies: The cleaning policies to apply.
  *
- *     Applies all the cleaning options we can (eg duplicate key ids) to a
- *     list of keys. Returns 0 if no changes were made, otherwise the number
- *     of keys cleaned.
+ *     Applies the requested cleaning policies to a list of keys. These are
+ *     specified from the ONAK_CLEAN_* set of flags, or ONAK_CLEAN_ALL to
+ *     apply all available cleaning options. Returns 0 if no changes were
+ *     made, otherwise the number of keys cleaned. Note that some options
+ *     may result in keys being removed entirely from the list.
  */
-int cleankeys(struct openpgp_publickey *keys)
+int cleankeys(struct onak_dbctx *dbctx, struct openpgp_publickey **keys,
+               uint64_t policies)
 {
-       int changed = 0, count;
+       struct openpgp_publickey **curkey, *tmp;
+       int changed = 0, count = 0;
+
+       if (keys == NULL)
+               return 0;
 
-       while (keys != NULL) {
-               count = dedupuids(keys);
-               count += dedupsubkeys(keys);
-               if (config.check_sighash) {
-                       count += clean_key_sighashes(keys);
+       curkey = keys;
+       while (*curkey != NULL) {
+               if (policies & ONAK_CLEAN_DROP_V3_KEYS) {
+                       if ((*curkey)->publickey->data[0] < 4) {
+                               /* Remove the key from the list if it's < v4 */
+                               tmp = *curkey;
+                               *curkey = tmp->next;
+                               tmp->next = NULL;
+                               free_publickey(tmp);
+                               changed++;
+                               continue;
+                       }
+               }
+               if (policies & ONAK_CLEAN_LARGE_PACKETS) {
+                       count += clean_large_packets(*curkey);
+               }
+               count += dedupuids(*curkey);
+               count += dedupsubkeys(*curkey);
+               if (policies & (ONAK_CLEAN_CHECK_SIGHASH |
+                                       ONAK_CLEAN_VERIFY_SIGNATURES)) {
+                       count += clean_key_signatures(dbctx, *curkey,
+                               policies & ONAK_CLEAN_VERIFY_SIGNATURES,
+                               policies & ONAK_CLEAN_NEED_OTHER_SIG);
                }
                if (count > 0) {
                        changed++;
                }
-               keys = keys->next;
+               if ((*curkey)->uids == NULL) {
+                       /* No valid UIDS so remove the key from the list */
+                       tmp = *curkey;
+                       *curkey = tmp->next;
+                       tmp->next = NULL;
+                       free_publickey(tmp);
+               } else {
+                       curkey = &(*curkey)->next;
+               }
        }
 
        return changed;