radmin - infinite loop

Brian Candler B.Candler at pobox.com
Mon Jul 4 17:54:45 CEST 2011


This is with freeradius 2.1.10

The server is running as root (yes I know, but I can't change that easily
right now).  radmin works fine with:

listen {
        type = control
        socket = ${run_dir}/${name}.sock
        uid = root
        mode = rw
}

Now, in order to step towards interfacing to a web app, I changed this to
uid = www-data

Of course, I can't yet connect from www-data because the server is running
as root, and the socket is owned by 0/0 mode 550.

But the strange thing is, if I run radmin as root, it no longer works; it
doesn't even display the startup banner.  strace shows that it's going into
an infinite loop:

...
set_tid_address(0x7f2fd45419d0)         = 7688
set_robust_list(0x7f2fd45419e0, 0x18)   = 0
futex(0x7fff3b496cdc, FUTEX_WAKE_PRIVATE, 1) = 0
futex(0x7fff3b496cdc, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 1, NULL, 7f2fd4541700) = -1 EAGAIN (Resource temporarily unavailable)
rt_sigaction(SIGRTMIN, {0x7f2fd3abd870, [], SA_RESTORER|SA_SIGINFO, 0x7f2fd3ac78f0}, NULL, 8) = 0
rt_sigaction(SIGRT_1, {0x7f2fd3abd900, [], SA_RESTORER|SA_RESTART|SA_SIGINFO, 0x7f2fd3ac78f0}, NULL, 8) = 0
rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
getrlimit(RLIMIT_STACK, {rlim_cur=8192*1024, rlim_max=RLIM_INFINITY}) = 0
socket(PF_FILE, SOCK_STREAM, 0)         = 3
connect(3, {sa_family=AF_FILE, path="/var/run/freeradius/freeradius.sock"}, 37) = 0
read(3, "", 8)                          = 0
read(3, "", 8)                          = 0
read(3, "", 8)                          = 0
read(3, "", 8)                          = 0
read(3, "", 8)                          = 0
read(3, "", 8)                          = 0
read(3, "", 8)                          = 0
read(3, "", 8)                          = 0
read(3, "", 8)                          = 0
... etc forever

I presume it's stuck here:

        /*
         *      Read initial magic && version information.
         */
        for (size = 0; size < 8; size += len) {
                len = read(sockfd, buffer + size, 8 - size);
                if (len < 0) {
                        fprintf(stderr, "%s: Error reading initial data from socket: %s\n",
                                progname, strerror(errno));
                        exit(1);
                }
        }

And this I imagine is because of the uid test failing in the server, and the
connection being dropped.  Normally read() returning 0 would indicate an
end-of-file condition, but for some reason that I don't understand, the
socket is being set into non-blocking mode, so it's not as simple as that.

However, I thought that if no data was available you'd get -1 and EAGAIN or
EWOULDBLOCK?  But as the code is currently written, it aborts on any
condition where len < 0.

I'm no expert in this area, but I suggest something like this (untested):

        for (size = 0; size < 8; ) {
                len = read(sockfd, buffer + size, 8 - size);
		if (len < 0) {
			if (errno == EAGAIN || errno == EWOULDBLOCK)
				continue;
                        fprintf(stderr, "%s: Error reading initial data from socket: %s\n",
                                progname, strerror(errno));
                        exit(1);
                }
		if (len == 0) {
			fprintf(stderr, "%s: Connection dropped by server\n",
				progname);
			exit(1);
		}
		size += len;
        }

A couple of other suggestions.

(1) When checking the uid of the socket peer, if you find it's 0 (root) you
may as well let them in anyway.

(2) When starting the control server, if the 'uid' or 'gid' values are set,
it could try chown'ing or chgrp'ing the socket to that value.

Thanks,

Brian.



More information about the Freeradius-Devel mailing list