SQ2
[obnox/samba/samba-obnox.git] / nsswitch / wb_common.c
index f989d194e1d008445a4d2826617270eb96fd3d9e..44bfaf42ce7e3fe2f9ccd9d86366627cb43e1610 100644 (file)
@@ -22,6 +22,8 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+#include "replace.h"
+#include "system/select.h"
 #include "winbind_client.h"
 
 /* Global variables.  These are effectively the client state information */
@@ -62,6 +64,9 @@ static void init_response(struct winbindd_response *response)
 
 /* Close established socket */
 
+#if HAVE_FUNCTION_ATTRIBUTE_DESTRUCTOR
+__attribute__((destructor))
+#endif
 static void winbind_close_sock(void)
 {
        if (winbindd_fd != -1) {
@@ -163,6 +168,31 @@ static int make_safe_fd(int fd)
        return new_fd;
 }
 
+/**
+ * @internal
+ *
+ * @brief Check if we talk to the priviliged pipe which should be owned by root.
+ *
+ * This checks if we have uid_wrapper running and if this is the case it will
+ * allow to connect to the winbind privileged pipe even it is not owned by root.
+ *
+ * @param[in]  uid      The uid to check if we can safely talk to the pipe.
+ *
+ * @return              If we have access it returns true, else false.
+ */
+static bool winbind_privileged_pipe_is_root(uid_t uid)
+{
+       if (uid == 0) {
+               return true;
+       }
+
+       if (uid_wrapper_enabled()) {
+               return true;
+       }
+
+       return false;
+}
+
 /* Connect to winbindd socket */
 
 static int winbind_named_pipe_sock(const char *dir)
@@ -181,8 +211,12 @@ static int winbind_named_pipe_sock(const char *dir)
                return -1;
        }
 
+       /*
+        * This tells us that the pipe is owned by a privileged
+        * process, as we will be sending passwords to it.
+        */
        if (!S_ISDIR(st.st_mode) ||
-           (st.st_uid != 0 && st.st_uid != geteuid())) {
+           !winbind_privileged_pipe_is_root(st.st_uid)) {
                errno = ENOENT;
                return -1;
        }
@@ -210,8 +244,12 @@ static int winbind_named_pipe_sock(const char *dir)
        SAFE_FREE(path);
        /* Check permissions on unix socket file */
 
+       /*
+        * This tells us that the pipe is owned by a privileged
+        * process, as we will be sending passwords to it.
+        */
        if (!S_ISSOCK(st.st_mode) ||
-           (st.st_uid != 0 && st.st_uid != geteuid())) {
+           !winbind_privileged_pipe_is_root(st.st_uid)) {
                errno = ENOENT;
                return -1;
        }
