Linux Capabilities

I was recently updating some code that uses fping. Initially it used exec() that was redirected to a temporary file but I changed it to use popen.  While it had been a while since I’ve done this sort of thing, I do recall there was an issue with running popen on setuid binary.  A later found it is mainly around setuid scripts which are very problematic and there are good reasons why you don’t do this.

Anyhow, the program worked fine which surprised me. Was fping setuid root to get the raw socket?

$ ls -l /usr/bin/fping
-rwxr-xr-x 1 root root 31464 May  6 21:42 /usr/bin/fping

It wasn’t which at first all I thought “ok, so that’s why popen is happy”. The way that fping and other programs work is they bind to a raw socket. This socket sits below the normal type sockets such as the ones used for TCP and UDP and normal users cannot use them by default. So how did fping work it’s magic and get access to this socket? It used Capabilities.

 

Previously getting privileged features had a big problem; it was an all or nothing thing. You want access to a raw socket? Sure, be setuid but that means you also could, for example, read any file on the system or set passwords. Capabilites provide a way of giving programs some better level of access, but not a blank cheque.

The tool getcap is the way of determining what capabilities are found on a file. These capabilities are attributes on the file which, when the file is run, turn into capabilities or extra permissions. fping has the capability cap_net_raw+ep applied to it. This gives access to the RAW and PACKET sockets which is what fping needs. The +ep after the capability name means it is an Effective and Permitted capability, which describes what happens with child processes and dropping privileges.

I hadn’t seen these Capabilities before. They are a nice way to give your programs the access they need, but limiting the risk of something going wrong and having a rouge program running as root.

PHP uniqid() not always a unique ID

For quite some time modern versions of JFFNMS have had a problem. In large installations hosts would randomly appear as down with the reachability interface going red. All other interface types worked, just this one.

Reachability interfaces are odd, because they call fping or fping6 do to the work. The reason is because to run a ping program you need to have root access to a socket and to do that is far too difficult and scary in PHP which is what JFFNMS is written in.

To capture the output of fping, the program is executed and the output captured to a temporary file. For my tiny setup this worked fine, for a lot of small setups this was also fine. For larger setups, it was not fine at all. Random failed interfaces and, most bizzarely of all, even though a file disappearing. The program checked for a file to exist and then ran stat in a loop to see if data was there. The file exist check worked but the stat said file not found.

At first I thought it was some odd load related problem, perhaps the filesystem not being happy and having a file there but not really there. That was, until someone said “Are these numbers supposed to be the same?”

The numbers he was referring to was the filename id of the temporary file. They were most DEFINITELY not supposed to be the same. They were supposed to be unique. Why were they always unique for me and not for large setups?

The problem is with the uniqid() function. It is basically a hex representation of the time.  Large setups often have large numbers of child processes for polling devices. As the number of poller children increases, the chance that two child processes start the reachability poll at the same time and have the same uniqid increases. It’s why the problem happened, but not all the time.

The stat error was another symptom of this bug, what would happen was:

  • Child 1 starts the poll, temp filename abc123
  • Child 2 starts the poll in the same microsecond, temp filename is also abc123
  • Child 1 and 2 wait poller starts, sees that the temp file exists and goes into a loop of stat and wait until there is a result
  • Child 1 finishes, grabs the details, deletes the temporary file
  • Child 2 loops, tries to run stat but finds no file

Who finishes first is entirely dependent on how quickly the fping returns and that is dependent on how quicky the remote host responds to pings, so its kind of random.

A minor patch to use tempnam() instead of uniqid() and adding the interface ID in the mix for good measure (no two children will poll the same interface, the parent’s scheduler makes sure of that.) The initial responses is that it is looking good.

 

No more dspam, now what?

I was surprised at first to see that a long-standing bug in dspam had been fixed. Until that is, I realised it was from the Debian ftp masters and the reason the bug was closing was that dspam was being removed from the Debian archive.

 

Damn!

 

So, now what? What is a good replacement for dspam that is actually maintained? I don’t need anti-virus because mutt just ignores those sorts of things and besides youbankdetails.zip.exe doesn’t run too well on Debian. dspam basically used tokens to find common patterns of spam and ham, with you bouncing misses so it learnt from its mistakes. Already got postgrey running for greylisting so its really something that does the bayesan filtering.

 

Some intial comments:

  • bogfilter looks interesting and seems the closest thing so far
  • cluebringer aka policyd seems like a policy and bld type of spam filter, not bayesan
  • I’ve heard crm114 is good but hard to use
  • spamassasin – I used to use this, not sure why I stopped

There really is only me on the mailserver with a pretty light load so no need to worry about efficiencies.  Not sure if it matters but my MTA is postfix and I already use procmail for delivery.

 

 

Rnms now using TurboGears 2.3.2

While the website might of been idle there has been a great deal of work behind the scenes (unless you’re looking at github) updating and improving RoseNMS, the python based Network Management System. The code has had a great many changes including:

  • Updating the TurboGears based code to 2.3.2
  • Simplifying the admin back to TG standard using the Twitter Bootstrap option
  • Making several GUI changes to make it easier to switch screens or get more information
  • Update to the pysnmp module to the latest, which is a much better module
  • Changing from paste to cliff for the command lines and bringing it all into one binary

I’m hoping to get to release one in the next few months. The back-end is largely complete with some work required on the daemon and also more webGUI work to make sure its easy to get the right information at the right time.

 

killing a process in TCL

Suppose you had spawned a process in TCL and knew its PID and wanted to kill it? Sounds simple enough thing to do, right? This problem has plagued me for many months because some things you can assume on a normal system do not hold true on strange environments, such as build deaemons.

Seems simple enough, I started off with:

exec kill $pid

Except.. not every environment has the kill binary, and with that piece of code exec has to be a binary and not a shell builtin. The funny thing is that /bin/kill is in the procps package, which is the package having the buildd problems.

So next idea was to use command -v to check for the existence of kill and skip those tests that needed kill if not found. Good idea except, so I found out later, it also finds built-ins. That means we are back to problem #1.

There is a kill command in tcl, but it requires tclx. That seems excessive for just one little simple command. How can I run a shell out of TCL that runs the kill builtin? On the command line, something like below would do it.

/bin/sh -c 'kill 1234'

I was closer, but then hit TCL quoting hell. No matter what I (initially) did I’d either get the shell to complain or my variable to not be evaluated. In the end, I had to write it to a separate variable for the command line then apply that to the exec. Not perfect but at least it works now.

The resulting code (found in testsuite/config/unix.exp) looks like:

proc kill_process pid {
    set cmdline "kill $pid"
    if { [catch { exec /bin/sh -c $cmdline } msg]} {
        warning "Could not kill process: $msgn"
    }
}

Perhaps there is a more elegant way, I’m certainly no star TCL programmer, but of all the combinations I saw this was the only that worked.