From: "James F. Hranicky" Date: 2006-07-19T04:27:00+09:00 Subject: Re: Patch for Unix socket peer credentials --Boundary-00=_/XTvEdgNiSyvcWi Content-Type: Multipart/Mixed; boundary="Boundary-00=_/XTvEdgNiSyvcWi" --Boundary-00=_/XTvEdgNiSyvcWi Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: 7bit Content-Disposition: inline On Thursday 13 July 2006 22:48, nobu@ruby-lang.org wrote: > Hi, > > At Fri, 14 Jul 2006 04:04:13 +0900, > > James F. Hranicky wrote in [ruby-core:08212]: > > +if have_library("c", "getpeerucred") > > + $defs << "-DHAVE_GETPEERUCRED " > > +else > > + puts "no getpeerucred" > > +end > > have_library will append that macro automatically, if > succeeded. It didn't work for me, but I may have done something wrong. If this gets committed whoever does so is free to clean up any part of it. Attached is the latest patch. Changes from the original: - UNIXSocket::{uid,gid} are replaced with UNIXSocket::peer_cred that returns a hash of credentials { :uid => uid, ... }. peer_cred[:uid] and peer_cred[:gid] are defined to be ruid || euid || -1 and rgid || egid || -1 respectively. -1 for :uid or :gid will raise an error. - UNIXSocket::peer_cred is now a method of BasicSocket, meaning other sockets can make use of the routine if possible (apparently, local TCP sockets on Sol10 pass credentials like Unix sockets do). Depending on what the syscalls return for sockets that don't support credentials, a system error will be raised if the syscall fails, otherwise an error should be raised if uid or gid is still -1. Jim --Boundary-00=_/XTvEdgNiSyvcWi Content-Type: text/x-diff; charset="iso-8859-1"; name="ruby-sock-cred-01.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="ruby-sock-cred-01.patch" diff -ur ruby-1.8.5-preview1/ext/socket/extconf.rb ruby-1.8.5-preview1.mod/ext/socket/extconf.rb --- ruby-1.8.5-preview1/ext/socket/extconf.rb Tue Jun 6 22:40:22 2006 +++ ruby-1.8.5-preview1.mod/ext/socket/extconf.rb Tue Jul 18 13:43:37 2006 @@ -226,6 +226,20 @@ 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 -ur ruby-1.8.5-preview1/ext/socket/socket.c ruby-1.8.5-preview1.mod/ext/socket/socket.c --- ruby-1.8.5-preview1/ext/socket/socket.c Wed Jun 21 16:19:07 2006 +++ ruby-1.8.5-preview1.mod/ext/socket/socket.c Tue Jul 18 13:47:25 2006 @@ -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 @@ -1643,7 +1650,114 @@ return ipaddr((struct sockaddr*)&addr); } +/* + * Document-method: peer_cred + * call-seq: socket.peer_cred => hash + * hash[:uid] => ruid || euid + * hash[:gid] => rgid || egid + * hash[:ruid] => ruid + * hash[:euid] => euid + * hash[:rgid] => rgid + * hash[:egid] => egid + * } + * + * Returns a hash containing the credentials 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_cred[:uid]}" + * + * # Server example + * require 'socket' + * s = UNIXServer.new("/path/to/socket") + * ns = s.accept + * puts "Peer uid is #{ns.peer_cred[:uid]}" + * + */ + static VALUE +bsock_peer_cred(sock) + VALUE sock; +{ + char buf[1024]; + socklen_t len = sizeof buf; + OpenFile *fptr; + VALUE kuid, kgid, kruid, krgid, keuid, kegid, ahash; + int uid, gid, ruid, rgid, euid, egid; + uid = gid = ruid = rgid = euid = egid = -1; + +#if defined(HAVE_GETPEERUCRED) + ucred_t *creds; +#elif defined(HAVE_SO_PEERCRED) + struct ucred creds; +#else + rb_raise(rb_eSocket, "peer_cred not implemented on this platform"); +#endif + + GetOpenFile(sock, fptr); + + kuid = rb_str_intern(rb_str_new2("uid")); + kgid = rb_str_intern(rb_str_new2("gid")); + kruid = rb_str_intern(rb_str_new2("ruid")); + krgid = rb_str_intern(rb_str_new2("rgid")); + keuid = rb_str_intern(rb_str_new2("euid")); + kegid = rb_str_intern(rb_str_new2("egid")); + +#if defined(HAVE_GETPEERUCRED) + if ((creds = malloc(ucred_size())) == NULL) + rb_sys_fail("malloc"); + + if (getpeerucred(fileno(fptr->f), &creds) < 0) + rb_sys_fail("getpeerucred(2)"); + + uid = ucred_getruid(creds); + gid = ucred_getrgid(creds); + ruid = ucred_getruid(creds); + rgid = ucred_getrgid(creds); + euid = ucred_geteuid(creds); + egid = ucred_getegid(creds); + + ucred_free(creds); + +#elif defined(HAVE_SO_PEERCRED) + + if (getsockopt(fileno(fptr->f), SOL_SOCKET, SO_PEERCRED, &creds, &len) < 0) + rb_sys_fail("getsockopt"); + + uid = creds.uid; + gid = creds.gid; + euid = creds.uid; + egid = creds.gid; + +#elif defined(HAVE_GETPEEREID) + if (getpeereid(fileno(fptr->f), &euid, &egid) < 0) + rb_sys_fail("getpeereid"); + + uid = euid; + gid = egid; + +#endif + + if (uid < 0 || gid < 0) + rb_raise(rb_eSocket, "Invalid credentials: uid %d, gid %d", uid, gid); + + ahash = rb_hash_new(); + + rb_hash_aset(ahash, kuid, INT2FIX(uid)); + rb_hash_aset(ahash, kgid, INT2FIX(gid)); + rb_hash_aset(ahash, kruid, INT2FIX(ruid)); + rb_hash_aset(ahash, krgid, INT2FIX(rgid)); + rb_hash_aset(ahash, keuid, INT2FIX(euid)); + rb_hash_aset(ahash, kegid, INT2FIX(egid)); + + return ahash; + +} + +static VALUE ip_recvfrom(argc, argv, sock) int argc; VALUE *argv; @@ -3864,6 +3978,8 @@ rb_define_method(rb_cBasicSocket, "recv", bsock_recv, -1); rb_define_method(rb_cBasicSocket, "recv_nonblock", bsock_recv_nonblock, -1); + rb_define_method(rb_cBasicSocket, "peer_cred", bsock_peer_cred, 0); + rb_cIPSocket = rb_define_class("IPSocket", rb_cBasicSocket); rb_define_global_const("IPsocket", rb_cIPSocket); rb_define_method(rb_cIPSocket, "addr", ip_addr, 0); --Boundary-00=_/XTvEdgNiSyvcWi-- --Boundary-00=_/XTvEdgNiSyvcWi--