18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/capability.h> 88c2ecf20Sopenharmony_ci#include <linux/errno.h> 98c2ecf20Sopenharmony_ci#include <linux/types.h> 108c2ecf20Sopenharmony_ci#include <linux/socket.h> 118c2ecf20Sopenharmony_ci#include <linux/in.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/timer.h> 148c2ecf20Sopenharmony_ci#include <linux/string.h> 158c2ecf20Sopenharmony_ci#include <linux/sockios.h> 168c2ecf20Sopenharmony_ci#include <linux/net.h> 178c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <net/ax25.h> 208c2ecf20Sopenharmony_ci#include <linux/inet.h> 218c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 228c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 238c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 248c2ecf20Sopenharmony_ci#include <net/sock.h> 258c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 268c2ecf20Sopenharmony_ci#include <linux/fcntl.h> 278c2ecf20Sopenharmony_ci#include <linux/mm.h> 288c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 298c2ecf20Sopenharmony_ci#include <linux/list.h> 308c2ecf20Sopenharmony_ci#include <linux/notifier.h> 318c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 328c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 338c2ecf20Sopenharmony_ci#include <linux/stat.h> 348c2ecf20Sopenharmony_ci#include <linux/sysctl.h> 358c2ecf20Sopenharmony_ci#include <linux/export.h> 368c2ecf20Sopenharmony_ci#include <net/ip.h> 378c2ecf20Sopenharmony_ci#include <net/arp.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* 408c2ecf20Sopenharmony_ci * Callsign/UID mapper. This is in kernel space for security on multi-amateur machines. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic HLIST_HEAD(ax25_uid_list); 448c2ecf20Sopenharmony_cistatic DEFINE_RWLOCK(ax25_uid_lock); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ciint ax25_uid_policy; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ax25_uid_policy); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ciax25_uid_assoc *ax25_findbyuid(kuid_t uid) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci ax25_uid_assoc *ax25_uid, *res = NULL; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci read_lock(&ax25_uid_lock); 558c2ecf20Sopenharmony_ci ax25_uid_for_each(ax25_uid, &ax25_uid_list) { 568c2ecf20Sopenharmony_ci if (uid_eq(ax25_uid->uid, uid)) { 578c2ecf20Sopenharmony_ci ax25_uid_hold(ax25_uid); 588c2ecf20Sopenharmony_ci res = ax25_uid; 598c2ecf20Sopenharmony_ci break; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci read_unlock(&ax25_uid_lock); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci return res; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ax25_findbyuid); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ciint ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci ax25_uid_assoc *ax25_uid; 728c2ecf20Sopenharmony_ci ax25_uid_assoc *user; 738c2ecf20Sopenharmony_ci unsigned long res; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci switch (cmd) { 768c2ecf20Sopenharmony_ci case SIOCAX25GETUID: 778c2ecf20Sopenharmony_ci res = -ENOENT; 788c2ecf20Sopenharmony_ci read_lock(&ax25_uid_lock); 798c2ecf20Sopenharmony_ci ax25_uid_for_each(ax25_uid, &ax25_uid_list) { 808c2ecf20Sopenharmony_ci if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) { 818c2ecf20Sopenharmony_ci res = from_kuid_munged(current_user_ns(), ax25_uid->uid); 828c2ecf20Sopenharmony_ci break; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci read_unlock(&ax25_uid_lock); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci return res; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci case SIOCAX25ADDUID: 908c2ecf20Sopenharmony_ci { 918c2ecf20Sopenharmony_ci kuid_t sax25_kuid; 928c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 938c2ecf20Sopenharmony_ci return -EPERM; 948c2ecf20Sopenharmony_ci sax25_kuid = make_kuid(current_user_ns(), sax->sax25_uid); 958c2ecf20Sopenharmony_ci if (!uid_valid(sax25_kuid)) 968c2ecf20Sopenharmony_ci return -EINVAL; 978c2ecf20Sopenharmony_ci user = ax25_findbyuid(sax25_kuid); 988c2ecf20Sopenharmony_ci if (user) { 998c2ecf20Sopenharmony_ci ax25_uid_put(user); 1008c2ecf20Sopenharmony_ci return -EEXIST; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci if (sax->sax25_uid == 0) 1038c2ecf20Sopenharmony_ci return -EINVAL; 1048c2ecf20Sopenharmony_ci if ((ax25_uid = kmalloc(sizeof(*ax25_uid), GFP_KERNEL)) == NULL) 1058c2ecf20Sopenharmony_ci return -ENOMEM; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci refcount_set(&ax25_uid->refcount, 1); 1088c2ecf20Sopenharmony_ci ax25_uid->uid = sax25_kuid; 1098c2ecf20Sopenharmony_ci ax25_uid->call = sax->sax25_call; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci write_lock(&ax25_uid_lock); 1128c2ecf20Sopenharmony_ci hlist_add_head(&ax25_uid->uid_node, &ax25_uid_list); 1138c2ecf20Sopenharmony_ci write_unlock(&ax25_uid_lock); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci case SIOCAX25DELUID: 1188c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 1198c2ecf20Sopenharmony_ci return -EPERM; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci ax25_uid = NULL; 1228c2ecf20Sopenharmony_ci write_lock(&ax25_uid_lock); 1238c2ecf20Sopenharmony_ci ax25_uid_for_each(ax25_uid, &ax25_uid_list) { 1248c2ecf20Sopenharmony_ci if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) 1258c2ecf20Sopenharmony_ci break; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci if (ax25_uid == NULL) { 1288c2ecf20Sopenharmony_ci write_unlock(&ax25_uid_lock); 1298c2ecf20Sopenharmony_ci return -ENOENT; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci hlist_del_init(&ax25_uid->uid_node); 1328c2ecf20Sopenharmony_ci ax25_uid_put(ax25_uid); 1338c2ecf20Sopenharmony_ci write_unlock(&ax25_uid_lock); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return 0; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci default: 1388c2ecf20Sopenharmony_ci return -EINVAL; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return -EINVAL; /*NOTREACHED */ 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic void *ax25_uid_seq_start(struct seq_file *seq, loff_t *pos) 1478c2ecf20Sopenharmony_ci __acquires(ax25_uid_lock) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci read_lock(&ax25_uid_lock); 1508c2ecf20Sopenharmony_ci return seq_hlist_start_head(&ax25_uid_list, *pos); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic void *ax25_uid_seq_next(struct seq_file *seq, void *v, loff_t *pos) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci return seq_hlist_next(v, &ax25_uid_list, pos); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic void ax25_uid_seq_stop(struct seq_file *seq, void *v) 1598c2ecf20Sopenharmony_ci __releases(ax25_uid_lock) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci read_unlock(&ax25_uid_lock); 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic int ax25_uid_seq_show(struct seq_file *seq, void *v) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci char buf[11]; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (v == SEQ_START_TOKEN) 1698c2ecf20Sopenharmony_ci seq_printf(seq, "Policy: %d\n", ax25_uid_policy); 1708c2ecf20Sopenharmony_ci else { 1718c2ecf20Sopenharmony_ci struct ax25_uid_assoc *pt; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci pt = hlist_entry(v, struct ax25_uid_assoc, uid_node); 1748c2ecf20Sopenharmony_ci seq_printf(seq, "%6d %s\n", 1758c2ecf20Sopenharmony_ci from_kuid_munged(seq_user_ns(seq), pt->uid), 1768c2ecf20Sopenharmony_ci ax2asc(buf, &pt->call)); 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ciconst struct seq_operations ax25_uid_seqops = { 1828c2ecf20Sopenharmony_ci .start = ax25_uid_seq_start, 1838c2ecf20Sopenharmony_ci .next = ax25_uid_seq_next, 1848c2ecf20Sopenharmony_ci .stop = ax25_uid_seq_stop, 1858c2ecf20Sopenharmony_ci .show = ax25_uid_seq_show, 1868c2ecf20Sopenharmony_ci}; 1878c2ecf20Sopenharmony_ci#endif 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/* 1908c2ecf20Sopenharmony_ci * Free all memory associated with UID/Callsign structures. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_civoid __exit ax25_uid_free(void) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci ax25_uid_assoc *ax25_uid; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci write_lock(&ax25_uid_lock); 1978c2ecf20Sopenharmony_ciagain: 1988c2ecf20Sopenharmony_ci ax25_uid_for_each(ax25_uid, &ax25_uid_list) { 1998c2ecf20Sopenharmony_ci hlist_del_init(&ax25_uid->uid_node); 2008c2ecf20Sopenharmony_ci ax25_uid_put(ax25_uid); 2018c2ecf20Sopenharmony_ci goto again; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci write_unlock(&ax25_uid_lock); 2048c2ecf20Sopenharmony_ci} 205