On Monday, August 9th, 2021 at 5:36 AM, Philip Guenther <guent...@gmail.com> wrote:
> If you're 100% sure you have it right, then it should be easy to provide a > program that demonstrates > 1. passing an fd between processes > 2. using it successfully in the receiving process > 3. the sending process exiting > 4. attempts to us it failing the receiving process Not 100%, but I'm out of ideas, so here goes nothing. client.c (process A): #include<unistd.h> #include<sys/socket.h> #include<sys/un.h> #include<string.h> #include<assert.h> int sendfd(int sock, int fd) { struct msghdr msg; struct iovec iov[1]; struct cmsghdr *cmsg = NULL; char ctrl_buf[CMSG_SPACE(sizeof(int))]; char data[1]; memset(&msg, 0, sizeof(struct msghdr)); memset(ctrl_buf, 0, CMSG_SPACE(sizeof(int))); data[0] = ' '; iov[0].iov_base = data; iov[0].iov_len = sizeof(data); msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_controllen = CMSG_SPACE(sizeof(int)); msg.msg_control = ctrl_buf; cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(int)); *((int*) CMSG_DATA(cmsg)) = fd; return sendmsg(sock, &msg, 0); } int main(int argc, char **argv) { int c = socket(AF_UNIX, SOCK_STREAM, 0); assert(c != -1); struct sockaddr_un a; a.sun_family = AF_UNIX; strcpy(a.sun_path, "/service/sock"); assert(connect(c, (struct sockaddr*) &a, sizeof(a)) != -1); sendfd(c, 0); sendfd(c, 1); close(c); /* The SSH conn should stay after returning, but it doesn't. */ } server.c (process B): #include<unistd.h> #include<sys/socket.h> #include<sys/un.h> #include<string.h> #include<assert.h> #include<stdio.h> #include<sys/stat.h> int recvfd(int socket) { int len; int fd; char buf[1]; struct iovec iov; struct msghdr msg; struct cmsghdr *cmsg; char cms[CMSG_SPACE(sizeof(int))]; iov.iov_base = buf; iov.iov_len = sizeof(buf); msg.msg_name = 0; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_flags = 0; msg.msg_control = (caddr_t) cms; msg.msg_controllen = sizeof(cms); len = recvmsg(socket, &msg, 0); if(len <= 0) { return -1; } cmsg = CMSG_FIRSTHDR(&msg); memmove(&fd, CMSG_DATA(cmsg), sizeof(int)); return fd; } int main(int argc, char **argv) { unlink("/service/sock"); int s = socket(AF_UNIX, SOCK_STREAM, 0); assert(s != -1); struct sockaddr_un a; a.sun_family = AF_UNIX; strcpy(a.sun_path, "/service/sock"); assert(bind(s, (struct sockaddr*) &a, sizeof(a)) != -1); assert(chmod("/service/sock", 0777) != -1); /* Quick workaround. */ assert(listen(s, 20) != -1); while(1) { puts("Waiting.."); int c = accept(s, NULL, NULL); assert(c != -1); puts("Accepted"); int in = recvfd(c); int out = recvfd(c); printf("Received: in=%i out=%i\n", in, out); char *outstr = "Hello from the Server\n"; assert(write(out, outstr, strlen(outstr)) != -1); assert(close(c) != -1); /* Intentionally leaking SSH FDs */ } } Compiled with: cc -std=c99 -o server server.c cc -std=c99 -o client client.c `client` is also the shell of the user, but the results are the same if I call it from within a "real" shell, too. The server receives the correct FDs, and prints "Hello from the Server\n" correctly, too. But as soon as `client` exits, the SSH connection goes with it, instead of staying (as in, I get "Connection to localhost closed").