KPPP

Three weeks ago, I had to fix some issues a customer had with connecting using KPPP. The following is what I've found and how I've fixed it and how I think it may be better fixed for the modern ages.

KPPP does a lot. It may handle routing and DNS for the user, without the help of pppd. It may use the pppd help, too. But because of that, it requires to be suid. Well, a graphical application running as root does not inspire that much trust, so the developers were very smart to separate the privileged section of kppp from the unprivileged one.

But how does it work and how do these different sections communicate? Well, kppp creates a socket pair and forks right in the beginning of its life. The childs process keeps privileges and the parent process drops them. So, in the end, there are still plenty of things that could go wrong in the child process in the case it ends up executing the rest of kppp code. But, then, it's less code that need careful attention so it will not execute the rest of the code.

Well, some distributors do prefer not to give that much power to kppp. So, it's not suid and everything would work pretty much well if the user has just setup kppp to ask pppd's help. That would work because pppd itself is usually suid. But why does it not work?

There are some ways of telling pppd which password to use when authenticating. One of these ways is giving it the pass option, which could go in the command line. But, obviously, this is not a very good option since, then, any other user in the system could look at the password using ps or listing processes some other way.

Then, there is /etc/ppp/{chap,pap}.secrets. These two files may cointain the passwords. And that's how kppp does it. It writes into these files, trying to keep its original contents intact and restoring them after disconnecting. However, it is also inapropriate to give any user privileges into writing and reading these files. So, if kppp is not suid, it does not work.

But instead of giving the user an error, kppp simply tries to connect after failing to write the password into those files. And the user never knows why the connection could not succeed. Fixing this was pretty simple. Return an error when not able to write into the files. That's what I did in my first patch and it worked pretty well. Now, the user was presented with an error dialog before kppp would even try to connect.

But always giving the user an error would not solve the problem: successfully connecting. Some distributors simply point the user to write the password files himself/herself. Note that this solution conflicts with mine. And, then, perhaps, another patch would be required so the user would tell ppp not to try writing the secrets files and connecting right away.

But pppd has plugin support and one of those plugins (there are others similar to it) allows the parent process to give it the password through a file descriptor. That's easily done with a pipe. The problem is that loading plugins for pppd requires privileges.

The solution is to call a peer, a pppd option that would allow privileged options to be given through this peer file. If the distributor includes such a peer file with the option to load the passwordfd plugin, that would work. And some distributors already do that, for some old versions of kppp.

So, that was my solution: if it was not possible to write the secrets files, it would call pppd using the passwordfd plugin and give it the secret using that.

But how would I do it differently? I wouldn't. It was a pretty smart move from the kppp people to use a different process with privileges to do whatever privileged operations were needed: network operations. The user interface would be unprivileged and communicate with it using some IPC mechanism.

However, we have pretty much that nowadays with D-BUS as the IPC mechanism and NetworkManager as the privileged process that does all kind of network operations. Unfortunately, it does not support using old dialup. So I decided to take a look at that and started working on this support. But that's a topic for a next entry.