Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 28 Jul 2002 10:25:36 +0100
From:      David Malone <dwmalone@maths.tcd.ie>
To:        audit@freebsd.org
Cc:        yokota@freebsd.org
Subject:   Controling keymap changes
Message-ID:   <200207281025.aa05845@salmon.maths.tcd.ie>

next in thread | raw e-mail | index | archive | help
I've had this patch for controling changes to the console keymap
in my tree at home for a bit. The idea is to give some control over
who can twiddle sensitive bits of the keymap. I provide a sysctl,
hw.kbd.keymap_restrict_change, which acts a bit like secure level:

>= 1: Only root can change restricted keys (like boot, panic, ...)
>= 2: Only root can change restricted keys and regular keys.
      Regular users still can change accents and function keys.
>= 3: Only root can change restricted keys, regular keys and accents.
>= 4: Only root can change any of the keymap.

Unfortunately, the kbd driver clears the keyboard's accent map if
you load a new keymap, which makes the distinction between level 3
and level 4 less useful.

	David.

Index: sys/dev/kbd/kbd.c
===================================================================
RCS file: /cvs/FreeBSD-CVS/src/sys/dev/kbd/kbd.c,v
retrieving revision 1.28
diff -u -r1.28 kbd.c
--- sys/dev/kbd/kbd.c	14 Mar 2002 01:32:22 -0000	1.28
+++ sys/dev/kbd/kbd.c	20 Apr 2002 09:33:47 -0000
@@ -35,6 +35,8 @@
 #include <sys/conf.h>
 #include <sys/tty.h>
 #include <sys/poll.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
 #include <sys/vnode.h>
 #include <sys/uio.h>
 
@@ -70,6 +72,11 @@
 static keyboard_switch_t *kbdsw_ini;
        keyboard_switch_t **kbdsw = &kbdsw_ini;
 
+static int keymap_restrict_change;
+SYSCTL_NODE(_hw, OID_AUTO, kbd, CTLFLAG_RD, 0, "kbd");
+SYSCTL_INT(_hw_kbd, OID_AUTO, keymap_restrict_change, CTLFLAG_RW,
+    &keymap_restrict_change, 0, "restrict ability to change keymap");
+
 #define ARRAY_DELTA	4
 
 static int
@@ -764,6 +771,11 @@
  * functions.
  */
 
+static int key_change_ok(struct keyent_t *, struct keyent_t *, struct thread *);
+static int keymap_change_ok(keymap_t *, keymap_t *, struct thread *);
+static int accent_change_ok(accentmap_t *, accentmap_t *, struct thread *);
+static int fkey_change_ok(fkeytab_t *, fkeyarg_t *, struct thread *);
+
 int
 genkbd_commonioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
 {
@@ -771,6 +783,7 @@
 	fkeyarg_t *fkeyp;
 	int s;
 	int i;
+	int error;
 
 	s = spltty();
 	switch (cmd) {
@@ -800,6 +813,12 @@
 		break;
 	case PIO_KEYMAP:	/* set keyboard translation table */
 #ifndef KBD_DISABLE_KEYMAP_LOAD
+		error = keymap_change_ok(kbd->kb_keymap, (keymap_t* )arg,
+		    curthread);
+		if (error != 0) {
+			splx(s);
+			return error;
+		}
 		bzero(kbd->kb_accentmap, sizeof(*kbd->kb_accentmap));
 		bcopy(arg, kbd->kb_keymap, sizeof(*kbd->kb_keymap));
 		break;
@@ -826,6 +845,12 @@
 			splx(s);
 			return EINVAL;
 		}
+		error = key_change_ok(&kbd->kb_keymap->key[keyp->keynum],
+		    &keyp->key, curthread);
+		if (error != 0) {
+			splx(s);
+			return error;
+		}
 		bcopy(&keyp->key, &kbd->kb_keymap->key[keyp->keynum],
 		      sizeof(keyp->key));
 		break;
@@ -839,6 +864,12 @@
 		break;
 	case PIO_DEADKEYMAP:	/* set accent key translation table */
 #ifndef KBD_DISABLE_KEYMAP_LOAD
+		error = accent_change_ok(kbd->kb_accentmap,
+		    (accentmap_t *)arg, curthread);
+		if (error != 0) {
+			splx(s);
+			return error;
+		}
 		bcopy(arg, kbd->kb_accentmap, sizeof(*kbd->kb_accentmap));
 		break;
 #else
@@ -863,6 +894,12 @@
 			splx(s);
 			return EINVAL;
 		}