@@ -230,8 +268,7 @@ static int winbind_named_pipe_sock(const char *dir)
 
        for (wait_time = 0; connect(fd, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1;
                        wait_time += slept) {
-               struct timeval tv;
-               fd_set w_fds;
+               struct pollfd pfd;
                int ret;
                int connect_errno = 0;
                socklen_t errnosize;
@@ -241,12 +278,10 @@ static int winbind_named_pipe_sock(const char *dir)
 
                switch (errno) {
                        case EINPROGRESS:
-                               FD_ZERO(&w_fds);
-                               FD_SET(fd, &w_fds);
-                               tv.tv_sec = CONNECT_TIMEOUT - wait_time;
-                               tv.tv_usec = 0;
+                               pfd.fd = fd;
+                               pfd.events = POLLOUT;
 
-                               ret = select(fd + 1, NULL, &w_fds, NULL, &tv);
+                               ret = poll(&pfd, 1, (CONNECT_TIMEOUT - wait_time) * 1000);
 
                                if (ret > 0) {
                                        errnosize = sizeof(connect_errno);
@@ -284,14 +319,14 @@ static int winbind_named_pipe_sock(const char *dir)
 
 static const char *winbindd_socket_dir(void)
 {
-#ifdef SOCKET_WRAPPER
-       const char *env_dir;
+       if (nss_wrapper_enabled()) {
+               const char *env_dir;
 
-       env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR);
-       if (env_dir) {
-               return env_dir;
+               env_dir = getenv("SELFTEST_WINBINDD_SOCKET_DIR");
+               if (env_dir != NULL) {
+                       return env_dir;
+               }
        }
-#endif
 
        return WINBINDD_SOCKET_DIR;
 }
@@ -341,6 +376,13 @@ static int winbind_open_pipe_sock(int recursing, int need_priv)
        /* try and get priv pipe */
 
        request.wb_flags = WBFLAG_RECURSE;
+
+       /* Note that response needs to be initialized to avoid
+        * crashing on clean up after WINBINDD_PRIV_PIPE_DIR call failed
+        * as interface version (from the first request) returned as a fstring,
+        * thus response.extra_data.data will not be NULL even though
+        * winbindd response did not write over it due to a failure */
+       ZERO_STRUCT(response);
        if (winbindd_request_response(WINBINDD_PRIV_PIPE_DIR, &request, &response) == NSS_STATUS_SUCCESS) {
                int fd;
                if ((fd = winbind_named_pipe_sock((char *)response.extra_data.data)) != -1) {
@@ -367,13 +409,14 @@ static int winbind_open_pipe_sock(int recursing, int need_priv)
 static int winbind_write_sock(void *buffer, int count, int recursing,
                              int need_priv)
 {
-       int result, nwritten;
+       int fd, result, nwritten;
 
        /* Open connection to winbind daemon */
 
  restart:
 
-       if (winbind_open_pipe_sock(recursing, need_priv) == -1) {
+       fd = winbind_open_pipe_sock(recursing, need_priv);
+       if (fd == -1) {
                errno = ENOENT;
                return -1;
        }
@@ -383,48 +426,45 @@ static int winbind_write_sock(void *buffer, int count, int recursing,
        nwritten = 0;
 
        while(nwritten < count) {
-               struct timeval tv;
-               fd_set r_fds;
+               struct pollfd pfd;
+               int ret;
 
                /* Catch pipe close on other end by checking if a read()
-                  call would not block by calling select(). */
+                  call would not block by calling poll(). */
 
-               FD_ZERO(&r_fds);
-               FD_SET(winbindd_fd, &r_fds);
-               ZERO_STRUCT(tv);
+               pfd.fd = fd;
+               pfd.events = POLLIN|POLLOUT|POLLHUP;
 
-               if (select(winbindd_fd + 1, &r_fds, NULL, NULL, &tv) == -1) {
+               ret = poll(&pfd, 1, -1);
+               if (ret == -1) {
                        winbind_close_sock();
-                       return -1;                   /* Select error */
+                       return -1;                   /* poll error */
                }
 
                /* Write should be OK if fd not available for reading */
 
-               if (!FD_ISSET(winbindd_fd, &r_fds)) {
-
-                       /* Do the write */
+               if ((ret == 1) && (pfd.revents & (POLLIN|POLLHUP|POLLERR))) {
 
-                       result = write(winbindd_fd,
-                                      (char *)buffer + nwritten,
-                                      count - nwritten);
-
-                       if ((result == -1) || (result == 0)) {
+                       /* Pipe has closed on remote end */
 
-                               /* Write failed */
+                       winbind_close_sock();
+                       goto restart;
+               }
 
-                               winbind_close_sock();
-                               return -1;
-                       }
+               /* Do the write */
 
-                       nwritten += result;
+               result = write(fd, (char *)buffer + nwritten,
+                              count - nwritten);
 
-               } else {
+               if ((result == -1) || (result == 0)) {
 
-                       /* Pipe has closed on remote end */
+                       /* Write failed */
 
                        winbind_close_sock();
-                       goto restart;
+                       return -1;
                }
+
+               nwritten += result;
        }
 
        return nwritten;
@@ -434,33 +474,35 @@ static int winbind_write_sock(void *buffer, int count, int recursing,
 
 static int winbind_read_sock(void *buffer, int count)
 {
+       int fd;
        int nread = 0;
-       int total_time = 0, selret;
+       int total_time = 0;
 
-       if (winbindd_fd == -1) {
+       fd = winbind_open_pipe_sock(false, false);
+       if (fd == -1) {
                return -1;
        }
 
        /* Read data from socket */
        while(nread < count) {
-               struct timeval tv;
-               fd_set r_fds;
+               struct pollfd pfd;
+               int ret;
 
                /* Catch pipe close on other end by checking if a read()
-                  call would not block by calling select(). */
+                  call would not block by calling poll(). */
+
+               pfd.fd = fd;
+               pfd.events = POLLIN|POLLHUP;
 
-               FD_ZERO(&r_fds);
-               FD_SET(winbindd_fd, &r_fds);
-               ZERO_STRUCT(tv);
                /* Wait for 5 seconds for a reply. May need to parameterise this... */
-               tv.tv_sec = 5;
 
-               if ((selret = select(winbindd_fd + 1, &r_fds, NULL, NULL, &tv)) == -1) {
+               ret = poll(&pfd, 1, 5000);
+               if (ret == -1) {
                        winbind_close_sock();
-                       return -1;                   /* Select error */
+                       return -1;                   /* poll error */
                }
 
-               if (selret == 0) {
+               if (ret == 0) {
                        /* Not ready for read yet... */
                        if (total_time >= 30) {
                                /* Timeout */
@@ -471,11 +513,11 @@ static int winbind_read_sock(void *buffer, int count)
                        continue;
                }
 
-               if (FD_ISSET(winbindd_fd, &r_fds)) {
+               if ((ret == 1) && (pfd.revents & (POLLIN|POLLHUP|POLLERR))) {
 
                        /* Do the Read */
 
-                       int result = read(winbindd_fd, (char *)buffer + nread,
+                       int result = read(fd, (char *)buffer + nread,
                              count - nread);
 
                        if ((result == -1) || (result == 0)) {
@@ -514,6 +556,10 @@ static int winbindd_read_reply(struct winbindd_response *response)
                return -1;
        }
 
+       if (response->length < sizeof(struct winbindd_response)) {
+               return -1;
+       }
+
        /* We actually send the pointer value of the extra_data field from
           the server.  This has no meaning in the client's address space
           so we clear it out. */
@@ -667,26 +713,3 @@ NSS_STATUS winbindd_priv_request_response(int req_type,
 
        return status;
 }
-
-/*************************************************************************
- ************************************************************************/
-
-const char *nss_err_str(NSS_STATUS ret)
-{
-       switch (ret) {
-               case NSS_STATUS_TRYAGAIN:
-                       return "NSS_STATUS_TRYAGAIN";
-               case NSS_STATUS_SUCCESS:
-                       return "NSS_STATUS_SUCCESS";
-               case NSS_STATUS_NOTFOUND:
-                       return "NSS_STATUS_NOTFOUND";
-               case NSS_STATUS_UNAVAIL:
-                       return "NSS_STATUS_UNAVAIL";
-#ifdef NSS_STATUS_RETURN
-               case NSS_STATUS_RETURN:
-                       return "NSS_STATUS_RETURN";
-#endif
-               default:
-                       return "UNKNOWN RETURN CODE!!!!!!!";
-       }
-}