aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHendrik Brueckner <brueckner@linux.vnet.ibm.com>2009-03-26 10:23:55 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2009-03-26 10:24:08 -0400
commit431429ff788598a19c1a193b9fca3961b7f55916 (patch)
treeba860720c0b2d329cf1587e23f52108e428ad71b
parent82f3a79bc6b50ab82744ebc32efba31c78dbccf7 (diff)
[S390] hvc_iucv: Provide IUCV z/VM user ID filtering
This patch introduces the kernel parameter hvc_iucv_allow= that specifies a comma-separated list of z/VM user IDs. If specified, the z/VM IUCV hypervisor console device driver accepts IUCV connections from listed z/VM user IDs only. Signed-off-by: Hendrik Brueckner <brueckner@linux.vnet.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r--Documentation/kernel-parameters.txt3
-rw-r--r--drivers/char/hvc_iucv.c254
2 files changed, 249 insertions, 8 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 54f21a5c262b..9f932a76a940 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -829,6 +829,9 @@ and is between 256 and 4096 characters. It is defined in the file
829 829
830 hvc_iucv= [S390] Number of z/VM IUCV hypervisor console (HVC) 830 hvc_iucv= [S390] Number of z/VM IUCV hypervisor console (HVC)
831 terminal devices. Valid values: 0..8 831 terminal devices. Valid values: 0..8
832 hvc_iucv_allow= [S390] Comma-separated list of z/VM user IDs.
833 If specified, z/VM IUCV HVC accepts connections
834 from listed z/VM user IDs only.
832 835
833 i8042.debug [HW] Toggle i8042 debug mode 836 i8042.debug [HW] Toggle i8042 debug mode
834 i8042.direct [HW] Put keyboard port into non-translated mode 837 i8042.direct [HW] Put keyboard port into non-translated mode
diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c
index 146be5a60947..54481a887769 100644
--- a/drivers/char/hvc_iucv.c
+++ b/drivers/char/hvc_iucv.c
@@ -13,10 +13,11 @@
13 13
14#include <linux/types.h> 14#include <linux/types.h>
15#include <asm/ebcdic.h> 15#include <asm/ebcdic.h>
16#include <linux/ctype.h>
16#include <linux/delay.h> 17#include <linux/delay.h>
17#include <linux/init.h> 18#include <linux/init.h>
18#include <linux/mempool.h> 19#include <linux/mempool.h>
19#include <linux/module.h> 20#include <linux/moduleparam.h>
20#include <linux/tty.h> 21#include <linux/tty.h>
21#include <linux/wait.h> 22#include <linux/wait.h>
22#include <net/iucv/iucv.h> 23#include <net/iucv/iucv.h>
@@ -95,6 +96,12 @@ static unsigned long hvc_iucv_devices = 1;
95/* Array of allocated hvc iucv tty lines... */ 96/* Array of allocated hvc iucv tty lines... */
96static struct hvc_iucv_private *hvc_iucv_table[MAX_HVC_IUCV_LINES]; 97static struct hvc_iucv_private *hvc_iucv_table[MAX_HVC_IUCV_LINES];
97#define IUCV_HVC_CON_IDX (0) 98#define IUCV_HVC_CON_IDX (0)
99/* List of z/VM user ID filter entries (struct iucv_vmid_filter) */
100#define MAX_VMID_FILTER (500)
101static size_t hvc_iucv_filter_size;
102static void *hvc_iucv_filter;
103static const char *hvc_iucv_filter_string;
104static DEFINE_RWLOCK(hvc_iucv_filter_lock);
98 105
99/* Kmem cache and mempool for iucv_tty_buffer elements */ 106/* Kmem cache and mempool for iucv_tty_buffer elements */
100static struct kmem_cache *hvc_iucv_buffer_cache; 107static struct kmem_cache *hvc_iucv_buffer_cache;
@@ -618,6 +625,27 @@ static void hvc_iucv_notifier_del(struct hvc_struct *hp, int id)
618} 625}
619 626
620/** 627/**
628 * hvc_iucv_filter_connreq() - Filter connection request based on z/VM user ID
629 * @ipvmid: Originating z/VM user ID (right padded with blanks)
630 *
631 * Returns 0 if the z/VM user ID @ipvmid is allowed to connection, otherwise
632 * non-zero.
633 */
634static int hvc_iucv_filter_connreq(u8 ipvmid[8])
635{
636 size_t i;
637
638 /* Note: default policy is ACCEPT if no filter is set */
639 if (!hvc_iucv_filter_size)
640 return 0;
641
642 for (i = 0; i < hvc_iucv_filter_size; i++)
643 if (0 == memcmp(ipvmid, hvc_iucv_filter + (8 * i), 8))
644 return 0;
645 return 1;
646}
647
648/**
621 * hvc_iucv_path_pending() - IUCV handler to process a connection request. 649 * hvc_iucv_path_pending() - IUCV handler to process a connection request.
622 * @path: Pending path (struct iucv_path) 650 * @path: Pending path (struct iucv_path)
623 * @ipvmid: z/VM system identifier of originator 651 * @ipvmid: z/VM system identifier of originator
@@ -641,6 +669,7 @@ static int hvc_iucv_path_pending(struct iucv_path *path,
641{ 669{
642 struct hvc_iucv_private *priv; 670 struct hvc_iucv_private *priv;
643 u8 nuser_data[16]; 671 u8 nuser_data[16];
672 u8 vm_user_id[9];
644 int i, rc; 673 int i, rc;
645 674
646 priv = NULL; 675 priv = NULL;
@@ -653,6 +682,20 @@ static int hvc_iucv_path_pending(struct iucv_path *path,
653 if (!priv) 682 if (!priv)
654 return -ENODEV; 683 return -ENODEV;
655 684
685 /* Enforce that ipvmid is allowed to connect to us */
686 read_lock(&hvc_iucv_filter_lock);
687 rc = hvc_iucv_filter_connreq(ipvmid);
688 read_unlock(&hvc_iucv_filter_lock);
689 if (rc) {
690 iucv_path_sever(path, ipuser);
691 iucv_path_free(path);
692 memcpy(vm_user_id, ipvmid, 8);
693 vm_user_id[8] = 0;
694 pr_info("A connection request from z/VM user ID %s "
695 "was refused\n", vm_user_id);
696 return 0;
697 }
698
656 spin_lock(&priv->lock); 699 spin_lock(&priv->lock);
657 700
658 /* If the terminal is already connected or being severed, then sever 701 /* If the terminal is already connected or being severed, then sever
@@ -877,6 +920,171 @@ static int __init hvc_iucv_alloc(int id, unsigned int is_console)
877} 920}
878 921
879/** 922/**
923 * hvc_iucv_parse_filter() - Parse filter for a single z/VM user ID
924 * @filter: String containing a comma-separated list of z/VM user IDs
925 */
926static const char *hvc_iucv_parse_filter(const char *filter, char *dest)
927{
928 const char *nextdelim, *residual;
929 size_t len;
930
931 nextdelim = strchr(filter, ',');
932 if (nextdelim) {
933 len = nextdelim - filter;
934 residual = nextdelim + 1;
935 } else {
936 len = strlen(filter);
937 residual = filter + len;
938 }
939
940 if (len == 0)
941 return ERR_PTR(-EINVAL);
942
943 /* check for '\n' (if called from sysfs) */
944 if (filter[len - 1] == '\n')
945 len--;
946
947 if (len > 8)
948 return ERR_PTR(-EINVAL);
949
950 /* pad with blanks and save upper case version of user ID */
951 memset(dest, ' ', 8);
952 while (len--)
953 dest[len] = toupper(filter[len]);
954 return residual;
955}
956
957/**
958 * hvc_iucv_setup_filter() - Set up z/VM user ID filter
959 * @filter: String consisting of a comma-separated list of z/VM user IDs
960 *
961 * The function parses the @filter string and creates an array containing
962 * the list of z/VM user ID filter entries.
963 * Return code 0 means success, -EINVAL if the filter is syntactically
964 * incorrect, -ENOMEM if there was not enough memory to allocate the
965 * filter list array, or -ENOSPC if too many z/VM user IDs have been specified.
966 */
967static int hvc_iucv_setup_filter(const char *val)
968{
969 const char *residual;
970 int err;
971 size_t size, count;
972 void *array, *old_filter;
973
974 count = strlen(val);
975 if (count == 0 || (count == 1 && val[0] == '\n')) {
976 size = 0;
977 array = NULL;
978 goto out_replace_filter; /* clear filter */
979 }
980
981 /* count user IDs in order to allocate sufficient memory */
982 size = 1;
983 residual = val;
984 while ((residual = strchr(residual, ',')) != NULL) {
985 residual++;
986 size++;
987 }
988
989 /* check if the specified list exceeds the filter limit */
990 if (size > MAX_VMID_FILTER)
991 return -ENOSPC;
992
993 array = kzalloc(size * 8, GFP_KERNEL);
994 if (!array)
995 return -ENOMEM;
996
997 count = size;
998 residual = val;
999 while (*residual && count) {
1000 residual = hvc_iucv_parse_filter(residual,
1001 array + ((size - count) * 8));
1002 if (IS_ERR(residual)) {
1003 err = PTR_ERR(residual);
1004 kfree(array);
1005 goto out_err;
1006 }
1007 count--;
1008 }
1009
1010out_replace_filter:
1011 write_lock_bh(&hvc_iucv_filter_lock);
1012 old_filter = hvc_iucv_filter;
1013 hvc_iucv_filter_size = size;
1014 hvc_iucv_filter = array;
1015 write_unlock_bh(&hvc_iucv_filter_lock);
1016 kfree(old_filter);
1017
1018 err = 0;
1019out_err:
1020 return err;
1021}
1022
1023/**
1024 * param_set_vmidfilter() - Set z/VM user ID filter parameter
1025 * @val: String consisting of a comma-separated list of z/VM user IDs
1026 * @kp: Kernel parameter pointing to hvc_iucv_filter array
1027 *
1028 * The function sets up the z/VM user ID filter specified as comma-separated
1029 * list of user IDs in @val.
1030 * Note: If it is called early in the boot process, @val is stored and
1031 * parsed later in hvc_iucv_init().
1032 */
1033static int param_set_vmidfilter(const char *val, struct kernel_param *kp)
1034{
1035 int rc;
1036
1037 if (!MACHINE_IS_VM || !hvc_iucv_devices)
1038 return -ENODEV;
1039
1040 if (!val)
1041 return -EINVAL;
1042
1043 rc = 0;
1044 if (slab_is_available())
1045 rc = hvc_iucv_setup_filter(val);
1046 else
1047 hvc_iucv_filter_string = val; /* defer... */
1048 return rc;
1049}
1050
1051/**
1052 * param_get_vmidfilter() - Get z/VM user ID filter
1053 * @buffer: Buffer to store z/VM user ID filter,
1054 * (buffer size assumption PAGE_SIZE)
1055 * @kp: Kernel parameter pointing to the hvc_iucv_filter array
1056 *
1057 * The function stores the filter as a comma-separated list of z/VM user IDs
1058 * in @buffer. Typically, sysfs routines call this function for attr show.
1059 */
1060static int param_get_vmidfilter(char *buffer, struct kernel_param *kp)
1061{
1062 int rc;
1063 size_t index, len;
1064 void *start, *end;
1065
1066 if (!MACHINE_IS_VM || !hvc_iucv_devices)
1067 return -ENODEV;
1068
1069 rc = 0;
1070 read_lock_bh(&hvc_iucv_filter_lock);
1071 for (index = 0; index < hvc_iucv_filter_size; index++) {
1072 start = hvc_iucv_filter + (8 * index);
1073 end = memchr(start, ' ', 8);
1074 len = (end) ? end - start : 8;
1075 memcpy(buffer + rc, start, len);
1076 rc += len;
1077 buffer[rc++] = ',';
1078 }
1079 read_unlock_bh(&hvc_iucv_filter_lock);
1080 if (rc)
1081 buffer[--rc] = '\0'; /* replace last comma and update rc */
1082 return rc;
1083}
1084
1085#define param_check_vmidfilter(name, p) __param_check(name, p, void)
1086
1087/**
880 * hvc_iucv_init() - z/VM IUCV HVC device driver initialization 1088 * hvc_iucv_init() - z/VM IUCV HVC device driver initialization
881 */ 1089 */
882static int __init hvc_iucv_init(void) 1090static int __init hvc_iucv_init(void)
@@ -884,19 +1092,44 @@ static int __init hvc_iucv_init(void)
884 int rc; 1092 int rc;
885 unsigned int i; 1093 unsigned int i;
886 1094
1095 if (!hvc_iucv_devices)
1096 return -ENODEV;
1097
887 if (!MACHINE_IS_VM) { 1098 if (!MACHINE_IS_VM) {
888 pr_notice("The z/VM IUCV HVC device driver cannot " 1099 pr_notice("The z/VM IUCV HVC device driver cannot "
889 "be used without z/VM\n"); 1100 "be used without z/VM\n");
890 return -ENODEV; 1101 rc = -ENODEV;
1102 goto out_error;
891 } 1103 }
892 1104
893 if (!hvc_iucv_devices)
894 return -ENODEV;
895
896 if (hvc_iucv_devices > MAX_HVC_IUCV_LINES) { 1105 if (hvc_iucv_devices > MAX_HVC_IUCV_LINES) {
897 pr_err("%lu is not a valid value for the hvc_iucv= " 1106 pr_err("%lu is not a valid value for the hvc_iucv= "
898 "kernel parameter\n", hvc_iucv_devices); 1107 "kernel parameter\n", hvc_iucv_devices);
899 return -EINVAL; 1108 rc = -EINVAL;
1109 goto out_error;
1110 }
1111
1112 /* parse hvc_iucv_allow string and create z/VM user ID filter list */
1113 if (hvc_iucv_filter_string) {
1114 rc = hvc_iucv_setup_filter(hvc_iucv_filter_string);
1115 switch (rc) {
1116 case 0:
1117 break;
1118 case -ENOMEM:
1119 pr_err("Allocating memory failed with "
1120 "reason code=%d\n", 3);
1121 goto out_error;
1122 case -EINVAL:
1123 pr_err("hvc_iucv_allow= does not specify a valid "
1124 "z/VM user ID list\n");
1125 goto out_error;
1126 case -ENOSPC:
1127 pr_err("hvc_iucv_allow= specifies too many "
1128 "z/VM user IDs\n");
1129 goto out_error;
1130 default:
1131 goto out_error;
1132 }
900 } 1133 }
901 1134
902 hvc_iucv_buffer_cache = kmem_cache_create(KMSG_COMPONENT, 1135 hvc_iucv_buffer_cache = kmem_cache_create(KMSG_COMPONENT,
@@ -904,7 +1137,8 @@ static int __init hvc_iucv_init(void)
904 0, 0, NULL); 1137 0, 0, NULL);
905 if (!hvc_iucv_buffer_cache) { 1138 if (!hvc_iucv_buffer_cache) {
906 pr_err("Allocating memory failed with reason code=%d\n", 1); 1139 pr_err("Allocating memory failed with reason code=%d\n", 1);
907 return -ENOMEM; 1140 rc = -ENOMEM;
1141 goto out_error;
908 } 1142 }
909 1143
910 hvc_iucv_mempool = mempool_create_slab_pool(MEMPOOL_MIN_NR, 1144 hvc_iucv_mempool = mempool_create_slab_pool(MEMPOOL_MIN_NR,
@@ -912,7 +1146,8 @@ static int __init hvc_iucv_init(void)
912 if (!hvc_iucv_mempool) { 1146 if (!hvc_iucv_mempool) {
913 pr_err("Allocating memory failed with reason code=%d\n", 2); 1147 pr_err("Allocating memory failed with reason code=%d\n", 2);
914 kmem_cache_destroy(hvc_iucv_buffer_cache); 1148 kmem_cache_destroy(hvc_iucv_buffer_cache);
915 return -ENOMEM; 1149 rc = -ENOMEM;
1150 goto out_error;
916 } 1151 }
917 1152
918 /* register the first terminal device as console 1153 /* register the first terminal device as console
@@ -956,6 +1191,8 @@ out_error_hvc:
956out_error_memory: 1191out_error_memory:
957 mempool_destroy(hvc_iucv_mempool); 1192 mempool_destroy(hvc_iucv_mempool);
958 kmem_cache_destroy(hvc_iucv_buffer_cache); 1193 kmem_cache_destroy(hvc_iucv_buffer_cache);
1194out_error:
1195 hvc_iucv_devices = 0; /* ensure that we do not provide any device */
959 return rc; 1196 return rc;
960} 1197}
961 1198
@@ -971,3 +1208,4 @@ static int __init hvc_iucv_config(char *val)
971 1208
972device_initcall(hvc_iucv_init); 1209device_initcall(hvc_iucv_init);
973__setup("hvc_iucv=", hvc_iucv_config); 1210__setup("hvc_iucv=", hvc_iucv_config);
1211core_param(hvc_iucv_allow, hvc_iucv_filter, vmidfilter, 0640);