UNIX domain sockets not accessable across users? - linux

I'm running a client/server application on Red Hat Enterprise using ZMQ for message passing. The IPC socket used to associate a client with the server is implemented using a Unix domain socket.
If user A starts the server process, it seems like only clients started by user A can connect to and communicate over that socket. Our project requires that the clients be able to be run by different users, so this is a major sticking point.
The socket is located at /tmp/ipc_assoc with default 755 permissions. chmod 777 does not fix the problem. chown userB allows user B to access the socket, but user A then loses access. Not even root can access the socket. There is no ACL or SeLinux in use on the machine.
Is this typical behavior for Unix domain sockets? Has anyone figured out how to work around it?

chmod(s.sun_path, 0777); after listening to the socket.
domain = AF_UNIX;
name = servname;
port = -1;
n = MakeLocalSocket(s);
if (n == 0) {
fatal("can't create socket");
}
unlink(s.sun_path);
if (bind(fd, & s, n) < 0) {
fatal("can't bind socket");
}
if (listen(fd, 5) != 0) {
fatal("can't listen to socket");
}
/* UNIX domain sockets need to be mode 777 on 4.3 */
chmod(s.sun_path, 0777);

Temporarily alter the umask:
mode_t umask_ = umask(0000); /* let the socket be mode 0777 so that other users can connect */
int err = bind(fd, (struct sockaddr *)&unix_bind_addr, sizeof(unix_bind_addr));
umask(umask_); /* restore old umask (0777 is a pretty bad choice for normal stuff) */
if (err) {
/* handle bind error here */
}
Of course, this is a bad idea if you're using threads.

With some assistance from the ZMQ mailing list, I have made a work around. It's ugly, but seems to work consistently.
I had to make a subdirectory under /tmp and chmod 777 it. The server now creates the socket in that new folder. It also programmatically chmod 777 the socket. Now, as long as the server is run as root, any user can run a client and talk to the server.
I don't know why UNIX domain socket behave this way, but it sure is annoying.

Have you tried adding UserA and UserB into a common user group?

Related

Using the Hyper-V sockets between Windows host and Linux guest

