View Issue Details

IDProjectCategoryView StatusLast Update
0001514unrealdocumentationpublic2004-02-11 03:53
Reporterthilo Assigned Tocodemastr 
PrioritynormalSeverityfeatureReproducibilityN/A
Status resolvedResolutionfixed 
Summary0001514: Chrooting should be better documented
DescriptionCHROOTing the ircd is a really very nice feature of unreal. Firstly, it builds up a barrier against hackers, secondly one can use ports below 1024, like 194 for the officially IANA assigned IRC port and 994 for IANA assigned irc-ssl port.
Actually, there is not much work to be done once the ircd was started up... the only problem there is with chroot is when trying to run the ircd with SSL support. SSL Connections are not possible without a little trick:

create the directory dev/ in the main ircd directory and hardlink /dev/urandom and /dev/tty into there. The directory must be accessible for the ircd user of course.

Took me a few strace sessions to find out, maybe you can spare less experienced users this bug hunt.
TagsNo tags attached.
3rd party modules

Activities

codemastr

2004-01-30 23:37

reporter   ~0004803

Hmm, I'll look into that, but another thing.

What version of Unreal is this? I ask because we've made some changes since b19 (related to remote includes and the module system) that could potentially affect the chroot stuff, so I was wondering if that is working for you or not...

thilo

2004-01-31 00:19

reporter   ~0004804

sorry, can't confirm anything, I am using the plain beta19 version.

codemastr

2004-01-31 01:12

reporter   ~0004805

Ok, well then I guess we'll just have to hope it works for now ;)

codemastr

2004-01-31 01:45

reporter   ~0004806

I'm thinking maybe I'll make it auto do this (would you mind testing it if I get around to it?)

Basically there is a function called mknod() [which does the same thing as the mknod command] which should allow me to auto create these files. I'd just need someone to test it out when I finish it.

I think this would be a better solution than having to force the user to hard link...

syzop

2004-01-31 05:32

administrator   ~0004807

thilo: @"secondly one can use ports below 1024": I hope you don't mean running the whole thing as root... Running the whole ircd as root is a very bad idea. Even if it's chrooted, in fact running a chrooted daemon as root does not help much at all... every security person will tell you that ;).

codemastr: problem is mknod() can only be done by root [otherwise you could simply create a few devices with write permissions and hack the box] :p
And hardlinks will (obviously) in many cases fail due to crossing of filesystems, and symbolic links to somewhere outside the chroot environment won't work... so you would still end up with root having to create the entry unless you are lucky ;). fun.

Still, thanks for letting us know this SSL+chroot-specific problem! :)

Cnils

2004-01-31 11:30

reporter   ~0004809

Best solution may be to start unreal from a suid helper app. The helper should only make the apropriate /dev entries and libraries needed. Then start unreal chrooted as the correct user.

thilo

2004-01-31 12:12

reporter   ~0004810

Last edited: 2004-01-31 12:21

come on syzop .. you should know me better than that now :P
OF COURSE! I did not overlook the SETUID defines in config.h ....

generally, you need root access to be able to execute the chroot() call anyways.

Cnils: libraries are preloaded before the chroot call is actually issued, thus, all libraries are loaded without the necessity of the libraries being present in the chroot environment.

I do not know the OpenSSL library. But what struck me as possibility to avoid the problem, is open()ing the files _before_ the chroot() call is issued .. that is not as easy of course if the open() call is within the OpenSSL library itself.

In the case of chrooting, the ircd must be started as root anyways, and it only later on changes its effective user id. Why would we need any helper app at all? You could just check whether SSL is included, then check for the presence of these devices, mknod() them if necessary, and be done with.

edited on: 01-31-04 12:21

syzop

2004-01-31 14:54

administrator   ~0004811

Heh, we even had "exploits" for people running suid with CMDLINE_CONFIG enabled... so I'm not surprised of anything anymore ;).

Oh mknod() at _runtime_ hmm, right... just stat() the original entry and mknod() the new one... that would be good indeed... Btw, are you sure you need /dev/tty? seems unlogical :P. Ah well, that's for code to find out ;). I'll have fun with less interresting bugs...

