From: "James F. Hranicky" Date: 2006-06-10T04:28:36+09:00 Subject: Patch for Unix socket peer credentials --Boundary-00=_evciEnzf9N+e4Eh Content-Type: Multipart/Mixed; boundary="Boundary-00=_evciEnzf9N+e4Eh" --Boundary-00=_evciEnzf9N+e4Eh Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline This patch adds support for getting the uid and gid of the peer socket connected to a Unix domain socket. The patch is really just a suggestion, as you can't necessarily get the same kinds of information across platforms. Unless I'm mistaken this is what you can get for the following platforms: FreeBSD, OSX (getpeereid) : euid, egid Linux (getsockopt) : pid, uid, gid Solaris 10 (getpeerucred) : uid, euid, gid, egid, pid and more The patch adds checks to extconf.rb for the getpeereid and getpeerucred functions and a check for the SO_PEERCRED macro. If none of these are found calls to the peer_uid and peer_gid method raise an error. Questions and comments welcome, and I'm open to suggestions for changes/improvements. Jim --Boundary-00=_evciEnzf9N+e4Eh Content-Type: text/x-diff; charset="us-ascii"; name="ruby-sock-cred.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="ruby-sock-cred.patch" diff -ru ruby-1.8.4/ext/socket/extconf.rb ruby-1.8.4.mod/ext/socket/extconf.rb --- ruby-1.8.4/ext/socket/extconf.rb 2005-07-19 15:04:46.000000000 -0400 +++ ruby-1.8.4.mod/ext/socket/extconf.rb 2006-06-09 12:04:47.518827000 -0400 @@ -251,6 +251,21 @@ EOS end +if have_library("c", "getpeerucred") + $defs << "-DHAVE_GETPEERUCRED " +else + puts "no getpeerucred" +end + +if have_library("c", "getpeereid") + $defs << "-DHAVE_GETPEEREID " +end + +if have_macro("SO_PEERCRED", "sys/socket.h") + $defs << "-DHAVE_SO_PEERCRED " +end + + case with_config("lookup-order-hack", "UNSPEC") when "INET" $defs << "-DLOOKUP_ORDER_HACK_INET" diff -ru ruby-1.8.4/ext/socket/socket.c ruby-1.8.4.mod/ext/socket/socket.c --- ruby-1.8.4/ext/socket/socket.c 2005-11-28 04:56:45.000000000 -0500 +++ ruby-1.8.4.mod/ext/socket/socket.c 2006-06-09 14:33:45.199625000 -0400 @@ -71,6 +71,13 @@ #endif #include "sockport.h" +#if defined(HAVE_GETPEERUCRED) +#include +#elif defined(HAVE_GETPEEREID) +#include +#include +#endif + #if defined(__vms) #include #endif @@ -1916,6 +1923,142 @@ addr.sun_path[0] = '\0'; return unixaddr(&addr); } + +/* + * Document-method: peer_uid + * call-seq: socket.peer_uid => int + * + * Returns the uid of the peer socket for Unix domain stream sockets + * + * === Example + * # Client example + * require 'socket' + * s = UNIXSocket.new("/path/to/socket") + * puts "Peer uid is #{s.peer_uid}" + * + * # Server example + * require 'socket' + * s = UNIXServer.new("/path/to/socket") + * ns = s.accept + * puts "Peer uid is #{ns.peer_uid}" + * + */ + +static VALUE +unix_peer_uid(sock) + VALUE sock; +{ + char buf[1024]; + socklen_t len = sizeof buf; + OpenFile *fptr; + int uid; + +#if defined(HAVE_GETPEERUCRED) + ucred_t *creds; +#elif defined(HAVE_SO_PEERCRED) + struct ucred creds; +#elif defined(HAVE_GETPEEREID) + int gid; +#else + rb_raise(rb_eSocket, "UNIXSocket::uid not implemented on this platform"); +#endif + + GetOpenFile(sock, fptr); + +#if defined(HAVE_GETPEERUCRED) + if ((creds = malloc(ucred_size())) < 0) + rb_sys_fail("malloc"); + + if (getpeerucred(fileno(fptr->f), &creds) < 0) + rb_sys_fail("getpeerucred(2)"); + + uid = ucred_getruid(creds); + ucred_free(creds); + + return INT2FIX(uid); + +#elif defined(HAVE_SO_PEERCRED) + + if (getsockopt(fileno(fptr->f), SOL_SOCKET, SO_PEERCRED, &creds, &len) < 0) + rb_sys_fail("getsockopt"); + + return INT2FIX(creds.uid); + +#elif defined(HAVE_GETPEEREID) + if (getpeereid(fileno(fptr->f), &uid, &gid) < 0) + rb_sys_fail("getpeereid"); + + return INT2FIX(uid); +#endif +} + +/* + * Document-method: peer_gid + * call-seq: socket.peer_gid => int + * + * Returns the gid of the peer socket for Unix domain stream sockets + * + * === Example + * # Client example + * require 'socket' + * s = UNIXSocket.new("/path/to/socket") + * puts "Peer gid is #{s.peer_gid}" + * + * # Server example + * require 'socket' + * s = UNIXServer.new("/path/to/socket") + * ns = s.accept + * puts "Peer gid is #{ns.peer_gid}" + * + */ + +static VALUE +unix_peer_gid(sock) + VALUE sock; +{ + char buf[1024]; + socklen_t len = sizeof buf; + OpenFile *fptr; + int gid; + +#if defined(HAVE_GETPEERUCRED) + ucred_t *creds; +#elif defined(HAVE_SO_PEERCRED) + struct ucred creds; +#elif defined(HAVE_GETPEEREID) + int uid; +#else + rb_raise(rb_eSocket, "UNIXSocket::gid not implemented on this platform"); +#endif + + GetOpenFile(sock, fptr); + +#if defined(HAVE_GETPEERUCRED) + if ((creds = malloc(ucred_size())) < 0) + rb_sys_fail("malloc"); + + if (getpeerucred(fileno(fptr->f), &creds) < 0) + rb_sys_fail("getpeerucred(2)"); + + gid = ucred_getrgid(creds); + ucred_free(creds); + + return INT2FIX(gid); + +#elif defined(HAVE_SO_PEERCRED) + + if (getsockopt(fileno(fptr->f), SOL_SOCKET, SO_PEERCRED, &creds, &len) < 0) + rb_sys_fail("getsockopt"); + + return INT2FIX(creds.gid); + +#elif defined(HAVE_GETPEEREID) + if (getpeereid(fileno(fptr->f), &uid, &gid) < 0) + rb_sys_fail("getpeereid"); + + return INT2FIX(gid); +#endif +} #endif static void @@ -3199,6 +3342,8 @@ 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, "peer_uid", unix_peer_uid, 0); + rb_define_method(rb_cUNIXSocket, "peer_gid", unix_peer_gid, 0); rb_define_singleton_method(rb_cUNIXSocket, "socketpair", unix_s_socketpair, -1); rb_define_singleton_method(rb_cUNIXSocket, "pair", unix_s_socketpair, -1); --Boundary-00=_evciEnzf9N+e4Eh-- --Boundary-00=_evciEnzf9N+e4Eh--