Impersonating a user on Mac OS X
Asked Answered
L

2

6

On Windows it is possible to have a service that allows clients running in a user context to connect to it using sockets or pipes, and then impersonate the connecting user in order to act on behalf of that user, for instance to access files that only the user has access to (or making sure that no other files are accessed).

What is the equivalent way of accomplishing this on Mac OS X (Linux is interesting too)? I would assume that the set*uid functions would be used for this in some way?

But how do I authenticate the user that I want to impersonate and get the uid to set when the user is connecting on a socket?

Also, the set*uid functions seem to affect the entire process, which makes them difficult to use in a multithreaded daemon. Is there a different commonly used design pattern for this type of services on Mac OS X/Linux?

Edit: pmjordan's answer seems to take care of the set*uid per-process-only issue, and the question How can I pass user credentials through a Unix-domain socket on Mac OS X? seems to take care of the actual authentication problem by using unix domain sockets instead of plain sockets.

Lepanto answered 2/3, 2012 at 16:40 Comment(0)
J
2

For OS X specifics: have you looked at the Authentication, Authorization, and Permissions Guide for Mac OS X?

Generally, in UNIX-like operating systems, processes typically are owned by one specific user, and what they are permitted to do is determined primarily by this. There are some exceptions to this, but generally, the idea tends to be to do this on a per-process granularity. On the plus side, starting new processes is really easy - see the fork() function.

So a typical way for a daemon (such as sshd) to impersonate different users is to run the main process as root. Then, accept incoming connections and pass them off to fork()ed child processes, which, as you say, immediately drop privileges using set*uid. There are various inter-process communication channels, such as pipes, that you can set up if the child processes need to communicate with the parent process. Obviously, the less code runs as root, the better, from a security perspective, so you'll want to aim for the child processes to be autonomous.

If you need users to actually provide their username and password, things get a bit more complicated; you might want to look at the source code for the su and sudo utilities, and read the platform-specific documentation for authentication APIs.

Jaimie answered 2/3, 2012 at 19:2 Comment(9)
Creating a subprocess running as a specific user from the daemon, does seem like the way to go. But how do I know which uid to use? I might be trying to do something that should be done in a different way - I want the daemon to make sure that it is only accessing files that the calling user has access to, and I want the user application to be able to know that it communicates with something that is running with sufficient privileges to run as me. Maybe there is a different way of doing this?Lepanto
I'm not quite sure what you mean. "I want the daemon to make sure that it is only accessing files that the calling user has access to" - if the child process is running as the user in question, it will only have that user's rights, except for any handles open during the fork(), which you can close. Or are you asking something else?Jaimie
"I want the user application to be able to know that it communicates with something that is running with sufficient privileges to run as me." - I'm afraid I don't understand what you're asking with this part at all. I think it would help to give a rough idea/example of what your program should actually do.Jaimie
Sorry for my convoluted explanations... :) The daemon, running as root, opens a socket that clients can connect to. The clients want the service to perform a task on their behalf that requires knowledge of some per user information that must not leak between users. So I would like the daemon to drop privileges so that it becomes impossible for a client to ask it to access anything but files that belong to the user that started the client. The client on the other hand (that already has access to the per user information) does not want to transmit the information to anyone.Lepanto
Instead, if the daemon is able to authenticate the user, it can safely drop privileges to being that user and access the file containing the user information. The client can rest assured that since it has never transmitted the information, but the daemon was able to access it anyway so it must have been accessed by someone that has enough privileges to do so, i.e. root acting on the user's behalf.Lepanto
OK, unless I'm misunderstanding you, the separate process per connection model is perfect for this. Run the daemon as root, open socket. This parent process does nothing but accept and authenticate connections. Once you have an established connection, you fork(), with the parent process closing its connection handle, and the child process closing its socket handle. The parent daemon goes back to handling new connection requests. The child drops privileges to the authenticated user, and only then starts accepting commands over the network connection.Jaimie
You can also fork() the instant your request comes in, but this is vulnerable to DoS attacks: an attacker can start processes until the system runs out of PIDs. So the parent process is probably better off using an event-driven approach.Jaimie
Yes, I agree with you on that. So the question still applies, how do I authenticate the user? I assume that there is a standard way of doing this, similar to the SSPI interfaces on Windows?Lepanto
Ah, I see. I think you're looking for PAM, the Pluggable Authentication Module. I have to admit I don't know an awful lot about it.Jaimie
B
2

from Technical Note TN2083 - Apple Developer

In some cases it is helpful to impersonate the user, at least as far as the permissions checking done by the BSD subsystem of the kernel. A single-threaded daemon can do this using seteuid and setegid. These set the effective user and group ID of the process as a whole. This will cause problems if your daemon is using multiple threads to handle requests from different users. In that case you can set the effective user and group ID of a thread using pthread_setugid_np. This was introduced in Mac OS X 10.4.

Bobsleigh answered 5/9, 2014 at 18:55 Comment(2)
Interesting. When I use pthread_setugid_np, I'm hitting a warning: __deprecated_msg("Use of per-thread security contexts is error-prone and discouraged."). I wonder why? and what shall we use instead? also @JaimieWhomsoever
@Whomsoever how did you eventually address the issue? using pthread_setugid_np?Cadent

© 2022 - 2024 — McMap. All rights reserved.