thilo

2004-01-31 15:05

reporter   ~0004812

I am pretty sure. Both turned up in the strace output, and both were needed in order for SSL to work. I tested it.

syzop

2004-01-31 19:30

administrator   ~0004816

oh btw, something I also noticed is that it tries to read /etc/resolv.conf after being chrooted, thus DNS might not be working:
[20:22:13] -maintest.test.net- Nameserver list has 1 server(s):
[20:22:13] -maintest.test.net- 0. 0.0.0.0
[..]
the fun is, 0.0.0.0 works just like 127.0.0.1 (loopback), so if you have a local nameserver running it will work... but it's still a bug ;).

thilo : I can confirm that /dev/urandom is (obviously) required, but /dev/tty is not... if you remove the dev/tty thing in the chroot, does it refuse to run then or do you experience problems?

codemastr

2004-01-31 19:48

reporter   ~0004817

src/ircd.c:1492: if ((fd = open("/dev/null", O_WRONLY)) < 0)
src/random.c:204: fd = open("/dev/urandom", O_RDONLY);
src/s_bsd.c:635: if ((fd = open("/dev/tty", O_RDWR)) >= 0)

thilo

2004-01-31 20:03

reporter   ~0004818

i thought, the dns server was set in the configuration file? (although i don't like it ...)

on tty: nope, i see now it's not required for SSL to work, nevertheless turned up in the strace output. codemaster showed you the reason why.

thilo@Thilo $ strings /usr/lib/libcrypto.so | grep dev
ENGINE_load_cryptodev
/dev/ubskey
/dev/urandom
/dev/random
/dev/srandom
/dev/egd-pool
secure device signature
/dev/tty

syzop

2004-01-31 20:04

administrator   ~0004819

src/ircd.c:1492: if ((fd = open("/dev/null", O_WRONLY)) < 0)
seems only to be (somtimes) used if compiled in debugmode, but I would still recommend to have a /dev/null device yes... I won't be surprised if any other lib depends on it, and it's a good idea anyway ;).

src/random.c:204: fd = open("/dev/urandom", O_RDONLY);
right.. highly recommended to have. And in in case of SSL required to make it work :).

src/s_bsd.c:635: if ((fd = open("/dev/tty", O_RDWR)) >= 0)
hmm
#ifdef TIOCNOTTY
    if ((fd = open("/dev/tty", O_RDWR)) >= 0)
    {
        (void)ioctl(fd, TIOCNOTTY, (char *)NULL);
        (void)close(fd);
    }
#endif
hmmm..
"TIOCNOTTY
Detach the current process from its controlling terminal.

If the process is the session leader, then SIGHUP and SIGCONT signals are sent to the foreground process group and all processes in the current session lose their controlling tty. "

not sure if that is very useful, since we already fork() and stuff? ;)
Perhaps this was for src/ircd -F& or something weird...

I don't know.. all I know is that it worked perfectly without a /dev/tty entry here, but if you think it's required for some OS's... :p.

It's just that one should always try to use a minimum amount of /dev entries (the more you add, the more you help a hacker) ;)

thilo

2004-01-31 20:22

reporter   ~0004820

#####
It's just that one should always try to use a minimum amount of /dev entries (the more you add, the more you help a hacker) ;)
#####

Yes, that's right of course.

codemastr

2004-01-31 20:32

reporter   ~0004821

Are you sure /etc/resolv.conf screws things up? From what I see, ircd_res_init() is called before chroot(). So that means it should work.

Also, about all those /dev entries in ssl, that shouldn't be a problem. the /dev/urandom, /dev/srandom, /dev/random are an "or" thing. Meaning it tries to find one of those three. The /dev/egd-pool shouldn't be a problem since Unreal already allows you to specify the path to an egd socket, so you can (and probably should) run your own. And /dev/ubskey, I have no idea what that is :)

codemastr

2004-01-31 21:06

reporter   ~0004822

Ok can you try this, apply http://www.codemastr.com/chroot.diff then recompile, then remove the dev dir you created in the Unreal dir, then start Unreal. It should create the dev dir and the necessary devices.

