in.tcpmuxd: A secure, RFC compliant TCPMUX server

Yesterday, on IRC, neale asked if it was wise to run a TCP service on port 1. sneakums replied it was not, since it was a registered service, "tcpmux". However, nobody immediately knew what "tcpmux" was; Wikipedia provided the answer.

TCPMUX is an ancient, horrible protocol. You connect to a TCPMUX server on port 1, then tell it which TCP service you actually wanted, and it forwards locally for you. Obviously fraught with security problems on the modern Internet. Nonetheless, I immediately wanted to write a TCPMUX server.

I started out by coding to the description in the Wikipedia entry, not knowing there was an RFC. We did find it (RFC 1078), and Neale and I went back and forth tweaking the code. Eventually I stopped with this:

#!/usr/bin/perl

while(<>) {
  if($_ eq "HELP\r\n") {
    print "tcpmux\r\n";
    exit 0;
  } elsif(lc($_) eq "tcpmux\r\n") {
    print "+OK FINE\r\n";
  } else {
    print "-BLOW ME\r\n";
    exit 0;
  }
}

My friends, that is a fully functional, RFC 1078-compliant, completely secure TCPMUX server, in 11 lines of Perl. Neale has a bash version that he prefers, but I argue mine is better because it's strictly RFC-compliant (only accepts CRLF, etc). To use it, add this to /etc/inetd.conf:

tcpmux stream tcp nowait nobody /path/to/in.tcpmuxd

To use, telnet to port 1. (You can use nc, but you will have to do something like "echo -ne 'tcpmux\r\n' | nc localhost 1" because it will only recognize CRLF-terminated lines per the RFC.) in.tcpmuxd will accept and forward exactly one service, tcpmux. All others will be rejected with a kind explanation. "HELP" will also conveniently list all services it will forward.

You can also test this by telnetting to colobox.com port 1, which is running a fully functional TCPMUX server.

This service has been painstakingly checked for security flaws. A highly skilled team has gone through the entire codebase, line by line, and has determined that there are no known implementation or security flaws in the service. You're welcome.

4 Comments

  1. Ryan, would you care to elaborate on what the 'obvious' security flaws are that this protocol is sure to be fraught with, and on why it is a horrible protocol?

    (Please be aware that this is a serious question, not mere rhetoric. I implement system software.)

    • Using TCPMUX for production uses would allow the visitor to bypass port-based ACLs and firewalls (or at the very least, force the administrator to re-implement the restrictions in the TCPMUX service itself). Additionally, the final service would see the connection coming from localhost, which presents its own problems (allowing SMTP through TCPMUX would be a bad idea since almost all SMTP servers trust localhost as a relay.

      The only benefit of TCPMUX I can see would being able to connect to connect to a service by service name without knowing the port number, but the advent of DNS SRV makes that possible without the security drawbacks. ("_newservice._tcp.example.com. IN SRV 0 0 12345 server.example.com." would allow you to connect to the "newservice" service on "example.com", which corresponds to port 12345 on server.example.com.)

      • No, a tcpmux service does not need to forward to another server that would see it as localhost. It simply execs the server's process and lets it inherit the file descriptor(s) for the socket. In other words, you write a tcpmux that's invoked from inetd (or equivalent), and it in turn invokes the appropriate daemon. If the invoked daemon then did a getpeername() call, it would see the IP address of the actual client, just as always. (Alternatively, some implementations of inetd include tcpmux functionality internally.)

        There's an implementation out there, somewhere; it uses a file /etc/tcpmux.cf to list service names and the corresponding daemons. It did have an overflow problem, because it used sscanf() without field widths to parse the lines of the file, but that's easily fixed.

        I think the original premise was that one didn't have to worry about running out of ports if one was using names instead. Thus, tcpmux was on port 1, in very early anticipation of a future problem that hasn't yet really cropped up. Another advantage that was mentioned either in the RFC or the Wikipedia page (forgot which) is that it allows unofficial services without tying up a port for them that might come in conflict with a later official service.

        One could probably modify a basic tcpmux daemon easily enough to implement tcp_wrappers (certainly with respect to official services that also appear in /etc/services). That would take care of your other objection, at least with respect to user-space host-based checks. It would of course make packet filtering (kernel based or separate firewall) more difficult, although there are some packet filters that already inspect the contents as well as IP addresses and ports. Those could doubtless incorporate code to control tcpmux sub-services, although since those needn't be standard, there are still some awkward situations possible.

        I'd seen a mention somewhere that certain SGI services at least at one time actually used tcpmux.

        If I were to use it client-side, I'd probably do a C wrapper that
        first tried looking up a name as a service and connecting to that port, but if either the name lookup or the connection failed, would try using that name with the server's tcpmux port instead. That would probably give best results by making it transparent (at least for standard names) to the client which way it connected (although it could still find out with getpeername() if necessary).

        To my way of thinking, tcpmux isn't nearly as ugly for filtering or using as sunrpc (where you first have to inquire as to the port for the rpc service you desire on the server, and then connect to it), not only because of the greater complexity of connecting, but because most sunrpc port assignments (other than NFS and some associated ones, and sunrpc itself) are dynamic. There are ways around even that problem (like generating rules on the fly as soon as all RPC services have started), but they're not clean, and most portmapper or rpcbind implementations don't have hooks to communicate to a packet filter when RPC services are registered or unregistered.

  2. Awesome d00d, of course you couldn't help but write one =)

Leave a Reply

Your email address will not be published.

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

© 2014 Ryan Finnie

Theme by Anders NorenUp ↑