How to create a Unix-domain socket with specific a

2019-07-24 19:13发布

问题:

I have a situation in which I intend to communicate with a service through a command interface made available via a UNIX-domain socket on the file system. I am able to successfully send it commands, but for a while sat perplexed as to why I could not receive any response to my queries.

As it turns out, the service did not have sufficient permissions to write to the address I (or the OS) provided for it. However, I realized that if I explicitly bind to an address on the file system then I could adjust the file permissions by leveraging chmod.

Something like:

int mySocket;
struct sockaddr_un local_addr; 

mySocket = socket(AF_UNIX, SOCK_DGRAM, 0);
local_addr.sun_family = AF_UNIX;
snprintf(local_addr.sun_path, 108  "/path/to/mySocket");

bind(mySocket, (struct sockaddr *) &local_addr, sizeof(struct sockaddr_un));
chmod("/path/to/mySocket", 777);

That is to say, without the final chmod step, the service is unable to write to mySocket because it does not have the appropriate write permissions. Obviously, this is an even harder problem to spot if one does not explicitly bind to a specific address, since the underlying OS will implicitly generate this socket for the user - but it still exists and still will have the same access problems.

My question, then, is with respect to this final step. Is there a way to allow the OS to implicitly generate the socket for my endpoint (i.e. the address to which the service will respond) but request that it be given certain permissions?


The Explanation

The reason this issue is becoming a problem is due to the requirement that other portions of the program be executed as root. As such, when I, as root, attempt to connect/send to the background service, the OS will implicitly create an address to which replies will be directed. However, this leads to the problem that my socket-file, whether implicit or created with bind, will have permissions like srw- --- ---, so the other endpoint can only reply if they, too, elevate themselves.

Thus, the problem goes away if I first bind and then chmod the permissions as I showed above.

回答1:

Is there a way to allow the OS to implicitly generate the socket for my endpoint (i.e. the address to which the service will respond) but request that it be given certain permissions?

I solved this very problem once using two calls to umask().

Pseudo code:

current_mask = umask(umask_to_be_used_on_afunix_socket_file_system_entry_creation);
bind afunix socket here
umask(current_umask);


回答2:

[edited] My first guess would be to instead use a fifo, which lets you create file first and set its permissions. Also, if two users are to communicate through the fifo, it's best to use group-level read/write and have them in the same group.

$ mkfifo -m 660 fifo_name

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *path, mode_t mode);

As sherrellbc mentions, this is unrelated to unix sockets. If you are forced into using sockets, why not drop permissions before creating it? This seems like an important step when running suid anyway.

struct sockaddr_un to_addr;
memset(&to_addr, 0, sizeof(struct sockaddr_un));
strncpy(to_addr.sun_path, "/path/to/socket", sizeof(to_addr.sun_path) - 1);
to_addr.sun_family = AF_UNIX;

/* ------------ change user ------------ */
if(setgid(new_gid) || setuid(new_uid))
    goto error;

handle_conn(&to_addr, sizeof(struct sockaddr_un));

int handle_conn(struct sockaddr *to_addr, socklen_t addrlen) {
    int to_sock;
    int ret;
    pid_t pid;
    if( (to_sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
            goto error;
    }
    if(connect(to_sock, to_addr, addrlen)) {
            close(to_sock);
            goto error;
    }

    ...                               

    close(to_sock);
}