iwlwifi: mvm: scrub key material in firmware dumps
authorJohannes Berg <johannes.berg@intel.com>
Sun, 17 Oct 2021 09:40:13 +0000 (12:40 +0300)
committerLuca Coelho <luciano.coelho@intel.com>
Fri, 22 Oct 2021 07:49:00 +0000 (10:49 +0300)
Use the previously added infrastructure to scrub key material
in firmware dumps:
 * in the TX FIFO data, just search for each key that we
   know about and override such data
 * scrub various commands that we sent to the firmware if
   they're present
 * in firmware memory, where advertised by firmware TLVs

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
Link: https://lore.kernel.org/r/iwlwifi.20211017123741.d1514964e6a7.I18f8c2ce8082952af7cfe5f8fe75fe51851b8853@changeid
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
drivers/net/wireless/intel/iwlwifi/mvm/ops.c

index b7f203fe7753fa9166026ac78d3e81ce79eb6a2c..3b0d5a0685414c264b4100d29b1f30f75fc8afd7 100644 (file)
@@ -725,6 +725,183 @@ static int iwl_mvm_start_post_nvm(struct iwl_mvm *mvm)
        return 0;
 }
 
+struct iwl_mvm_frob_txf_data {
+       u8 *buf;
+       size_t buflen;
+};
+
+static void iwl_mvm_frob_txf_key_iter(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif *vif,
+                                     struct ieee80211_sta *sta,
+                                     struct ieee80211_key_conf *key,
+                                     void *data)
+{
+       struct iwl_mvm_frob_txf_data *txf = data;
+       u8 keylen, match, matchend;
+       u8 *keydata;
+       size_t i;
+
+       switch (key->cipher) {
+       case WLAN_CIPHER_SUITE_CCMP:
+               keydata = key->key;
+               keylen = key->keylen;
+               break;
+       case WLAN_CIPHER_SUITE_WEP40:
+       case WLAN_CIPHER_SUITE_WEP104:
+       case WLAN_CIPHER_SUITE_TKIP:
+               /*
+                * WEP has short keys which might show up in the payload,
+                * and then you can deduce the key, so in this case just
+                * remove all FIFO data.
+                * For TKIP, we don't know the phase 2 keys here, so same.
+                */
+               memset(txf->buf, 0xBB, txf->buflen);
+               return;
+       default:
+               return;
+       }
+
+       /* scan for key material and clear it out */
+       match = 0;
+       for (i = 0; i < txf->buflen; i++) {
+               if (txf->buf[i] != keydata[match]) {
+                       match = 0;
+                       continue;
+               }
+               match++;
+               if (match == keylen) {
+                       memset(txf->buf + i - keylen, 0xAA, keylen);
+                       match = 0;
+               }
+       }
+
+       /* we're dealing with a FIFO, so check wrapped around data */
+       matchend = match;
+       for (i = 0; match && i < keylen - match; i++) {
+               if (txf->buf[i] != keydata[match])
+                       break;
+               match++;
+               if (match == keylen) {
+                       memset(txf->buf, 0xAA, i + 1);
+                       memset(txf->buf + txf->buflen - matchend, 0xAA,
+                              matchend);
+                       break;
+               }
+       }
+}
+
+static void iwl_mvm_frob_txf(void *ctx, void *buf, size_t buflen)
+{
+       struct iwl_mvm_frob_txf_data txf = {
+               .buf = buf,
+               .buflen = buflen,
+       };
+       struct iwl_mvm *mvm = ctx;
+
+       /* embedded key material exists only on old API */
+       if (iwl_mvm_has_new_tx_api(mvm))
+               return;
+
+       rcu_read_lock();
+       ieee80211_iter_keys_rcu(mvm->hw, NULL, iwl_mvm_frob_txf_key_iter, &txf);
+       rcu_read_unlock();
+}
+
+static void iwl_mvm_frob_hcmd(void *ctx, void *hcmd, size_t len)
+{
+       /* we only use wide headers for commands */
+       struct iwl_cmd_header_wide *hdr = hcmd;
+       unsigned int frob_start = sizeof(*hdr), frob_end = 0;
+
+       if (len < sizeof(hdr))
+               return;
+
+       /* all the commands we care about are in LONG_GROUP */
+       if (hdr->group_id != LONG_GROUP)
+               return;
+
+       switch (hdr->cmd) {
+       case WEP_KEY:
+       case WOWLAN_TKIP_PARAM:
+       case WOWLAN_KEK_KCK_MATERIAL:
+       case ADD_STA_KEY:
+               /*
+                * blank out everything here, easier than dealing
+                * with the various versions of the command
+                */
+               frob_end = INT_MAX;
+               break;
+       case MGMT_MCAST_KEY:
+               frob_start = offsetof(struct iwl_mvm_mgmt_mcast_key_cmd, igtk);
+               BUILD_BUG_ON(offsetof(struct iwl_mvm_mgmt_mcast_key_cmd, igtk) !=
+                            offsetof(struct iwl_mvm_mgmt_mcast_key_cmd_v1, igtk));
+
+               frob_end = offsetofend(struct iwl_mvm_mgmt_mcast_key_cmd, igtk);
+               BUILD_BUG_ON(offsetof(struct iwl_mvm_mgmt_mcast_key_cmd, igtk) <
+                            offsetof(struct iwl_mvm_mgmt_mcast_key_cmd_v1, igtk));
+               break;
+       }
+
+       if (frob_start >= frob_end)
+               return;
+
+       if (frob_end > len)
+               frob_end = len;
+
+       memset((u8 *)hcmd + frob_start, 0xAA, frob_end - frob_start);
+}
+
+static void iwl_mvm_frob_mem(void *ctx, u32 mem_addr, void *mem, size_t buflen)
+{
+       const struct iwl_dump_exclude *excl;
+       struct iwl_mvm *mvm = ctx;
+       int i;
+
+       switch (mvm->fwrt.cur_fw_img) {
+       case IWL_UCODE_INIT:
+       default:
+               /* not relevant */
+               return;
+       case IWL_UCODE_REGULAR:
+       case IWL_UCODE_REGULAR_USNIFFER:
+               excl = mvm->fw->dump_excl;
+               break;
+       case IWL_UCODE_WOWLAN:
+               excl = mvm->fw->dump_excl_wowlan;
+               break;
+       }
+
+       BUILD_BUG_ON(sizeof(mvm->fw->dump_excl) !=
+                    sizeof(mvm->fw->dump_excl_wowlan));
+
+       for (i = 0; i < ARRAY_SIZE(mvm->fw->dump_excl); i++) {
+               u32 start, end;
+
+               if (!excl[i].addr || !excl[i].size)
+                       continue;
+
+               start = excl[i].addr;
+               end = start + excl[i].size;
+
+               if (end <= mem_addr || start >= mem_addr + buflen)
+                       continue;
+
+               if (start < mem_addr)
+                       start = mem_addr;
+
+               if (end > mem_addr + buflen)
+                       end = mem_addr + buflen;
+
+               memset((u8 *)mem + start - mem_addr, 0xAA, end - start);
+       }
+}
+
+static const struct iwl_dump_sanitize_ops iwl_mvm_sanitize_ops = {
+       .frob_txf = iwl_mvm_frob_txf,
+       .frob_hcmd = iwl_mvm_frob_hcmd,
+       .frob_mem = iwl_mvm_frob_mem,
+};
+
 static struct iwl_op_mode *
 iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
                      const struct iwl_fw *fw, struct dentry *dbgfs_dir)
@@ -774,7 +951,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        mvm->hw = hw;
 
        iwl_fw_runtime_init(&mvm->fwrt, trans, fw, &iwl_mvm_fwrt_ops, mvm,
-                           NULL, NULL, dbgfs_dir);
+                           &iwl_mvm_sanitize_ops, mvm, dbgfs_dir);
 
        iwl_mvm_get_acpi_tables(mvm);