Subject: Re: Passing credentials over sockets?
To: None <current-users@NetBSD.ORG>
From: der Mouse <mouse@Rodents.Montreal.QC.CA>
List: current-users
Date: 01/16/1998 10:02:29
> I read about this a few days ago on -current.

> How exactly does passing credentials over PF_LOCAL sockets work?

Just Fine. :-)

If you want details of the mechanism internals, UTSLing is the only doc
I know of (though if there is more, I'm sure someone will correct me).

The interface is fairly straightforward.  You just use sendmsg, as in
the following snippet, which is conditionalized to use the 4.3 or 4.4
mechanisms depending on whether SCM_RIGHTS is defined (AFAICT
SCM_RIGHTS came along at the same time as the msg_accrights{,len}
fields got renamed to msg_control{,len} and the interface changed; if
anyone has a better thing to conditionalize on, please tell me).

This mechanism appears to be a good place for lurking bugs.  NeXT's OS
release 2.1 (old, but it's what I've got) crashes when I try this.  At
least one Linux kernel appears to not even try - I never thought I'd
see the day Solaris supported something from BSD that Linux didn't.  I
suspect a circularity - nobody uses it 'cause it's buggy, and with
nobody using it, there's no incentive for vendors to make it non-buggy.

/* Sending code */

 int s;
 struct sockaddr_un s_un;
 struct iovec iov;
 struct msghdr mh;
 int fd;
#ifdef SCM_RIGHTS
 struct cmsghdr cmh;
 char cmbuf[sizeof(struct cmsghdr)+sizeof(int)];
#endif

...set fd to the file descriptor to send...
 s = socket(AF_LOCAL,SOCK_STREAM,0);
 if (s < 0)
  { fprintf(stderr,"%s: socket: %s\n",__progname,strerror(errno));
    exit(1);
  }
 bzero(&s_un,sizeof(s_un));
 s_un.sun_family = AF_LOCAL;
...load s_un.sun_path...
 if (connect(s,(struct sockaddr *)&s_un,sizeof(s_un)-sizeof(s_un.sun_path)+strlen(s_un.sun_path)+1) < 0)
  { fprintf(stderr,"%s: connect: %s\n",__progname,strerror(errno));
    exit(1);
  }
...I don't know whether it works to send rights with no data; when I am
constructing protocols involving rights, I always send a garbage byte...
 iov.iov_base = (void *) &s;
 iov.iov_len = 1;
 mh.msg_name = 0;
 mh.msg_namelen = 0;
 mh.msg_iov = &iov;
 mh.msg_iovlen = 1;
#ifdef SCM_RIGHTS
 mh.msg_control = &cmbuf[0];
 cmh.cmsg_len = sizeof(cmh) + sizeof(fd);
 cmh.cmsg_level = SOL_SOCKET;
 cmh.cmsg_type = SCM_RIGHTS;
 bcopy(&cmh,&cmbuf[0],sizeof(cmh));
 bcopy(&fd,&cmbuf[sizeof(cmh)],sizeof(fd));
 mh.msg_controllen = cmh.cmsg_len;
 mh.msg_flags = 0;
#else
 mh.msg_accrights = (void *) &fd;
 mh.msg_accrightslen = sizeof(fd);
#endif
 if (sendmsg(s,&mh,0) < 0)
  { fprintf(stderr,"%s: sendmsg: %s\n",__progname,strerror(errno));
    exit(1);
  }

/* Receiving code */

 int fd;
 char junkbuf;
 struct msghdr mh;
 struct iovec iov;
 int n;
#ifdef SCM_RIGHTS
 struct cmsghdr cmh;
 char cmbuf[sizeof(struct cmsghdr)+sizeof(int)];
#endif

 mh.msg_name = 0;
 mh.msg_namelen = 0;
 mh.msg_iov = &iov;
 iov.iov_base = &junkbuf;
 iov.iov_len = 1;
 mh.msg_iovlen = 1;
#ifdef SCM_RIGHTS /* is this the right thing to test? */
 mh.msg_control = &cmbuf[0];
 mh.msg_controllen = sizeof(struct cmsghdr) + sizeof(int);
 mh.msg_flags = 0;
#else
 mh.msg_accrights = (void *)&fd;
 mh.msg_accrightslen = sizeof(fd);
#endif
 n = recvmsg(cfd,&mh,0);
 if (n != 1)
  { ...we send exactly one byte of data, so this is an error...
  }
#ifdef SCM_RIGHTS
 if (mh.msg_controllen != sizeof(struct cmsghdr)+sizeof(int))
  { syslog(LOG_WARNING,"protocol failure: controllen %d not %d\n",(int)mh.msg_controllen,(int)(sizeof(struct cmsghdr)+sizeof(int)));
    exit(0);
  }
 bcopy(&cmbuf[0],&cmh,sizeof(cmh));
 if (cmh.cmsg_len != mh.msg_controllen)
  { syslog(LOG_WARNING,"protocol failure: cmsg_len %d not %d\n",(int)cmh.cmsg_len,(int)(sizeof(struct cmsghdr)+sizeof(int)));
    exit(0);
  }
 if (cmh.cmsg_level != SOL_SOCKET)
  { syslog(LOG_WARNING,"protocol failure: cmsg_level %d not %d\n",(int)cmh.cmsg_level,(int)SOL_SOCKET);
    exit(0);
  }
 if (cmh.cmsg_type != SCM_RIGHTS)
  { syslog(LOG_WARNING,"protocol failure: cmsg_type %d not %d\n",(int)cmh.cmsg_level,(int)SCM_RIGHTS);
    exit(0);
  }
 bcopy(&cmbuf[sizeof(cmh)],&fd,sizeof(int));
#endif
...at this point, fd holds the received descriptor...

					der Mouse

			       mouse@rodents.montreal.qc.ca
		     7D C8 61 52 5D E7 2D 39  4E F1 31 3E E8 B3 27 4B