5 ms·
Alternatively - int send_fd(int conn, int fd_to_send) { struct msghdr msg = { 0 }; struct cmsghdr * hdr; char buf[CMSG_SPACE(sizeof(int))];
by latitude 13y ago
Alternatively -
int send_fd(int conn, int fd_to_send)
{
struct msghdr msg = { 0 };
struct cmsghdr * hdr;
char buf[CMSG_SPACE(sizeof(int))];
msg.msg_control = buf;
msg.msg_controllen = sizeof(buf);
hdr = CMSG_FIRSTHDR(&msg);
hdr->cmsg_len = CMSG_LEN(sizeof(int));
hdr->cmsg_level = SOL_SOCKET;
hdr->cmsg_type = SCM_RIGHTS;
*(int*)CMSG_DATA(hdr) = fd_to_send;
return sendmsg(conn, &msg, 0);
}
To those wondering why and when this is needed - it is useful for cases when your primary process lacks rights required to open a file or a device (!). So another, privileged process does the open on its behalf and sends the resulting file descriptor to the primary process for processing.
- archgoon 13y ago> So another, privileged process does the open on its behalf and sends the resulting file descriptor to the primary process for processing. I must be misunderstanding something. I assume that the unprivileged process still cannot actually read the file, otherwise you could access unauthorized open files by going through every possible file descriptor. What processing can the unprivileged process do? Keep track processes that have open files?
- evmar 13y agoFile descriptors are local to a process (after all, fd 0 is each process's personal stdin). The special extra data passed to sendmsg makes the kernel map a new fd in the receiving process, granting it access to the underlying file. It might help to consider that the fds in the two processes are likely not the same number.
- nkurz 13y agoThe only part you might be missing is that the 'send_fd' command needs to be executed by a cooperating privileged process that 'serves' the fd to the receiver. Since the permission checks are done at the time the fd is opened (and not repeated[1]) the receiving process has the full privileges that the sender had: read, write, truncate, etc. Receiving the fd via this mechanism is very different that just getting the number of the fd. Nothing is really passed over the socket, rather behind the scenes the kernel is remapping memory within the calling process. It's very similar to what happens when a process 'forks' and its file descriptors are duplicated, but with individual fd granularity. [1] There are exceptions to this in certain device handlers that use file interfaces, but it holds for regular files.
- archgoon 13y agoThank you (and others). I feel silly for not checking the man pages for the macros I didn't recognize.
- noselasd 13y agoPermission checks are done when you open[1] a file, not when you otherwise operate on it. So yes, you could access "unauthorized" files. It's up to the sending process whether it wants to share particular filedescriptor to someone else, so you can't gain access to something you're not supposed to though. [1] I'm not sure about security frameworks, it may be e.g. SELinux provides more granular checks.
- saurik 13y ago(This is pretty much exactly what this library does--that code is almost identical to lines 89 through 101 in flingfd_send()--so not quite "alternatively" ;P. It also implements receiving the file descriptor, as well as opening the socket, and has some error checking; but the code is only one file with fewer than ten functions wrapping around unix domain sockets.)
- clarry 13y ago> To those wondering why and when this is needed - it is useful for cases when your primary process lacks rights required to open a file or a device (!). So another, privileged process does the open on its behalf and sends the resulting file descriptor to the primary process for processing. But be careful with who can connect to that socket and grab that fd. If you just name that socket and then tell another process to open it, there's a race. In a traditional privsep scenario the socket between the privileged and unprivileged process is created prior to and shared between them with fork(). Other scenarios (e.g. daemon serving fds to local clients that weren't initially there), you'll need some way to communicate with that client before you can tell it to grab the fd; why not use that ipc socket? EDIT: Just to clarify, I'm commenting on the approach used by the linked library on github.