Replacement in 3.x kernel for ioctl?


Recommended Posts

So I'm attempting to get pgrdm (program guard) working on arch linux with a 3.6 kernel...

Had to google loads of errors and work out solutions (mostly adding stdio.h etc. includes) but now I'm against a problem I don't know the answer to...

So the whole daemon, kernel module and GUI compiles fine if I remove a line from the kernel module, but if I leave the line in, the kernel module refuses to compile. If I compile and run without this line the output I get is;

Conf file settings: LOG_ALL=1, QUERY_MODE=1, QUERY_TEST_MODE=0, BE_A_DAEMON=0, CHECK_ALL=0, LOG_STD_OUT=1.

SignalThread: MarkActive was successful

SignalThread: PID FROM IsActive IS 0

Signal thread started pid=16373

Opening firewall module.

Entering Debug Mode.

Error requesting the firewall to print debug messages; errno=25.

/opt/pgrd/pgmgrdgui: Unable to connect to client cmd socket; errno=-2.

Which I believe is because of this 'ioctl' thing which appears to allow communication with kernel modules like it's a normal named file or something.

So here is the code causing the problems;

/*
** init_firewall - Module entry routine.
*/
int init_firewall()
{
int rc;

   myprintk(KERN_ALERT "entering module_init\n");
   Firewall.FileOps.ioctl = FirewallIoctl;
   Firewall.FileOps.open = FirewallOpen;
   Firewall.FileOps.read = FirewallRead;
   Firewall.FileOps.release = FirewallClose;
   rc = FirewallInit();
   if(rc == 0)

/tmp/pgrd-7.0/pgrdm/firewall.c: In function ?init_firewall?:
/tmp/pgrd-7.0/pgrdm/firewall.c:1009:20: error: ?struct file_operations? has no member named ?ioctl?

Any ideas on how to solve this error and get the program working?

Link to comment
Share on other sites

According to this page, the file_operations structure is sourced in linux/fs.h. The reference structure on the aforementioned page (from Linux 2.4.2) indicates that your code should be correct. However, the same structure from Linux 3.7 RC2 looks somewhat different.


/* These macros are for out of kernel modules to test that
* the kernel supports the unlocked_ioctl and compat_ioctl
* fields in struct file_operations. */
#define HAVE_COMPAT_IOCTL 1
#define HAVE_UNLOCKED_IOCTL 1

struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
};
[/CODE]

Notice in particular that there is no longer a member named [i]ioctl[/i], but rather [i]unlocked_ioctl[/i] and [i]compat_ioctl[/i]. Also notice that the [i]ioctl[/i] function pointer prototypes are different.

[CODE]
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
[/CODE]

versus

[CODE]
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
[/CODE]

Theoretically, all you need to do is modify the existing FirewallIoctl function to conform to the new prototype and assign it to compat_ioctl.

[CODE]
/*
** init_firewall - Module entry routine.
*/
int init_firewall()
{
int rc;

myprintk(KERN_ALERT "entering module_init\n");
Firewall.FileOps.compat_ioctl = FirewallIoctlCompat;
Firewall.FileOps.open = FirewallOpen;
Firewall.FileOps.read = FirewallRead;
Firewall.FileOps.release = FirewallClose;
rc = FirewallInit();
if(rc == 0)
[/CODE]

[b]PS:[/b] If you're going to be writing kernel-level code, it might be a good idea to keep a local copy of the Linux git repository. That way you can see not only the code and comments for any version of Linux, but also the commit messages that go along with them.

Link to comment
Share on other sites

And changed! It does now compile but ermm...

Conf file settings: LOG_ALL=1, QUERY_MODE=1, QUERY_TEST_MODE=0, BE_A_DAEMON=0, CHECK_ALL=0, LOG_STD_OUT=1.

SignalThread: MarkActive was successful

SignalThread: PID FROM IsActive IS 0

Signal thread started pid=1405

Opening firewall module.

Entering Debug Mode.

Error requesting the firewall to print debug messages; errno=25.

/opt/pgrd/pgmgrdgui: Unable to connect to client cmd socket; errno=-2

Think this old code is broken somewhere :/

Link to comment
Share on other sites

I'm not sure exactly how far back the introduction of compat_ioctl goes, but it is present in every release of Linux since they moved to git for their SCM (Linux 2.6.12 RC2). Considering that the older version of the structure I found was from a 2.4 series release, I'm guessing that compat_ioctl was introduced in Linux 2.6. If you're trying to compile a kernel module written for Linux 2.4 on Linux 3.6, its quite likely that other things have changed as well. For example, the return value of compat_ioctl is long while the return value of ioctl is int. You're probably going to need to audit the code for compatibility problems such as that. (KGDB is likely to be very helpful as well.) May the force be with you.

Edit: Ah. I see from the README that the version you uploaded was specifically designed to compile against a 2.6 series kernel and is incompatible with 2.4. Based on that information, compat_ioctl must have been introduced somewhere between Linux 2.6.0 and Linux 2.6.12 RC2. You could download a copy of Fedora Core 3 and try it out if you're that interested!

Edit 2: Based on the description of Program Guard in its README, you could probably accomplish the same thing using a properly targeted SELinux security policy and iptables.

Link to comment
Share on other sites

Haha nope, no point having it run just on an old kernel :p.

All the decent security software for *nix only runs in old kernels, dig_sig, this, tuxguardian, etc. :/

Link to comment
Share on other sites

Haha nope, no point having it run just on an old kernel :p.

All the decent security software for *nix only runs in old kernels, dig_sig, this, tuxguardian, etc. :/

That's not true at all! Especially considering its widespread use in industry and government, security experts have put a lot of time into securing Linux. Some of the old security tools, such as the ones you mentioned, are no longer supported, but there is a reason for that: they were replaced by some something better. If you're interested in hardening your installation, I recommend that you read the RHEL 6 Security Guide. It is bound to have some practical application no matter which distro you're using. (This advice is coming from a heavy Debian user, not a RHEL/CentOS/Fedora user.)

Link to comment
Share on other sites

Can't replace it with SElinux and iptables, leopard flower does that but it does it on a per-applcation basis, with no support for per-port and will including all child applications in the parent application, e.g. firefox access controls plugin_container and flash plugin.

SElinux can't do what dig_sig does, the closest to it is IMA and I've never been able to get it working at all, other than that, there's no run-only-signed-programs system for current linux kernels :(

Link to comment
Share on other sites

Can't replace it with SElinux and iptables, leopard flower does that but it does it on a per-applcation basis, with no support for per-port and will including all child applications in the parent application, e.g. firefox access controls plugin_container and flash plugin.

SElinux can't do what dig_sig does, the closest to it is IMA and I've never been able to get it working at all, other than that, there's no run-only-signed-programs system for current linux kernels :(

SELinux lets you limit permissions per-application, and iptables lets you limit access per-route and per-port. You also have standard Linux permissions in the mix. SELinux is merely meant to complement them, not replace them. If you target your SELinux security policy properly, you can make it limit each child process individually as well. However, this could have some unintended consequences, I think.

If you really want to run some of the older security programs, I say go for it. No one is stopping you. One of the greatest things about open-source software is that you can download the source code and modify it to fit your needs. Maybe you will pickup new techniques, discover why the software was abandoned in the first place, or create the next amazing security program for Linux. That's how you become a better developer: just do it. I have learned a lot that way; I'm sure you can too.

Link to comment
Share on other sites

This topic is now closed to further replies.