Found the solution here.
What you want to do is write a trusted I/O enabling program in C that allows access to only the desired ports, then uses execvp() to execute your script at the caller's address space. You'll then setuid root to the compiled I/O enabler.
Here's some sample code adapted from the above source (be sure to use an address block you don't mind writing to):
#include <stdio.h>
#include <stdlib.h>
#include <sys/io.h>
#define DESIRED_PORT 0x300
#define NUM_BYTES 8
int main(int argc, char*argv[])
{
if (argc < 2) {
printf("Error: no target program specified.\n");
exit(1);
}
if (ioperm(DESIRED_PORT, NUM_BYTES, 1)) {
printf("Error: couldn't set port permissions.\n");
exit(1);
}
// Set uid to current user's id before executing the script
setgid(getgid());
setuid(getuid());
if (execvp(argv[1], &argv[1]) < 0) {
printf("Error: target program execution error.\n");
exit(1);
}
}
Let's call it io_enable.c, then compile and setuid root:
$ gcc io_enable.c -o io_enable
$ sudo chown root io_enable
$ sudo chmod u+s io_enable
Next, we can test it with the following python script:
#!/usr/bin/python
import portio
ADDR = 0x300
fd = open('/tmp/portio.log', 'w')
for i in range(10):
portio.outb(i, ADDR)
fd.write('Wrote %d, read %d.\n' % (i, portio.inb(ADDR)))
fd.close()
I'm calling it io_test.py and then running it like so:
$ ./io_enable python io_test.py
Looks like it works:
$ cat /tmp/portio.log
Wrote 0, read 0.
Wrote 1, read 1.
Wrote 2, read 2.
Wrote 3, read 3.
Wrote 4, read 4.
Wrote 5, read 5.
Wrote 6, read 6.
Wrote 7, read 7.
Wrote 8, read 8.
Wrote 9, read 9.
Best Answer
It starter to show the appropriate groups only after a system restart. The logout - login wasn't enough.
Don't know what to make of it.