thilo

2004-01-31 21:55

reporter   ~0004823

small uh-oh:

1. there is no EEXISTS ...
it's called "EEXIST" ...

2. Permissions!

Kickchat:/home/ircd/ircd-testing# ls -l dev/
total 0
-rw------- 1 root root 0 Jan 31 21:53 null
-rw------- 1 root root 0 Jan 31 21:53 tty
-r-------- 1 root root 0 Jan 31 21:53 urandom

thilo

2004-01-31 22:04

reporter   ~0004824

Last edited: 2004-01-31 22:06

Ah .. forgot.

#####
Are you sure /etc/resolv.conf screws things up
#####

like I said, I thought the nameserver was set in the unrealircd.conf rather than resolv.conf?

#####
And /dev/ubskey, I have no idea what that is :)
#####

Maybe some usb memory stick with a key on it? =)

I guess mode_t should be:
S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH for /dev/null and /dev/tty and
S_IRUSR | S_IRGRP | S_IROTH for /dev/random ....

and finally, yet another issue:
The ircd.pid file is created before the chroot takes place, resulting in the ircd.pid to be unreadable in my setup by the default ircd user. (i prefer more restrictive umasks ...)

edited on: 01-31-04 22:06

syzop

2004-01-31 22:20

administrator   ~0004826

like I said, I thought the nameserver was set in the unrealircd.conf rather than resolv.conf?

Trust me... I'm a unreal coder you know ;p.

thilo

2004-01-31 22:52

reporter   ~0004827

yeah, i see it in the source code, but then what is the purpose of the
set::dns::nameserver directive?

syzop

2004-01-31 23:17

administrator   ~0004828

the purpose of it is to get annoying questions

thilo

2004-02-01 00:10

reporter   ~0004829

Last edited: 2004-02-01 00:11

I apologize annoying the great ircd coders for asking such stupid questions... especially since the changelog claims to not anymore use the resolv.conf as reference while prior posts suggested the opposite ..

edited on: 02-01-04 00:11

syzop

2004-02-01 00:18

administrator   ~0004830

no problem!

codemastr

2004-02-01 05:32

reporter   ~0004836

Ok I made some changes, I corrected the EEXISTS thing (I must be blind cause I could swear that's what the man page said!) and I changed the permissions, stupid me, I hardcoded those permissions... now it will just use whatever permissions the original file had. So if /dev/null was 777, then dev/null will be 777, that seems like the best way to go.

Would you be able to apply the new patch (same URL) and make sure it works now? Again you'll have to remove the dev/ directory so that Unreal will recreate the files (this time with the correct permissions).

thilo

2004-02-01 11:33

reporter   ~0004839

The dev-creating part works now.

The mknod / stat etc functions though do not return errno codes ... they only return 0 or -1 ... you must look in the errno variable for the error code.

codemastr

2004-02-01 18:36

reporter   ~0004845

Last edited: 2004-02-01 18:45

Ok can you reinstall the patch yet again, this time it should work perfectly (at least I hope) :P It stinks not being able to test it myself...

*** Edit:
I also made this patch correct the problem with permissions on ircd.pid due to setu/gid. It should use the correct user/group now. Could you try deleting ircd.pid first though (since the ircd won't be able to overwrite the file since it is owned by root) then start Unreal. It should use the correct stuff now.

edited on: 02-01-04 18:45

thilo

2004-02-03 18:12

reporter   ~0004857

ircd@Kickchat:~/Unreal$ patch -p0 < chroot.diff
patching file src/ircd.c
Hunk #1 succeeded at 840 (offset -8 lines).
Hunk #2 succeeded at 1249 (offset -19 lines).
Hunk #3 FAILED at 1281.
1 out of 3 hunks FAILED -- saving rejects to file src/ircd.c.rej
ircd@Kickchat:~/Unreal$ cat src/ircd.c.rej
***************
*** 1244,1249 ****
                }
        }
  #endif
        Debug((DEBUG_NOTICE, "Server ready..."));
        SetupEvents();
  #ifdef THROTTLING
