This document explains how these blocks are implemented on Grex. Most of the discussion is in terms of the telnet program, but the same applies to ftp, IRC and other services.
#!/bin/sh
REAL_CLIENTS=/usr/local/grex-scripts/.inet_real
MYNAME=`basename $0`
if [ -r /usr/local/lib/internet.test ]; then
echo " "
else
/usr/local/grex-scripts/no_internet
fi
exec ${REAL_CLIENTS}/${MYNAME} $*
The ftp and irc wrappers scripts are similar.
Sometimes people find these and say "Aha! I've found a way to bypass the
blocks. The real telnet program is hidden in the .inet_real
directory.
If I run that instead of the script I'll be able to telnet out."
No such luck. The script is only there to print a nice message explaining to people why the telnet program won't work for them (and to suggest to them that they might want to become members). The blocks are lower down.
This isn't a terribly smart thing to try, so it's no wonder that a lot of the people who try it, do it badly. Some download binaries from incompatible systems (clue: only binaries for a SPARC system running SunOS 4.1 have a ghost of a chance of running on Grex). Others download source code and try to compile it (clue: yes, you are allowed to compile programs on Grex, but be aware that Grex is so heavily loaded that large compiles will slow down the system noticably and take a very long time. Grex's staff is almost sure to notice, so be prepared for some attention. If you want to compile something large here, it is always best to talk to the staff first.)
Those people who successfully download or compile their own telnet clients on Grex quickly discover that their's don't work any one wit better than the one in the .inet_real directory. The blocks are not in the telnet program. In fact, the telnet, ftp and irc clients on Grex are among the few programs that we haven't made any changes to. Please don't waste our bandwidth moving other telnet programs here. They really, truely, honestly won't work. The blocks are lower down.
And by the way, the same goes for eggdrop. We don't allow anyone to use eggdrop, so we don't have any version installed here, but if you do succeed in downloading and compiling the thing here without bringing annoyed staff members down on your head, you'll find that it won't work for non-members for exactly the same reasons that telnet and ftp don't work for non-members: the kernel blocks.
If you are used to older microcomputers, you may think a program could just access the serial ports directly, and bypass the kernel. But mainframe computers (and these days, most microcomputers) can run in either supervisor mode or in user mode. The machine code instructions you need to directly access devices only work in supervisor mode and only the kernel runs in supervisor mode. (Even if you are "root", all your programs run in user mode.) Because of this, only the kernel can directly access the hardware devices, and it is not possible to bypass the kernel. This has been the basic concept behind all real security on all real computers for the last thirty years, and it works very, very well.
There are several layers to the implementation of networking in BSD kernels. We are specifically interested in the layers implementing the basic Internet UDP and TCP protocols. UDP is a very simple protocol for sending individual packets (which may or may not arrive) over the net. It is most commonly used for communicating with domain name servers and establishing talk connections. TCP is a much more complex and much more widely used protocol that establishes connections between pairs of machines, over which streams of information may be reliably sent.
The kernel routines handling TCP and UDP communications activity are organized by the kinds of events they respond to:
It is around the in_pcbconnect() routine that we place our wrappers. The wrappers cause in_pcbconnect() to appear to fail if the particular connection being requested is not permitted by Grex policy.
Sun doesn't distribute source code for their kernel, but they do distribute a kernel kit, consisting of hundreds of already compiled ".o" object files, which can be linked together to form a runable kernel. So we were able to make our changes by making some small edits to one of the object modules, causing it to call our routines instead of Sun's, and adding in our own new module.
What we did was write two new routines, called in_TCPconnect() and in_UCPconnect(), which do the needed checks, and then either exit with an error code or call the in_pcbconnect() routine. We then manually editted the symbol tables in the SunOS kernel object files, replacing calls to in_pcbconnect() with either in_TCPconnect() or in_UDPconnect() (the names are the same lengths, so this is an easy edit). The call to in_pcbconnect() in tcp_usrreq was replaced with a call to in_TCPconnect(), and the two calls in upd_usrreq were replaced with calls to in_UDPconnect(). That way, after we relinked the kernel, all calls to in_pcbconnect() had to pass our tests before being processed.
Most of the work on this was done by Grex staff members Marcus Watts and Steve Weiss.
Thus, all users, even non-members, can have access to these standard Internet services.
43 whois 53 dns 70 gopher 79 finger 80 http 113 ident 517 talk 518 ntalk
Here's the actual code (actually, it's a slightly out-of-date version, with Grex's old IP address). Note that if all goes well, we call the real routine, in_pcbconnect(). Otherwise, we log an error and return the access error code.
in_TCPconnect(inp, nam)
struct inpcb *inp;
struct mbuf *nam;
{
int i;
struct proc *p= u.u_procp;
ushort uid = p->p_cred->cr_uid;
int *grps = p->p_cred->cr_groups;
struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
long saddr;
if (nam->m_len < sizeof *sin) goto Skip;
saddr= ntohl(sin->sin_addr.s_addr); /* for platform completeness */
if ( (uid >= 1000) && u.u_ruid
&& ((saddr & 0xFFFFFF00) != (127 << 24))
&& ((saddr & 0xFFFFFF00) != ((152 << 24) + (160 << 16) + (30 << 8) ))
&& ((saddr & 0xFFFFFFE0) != ((198 << 24) + (87 << 16) + (20 << 8) + 32)) && (sin->sin_port != 43) /* whois */
&& (sin->sin_port != 53) /* DNS (dig) */
&& (sin->sin_port != 70) /* gopher */
&& (sin->sin_port != 79) /* finger */
&& (sin->sin_port != 80) /* http */
&& (sin->sin_port != 113) /* ident */
&& (sin->sin_port != 517) /* talk */
&& (sin->sin_port != 518) /* ntalk */
)
/* if all of the above *and* conditions are true, the user must be a
member */
{
/* Test for membership: This assumes that group "internet" == 95 */
for(i = 0;(i < NGROUPS) && (grps[i] != 0) && (grps[i] != 95);i++)
;
if ( (i == NGROUPS) || (grps[i] == 0) )
{
log(LOG_INFO,
"b_TCP: %d/<%s> denied %d.%d.%d.%d:%d, %d/%d\n",
p->p_pid, u.u_comm,
(unsigned char)(saddr>>24),
(unsigned char)(saddr>>16),
(unsigned char)(saddr>>8),
(unsigned char)(saddr),
sin->sin_port, u.u_ruid, u.u_rgid);
return EACCES;
}
}
Skip:
return in_pcbconnect(inp, nam);
}
53 dns 517 talk 518 ntalk
Here's the actual code for the in_UDPconnect() wrapper.
in_UDPconnect(inp, nam)
struct inpcb *inp;
struct mbuf *nam;
{
int i;
struct proc *p= u.u_procp;
ushort uid = p->p_cred->cr_uid;
int *grps = p->p_cred->cr_groups;
struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
long saddr;
if (nam->m_len < sizeof *sin) goto Skip;
saddr= ntohl(sin->sin_addr.s_addr); /* for platform completeness */
if ( (uid >= 1000)
&& ((saddr & 0xFFFFFF00) != (127 << 24))
&& ((saddr & 0xFFFFFF00) != ((152 << 24) + (160 << 16) + (30 << 8) ))
&& ((saddr & 0xFFFFFFE0) != ((198 << 24) + (87 < 16) + (20 << 8) + 32))
&& (sin->sin_port != 53) /* dns */
&& (sin->sin_port != 517) /* talk */
&& (sin->sin_port != 518) /* ntalk */
)
/* if all of the above *and* conditions are true, the user must be a
member */
{
/* Test for membership: This assumes that group "internet" == 95 */
for(i = 0;(i < NGROUPS) && (grps[i] != 0) && (grps[i] != 95);i++)
;
if ( (i == NGROUPS) || (grps[i] == 0) )
{
log(LOG_INFO,
"b_UDP: %d/<%s> denied %d.%d.%d.%d:%d, %d/%d\n",
p->p_pid, u.u_comm,
(unsigned char)(saddr>>24),
(unsigned char)(saddr>>16),
(unsigned char)(saddr>>8),
(unsigned char)(saddr),
sin->sin_port, u.u_ruid, u.u_rgid);
return EACCES;
}
}
Skip:
return in_pcbconnect(inp, nam);
}
Still looking for an easy way to get around our blocks? Click here.