121baa36dSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
28fc8598eSJerry Chuang /*
38fc8598eSJerry Chuang * Host AP crypto routines
48fc8598eSJerry Chuang *
58fc8598eSJerry Chuang * Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
68fc8598eSJerry Chuang * Portions Copyright (C) 2004, Intel Corporation <jketreno@linux.intel.com>
78fc8598eSJerry Chuang */
88fc8598eSJerry Chuang
98fc8598eSJerry Chuang #include <linux/module.h>
108fc8598eSJerry Chuang #include <linux/init.h>
118fc8598eSJerry Chuang #include <linux/slab.h>
123033669eSKsenija Stanojevic #include <linux/string.h>
133033669eSKsenija Stanojevic #include <linux/errno.h>
148fc8598eSJerry Chuang
158fc8598eSJerry Chuang #include "ieee80211.h"
168fc8598eSJerry Chuang
178fc8598eSJerry Chuang MODULE_AUTHOR("Jouni Malinen");
188fc8598eSJerry Chuang MODULE_DESCRIPTION("HostAP crypto");
198fc8598eSJerry Chuang MODULE_LICENSE("GPL");
208fc8598eSJerry Chuang
218fc8598eSJerry Chuang struct ieee80211_crypto_alg {
228fc8598eSJerry Chuang struct list_head list;
238fc8598eSJerry Chuang struct ieee80211_crypto_ops *ops;
248fc8598eSJerry Chuang };
258fc8598eSJerry Chuang
268fc8598eSJerry Chuang
278fc8598eSJerry Chuang struct ieee80211_crypto {
288fc8598eSJerry Chuang struct list_head algs;
298fc8598eSJerry Chuang spinlock_t lock;
308fc8598eSJerry Chuang };
318fc8598eSJerry Chuang
328fc8598eSJerry Chuang static struct ieee80211_crypto *hcrypt;
338fc8598eSJerry Chuang
ieee80211_crypt_deinit_entries(struct ieee80211_device * ieee,int force)348fc8598eSJerry Chuang void ieee80211_crypt_deinit_entries(struct ieee80211_device *ieee,
358fc8598eSJerry Chuang int force)
368fc8598eSJerry Chuang {
378fc8598eSJerry Chuang struct list_head *ptr, *n;
388fc8598eSJerry Chuang struct ieee80211_crypt_data *entry;
398fc8598eSJerry Chuang
408fc8598eSJerry Chuang for (ptr = ieee->crypt_deinit_list.next, n = ptr->next;
418fc8598eSJerry Chuang ptr != &ieee->crypt_deinit_list; ptr = n, n = ptr->next) {
428fc8598eSJerry Chuang entry = list_entry(ptr, struct ieee80211_crypt_data, list);
438fc8598eSJerry Chuang
448fc8598eSJerry Chuang if (atomic_read(&entry->refcnt) != 0 && !force)
458fc8598eSJerry Chuang continue;
468fc8598eSJerry Chuang
478fc8598eSJerry Chuang list_del(ptr);
488fc8598eSJerry Chuang
49f61fb935SMauro Carvalho Chehab if (entry->ops)
508fc8598eSJerry Chuang entry->ops->deinit(entry->priv);
518fc8598eSJerry Chuang kfree(entry);
528fc8598eSJerry Chuang }
538fc8598eSJerry Chuang }
548fc8598eSJerry Chuang
ieee80211_crypt_deinit_handler(struct timer_list * t)55d2e5af14SKees Cook void ieee80211_crypt_deinit_handler(struct timer_list *t)
568fc8598eSJerry Chuang {
57d2e5af14SKees Cook struct ieee80211_device *ieee = from_timer(ieee, t, crypt_deinit_timer);
588fc8598eSJerry Chuang unsigned long flags;
598fc8598eSJerry Chuang
608fc8598eSJerry Chuang spin_lock_irqsave(&ieee->lock, flags);
618fc8598eSJerry Chuang ieee80211_crypt_deinit_entries(ieee, 0);
628fc8598eSJerry Chuang if (!list_empty(&ieee->crypt_deinit_list)) {
6380c598a6SDilek Uzulmez netdev_dbg(ieee->dev, "%s: entries remaining in delayed crypt deletion list\n",
6480c598a6SDilek Uzulmez ieee->dev->name);
658fc8598eSJerry Chuang ieee->crypt_deinit_timer.expires = jiffies + HZ;
668fc8598eSJerry Chuang add_timer(&ieee->crypt_deinit_timer);
678fc8598eSJerry Chuang }
688fc8598eSJerry Chuang spin_unlock_irqrestore(&ieee->lock, flags);
698fc8598eSJerry Chuang
708fc8598eSJerry Chuang }
718fc8598eSJerry Chuang
ieee80211_crypt_delayed_deinit(struct ieee80211_device * ieee,struct ieee80211_crypt_data ** crypt)728fc8598eSJerry Chuang void ieee80211_crypt_delayed_deinit(struct ieee80211_device *ieee,
738fc8598eSJerry Chuang struct ieee80211_crypt_data **crypt)
748fc8598eSJerry Chuang {
758fc8598eSJerry Chuang struct ieee80211_crypt_data *tmp;
768fc8598eSJerry Chuang unsigned long flags;
778fc8598eSJerry Chuang
78f8f65f2bSsimran singhal if (!(*crypt))
798fc8598eSJerry Chuang return;
808fc8598eSJerry Chuang
818fc8598eSJerry Chuang tmp = *crypt;
828fc8598eSJerry Chuang *crypt = NULL;
838fc8598eSJerry Chuang
848fc8598eSJerry Chuang /* must not run ops->deinit() while there may be pending encrypt or
858fc8598eSJerry Chuang * decrypt operations. Use a list of delayed deinits to avoid needing
863838eedfSDerek Robson * locking.
873838eedfSDerek Robson */
888fc8598eSJerry Chuang
898fc8598eSJerry Chuang spin_lock_irqsave(&ieee->lock, flags);
908fc8598eSJerry Chuang list_add(&tmp->list, &ieee->crypt_deinit_list);
918fc8598eSJerry Chuang if (!timer_pending(&ieee->crypt_deinit_timer)) {
928fc8598eSJerry Chuang ieee->crypt_deinit_timer.expires = jiffies + HZ;
938fc8598eSJerry Chuang add_timer(&ieee->crypt_deinit_timer);
948fc8598eSJerry Chuang }
958fc8598eSJerry Chuang spin_unlock_irqrestore(&ieee->lock, flags);
968fc8598eSJerry Chuang }
978fc8598eSJerry Chuang
ieee80211_register_crypto_ops(struct ieee80211_crypto_ops * ops)988fc8598eSJerry Chuang int ieee80211_register_crypto_ops(struct ieee80211_crypto_ops *ops)
998fc8598eSJerry Chuang {
1008fc8598eSJerry Chuang unsigned long flags;
1018fc8598eSJerry Chuang struct ieee80211_crypto_alg *alg;
1028fc8598eSJerry Chuang
103f8f65f2bSsimran singhal if (!hcrypt)
1048fc8598eSJerry Chuang return -1;
1058fc8598eSJerry Chuang
1067a6cb0d5SJulia Lawall alg = kzalloc(sizeof(*alg), GFP_KERNEL);
107f8f65f2bSsimran singhal if (!alg)
1088fc8598eSJerry Chuang return -ENOMEM;
1098fc8598eSJerry Chuang
1108fc8598eSJerry Chuang alg->ops = ops;
1118fc8598eSJerry Chuang
1128fc8598eSJerry Chuang spin_lock_irqsave(&hcrypt->lock, flags);
1138fc8598eSJerry Chuang list_add(&alg->list, &hcrypt->algs);
1148fc8598eSJerry Chuang spin_unlock_irqrestore(&hcrypt->lock, flags);
1158fc8598eSJerry Chuang
116fb99f874SKsenija Stanojevic pr_debug("ieee80211_crypt: registered algorithm '%s'\n",
1178fc8598eSJerry Chuang ops->name);
1188fc8598eSJerry Chuang
1198fc8598eSJerry Chuang return 0;
1208fc8598eSJerry Chuang }
1218fc8598eSJerry Chuang
ieee80211_unregister_crypto_ops(struct ieee80211_crypto_ops * ops)1228fc8598eSJerry Chuang int ieee80211_unregister_crypto_ops(struct ieee80211_crypto_ops *ops)
1238fc8598eSJerry Chuang {
1248fc8598eSJerry Chuang unsigned long flags;
1258fc8598eSJerry Chuang struct list_head *ptr;
1268fc8598eSJerry Chuang struct ieee80211_crypto_alg *del_alg = NULL;
1278fc8598eSJerry Chuang
128f8f65f2bSsimran singhal if (!hcrypt)
1298fc8598eSJerry Chuang return -1;
1308fc8598eSJerry Chuang
1318fc8598eSJerry Chuang spin_lock_irqsave(&hcrypt->lock, flags);
1328fc8598eSJerry Chuang for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) {
1338fc8598eSJerry Chuang struct ieee80211_crypto_alg *alg =
1348fc8598eSJerry Chuang (struct ieee80211_crypto_alg *)ptr;
1358fc8598eSJerry Chuang if (alg->ops == ops) {
1368fc8598eSJerry Chuang list_del(&alg->list);
1378fc8598eSJerry Chuang del_alg = alg;
1388fc8598eSJerry Chuang break;
1398fc8598eSJerry Chuang }
1408fc8598eSJerry Chuang }
1418fc8598eSJerry Chuang spin_unlock_irqrestore(&hcrypt->lock, flags);
1428fc8598eSJerry Chuang
1438fc8598eSJerry Chuang if (del_alg) {
14480c598a6SDilek Uzulmez pr_debug("ieee80211_crypt: unregistered algorithm '%s'\n",
14580c598a6SDilek Uzulmez ops->name);
1468fc8598eSJerry Chuang kfree(del_alg);
1478fc8598eSJerry Chuang }
1488fc8598eSJerry Chuang
1498fc8598eSJerry Chuang return del_alg ? 0 : -1;
1508fc8598eSJerry Chuang }
1518fc8598eSJerry Chuang
1528fc8598eSJerry Chuang
ieee80211_get_crypto_ops(const char * name)1538fc8598eSJerry Chuang struct ieee80211_crypto_ops *ieee80211_get_crypto_ops(const char *name)
1548fc8598eSJerry Chuang {
1558fc8598eSJerry Chuang unsigned long flags;
1568fc8598eSJerry Chuang struct list_head *ptr;
1578fc8598eSJerry Chuang struct ieee80211_crypto_alg *found_alg = NULL;
1588fc8598eSJerry Chuang
159f8f65f2bSsimran singhal if (!hcrypt)
1608fc8598eSJerry Chuang return NULL;
1618fc8598eSJerry Chuang
1628fc8598eSJerry Chuang spin_lock_irqsave(&hcrypt->lock, flags);
1638fc8598eSJerry Chuang for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) {
1648fc8598eSJerry Chuang struct ieee80211_crypto_alg *alg =
1658fc8598eSJerry Chuang (struct ieee80211_crypto_alg *)ptr;
1668fc8598eSJerry Chuang if (strcmp(alg->ops->name, name) == 0) {
1678fc8598eSJerry Chuang found_alg = alg;
1688fc8598eSJerry Chuang break;
1698fc8598eSJerry Chuang }
1708fc8598eSJerry Chuang }
1718fc8598eSJerry Chuang spin_unlock_irqrestore(&hcrypt->lock, flags);
1728fc8598eSJerry Chuang
1738fc8598eSJerry Chuang if (found_alg)
1748fc8598eSJerry Chuang return found_alg->ops;
1758fc8598eSJerry Chuang return NULL;
1768fc8598eSJerry Chuang }
1778fc8598eSJerry Chuang
1788fc8598eSJerry Chuang
ieee80211_crypt_null_init(int keyidx)1798fc8598eSJerry Chuang static void *ieee80211_crypt_null_init(int keyidx) { return (void *)1; }
ieee80211_crypt_null_deinit(void * priv)1808fc8598eSJerry Chuang static void ieee80211_crypt_null_deinit(void *priv) {}
1818fc8598eSJerry Chuang
1828fc8598eSJerry Chuang static struct ieee80211_crypto_ops ieee80211_crypt_null = {
1838fc8598eSJerry Chuang .name = "NULL",
1848fc8598eSJerry Chuang .init = ieee80211_crypt_null_init,
1858fc8598eSJerry Chuang .deinit = ieee80211_crypt_null_deinit,
1868fc8598eSJerry Chuang .encrypt_mpdu = NULL,
1878fc8598eSJerry Chuang .decrypt_mpdu = NULL,
1888fc8598eSJerry Chuang .encrypt_msdu = NULL,
1898fc8598eSJerry Chuang .decrypt_msdu = NULL,
1908fc8598eSJerry Chuang .set_key = NULL,
1918fc8598eSJerry Chuang .get_key = NULL,
1928fc8598eSJerry Chuang .extra_prefix_len = 0,
1938fc8598eSJerry Chuang .extra_postfix_len = 0,
1948fc8598eSJerry Chuang .owner = THIS_MODULE,
1958fc8598eSJerry Chuang };
1968fc8598eSJerry Chuang
ieee80211_crypto_init(void)197f61fb935SMauro Carvalho Chehab int __init ieee80211_crypto_init(void)
1988fc8598eSJerry Chuang {
1998fc8598eSJerry Chuang int ret = -ENOMEM;
2008fc8598eSJerry Chuang
2017a6cb0d5SJulia Lawall hcrypt = kzalloc(sizeof(*hcrypt), GFP_KERNEL);
2028fc8598eSJerry Chuang if (!hcrypt)
2038fc8598eSJerry Chuang goto out;
2048fc8598eSJerry Chuang
2058fc8598eSJerry Chuang INIT_LIST_HEAD(&hcrypt->algs);
2068fc8598eSJerry Chuang spin_lock_init(&hcrypt->lock);
2078fc8598eSJerry Chuang
2088fc8598eSJerry Chuang ret = ieee80211_register_crypto_ops(&ieee80211_crypt_null);
2098fc8598eSJerry Chuang if (ret < 0) {
2108fc8598eSJerry Chuang kfree(hcrypt);
2118fc8598eSJerry Chuang hcrypt = NULL;
2128fc8598eSJerry Chuang }
2138fc8598eSJerry Chuang out:
2148fc8598eSJerry Chuang return ret;
2158fc8598eSJerry Chuang }
2168fc8598eSJerry Chuang
ieee80211_crypto_deinit(void)217*57078a3cSTong Zhang void ieee80211_crypto_deinit(void)
2188fc8598eSJerry Chuang {
2198fc8598eSJerry Chuang struct list_head *ptr, *n;
2208fc8598eSJerry Chuang
221f8f65f2bSsimran singhal if (!hcrypt)
2228fc8598eSJerry Chuang return;
2238fc8598eSJerry Chuang
2248fc8598eSJerry Chuang for (ptr = hcrypt->algs.next, n = ptr->next; ptr != &hcrypt->algs;
2258fc8598eSJerry Chuang ptr = n, n = ptr->next) {
2268fc8598eSJerry Chuang struct ieee80211_crypto_alg *alg =
2278fc8598eSJerry Chuang (struct ieee80211_crypto_alg *)ptr;
2288fc8598eSJerry Chuang list_del(ptr);
22980c598a6SDilek Uzulmez pr_debug("ieee80211_crypt: unregistered algorithm '%s' (deinit)\n",
23080c598a6SDilek Uzulmez alg->ops->name);
2318fc8598eSJerry Chuang kfree(alg);
2328fc8598eSJerry Chuang }
2338fc8598eSJerry Chuang
2348fc8598eSJerry Chuang kfree(hcrypt);
2358fc8598eSJerry Chuang }
236