17

I have a program that is listening to a Unix Domain Socket.

When a client connects to the socket I'd like to find out which program connected and then decide if I allow the connection or not (based on the user/group settings).

Is this possible under Linux, and if so, how?

Nils Pipenbrinck
  • 83,631
  • 31
  • 151
  • 221

3 Answers3

18

Yes, this is possible on Linux, but it won't be very portable. It's achieved using what is called "ancillary data" with sendmsg / recvmsg.

  • Use SO_PASSCRED with setsockopt
  • Use SCM_CREDENTIALS and the struct ucred structure

This structure is defined in Linux:

struct ucred {
    pid_t pid;    /* process ID of the sending process */
    uid_t uid;    /* user ID of the sending process */
    gid_t gid;    /* group ID of the sending process */
};

Note you have to fill these in your msghdr.control, and the kernel will check if they're correct.

The main portability hindrance is that this structure differs on other Unixes - for example on FreeBSD it's:

struct cmsgcred {
    pid_t   cmcred_pid;          /* PID of sending process */
    uid_t   cmcred_uid;          /* real UID of sending process */
    uid_t   cmcred_euid;         /* effective UID of sending process */
    gid_t   cmcred_gid;          /* real GID of sending process */
    short   cmcred_ngroups;      /* number or groups */
    gid_t   cmcred_groups[CMGROUP_MAX];     /* groups */
};
cnicutar
  • 178,505
  • 25
  • 365
  • 392
  • 1
    Thank you very much. Exactly what I was looking for. Portability is not an issue. The code will only run on Android as native code anyway. – Nils Pipenbrinck Nov 12 '11 at 14:17
  • Hi, I'm new to the socket. I'm wondering how can I know the PID of the sending process. Thanks. – Kevin Yue Feb 01 '23 at 06:33
13

I searched for this quite a bit, so I will show you this example on how to use SO_PEERCRED on a socket sock to get the pid/uid/gid of the peer of a socket:

int len;
struct ucred ucred;

len = sizeof(struct ucred);

if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) {
    //getsockopt failed
}

printf("Credentials from SO_PEERCRED: pid=%ld, euid=%ld, egid=%ld\n",
    (long) ucred.pid, (long) ucred.uid, (long) ucred.gid);
Zulakis
  • 7,859
  • 10
  • 42
  • 67
  • 1
    I also searched quite a lot, and feel very lucky to have found this answer! Most other sources insist that you need to use `sendmsg` and `recvmsg` when this is simply untrue. I'll add that the `sock` parameter in the example given would be the file descriptor of the client process you want to identify, and I found I needed to do at least one `read()` of data from the client, otherwise I got undefined values in the `ucred` struct. – Username Obfuscation Jun 29 '19 at 09:33
  • glibc since 2.8.0 (2009) requires `_GNU_SOURCE` to be defined. Ad-hoc source: https://github.com/apple/cups/issues/2860 – i336_ Apr 18 '20 at 12:32
2

Perhaps getpeername or getsockname could help. and I think that the permission of your unix socket are useful (not sure of that). And you might read the link inside /proc/self/fd/12 if your accept-ed socket is 12.

EDIT

using ancillary data for credentials and sendmsg is much better, as suggested by cnicutar below.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547