How to create a Unix-domain socket with specific access permissions - c

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.

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);

[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);
}

Related

Understanding USBIP bind function

I am trying to understand the USBIP tool code from Linux (https://github.com/torvalds/linux/tree/master/tools/usb/usbip).
USBIP has a command that attaches a remote USB from an IP to a client system. If you look at the code (https://github.com/torvalds/linux/blob/master/tools/usb/usbip/src/usbip_bind.c#L130) below, which binds the USB, if the function calls close(sockfd); and close the socket, then how the communication is done between client and server for USB data.
From Docs (https://docs.kernel.org/usb/usbip_protocol.html):
Once the client knows the list of exported USB devices it may decide
to use one of them. First the client opens a TCP/IP connection to the
server and sends an OP_REQ_IMPORT packet. The server replies with
OP_REP_IMPORT. If the import was successful the TCP/IP connection
remains open and will be used to transfer the URB traffic between the
client and the server.
It says connection remains open if import is successful, then what is the purpose of close(sockfd);. I can also see sockfd is sent inside query_import_device(sockfd, busid); but if it is closed how this is used?
static int attach_device(char *host, char *busid)
{
int sockfd;
int rc;
int rhport;
// creates a TCP connection to the specified host on the specified port.
sockfd = usbip_net_tcp_connect(host, usbip_port_string);
if (sockfd < 0) {
err("tcp connect");
return -1;
}
// sends a query to the connected host to import the device specified by "busid".
rhport = query_import_device(sockfd, busid);
if (rhport < 0)
return -1;
// closes the previously established TCP connection.
close(sockfd);
// records details of the connection, such as the host and port, the busid of the device, and the assigned rhport.
rc = record_connection(host, usbip_port_string, busid, rhport);
if (rc < 0) {
err("record connection");
return -1;
}
return 0;
}
If you look at the source code of usbip helper program, query_import_device(), it establishes the connection and then calls import_device() to finish it.
In turn, import_device() calls usbip_vhci_attach_device(), that calls usbip_vhci_attach_device2()... that eventually writes the sockfd and some extra data to the /sys/*/attach pseudo file for the given device.
To see what happens next we need to go to kernel mode. So, looking at the source code of the usbip, at function attach_store(). This function parses the sockfd back into an integer file descriptor and, most notably, does this:
struct socket *socket;
socket = sockfd_lookup(sockfd, &err);
This converts a file descriptor into a real kernel socket object. And it increases the reference count. Note how if any of the further checks fail, it calls sockfd_put(socket); to decrease that reference count.
But if the function succeeds, the socket is stored into the device. It is just as if an userland program had done a call to dup(): it doesn't matter if the original sockfd is closed, the kernel keeps the actual socket opened as long as it is needed.
You can see that the value of sockfd is also stored in the device, but it isn't actually used for anything other than reading back from /sys, so it doesn't matter if it is no longer a valid file descriptor.
So the close is actually necessary because the kernel dups the socket internally, that keeps the connection opened. And you do not want to keep an extra FD referencing the same socket, if you try to use that socket for anything that could mess up the device connection. The responsible thing to do is just to close it.

Choose a local source port that is already in use

I want to connect to a remote server using a specific local port, not one assigned randomly by the kernel. I can do this by calling bind() to bind to the local port before calling connect() to the remote server.
My question is what happens if the local port I want to use is already in use by another application? I should be able to use it anyway, provided the destination or remote port are different (the same way a server can accept() multiple connections on port 80). But shouldn't my bind call fail in this case, and if so how can I set up the socket to use the local port that's already in use by another application?
The reason I want to do this is I am trying to write a local proxy that connects to a server application who checks the source port. If the source port is wrong the server won't allow the connection. The client side application connects to my proxy, and I want my proxy to use the same port to connect to the server - but if the proxy is on the same machine it won't work because the port is already in use by the application connecting to my proxy.
You can make an argument that you should be able to use it anyway, but TCP implementations don't let you unless the two binds are different. For example, you are probably able to bind to the same port with different IP addresses.
There are two problems with permitting overlapping binds:
What happens if both applications call accept? Do they fight over incoming connections?
What happens if both applications try to make an outbound connection to the same IP and port? How can those two connections be distinguished?
Now these problems could be solved. But I don't know of any implementations that bother. The argument is that the applications would have to cooperate or they would get surprising results. And if they're cooperating, they can share the bound socket.
So the answer is: If you aren't cooperating with the other application that has the port, then you have no right to share it. If you are cooperating with the other application, ask it to give you a copy of its socket using the method your platform supports.
> But shouldn't my bind call fail in this case,
Yes if the socket does not have SO_REUSEADDR option set.
> and if so how can I set up the socket to use the local port that's already in use by another application?
Both your application and another application must set SO_REUSEADDR option on the socket, which wants to bind to the local port.
The code below connects to the HTTP server, given as command-line argument, from port 1111:
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <netdb.h>
#define CLIENT_PORT 1111
#define SERVER_PORT 80
int main(int argc, char *argv[]) {
struct sockaddr_in client_name, server_name;
struct hostent *server_info;
if (argc != 2)
return printf("Exactly one argument is required: host to connect\n"), 1;
int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (sock_fd < 0)
return perror("socket"), 1;
/* Without the next 4 lines, bind refuses to use the same port */
int reuseaddr = 1;
if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr,
sizeof(reuseaddr)) < 0)
return perror("setsockopt"), 1;
client_name.sin_family = AF_INET;
client_name.sin_port = htons(CLIENT_PORT);
client_name.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sock_fd, (struct sockaddr *) &client_name,
sizeof(struct sockaddr_in)) < 0)
return perror("bind"), 1;
server_name.sin_family = AF_INET;
server_name.sin_port = htons(SERVER_PORT);
if ((server_info = gethostbyname(argv[1])) == NULL)
return printf("Unknown host: %s\n", argv[1]), 1;
server_name.sin_addr = *(struct in_addr *) server_info->h_addr;
if (connect(sock_fd, (struct sockaddr *) &server_name,
sizeof(server_name)) < 0)
return perror("connect"), 1;
return 0;
}
> what happens if the local port I want to use is already in use by another application?
Without SO_REUSEADDR (try to comment out the 4 lines around setsockopt), bind fails:
$ ./client google.com
$ ./client stackoverflow.com
bind: Address already in use
With SO_REUSEADDR, you can connect to different remote servers:
$ ./client google.com
$ ./client stackoverflow.com
But then connect will not allow you to open two sockets with same source and destinations:
$ ./client google.com
$ ./client google.com
connect: Cannot assign requested address
bind knows just one endpoint.
Suppose that two sockets are bound to the same port. Which one the incoming packet shall be routed to?
accept on the other hand knows both peers.

