diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index a02fb4e33..4e77c597e 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,25 @@ +2005-02-23 Corinna Vinschen + + * cygwin.din (getpeereid): Export. + * fhandler.h (class fhandler_socket): Add pipe and id members to + exchange eid credentials for AF_LOCAL sockets. + (eid_pipe_name): Declare new method. + (getpeereid): Ditto. + * fhandler_socket.cc (fhandler_socket::eid_pipe_name): New method. + (fhandler_socket::fhandler_socket): Initialize sec_pipe. + (fhandler_socket::connect): Exchange eid credentials with accepting + socket process. + (fhandler_socket::listen): Prepare eid credential transaction. + (fhandler_socket::accept): Exchange eid credentials with connecting + socket process. + (fhandler_socket::close): Close eid credentials pipe if open. + (fhandler_socket::getpeereid): New method. + * net.cc (cygwin_getsockopt): Add SO_PEERCRED handling. + (getpeereid): New function. + * include/asm/socket.h (SO_PEERCRED): Define. + * include/cygwin/socket.h (struct ucred): Define new type. + * include/cygwin/version.h: Bump API minor version. + 2005-02-23 Corinna Vinschen * include/sys/statvfs.h (ST_RDONLY): Define. diff --git a/winsup/cygwin/cygwin.din b/winsup/cygwin/cygwin.din index dd8e5a8c4..594f114e7 100644 --- a/winsup/cygwin/cygwin.din +++ b/winsup/cygwin/cygwin.din @@ -307,6 +307,7 @@ gethostbyaddr = cygwin_gethostbyaddr SIGFE gethostbyname = cygwin_gethostbyname SIGFE _gethostname = cygwin_gethostname SIGFE gethostname = cygwin_gethostname SIGFE +getpeereid SIGFE getpeername = cygwin_getpeername SIGFE getprogname NOSIGFE getprotobyname = cygwin_getprotobyname SIGFE diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index 3199351ce..d3250d1b2 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -355,6 +355,16 @@ class fhandler_socket: public fhandler_base int type; int connect_secret [4]; HANDLE secret_event; + + HANDLE sec_pipe; + pid_t sec_pid; + __uid32_t sec_uid; + __gid32_t sec_gid; + pid_t sec_peer_pid; + __uid32_t sec_peer_uid; + __gid32_t sec_peer_gid; + char *eid_pipe_name (char *buf); + struct _WSAPROTOCOL_INFOA *prot_info_ptr; char *sun_path; struct status_flags @@ -395,6 +405,7 @@ class fhandler_socket: public fhandler_base int accept (struct sockaddr *peer, int *len); int getsockname (struct sockaddr *name, int *namelen); int getpeername (struct sockaddr *name, int *namelen); + int getpeereid (pid_t *pid, __uid32_t *euid, __gid32_t *egid); int open (int flags, mode_t mode = 0); ssize_t readv (const struct iovec *, int iovcnt, ssize_t tot = -1); diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc index 9f0b39f53..b5b4847ec 100644 --- a/winsup/cygwin/fhandler_socket.cc +++ b/winsup/cygwin/fhandler_socket.cc @@ -53,6 +53,13 @@ secret_event_name (char *buf, short port, int *secret_ptr) secret_ptr [2], secret_ptr [3]); } +char * +fhandler_socket::eid_pipe_name (char *buf) +{ + __small_sprintf (buf, "\\\\.\\pipe\\cygwin-unix-$s", get_sun_path ()); + return buf; +} + /* cygwin internal: map sockaddr into internet domain address */ static int get_inet_addr (const struct sockaddr *in, int inlen, @@ -124,6 +131,7 @@ get_inet_addr (const struct sockaddr *in, int inlen, fhandler_socket::fhandler_socket () : fhandler_base (), + sec_pipe (INVALID_HANDLE_VALUE), sun_path (NULL), status () { @@ -620,6 +628,22 @@ fhandler_socket::connect (const struct sockaddr *name, int namelen) set_errno (ECONNREFUSED); res = -1; } + + /* eid credential transaction. */ + struct ucred in = { getpid (), geteuid32 (), getegid32 () }; + struct ucred out = { (pid_t) -1, (__uid32_t) -1, (__gid32_t) -1 }; + DWORD bytes = 0; + if (CallNamedPipe(eid_pipe_name ((char *) alloca (CYG_MAX_PATH + 1)), + &in, sizeof in, &out, sizeof out, &bytes, 1000)) + { + debug_printf ("Received eid credentials: pid: %d, uid: %d, gid: %d", + out.pid, out.uid, out.gid); + sec_peer_pid = out.pid; + sec_peer_uid = out.uid; + sec_peer_gid = out.gid; + } + else + debug_printf ("Receiving eid credentials failed: %E"); } err = WSAGetLastError (); @@ -638,7 +662,26 @@ fhandler_socket::listen (int backlog) if (res) set_winsock_errno (); else - connect_state (connected); + { + if (get_addr_family () == AF_LOCAL && get_socket_type () == SOCK_STREAM) + { + /* Prepare eid credential transaction. */ + sec_pid = getpid (); + sec_uid = geteuid32 (); + sec_gid = getegid32 (); + sec_peer_pid = (pid_t) -1; + sec_peer_uid = (__uid32_t) -1; + sec_peer_gid = (__gid32_t) -1; + sec_pipe = + CreateNamedPipe (eid_pipe_name ((char *) alloca (CYG_MAX_PATH + 1)), + PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, + 1, sizeof (struct ucred), sizeof (struct ucred), + 1000, &sec_all); + debug_printf ("sec_pipe: %x", sec_pipe); + } + connect_state (connected); + } return res; } @@ -648,6 +691,8 @@ fhandler_socket::accept (struct sockaddr *peer, int *len) int res = -1; bool secret_check_failed = false; bool in_progress = false; + struct ucred in = { sec_pid, sec_uid, sec_gid }; + struct ucred out = { (pid_t) -1, (__uid32_t) -1, (__gid32_t) -1 }; /* Allows NULL peer and len parameters. */ struct sockaddr_in peer_dummy; @@ -700,6 +745,23 @@ fhandler_socket::accept (struct sockaddr *peer, int *len) set_errno (ECONNABORTED); return -1; } + + /* eid credential transaction. */ + DWORD bytes = 0; + bool ret = ConnectNamedPipe (sec_pipe, NULL); + if (ret || GetLastError () == ERROR_PIPE_CONNECTED) + { + if (!ReadFile (sec_pipe, &out, sizeof out, &bytes, NULL)) + debug_printf ("Receiving eid credentials failed: %E"); + else + debug_printf ("Received eid credentials: pid: %d, uid: %d, gid: %d", + out.pid, out.uid, out.gid); + if (!WriteFile (sec_pipe, &in, sizeof in, &bytes, NULL)) + debug_printf ("Sending eid credentials failed: %E"); + DisconnectNamedPipe (sec_pipe); + } + else + debug_printf ("Connecting the eid credential pipe failed: %E"); } if ((SOCKET) res == INVALID_SOCKET) @@ -709,10 +771,18 @@ fhandler_socket::accept (struct sockaddr *peer, int *len) cygheap_fdnew res_fd; if (res_fd >= 0 && fdsock (res_fd, &dev (), res)) { - if (get_addr_family () == AF_LOCAL) - ((fhandler_socket *) res_fd)->set_sun_path (get_sun_path ()); ((fhandler_socket *) res_fd)->set_addr_family (get_addr_family ()); ((fhandler_socket *) res_fd)->set_socket_type (get_socket_type ()); + if (get_addr_family () == AF_LOCAL) + { + ((fhandler_socket *) res_fd)->set_sun_path (get_sun_path ()); + if (get_socket_type () == SOCK_STREAM) + { + ((fhandler_socket *) res_fd)->sec_peer_pid = out.pid; + ((fhandler_socket *) res_fd)->sec_peer_uid = out.uid; + ((fhandler_socket *) res_fd)->sec_peer_gid = out.gid; + } + } ((fhandler_socket *) res_fd)->connect_state (connected); res = res_fd; } @@ -1322,6 +1392,10 @@ fhandler_socket::close () setsockopt (get_socket (), SOL_SOCKET, SO_LINGER, (const char *)&linger, sizeof linger); + /* Close eid credentials pipe handle. */ + if (sec_pipe != INVALID_HANDLE_VALUE) + CloseHandle (sec_pipe); + while ((res = closesocket (get_socket ())) != 0) { if (WSAGetLastError () != WSAEWOULDBLOCK) @@ -1539,3 +1613,25 @@ fhandler_socket::set_sun_path (const char *path) { sun_path = path ? cstrdup (path) : NULL; } + +int +fhandler_socket::getpeereid (pid_t *pid, __uid32_t *euid, __gid32_t *egid) +{ + if (get_addr_family () == AF_LOCAL && get_socket_type () == SOCK_STREAM) + { + if (connect_state () == connected && sec_peer_pid != (pid_t) -1) + { + if (!check_null_invalid_struct (pid)) + *pid = sec_peer_pid; + if (!check_null_invalid_struct (euid)) + *euid = sec_peer_uid; + if (!check_null_invalid_struct (egid)) + *egid = sec_peer_gid; + return 0; + } + set_errno (ENOTCONN); + } + else + set_errno (EINVAL); + return -1; +} diff --git a/winsup/cygwin/include/asm/socket.h b/winsup/cygwin/include/asm/socket.h index b1392ce22..ed062190c 100644 --- a/winsup/cygwin/include/asm/socket.h +++ b/winsup/cygwin/include/asm/socket.h @@ -54,6 +54,7 @@ details. */ #define SO_LINGER 0x0080 /* linger on close if data present */ #define SO_OOBINLINE 0x0100 /* leave received OOB data in line */ #define SO_DONTLINGER (u_int)(~SO_LINGER) +#define SO_PEERCRED 0x0200 /* same as getpeereid */ /* * Additional options. diff --git a/winsup/cygwin/include/cygwin/socket.h b/winsup/cygwin/include/cygwin/socket.h index 67e1345f2..f6dc11353 100644 --- a/winsup/cygwin/include/cygwin/socket.h +++ b/winsup/cygwin/include/cygwin/socket.h @@ -25,6 +25,12 @@ struct sockaddr { #include /* iovec support */ #include +struct ucred { + pid_t pid; + __uid32_t uid; + __gid32_t gid; +}; + struct linger { unsigned short l_onoff; /* Linger active */ unsigned short l_linger; /* How long to linger for */ diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h index 10cf2ba2d..11db44cc0 100644 --- a/winsup/cygwin/include/cygwin/version.h +++ b/winsup/cygwin/include/cygwin/version.h @@ -248,13 +248,13 @@ details. */ 118: Export getpriority, setpriority. 119: Export fdatasync. 120: Export basename, dirname. - 121: Export statvfs, fstatvfs. + 122: Export statvfs, fstatvfs. */ /* Note that we forgot to bump the api for ualarm, strtoll, strtoull */ #define CYGWIN_VERSION_API_MAJOR 0 -#define CYGWIN_VERSION_API_MINOR 121 +#define CYGWIN_VERSION_API_MINOR 122 /* There is also a compatibity version number associated with the shared memory regions. It is incremented when incompatible diff --git a/winsup/cygwin/net.cc b/winsup/cygwin/net.cc index 33356d13e..98e121c5a 100644 --- a/winsup/cygwin/net.cc +++ b/winsup/cygwin/net.cc @@ -780,6 +780,8 @@ cygwin_getsockopt (int fd, int level, int optname, void *optval, int *optlen) case SO_ERROR: name = "SO_ERROR"; break; + case SO_PEERCRED: + name = "SO_PEERCRED"; } if ((optval @@ -787,6 +789,11 @@ cygwin_getsockopt (int fd, int level, int optname, void *optval, int *optlen) || __check_null_invalid_struct_errno (optval, (unsigned) *optlen))) || !fh) res = -1; + else if (optname == SO_PEERCRED) + { + struct ucred *cred = (struct ucred *) optval; + res = fh->getpeereid (&cred->pid, &cred->uid, &cred->gid); + } else { res = getsockopt (fh->get_socket (), level, optname, (char *) optval, @@ -808,6 +815,16 @@ cygwin_getsockopt (int fd, int level, int optname, void *optval, int *optlen) return res; } +extern "C" int +getpeereid (int fd, __uid32_t *euid, __gid32_t *egid) +{ + sig_dispatch_pending (); + fhandler_socket *fh = get (fd); + if (fh) + return fh->getpeereid (NULL, euid, egid); + return -1; +} + /* exported as connect: standards? */ extern "C" int cygwin_connect (int fd, const struct sockaddr *name, int namelen)