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