RSA_generate_key() using prngd instead of /dev/random or /dev/urandom

I want to use RSA_generate_key() on HP-UX 11.11. But hp-ux 11.11 does not provide /dev/random or /dev/urandom, so I need to use openssl prngd.
Please let me know how to use it by default in C code. I have openssl installed and prngd is available.
$ ls /opt/openssl/prngd/prngd
/opt/openssl/prngd/prngd
Let me know if you need more information.
Noting that prngd uses the same interface that EGD does, checkout the instructions found here. A quote of interest is:
On systems without /dev/*random devices providing entropy from the kernel
Alternatively, the EGD-interface compatible daemon PRNGD can be used.
OpenSSL automatically queries EGD when entropy is requested via RAND_bytes() or the status is checked via RAND_status() for the first time, if the socket is located at /var/run/egd-pool, /dev/egd-pool or /etc/egd-pool.
So when you run prngd, run it as prngd /dev/egd-pool or one of the other alternatives
prngd simulates "/dev/random" and "/dev/urandom" over a network connection. It supports either a Unix stream-based domain socket ("/var/run/egd-pool") or (if configured to) or IP using TCP ports 708 or 4840 (default values---can be changed).
So, in using the Unix domain socket, it would look something like:
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
int devrandom(void)
{
union
{
struct sockaddr sa;
struct sockaddr_un path;
} location;
int sock;
memset(&location,0,sizeof(location));
location.path.sun_family = AF_UNIX;
strcpy(location.path.sun_path,"/var/run/egd-pool");
sock = socket(AF_UNIX,SOCK_STREAM,0);
if (sock < 0)
return -1;
if (connect(sock,&location.sa,sizeof(struct sockaddr_un)) < 0)
return -1;
return sock;
}
This will return a file descriptor you can pass to read() in order to obtain the random data (note: this code is untested). A TCP/IP based connection is a bit more involved, requiring binding the socket to a local address and connecting to the remote address but there are plenty of examples on the Internet for that type of code.

error in binding port "Address already in use" TCP socket programming in unix

I've gone through many posts and forums and I'm new to socket programming. Major parts of my code are similar to
BIND ERROR : Address already in use
but then i changed my code so that i include "setsockopt" function like so:
const char* port="5555";
int opt=1;
portno=atoi(port);
//parameters for server address
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(portno);
serv_addr.sin_addr.s_addr=INADDR_ANY;
//bind the socket to the address
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(const char *)&opt,sizeof(int));
if(bind(sockfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr))<0)
{close(sockfd);
error("error in binding port!");
}
But still i get the error. I have to close the terminal and restart it in order to use the port again. I want to use a hardcoded port (like i mentioned in the code above)
Thanks a lot in advance
Check to see if the port is in use. Either telnet to that port or use netstat -a. It should be in use (as the error indicates) and kill the appropriate process. Perhaps using ps to find the process.
A port number can only be used by one application at a time. That means you can not start the same program twice expecting both to bind to the same port.
The SO_REUSEADDR is for when the socket bound to an address has already been closed, the same address (ip-address/port pair) can be used again directly.

C pcap detecting inbound datagrams

In C I bind a datagram socket (AF_INET, SOCK_DGRAM) to INADDR_ANY. I then periodically use this socket to send and receive datagrams, and monitor the flow of packets with pcap. The problem is, I can't tell whether a packet is incoming or outgoing using pcap.
The transmission/receiving and the pcap monitoring are running in separate threads, and for synchronisation reasons they can't communicate. I only want to track the incoming packets, not the ones being sent, so does anyone have an idea as to how I can do that?
I thought already of testing the destination ip address, but I can't figure out any way to get my local ip. the machine this is running on doesn't have a static ip, much less an assigned domain name, and it seems that getsockname doesn't work on sockets bound to INADDR_ANY. Also tried using ioctl(sockfd, SIOCGIFCONF, &buffer), which didn't work either - sets buffer.ifc_len=0.
Found a solution. I can get my own ip using this:
char *command = malloc(100);
sprintf(command,"ifconfig %s|grep -o \"inet addr:[^ ]\"|grep -o -e \"[0-9]\.[0-9]\.[0-9]\.[0-9]*\"",device);
char path[1035];
FILE *fp;
fp = popen(command,"r");
fgets(path, 1034, fp);
pclose(fp);
my_ip = malloc(sizeof(char)*(1+strlen(path)));
memcpy(my_ip, path, strlen(path)-1);
my_ip[strlen(path)-1] = 0;

Resources