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").

Reply via email to