+		error = fkey_change_ok(&kbd->kb_fkeytab[fkeyp->keynum],
+		    fkeyp, curthread);
+		if (error != 0) {
+			splx(s);
+			return error;
+		}
 		kbd->kb_fkeytab[fkeyp->keynum].len = imin(fkeyp->flen, MAXFK);
 		bcopy(fkeyp->keydef, kbd->kb_fkeytab[fkeyp->keynum].str,
 		      kbd->kb_fkeytab[fkeyp->keynum].len);
@@ -878,6 +915,106 @@
 	}
 
 	splx(s);
+	return 0;
+}
+
+#define RESTRICTED_KEY(key, i) \
+	((key->spcl & (0x80 >> i)) && \
+		(key->map[i] == RBT || key->map[i] == SUSP || \
+		 key->map[i] == STBY || key->map[i] == DBG || \
+		 key->map[i] == PNC || key->map[i] == HALT || \
+		 key->map[i] == PDWN))
+
+static int
+key_change_ok(struct keyent_t *oldkey, struct keyent_t *newkey, struct thread *td)
+{
+	int i;
+
+	/* Low keymap_restrict_change means any changes are OK. */
+	if (keymap_restrict_change <= 0)
+		return 0;
+
+	/* High keymap_restrict_change means only root can change the keymap. */
+	if (keymap_restrict_change >= 2) {
+		for (i = 0; i < NUM_STATES; i++)
+			if (oldkey->map[i] != newkey->map[i])
+				return suser(td);
+		if (oldkey->spcl != newkey->spcl)
+			return suser(td);
+		if (oldkey->flgs != newkey->flgs)
+			return suser(td);
+		return 0;
+	}
+
+	/* Otherwise we have to see if any special keys are being changed. */
+	for (i = 0; i < NUM_STATES; i++) {
+		/*
+		 * If either the oldkey or the newkey action is restricted
+		 * then we must make sure that the action doesn't change.
+		 */
+		if (!RESTRICTED_KEY(oldkey, i) && !RESTRICTED_KEY(newkey, i))
+			continue;
+		if ((oldkey->spcl & (0x80 >> i)) == (newkey->spcl & (0x80 >> i))
+		    && oldkey->map[i] == newkey->map[i])
+			continue;
+		return suser(td);
+	}
+
+	return 0;
+}
+
+static int
+keymap_change_ok(keymap_t *oldmap, keymap_t *newmap, struct thread *td)
+{
+	int keycode, error;
+
+	for (keycode = 0; keycode < NUM_KEYS; keycode++) {
+		if ((error = key_change_ok(&oldmap->key[keycode],
+		    &newmap->key[keycode], td)) != 0)
+			return error;
+	}
+	return 0;
+}
+
+static int
+accent_change_ok(accentmap_t *oldmap, accentmap_t *newmap, struct thread *td)
+{
+	struct acc_t *oldacc, *newacc;
+	int accent, i;
+
+	if (keymap_restrict_change <= 2)
+		return 0;
+
+	if (oldmap->n_accs != newmap->n_accs)
+		return suser(td);
+
+	for (accent = 0; accent < oldmap->n_accs; accent++) {
+		oldacc = &oldmap->acc[accent];
+		newacc = &newmap->acc[accent];
+		if (oldacc->accchar != newacc->accchar)
+			return suser(td);
+		for (i = 0; i < NUM_ACCENTCHARS; ++i) {
+			if (oldacc->map[i][0] != newacc->map[i][0])
+				return suser(td);
+			if (oldacc->map[i][0] == 0)	/* end of table */
+				break;
+			if (oldacc->map[i][1] != newacc->map[i][1])
+				return suser(td);
+		}
+	}
+
+	return 0;
+}
+
+static int fkey_change_ok(fkeytab_t *oldkey, fkeyarg_t *newkey, struct thread *td)
+{
+	if (keymap_restrict_change <= 3)
+		return 0;
+
+	if (oldkey->len != newkey->flen ||
+	    bcmp(oldkey->str, newkey->keydef, oldkey->len != 0))
+		return suser(td);
+
 	return 0;
 }
 

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-audit" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi? <200207281025.aa05845>