--- 1281,1287 ----
                }
        }
  #endif
+ write_pidfile();
        Debug((DEBUG_NOTICE, "Server ready..."));
        SetupEvents();
  #ifdef THROTTLING
ircd@Kickchat:~/Unreal$


That was the result when trying to patch both with the original ircd.c file from beta19 and with the latest CVS version.

codemastr

2004-02-03 19:11

reporter   ~0004859

Strange that the current CVS would give you that error seeing as how the patch was made against the current CVS. In any case, I've committed the patch to the CVS server so if you just cvs update it should install that fine. However I believe I've identified yet another setuid problem in the current CVS (doesn't affect b19) which I'll have to look at.

thilo

2004-02-03 22:22

reporter   ~0004863

Last edited: 2004-02-03 22:29

well, the device making and all works correctly now. but the pid file is not being created at startup.

the write_pidfile function now only appears in some define for that weird OS ULTRIX in s_conf.c and there even is only executed when the ircd receives a SIGHUP.

Oh .. and to make the splitting of hair perfect:
Too restrictive umasks will probably also hinder the creation of files with correct permissions.

Kickchat:/home/ircd/Unreal# umask
0027

so the original chmod of the device files is:
Kickchat:/home/ircd/Unreal# ls -l /dev/null /dev/tty /dev/urandom
crw-rw-rw- 1 root root 1, 3 Jul 21 2002 /dev/null
crw-rw-rw- 2 root tty 5, 0 Jan 31 19:39 /dev/tty
cr--r--r-- 2 root root 1, 9 Feb 3 06:56 /dev/urandom
Kickchat:/home/ircd/Unreal# ls -l dev
total 0
crw-r----- 1 root ircd 1, 3 Feb 3 22:20 null
crw-r----- 1 root ircd 5, 0 Feb 3 22:20 tty
cr--r----- 1 root ircd 1, 9 Feb 3 22:20 urandom

and that's what it turned out to be. If I hadn't set the sticky gid bit for the parent directories, the group of these files would probably also have been root, thus unusable again for the ircd.

If you wish I can sit myself behind the source code, code it, test it and send you a diff if I'm done. It's probably only a simple chmod() to take care of it.

edited on: 02-03-04 22:29

syzop

2004-02-03 22:45

administrator   ~0004864

Fixed resolver issue (always being localhost) in .2072

codemastr

2004-02-04 02:10

reporter   ~0004865

If you'd like to work on it be my guest, but as for write_pidfile I have no idea what you are talking about there. Yeah it is in an #ifdef in s_conf.c, however it's also used in src/ircd.c which is what I changed (I moved it to be after the setuid code). So you totally lost me with that whole thing... In current CVS, src/ircd.c line 1303 is a call to write_pidfile(). Maybe something got screwed up with the patching?

As for the umask stuff... does that actually result in a problem? I mean does stuff fail if only the owner bits are set? (though I would have thought since mknod() has a param to specify the mode_t that it would have overwritten this). I would assume for this purpose the group/other flags are irrelevant. But of course, I suppose we could always work around this too. Before creating the devices we do:
mode_t old = umask(0777);
create the devices
umask(old);

Or something along those lines... It's fun how much it is to program when people make things secure :)

Also I noticed another problem (only affects CVS) that I have to deal with. The new tmp/ directory. Files created there at startup in a setuid environment will be root:root. However after the setuid we can no longer overwrite them since we're not root anymore. I think the solution to this is to at startup whenever we create a file in tmp/, we then call chown() to use the specified uid/gid. That should work since if we are root, then we can read the files even if they are a different uid/gid, then once we switch to that uid/gid everything will be fine.

thilo

2004-02-04 13:05

reporter   ~0004867

yes you're right. I haven't downloaded the CVS version from the developer branch, but got to some other branch where the changes were not incorporated yet? anyways. I have made a diff against the ircd.c from the Unreal3.2-beta19 package.

It's available at
http://thilo.kickchat.com/chroot.diff

i have tested it and it works now.

To the thing with the tmp files: maybe you could just set the effective user id to another user than root, and change it back when done? that would be another possibility too, and spare you doing any chowns.

syzop

2004-02-04 21:13

administrator   ~0004868

I'll have a look at the chown() stuff :).

thilo: no we cannot do that since we might need to bind to a <1024 port which is not allowed if the effective userid is not 0.. and since with for example remote includes we need to create the files BEFORE we might have all listen lines it's not an option to seteuid() later or something... There's of course setfsuid() but that's Linux specific... so let's just stick with chown() [on a sidenote I usually don't comment technical user suggestions but it might be informative to codemastr, although I doubt he would have overlooked this]

syzop

2004-02-04 23:17

administrator   ~0004876

file permissions stuff should be fixed in current CVS [.2074]. On a sidenote, curl (or rather ares) has a problem too... it doesn't read the nameserver info on init, no... it rather thinks it would be wise to reread it on every download... yeah right. So it will always fail, unless you create a etc/resolv.conf inside your chroot.

codemastr

2004-02-11 03:53

reporter   ~0004968

I added your umask stuff in .2090. To our knowledge there only exists one problem with regards to chroot. The problem results from using libcurl for remote includes. The library will attempt to parse /etc/resolv.conf and fail since it is after a chroot. The library author's have been notified of this however and a solution is planned for the next version of libcurl.

Issue History

Date Modified Username Field Change
2004-01-30 22:57 thilo New Issue
2004-01-30 23:37 codemastr Note Added: 0004803
2004-01-31 00:19 thilo Note Added: 0004804
2004-01-31 01:12 codemastr Note Added: 0004805
2004-01-31 01:45 codemastr Note Added: 0004806
2004-01-31 05:32 syzop Note Added: 0004807
2004-01-31 11:30 Cnils Note Added: 0004809
2004-01-31 12:12 thilo Note Added: 0004810
2004-01-31 12:21 thilo Note Edited: 0004810
2004-01-31 14:54 syzop Note Added: 0004811
2004-01-31 15:05 thilo Note Added: 0004812
2004-01-31 19:30 syzop Note Added: 0004816
2004-01-31 19:48 codemastr Note Added: 0004817
2004-01-31 20:03 thilo Note Added: 0004818
2004-01-31 20:04 syzop Note Added: 0004819
2004-01-31 20:22 thilo Note Added: 0004820
2004-01-31 20:32 codemastr Note Added: 0004821
2004-01-31 21:06 codemastr Note Added: 0004822
2004-01-31 21:55 thilo Note Added: 0004823
2004-01-31 22:04 thilo Note Added: 0004824
2004-01-31 22:06 thilo Note Edited: 0004824
2004-01-31 22:20 syzop Note Added: 0004826
2004-01-31 22:52 thilo Note Added: 0004827
2004-01-31 23:17 syzop Note Added: 0004828
2004-02-01 00:10 thilo Note Added: 0004829
2004-02-01 00:11 thilo Note Edited: 0004829
2004-02-01 00:18 syzop Note Added: 0004830
2004-02-01 05:32 codemastr Note Added: 0004836
2004-02-01 11:33 thilo Note Added: 0004839
2004-02-01 18:36 codemastr Note Added: 0004845
2004-02-01 18:45 codemastr Note Edited: 0004845
2004-02-03 18:12 thilo Note Added: 0004857
2004-02-03 19:11 codemastr Note Added: 0004859
2004-02-03 22:22 thilo Note Added: 0004863
2004-02-03 22:26 thilo Note Edited: 0004863
2004-02-03 22:29 thilo Note Edited: 0004863
2004-02-03 22:45 syzop Note Added: 0004864
2004-02-04 02:10 codemastr Note Added: 0004865
2004-02-04 13:05 thilo Note Added: 0004867
2004-02-04 21:13 syzop Note Added: 0004868
2004-02-04 23:17 syzop Note Added: 0004876
2004-02-11 03:53 codemastr Status new => resolved
2004-02-11 03:53 codemastr Resolution open => fixed
2004-02-11 03:53 codemastr Assigned To => codemastr
2004-02-11 03:53 codemastr Note Added: 0004968