From: Guillaume Marcais Date: 2004-05-19T09:36:35+09:00 Subject: RCR: UNIX credentials --=-LH2RX20AHG69TpQAZ+qL Content-Type: text/plain Content-Transfer-Encoding: 7bit To get a feel before submitting a real RCR: Abstract: Add calls to UNIXSocket to pass around the UNIX credentials (i.e. pid, uid and gid). Problem: There is no way, that I know of, within Ruby to find out which process is connected at the other end of a UNIX socket. Ex: [gus@comp unix_credentials]$ irb irb(main):001:0> require 'socket' => true irb(main):002:0> s = UNIXServer.new('unixtest') => # irb(main):003:0> t = s.accept => # irb(main):004:0> t.peeraddr => ["AF_UNIX", ""] # Doesn't tell you anything! Proposal: I propose the addition of two methods, send_credentials and recv_credentils to pass through a UNIX socket the process id, user id and group id. Analysis: The Linux system provide a mechanism to send UNIX credentials as ancilary data (the same way as you can pass file descriptors, implemented in Ruby as send_io). Other UNIX implementation I believe offer similar mechanism. One difference with just sending this data directly through the UNIX socket (for example as a Mashalled array containing the pid, uid and gid) is that the system does some checks on the values send (unless you are root and are allowed to lie). Implementation: The attached patch implements it. It has been tested on Linux 2.4 only. It is heavily inspired by the implementation of send_io. Usage example: server: irb(main):001:0> require 'socket' => true irb(main):002:0> s = UNIXServer.new('unixtest') => # irb(main):003:0> t = s.accept => # irb(main):004:0> t.recv_credentials => [1933, 501, 501] client: irb(main):001:0> require 'socket' => true irb(main):002:0> s = UNIXSocket.new("unixtest") => # irb(main):003:0> s.send_credentials => nil irb(main):004:0> Process.pid => 1933 Guillaume. --=-LH2RX20AHG69TpQAZ+qL Content-Disposition: attachment; filename=unix-credentials.patch Content-Transfer-Encoding: quoted-printable Content-Type: text/x-diff; charset=ISO-8859-1 --- ruby/ext/socket/socket.c Mon Apr 5 03:45:22 2004 +++ ruby-1.8.1-unix-credentials/ext/socket/socket.c Tue May 18 20:10:14 200= 4 @@ -1656,6 +1656,163 @@ } =20 static VALUE +unix_send_credentials(argc, argv, sock) + int argc; + VALUE *argv, sock; +{ +#if defined(HAVE_SENDMSG) && (defined(HAVE_ST_MSG_CONTROL) || defined(HAVE= _ST_MSG_ACCRIGHTS)) && defined(SCM_CREDENTIALS) + OpenFile *fptr; + int pid, uid, gid; + struct msghdr msg; + struct iovec vec[1]; + char buf[1]; + int len; + struct ucred *creds; +#if defined(HAVE_ST_MSG_CONTROL) + char ancillary[CMSG_SPACE(sizeof(struct ucred))]; + struct cmsghdr *cmsg; +#else + struct ucred creds_data; +#endif + =20 + GetOpenFile(sock, fptr); + + rb_scan_args(argc, argv, "03", &pid, &uid, &gid); + + /* Linux and Solaris doesn't work if msg_iov is NULL. */ + buf[0] =3D '\0'; + vec[0].iov_base =3D buf; + vec[0].iov_len =3D 1; + msg.msg_iov =3D vec; + msg.msg_iovlen =3D 1; + =20 + msg.msg_name =3D NULL; + msg.msg_namelen =3D 0; +#if defined(HAVE_ST_MSG_CONTROL) =20 + msg.msg_control =3D ancillary; + msg.msg_controllen =3D sizeof(ancillary); + msg.msg_flags =3D 0; + cmsg =3D CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len =3D CMSG_LEN(sizeof(struct ucred)); + cmsg->cmsg_level =3D SOL_SOCKET; + cmsg->cmsg_type =3D SCM_CREDENTIALS; + creds =3D (struct ucred *)CMSG_DATA(cmsg); + msg.msg_controllen =3D cmsg->cmsg_len; +#else + creds =3D &creds_data; + msg.msg_accrights =3D (caddr_t)creds; + msg.msg_accrightslen =3D sizeof(struct ucred); +#endif + + if (FIXNUM_P(pid)) { + creds->pid =3D FIX2INT(pid); + } else if (NIL_P(pid)) { + creds->pid =3D getpid(); + } else { + rb_raise(rb_eTypeError, "FixNum"); + } + if (FIXNUM_P(uid)) { + creds->uid =3D FIX2INT(uid); + } else if (NIL_P(uid)) { + creds->uid =3D getuid(); + } else { + rb_raise(rb_eTypeError, "FixNum"); + } + if (FIXNUM_P(gid)) { + creds->gid =3D FIX2INT(gid); + } else if (NIL_P(gid)) { + creds->gid =3D getgid(); + } else { + rb_raise(rb_eTypeError, "FixNum"); + } + =20 + if ((len =3D sendmsg(fileno(fptr->f), &msg, 0)) =3D=3D -1) + rb_sys_fail("sendmsg(2)"); + =20 + return Qnil; +#else=20 + rb_notimplement(); + return Qnil; /* not reached */ +#endif +} + + +static VALUE +unix_recv_credentials(sock) + VALUE sock; +{ +#if defined(HAVE_RECVMSG) && (defined(HAVE_ST_MSG_CONTROL) || defined(HAVE= _ST_MSG_ACCRIGHTS)) && defined(SCM_CREDENTIALS) + OpenFile *fptr; + struct msghdr msg; + struct iovec vec; + char data_buf[1], control_buf[1024]; + int len, true; + struct ucred *creds; +#if defined(HAVE_ST_MSG_CONTROL) + struct cmsghdr *cmsg; +#endif + =20 + GetOpenFile(sock, fptr); + =20 + thread_read_select(fileno(fptr->f)); + + true =3D 1; + if(setsockopt(fileno(fptr->f), SOL_SOCKET, SO_PASSCRED,=20 + &true, sizeof(int)) < 0) { + rb_raise(rb_eSocket, "UNIX credentials cannot be received"); + } + + msg.msg_name =3D NULL; + msg.msg_namelen =3D 0; + vec.iov_base =3D data_buf; + vec.iov_len =3D sizeof(data_buf); + msg.msg_iov =3D &vec; + msg.msg_iovlen =3D 1; + msg.msg_control =3D control_buf; + msg.msg_controllen =3D sizeof(control_buf); + msg.msg_flags =3D 0; + =20 +#if !defined(HAVE_ST_MSG_CONTROL) + msg.msg_accrights =3D (caddr_t)creds; + msg.msg_accrightslen =3D sizeof(struct ucred); +#endif + =20 + if ((len =3D recvmsg(fileno(fptr->f), &msg, 0)) =3D=3D -1) + rb_sys_fail("recvmsg(2)"); + =20 + +#if defined(HAVE_ST_MSG_CONTROL) + for(cmsg =3D CMSG_FIRSTHDR(&msg); + cmsg !=3D NULL; + cmsg =3D CMSG_NXTHDR(&msg, cmsg)) { + if(cmsg->cmsg_level =3D=3D SOL_SOCKET=20 + && cmsg->cmsg_type =3D=3D SCM_CREDENTIALS) { + creds =3D (struct ucred *)CMSG_DATA(cmsg); + break; + } + } +#endif + =20 + if( +#if defined(HAVE_ST_MSG_CONTROL) + cmsg =3D=3D NULL +#else =20 + msg.msg_accrightslen !=3D sizeof(struct ucred) +#endif =20 + ) { + rb_raise(rb_eSocket, "Credential were not passed"); + } + =20 + return rb_ary_new3(3, INT2FIX(creds->pid), INT2FIX(creds->uid), + INT2FIX(creds->gid)); + +#else + rb_notimplement(); + return Qnil; /* not reached */ +#endif +} + +static VALUE unix_accept(sock) VALUE sock; { @@ -2468,6 +2625,9 @@ rb_define_method(rb_cUNIXSocket, "recvfrom", unix_recvfrom, -1); rb_define_method(rb_cUNIXSocket, "send_io", unix_send_io, 1); rb_define_method(rb_cUNIXSocket, "recv_io", unix_recv_io, -1); + rb_define_method(rb_cUNIXSocket, "send_credentials", unix_send_credent= ials, -1); + rb_define_method(rb_cUNIXSocket, "recv_credentials", unix_recv_credent= ials, 0); + rb_define_singleton_method(rb_cUNIXSocket, "socketpair", unix_s_socket= pair, -1); rb_define_singleton_method(rb_cUNIXSocket, "pair", unix_s_socketpair, = -1); =20 --=-LH2RX20AHG69TpQAZ+qL--