I want to write simple application that communicates between the Hyper-V host and its virtual machine using Hyper-V sockets (netcat over vsock). In the Internet there are a few documents describing how to do it: Make your own integration services, Practical Hyper-V socket communication. However, any of them helps me to achieve my goal.
First of all, I've made sure that the connection using Hyper-V sockets is possible. On the guest Linux I loaded hv_sock module and run nc-vsock application which is able to listen on vsocks:
$ sudo modprobe hv_sock
$ nc-vsock -l 1234
On Windows in PowerShell I ran hvc, which utilises Hyper-V sockets and is able to emulate netcat:
hvc nc -t vsock little-helper 1234
and it works. I can see data sent from server to client and vice versa.
Then I wrote a simple application basing on 1 and 2 with slight changes.
I registered my application with the Hyper-V Host's registry as said in 1 and I ran my application. The connection was not established and the connect function returned error 10049.
I've tried to run my application as a administrator and manipulate GUIDs in the source code and on the Hyper-V Host's registry as well. However, nothing helps and application always reports error 10049.
In my opinion in the document are some ambiguity. E.g. it's said that the service id shall be a random GUID. But later on there is a note that the first four octets translate to port in AF_VSOCK address family, and the specific GUID is presented for this purpose.
Question is rather simple: what I did wrong or misunderstood. Is it possible to write netcat utilising vsock between Windows and Linux?
Full code:
#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <hvsocket.h>
#include <combaseapi.h>
int main()
{
struct __declspec(uuid("00000000-185c-4e04-985a-4c2eee3e03cc")) VSockTemplate {};
struct __declspec(uuid("2a9fa68e-4add-45cb-85c8-de97fc66d388")) ServerVsockTemplate {};
//----------------------
// Initialize Winsock
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != NO_ERROR) {
wprintf(L"WSAStartup function failed with error: %d\n", iResult);
return 1;
}
//----------------------
// Create a SOCKET for connecting to server
SOCKET ConnectSocket;
ConnectSocket = socket(AF_HYPERV, SOCK_STREAM, HV_PROTOCOL_RAW);
if (ConnectSocket == INVALID_SOCKET) {
wprintf(L"socket function failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
//----------------------
// The sockaddr_in structure specifies the address family,
// IP address, and port of the server to be connected to.
SOCKADDR_HV clientService;
clientService.Family = AF_HYPERV;
clientService.VmId = __uuidof(ServerVsockTemplate);
clientService.ServiceId = __uuidof(VSockTemplate);
clientService.ServiceId.Data1 = 1234;
//----------------------
// Connect to server.
iResult = connect(ConnectSocket, (SOCKADDR*)&clientService, sizeof(clientService));
if (iResult == SOCKET_ERROR) {
wprintf(L"connect function failed with error: %ld\n", WSAGetLastError());
iResult = closesocket(ConnectSocket);
if (iResult == SOCKET_ERROR)
wprintf(L"closesocket function failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
wprintf(L"Connected to server.\n");
iResult = closesocket(ConnectSocket);
if (iResult == SOCKET_ERROR) {
wprintf(L"closesocket function failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
WSACleanup();
return 0;
}
For whoever is not very familiar with Hyper-V and does not want to spend 2 hours debugging, like I did, a few points:
Ensure that the hv_sock kernel module is enabled on the guest, I used Ubuntu Server 20.04, which does not have this enabled by default.
lsmod | grep hv_sock
If it's not there you need to add it and reboot:
sudo sh -c 'echo "hv_sock" > /etc/modules-load.d/hv_sock.conf'
sudo reboot
You need to register a new application with Hyper-V Host's registry, but the docs are misleading, as the random GUID is only needed with a Windows guest, for Linux guests the GUID needs to be in a very specific format, as described by HV_GUID_VSOCK_TEMPLATE, meaning <port>-facb-11e6-bd58-64006a7986d3
So for port 5001 the registry key should be 00001389-facb-11e6-bd58-64006a7986d3 (1389 is 5001 in hex)
You can do that easily from powershell as described in the Register a new application section
$service = New-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\GuestCommunicationServices" -Name "00001389-facb-11e6-bd58-64006a7986d3"
$service.SetValue("ElementName", "HV Socket Demo")
You can find some simple code samples here, win_server.c for the Windows host and wsl_client.c for the Linux guest.
What is the ServerVsockTemplate GUID you have there? I'm pretty sure that's supposed to be the GUID of a running VM, e.g., (Get-VM -Name $VMName).Id, so it'd be hard to hard-code into your source. If that's the GUID you generated per 'Register a new application', that'll be the problem.
The docs aren't clear, but I have the strong suspicion that 'Register a new application' part is only for when you're listening on Windows for incoming connections from other Windows VMs, or when writing a Linux device driver that talks to a service on the host. It might also be to allow a VM to offer services to other VMs, but I would assume not.
Edit: Quick Testing VSOCK (Hyper-V) Support in X410 says you need to make the registry keys for the vsock GUIDs as well, to receive connections from the vm.
In Linux userspace, you only have access to vsock; the other services are managed by drivers under Linux.
There's a clearer explanation of the vsock workflow in the Linux source for the Hyper-V vsock implementation.
I assume it's possible to use VSock between a Windows VM and the host as well, of course.
Edit, because I actually went and tested this.
Two mistakes in the code, on top of the ServerVsockTemplate GUID needing to be the GUID of the target VM.
The VSockTemplate GUID is wrong. I don't know where that came from, but there's a constant HV_GUID_VSOCK_TEMPLATE in <hvsocket.h> anyway, which matches the one on the Microsoft Docs site: 00000000-facb-11e6-bd58-64006a7986d3
It turns out, you need to zero the Reserved member of the SOCKADDR_HV, or it'll fail. Traditionally, one would use memset to zero a new sockaddr_* structure, but in this case, we can take the easy path.
So to make this work, change the SOCKADDR_HV creation code to the following:
// The sockaddr_in structure specifies the address family,
// IP address, and port of the server to be connected to.
SOCKADDR_HV clientService;
clientService.Family = AF_HYPERV;
clientService.Reserved = 0;
clientService.VmId = __uuidof(ServerVsockTemplate);
clientService.ServiceId = HV_GUID_VSOCK_TEMPLATE;
clientService.ServiceId.Data1 = 1234;
Then you delete VSockTemplate, and make sure ServerVsockTemplate is the GUID of the VM or Micro-VM where you're running nc-vsock.
I actually tested this with the WSL2 micro-VM, for which the VM ID comes from hcsdiag list rather than Get-VM, but I was able to connect to nc-vsock inside my WSL2 session using the source here, modified as I have described.

Access Linux Root Files from .NetCore app

I have created a .net core app that runs very well on the Raspberry Pi.
I wish to try and connect this device to a wifi router or an access point from an iPhone.
After looking I know you can create a conf file and put it onto the sd card via a card reader.
What I would like to do is allow the user to enter their wifi details via my own interface and for my own c# program to make the chnages.
i have spent sometime but found no examples.
If anyone knows...
The usual approach for GUI based configuration is not to edit the system config files directly, but to talk through the configuration interface provided by the network daemons.
wpa_supplicant can be talked to through the wpa_cli utility. You use it by spawning wpa_cli as a separate process with stdio redirected into a pipe, into which/from you send the configurations commands.
Update: To talk to wpa_cli you'd create a process with redirected output. With Mono you'd do it as following
private void start_wpa_cli()
{
ProcessStartInfo psI = new ProcessStartInfo("wpa_cli");
psI.UseShellExecute = false;
psI.RedirectStandardInput = true;
psI.RedirectStandardOutput = true;
Process p = new Process();
p.StartInfo = psI;
p.Start();
StreamWriter sw = p.StandardInput;
sw.AutoFlush = true;
StreamReader sr = p.StandardOutput;
...
You can then send wpa_cli commands through sw and read the result from sr. The commands for wpa_cli you can find in its manpage.
If NetworkManager is used, you talk to it through its D-Bus interface. Update: To access D-Bus from .Net/Mono you could for example use https://github.com/mono/dbus-sharp

How can I know the host name on client server program

I am doing a program about client server communication using sockets. I saw examples of client server program on internet, but I have a question. How to get host name? I saw a client program example on the webpage int the link below. The program gets the host name from the user in command line arguments,but how can I know the host name? and I tried using my username on it but its says host does not found. I am running both on same machine. Thank you very much for your time.
client:
http://www.tutorialspoint.com/unix_sockets/socket_client_example.htm
server:
http://www.tutorialspoint.com/unix_sockets/socket_server_example.htm
Host name is different from the username to get the host name use the command "hostname"
and use this host name in command line
commands:
hostname -->gethostname
hostname < name > -->sethostname with name
If your client and server are on the same host, the hostname is localhost, or you could use the name of the machine, or you could use 127.0.0.1
Assuming you mean the host you are currently running on you use gethostname
int main(int argc, char *argv[])
{
int ret;
char buffer[100];
if ((ret = gethostname(buffer, sizeof(buffer))) == -1)
{
perror("gethostname");
exit(1);
}
printf("hostname is: %s\n", buffer);
return(0);
}
For a client server program , the client must know the hostname or the ip address of the machine it wants to communicate with.
Think of it like you are driving to a particular shop, you need to know the address of the shop to reach there. Similarly clients need the address of the host it needs to communicate with.
If you are running your own client and server, then type hostname in the machine and it will give you the host name. Usually you would need host name and port but in simple example programs the value may be hard coded.

Is it possible to create a proper interactive shell over http?

I want to have a shell access over http to interact with a program running on my server (as opposed to SSH and other protocol). I have done some research and found two main ways, the php way such as http://sourceforge.net/projects/phpterm/ and the CGI way. Although these result in shell-like terminals over http, I can't interact with programs with standard input/output without passing paramaters at run: ./prog -options etc..
With a standard shell over netcat for example ./prog would provide full interaction so that it could prompt for input etc..
The test program I am running is:
#include<stdio.h>
#include<sys/types.h>
include<stdlib.h>
int main ()
{
// set up keyword(passcode)
char this[14];
char that[128];
// check the password and exit if it doesn't match;
fgets(this, 14, stdin);
if (strncmp(this, "passwd\n", 14)) {
exit(0);
}
printf("shell interaction success! \n");
fgets(that, 128, stdin);
system(that);
exit(0);
}
If run from netcat this would occur:
./prog
passwd
Shell interaction success
If run from other shell like solutions over http I have come accrss:
./prog
then nothing.
Fingers crossed someone knows how!
You could do this, but since HTTP is a connectionless request-response protocol, it wouldn't use just one HTTP connection.
A browser would make a request to start a shell on the remote server
A backend service would be started that creates the desired process and captures the stdin/stdout pipes
Javascript on the browser would send (POST perhaps) a request to the server to say "this user typed some character"
Some kind of AJAX request polling loop would get new output from the backend process and display it on the browser
Or, this could be considerably simplified using WebSockets which is a stream protocol (and is implemented by some browsers but is not HTTP).

Linux: How to make a daemon/service usable with xinetd?

Anybody knows what changes are necessary for a server to work with xinetd ?
The server being a .NET mailserver that runs on Linux.
See the bottom of this post for reference:
Lumisoft Mailserver Forum Post
Note: xinetd, not mono-service. [x]inetd is an internet superserver.
A superserver starts a server service on demand.
(As opposed to the server service running continuously, which is what mono-service does)
An inetd service runs differently from a standalone server. inetd services read stdin and write to stdout, letting inetd handle the gory details of TCP/IP, rather than keeping track of their own sockets. If you want to make a server run under inetd, it'll have to do the same.
The following program runs just fine under xinetd on my machine:
#include <iostream>
#include <string>
using namespace std; // yeah, i'm lazy.
int main()
{
string name;
cout << "What's your name? " << flush;
cin >> name;
cout << "Hi, " << name << "!" << endl;
}
Note i'm not at all worried about sockets -- xinetd arranges things so that the service can read standard input and write to standard output. You just write your app like you'd be running it on the console, for the most part. The socket details are specified in the config file for the service. (Note, you might be able to get/set details about the socket using stdin/stdout, which may be the actual socket -- i'm not sure -- but you really should leave that stuff up to inetd.)
An inetd services are really great for one off apps that need to take in data and act with some degree of interaction with the user. IT works over tcp/udp by piping the data viva a socket from (x)inetd to std{in,out,err}. inetd apps also works well with tcpwrappers to inhance security though system policy files and ACL.
So yes you would write your app like its a console app since in reality it is a console app. Just think of inetd as a transparent reverse proxy from the network to your app's inputs.
A Word of advice, write your code to handle the process signals correctly and if you need to interact with another process on the system use unix sockets/fifo for that.
Also, don't try to write an app that streams a lot of data all at once or needs a lot of connections. Scalability is an issue as inetd becomes a bottle neck, this is why Apache and Sendmail dropped support for inetd and sit as mono apps instead. HTTP fits this role better and a fastcgi (or insert favorite framework) script with nginx works best for that use case.
A good example for an inetd would be:
lock = Mutex.new
trap :HUP { #log the connection and cleanup }
trap :USR1 { lock.synchronize do #stuff; end }
trap :TERM { #clean up }
trap :KILL { #clean up and die with error codes }
puts "App name - version"
loop do
('%s> ' % Console.prompt).display
input = gets.chomp
command, *params = input.split /\s/
case command
when /\Ahelp\z/i
puts App.help_text
when /\Ado\z/i
Action.perform *params
when /\Aquit\z/i
exit
else
puts 'Invalid command'
end
end
exit
Edit your /etc/services to include your app like this:
myapp port#/proto
and add your app to /etc/inetd.conf (or xinetd.d) like this:
myapp stream tcp6 nowait myappuser /path/to/myapp myapp -arg_flags

Resources