LCOV - code coverage report
Current view: top level - nsswitch - wb_common.c (source / functions) Hit Total Coverage
Test: coverage report for master 2b515b7d Lines: 301 393 76.6 %
Date: 2024-02-28 12:06:22 Functions: 30 30 100.0 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             : 
       4             :    winbind client common code
       5             : 
       6             :    Copyright (C) Tim Potter 2000
       7             :    Copyright (C) Andrew Tridgell 2000
       8             :    Copyright (C) Andrew Bartlett 2002
       9             :    Copyright (C) Matthew Newton 2015
      10             : 
      11             : 
      12             :    This library is free software; you can redistribute it and/or
      13             :    modify it under the terms of the GNU Lesser General Public
      14             :    License as published by the Free Software Foundation; either
      15             :    version 3 of the License, or (at your option) any later version.
      16             : 
      17             :    This library is distributed in the hope that it will be useful,
      18             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      19             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      20             :    Library General Public License for more details.
      21             : 
      22             :    You should have received a copy of the GNU Lesser General Public License
      23             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      24             : */
      25             : 
      26             : #include "replace.h"
      27             : #include "system/select.h"
      28             : #include "winbind_client.h"
      29             : #include "lib/util/dlinklist.h"
      30             : #include <assert.h>
      31             : 
      32             : #ifdef HAVE_PTHREAD_H
      33             : #include <pthread.h>
      34             : #endif
      35             : 
      36             : static __thread char client_name[32];
      37             : 
      38             : /* Global context */
      39             : 
      40             : struct winbindd_context {
      41             :         struct winbindd_context *prev, *next;
      42             :         int winbindd_fd;        /* winbind file descriptor */
      43             :         bool is_privileged;     /* using the privileged socket? */
      44             :         pid_t our_pid;          /* calling process pid */
      45             :         bool autofree;          /* this is a thread global context */
      46             : };
      47             : 
      48             : static struct wb_global_ctx {
      49             : #ifdef HAVE_PTHREAD
      50             :         pthread_once_t control;
      51             :         pthread_key_t key;
      52             :         bool key_initialized;
      53             : #ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
      54             : #define WB_GLOBAL_MUTEX_INITIALIZER PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
      55             : #else
      56             : #define WB_GLOBAL_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
      57             : #endif
      58             : #define WB_GLOBAL_LIST_LOCK do { \
      59             :         int __pret = pthread_mutex_lock(&wb_global_ctx.list_mutex); \
      60             :         assert(__pret == 0); \
      61             : } while(0)
      62             : #define WB_GLOBAL_LIST_UNLOCK do { \
      63             :         int __pret = pthread_mutex_unlock(&wb_global_ctx.list_mutex); \
      64             :         assert(__pret == 0); \
      65             : } while(0)
      66             :         pthread_mutex_t list_mutex;
      67             : #else /* => not HAVE_PTHREAD */
      68             : #define WB_GLOBAL_LIST_LOCK do { } while(0)
      69             : #define WB_GLOBAL_LIST_UNLOCK do { } while(0)
      70             : #endif /* not HAVE_PTHREAD */
      71             :         struct winbindd_context *list;
      72             : } wb_global_ctx = {
      73             : #ifdef HAVE_PTHREAD
      74             :         .control = PTHREAD_ONCE_INIT,
      75             :         .list_mutex = WB_GLOBAL_MUTEX_INITIALIZER,
      76             : #endif
      77             :         .list = NULL,
      78             : };
      79             : 
      80             : static void winbind_close_sock(struct winbindd_context *ctx);
      81             : static void winbind_ctx_free_locked(struct winbindd_context *ctx);
      82             : static void winbind_cleanup_list(void);
      83             : 
      84             : #ifdef HAVE_PTHREAD
      85             : static void wb_thread_ctx_initialize(void);
      86             : 
      87    42804644 : static void wb_atfork_prepare(void)
      88             : {
      89    42804644 :         WB_GLOBAL_LIST_LOCK;
      90    42804644 : }
      91             : 
      92    42759931 : static void wb_atfork_parent(void)
      93             : {
      94    42759931 :         WB_GLOBAL_LIST_UNLOCK;
      95    42759931 : }
      96             : 
      97       44713 : static void wb_atfork_child(void)
      98             : {
      99       44713 :         wb_global_ctx.list_mutex = (pthread_mutex_t)WB_GLOBAL_MUTEX_INITIALIZER;
     100             : 
     101       44713 :         if (wb_global_ctx.key_initialized) {
     102         842 :                 int ret;
     103             : 
     104             :                 /*
     105             :                  * After a fork the child still believes
     106             :                  * it is the same thread as in the parent.
     107             :                  * So pthread_getspecific() would return the
     108             :                  * value of the thread that called fork().
     109             :                  *
     110             :                  * But we don't want that behavior, so
     111             :                  * we just clear the reference and let
     112             :                  * winbind_cleanup_list() below 'autofree'
     113             :                  * the parent threads global context.
     114             :                  */
     115       44713 :                 ret = pthread_setspecific(wb_global_ctx.key, NULL);
     116       44713 :                 assert(ret == 0);
     117             :         }
     118             : 
     119             :         /*
     120             :          * But we need to close/cleanup the global state
     121             :          * of the parents threads.
     122             :          */
     123       44713 :         winbind_cleanup_list();
     124       44713 : }
     125             : 
     126           4 : static void wb_thread_ctx_destructor(void *p)
     127             : {
     128           4 :         struct winbindd_context *ctx = (struct winbindd_context *)p;
     129             : 
     130           4 :         winbindd_ctx_free(ctx);
     131           4 : }
     132             : 
     133       52181 : static void wb_thread_ctx_initialize(void)
     134             : {
     135         912 :         int ret;
     136             : 
     137       52181 :         ret = pthread_atfork(wb_atfork_prepare,
     138             :                              wb_atfork_parent,
     139             :                              wb_atfork_child);
     140       52181 :         assert(ret == 0);
     141             : 
     142       52181 :         ret = pthread_key_create(&wb_global_ctx.key,
     143             :                                  wb_thread_ctx_destructor);
     144       52181 :         assert(ret == 0);
     145             : 
     146       52181 :         wb_global_ctx.key_initialized = true;
     147       52181 : }
     148             : 
     149     1153696 : static struct winbindd_context *get_wb_thread_ctx(void)
     150             : {
     151     1153696 :         struct winbindd_context *ctx = NULL;
     152        5059 :         int ret;
     153             : 
     154     1153696 :         ret = pthread_once(&wb_global_ctx.control,
     155             :                            wb_thread_ctx_initialize);
     156     1153696 :         assert(ret == 0);
     157             : 
     158     1153696 :         ctx = (struct winbindd_context *)pthread_getspecific(
     159             :                 wb_global_ctx.key);
     160     1153696 :         if (ctx != NULL) {
     161     1041389 :                 return ctx;
     162             :         }
     163             : 
     164      109642 :         ctx = malloc(sizeof(struct winbindd_context));
     165      109642 :         if (ctx == NULL) {
     166           0 :                 return NULL;
     167             :         }
     168             : 
     169      109642 :         *ctx = (struct winbindd_context) {
     170             :                 .winbindd_fd = -1,
     171             :                 .is_privileged = false,
     172             :                 .our_pid = 0,
     173             :                 .autofree = true,
     174             :         };
     175             : 
     176      109642 :         WB_GLOBAL_LIST_LOCK;
     177      109642 :         DLIST_ADD_END(wb_global_ctx.list, ctx);
     178      109642 :         WB_GLOBAL_LIST_UNLOCK;
     179             : 
     180      109642 :         ret = pthread_setspecific(wb_global_ctx.key, ctx);
     181      109642 :         if (ret != 0) {
     182           0 :                 free(ctx);
     183           0 :                 return NULL;
     184             :         }
     185      107248 :         return ctx;
     186             : }
     187             : #endif /* HAVE_PTHREAD */
     188             : 
     189     1153696 : static struct winbindd_context *get_wb_global_ctx(void)
     190             : {
     191     1153696 :         struct winbindd_context *ctx = NULL;
     192             : #ifndef HAVE_PTHREAD
     193             :         static struct winbindd_context _ctx = {
     194             :                 .winbindd_fd = -1,
     195             :                 .is_privileged = false,
     196             :                 .our_pid = 0,
     197             :                 .autofree = false,
     198             :         };
     199             : #endif
     200             : 
     201             : #ifdef HAVE_PTHREAD
     202     1153696 :         ctx = get_wb_thread_ctx();
     203             : #else
     204             :         ctx = &_ctx;
     205             :         if (ctx->prev == NULL && ctx->next == NULL) {
     206             :                 DLIST_ADD_END(wb_global_ctx.list, ctx);
     207             :         }
     208             : #endif
     209             : 
     210     1153696 :         return ctx;
     211             : }
     212             : 
     213      141386 : void winbind_set_client_name(const char *name)
     214             : {
     215      141386 :         if (name == NULL || strlen(name) == 0) {
     216           0 :                 return;
     217             :         }
     218             : 
     219      141386 :         (void)snprintf(client_name, sizeof(client_name), "%s", name);
     220             : }
     221             : 
     222     1137903 : static const char *winbind_get_client_name(void)
     223             : {
     224     1137903 :         if (client_name[0] == '\0') {
     225       38317 :                 const char *progname = getprogname();
     226         854 :                 int len;
     227             : 
     228       38317 :                 if (progname == NULL) {
     229           0 :                         progname = "<unknown>";
     230             :                 }
     231             : 
     232       38317 :                 len = snprintf(client_name,
     233             :                                sizeof(client_name),
     234             :                                "%s",
     235             :                                progname);
     236       38317 :                 if (len <= 0) {
     237           0 :                         return progname;
     238             :                 }
     239             :         }
     240             : 
     241     1131303 :         return client_name;
     242             : }
     243             : 
     244             : /* Initialise a request structure */
     245             : 
     246     1137903 : static void winbindd_init_request(struct winbindd_request *request,
     247             :                                   int request_type)
     248             : {
     249     1137903 :         request->length = sizeof(struct winbindd_request);
     250             : 
     251     1137903 :         request->cmd = (enum winbindd_cmd)request_type;
     252     1137903 :         request->pid = getpid();
     253             : 
     254     1137903 :         (void)snprintf(request->client_name,
     255             :                        sizeof(request->client_name),
     256             :                        "%s",
     257             :                        winbind_get_client_name());
     258     1137903 : }
     259             : 
     260             : /* Initialise a response structure */
     261             : 
     262      521022 : static void init_response(struct winbindd_response *response)
     263             : {
     264             :         /* Initialise return value */
     265             : 
     266      521022 :         response->result = WINBINDD_ERROR;
     267      517852 : }
     268             : 
     269             : /* Close established socket */
     270             : 
     271      200199 : static void winbind_close_sock(struct winbindd_context *ctx)
     272             : {
     273      200199 :         if (!ctx) {
     274           0 :                 return;
     275             :         }
     276             : 
     277      197805 :         if (ctx->winbindd_fd != -1) {
     278       40056 :                 close(ctx->winbindd_fd);
     279       40056 :                 ctx->winbindd_fd = -1;
     280             :         }
     281             : }
     282             : 
     283      109908 : static void winbind_ctx_free_locked(struct winbindd_context *ctx)
     284             : {
     285      109908 :         winbind_close_sock(ctx);
     286      109908 :         DLIST_REMOVE(wb_global_ctx.list, ctx);
     287      109908 :         free(ctx);
     288      109908 : }
     289             : 
     290      237744 : static void winbind_cleanup_list(void)
     291             : {
     292      237744 :         struct winbindd_context *ctx = NULL, *next = NULL;
     293             : 
     294      237744 :         WB_GLOBAL_LIST_LOCK;
     295      344988 :         for (ctx = wb_global_ctx.list; ctx != NULL; ctx = next) {
     296      109638 :                 next = ctx->next;
     297             : 
     298      109638 :                 if (ctx->autofree) {
     299      109638 :                         winbind_ctx_free_locked(ctx);
     300             :                 } else {
     301        6143 :                         winbind_close_sock(ctx);
     302             :                 }
     303             :         }
     304      237744 :         WB_GLOBAL_LIST_UNLOCK;
     305      237744 : }
     306             : 
     307             : /* Destructor for global context to ensure fd is closed */
     308             : 
     309             : #ifdef HAVE_DESTRUCTOR_ATTRIBUTE
     310             : __attribute__((destructor))
     311             : #elif defined (HAVE_PRAGMA_FINI)
     312             : #pragma fini (winbind_destructor)
     313             : #endif
     314      193031 : static void winbind_destructor(void)
     315             : {
     316             : #ifdef HAVE_PTHREAD
     317      193031 :         if (wb_global_ctx.key_initialized) {
     318        1756 :                 int ret;
     319       87591 :                 ret = pthread_key_delete(wb_global_ctx.key);
     320       87591 :                 assert(ret == 0);
     321       87591 :                 wb_global_ctx.key_initialized = false;
     322             :         }
     323             : 
     324      193031 :         wb_global_ctx.control = (pthread_once_t)PTHREAD_ONCE_INIT;
     325             : #endif /* HAVE_PTHREAD */
     326             : 
     327      193031 :         winbind_cleanup_list();
     328      193031 : }
     329             : 
     330             : #define CONNECT_TIMEOUT 30
     331             : 
     332             : /* Make sure socket handle isn't stdin, stdout or stderr */
     333             : #define RECURSION_LIMIT 3
     334             : 
     335       43776 : static int make_nonstd_fd_internals(int fd, int limit /* Recursion limiter */)
     336             : {
     337        1541 :         int new_fd;
     338       43776 :         if (fd >= 0 && fd <= 2) {
     339             : #ifdef F_DUPFD
     340           0 :                 if ((new_fd = fcntl(fd, F_DUPFD, 3)) == -1) {
     341           0 :                         return -1;
     342             :                 }
     343             :                 /* Paranoia */
     344           0 :                 if (new_fd < 3) {
     345           0 :                         close(new_fd);
     346           0 :                         return -1;
     347             :                 }
     348           0 :                 close(fd);
     349           0 :                 return new_fd;
     350             : #else
     351             :                 if (limit <= 0)
     352             :                         return -1;
     353             : 
     354             :                 new_fd = dup(fd);
     355             :                 if (new_fd == -1)
     356             :                         return -1;
     357             : 
     358             :                 /* use the program stack to hold our list of FDs to close */
     359             :                 new_fd = make_nonstd_fd_internals(new_fd, limit - 1);
     360             :                 close(fd);
     361             :                 return new_fd;
     362             : #endif
     363             :         }
     364       42235 :         return fd;
     365             : }
     366             : 
     367             : /****************************************************************************
     368             :  Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
     369             :  else
     370             :  if SYSV use O_NDELAY
     371             :  if BSD use FNDELAY
     372             :  Set close on exec also.
     373             : ****************************************************************************/
     374             : 
     375       43776 : static int make_safe_fd(int fd)
     376             : {
     377        1541 :         int result, flags;
     378       43776 :         int new_fd = make_nonstd_fd_internals(fd, RECURSION_LIMIT);
     379       43776 :         if (new_fd == -1) {
     380           0 :                 close(fd);
     381           0 :                 return -1;
     382             :         }
     383             : 
     384             :         /* Socket should be nonblocking. */
     385             : #ifdef O_NONBLOCK
     386             : #define FLAG_TO_SET O_NONBLOCK
     387             : #else
     388             : #ifdef SYSV
     389             : #define FLAG_TO_SET O_NDELAY
     390             : #else /* BSD */
     391             : #define FLAG_TO_SET FNDELAY
     392             : #endif
     393             : #endif
     394             : 
     395       43776 :         if ((flags = fcntl(new_fd, F_GETFL)) == -1) {
     396           0 :                 close(new_fd);
     397           0 :                 return -1;
     398             :         }
     399             : 
     400       43776 :         flags |= FLAG_TO_SET;
     401       43776 :         if (fcntl(new_fd, F_SETFL, flags) == -1) {
     402           0 :                 close(new_fd);
     403           0 :                 return -1;
     404             :         }
     405             : 
     406             : #undef FLAG_TO_SET
     407             : 
     408             :         /* Socket should be closed on exec() */
     409             : #ifdef FD_CLOEXEC
     410       43776 :         result = flags = fcntl(new_fd, F_GETFD, 0);
     411       43776 :         if (flags >= 0) {
     412       43776 :                 flags |= FD_CLOEXEC;
     413       43776 :                 result = fcntl( new_fd, F_SETFD, flags );
     414             :         }
     415       43776 :         if (result < 0) {
     416           0 :                 close(new_fd);
     417           0 :                 return -1;
     418             :         }
     419             : #endif
     420       42235 :         return new_fd;
     421             : }
     422             : 
     423             : /**
     424             :  * @internal
     425             :  *
     426             :  * @brief Check if we talk to the privileged pipe which should be owned by root.
     427             :  *
     428             :  * This checks if we have uid_wrapper running and if this is the case it will
     429             :  * allow one to connect to the winbind privileged pipe even it is not owned by root.
     430             :  *
     431             :  * @param[in]  uid      The uid to check if we can safely talk to the pipe.
     432             :  *
     433             :  * @return              If we have access it returns true, else false.
     434             :  */
     435       87579 : static bool winbind_privileged_pipe_is_root(uid_t uid)
     436             : {
     437       87579 :         if (uid == 0) {
     438           0 :                 return true;
     439             :         }
     440             : 
     441       87579 :         if (uid_wrapper_enabled()) {
     442       84497 :                 return true;
     443             :         }
     444             : 
     445           0 :         return false;
     446             : }
     447             : 
     448             : /* Connect to winbindd socket */
     449             : 
     450      660657 : static int winbind_named_pipe_sock(const char *dir)
     451             : {
     452        4971 :         struct sockaddr_un sunaddr;
     453        4971 :         struct stat st;
     454        4971 :         int fd;
     455        4971 :         int wait_time;
     456        4971 :         int slept;
     457        4971 :         int ret;
     458             : 
     459             :         /* Check permissions on unix socket directory */
     460             : 
     461      660657 :         if (lstat(dir, &st) == -1) {
     462      616854 :                 errno = ENOENT;
     463      616854 :                 return -1;
     464             :         }
     465             : 
     466             :         /*
     467             :          * This tells us that the pipe is owned by a privileged
     468             :          * process, as we will be sending passwords to it.
     469             :          */
     470       43803 :         if (!S_ISDIR(st.st_mode) ||
     471       43803 :             !winbind_privileged_pipe_is_root(st.st_uid)) {
     472           0 :                 errno = ENOENT;
     473           0 :                 return -1;
     474             :         }
     475             : 
     476             :         /* Connect to socket */
     477             : 
     478       43803 :         sunaddr = (struct sockaddr_un) { .sun_family = AF_UNIX };
     479             : 
     480       43803 :         ret = snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path),
     481             :                        "%s/%s", dir, WINBINDD_SOCKET_NAME);
     482       43803 :         if ((ret == -1) || (ret >= sizeof(sunaddr.sun_path))) {
     483           0 :                 errno = ENAMETOOLONG;
     484           0 :                 return -1;
     485             :         }
     486             : 
     487             :         /* If socket file doesn't exist, don't bother trying to connect
     488             :            with retry.  This is an attempt to make the system usable when
     489             :            the winbindd daemon is not running. */
     490             : 
     491       43803 :         if (lstat(sunaddr.sun_path, &st) == -1) {
     492          27 :                 errno = ENOENT;
     493          27 :                 return -1;
     494             :         }
     495             : 
     496             :         /* Check permissions on unix socket file */
     497             : 
     498             :         /*
     499             :          * This tells us that the pipe is owned by a privileged
     500             :          * process, as we will be sending passwords to it.
     501             :          */
     502       43776 :         if (!S_ISSOCK(st.st_mode) ||
     503       43776 :             !winbind_privileged_pipe_is_root(st.st_uid)) {
     504           0 :                 errno = ENOENT;
     505           0 :                 return -1;
     506             :         }
     507             : 
     508             :         /* Connect to socket */
     509             : 
     510       43776 :         if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
     511           0 :                 return -1;
     512             :         }
     513             : 
     514             :         /* Set socket non-blocking and close on exec. */
     515             : 
     516       43776 :         if ((fd = make_safe_fd( fd)) == -1) {
     517           0 :                 return fd;
     518             :         }
     519             : 
     520       43776 :         for (wait_time = 0; connect(fd, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1;
     521           0 :                         wait_time += slept) {
     522           0 :                 struct pollfd pfd;
     523           0 :                 int connect_errno = 0;
     524           0 :                 socklen_t errnosize;
     525             : 
     526           0 :                 if (wait_time >= CONNECT_TIMEOUT)
     527           0 :                         goto error_out;
     528             : 
     529           0 :                 switch (errno) {
     530           0 :                         case EINPROGRESS:
     531           0 :                                 pfd.fd = fd;
     532           0 :                                 pfd.events = POLLOUT;
     533             : 
     534           0 :                                 ret = poll(&pfd, 1, (CONNECT_TIMEOUT - wait_time) * 1000);
     535             : 
     536           0 :                                 if (ret > 0) {
     537           0 :                                         errnosize = sizeof(connect_errno);
     538             : 
     539           0 :                                         ret = getsockopt(fd, SOL_SOCKET,
     540             :                                                         SO_ERROR, &connect_errno, &errnosize);
     541             : 
     542           0 :                                         if (ret >= 0 && connect_errno == 0) {
     543             :                                                 /* Connect succeed */
     544           0 :                                                 goto out;
     545             :                                         }
     546             :                                 }
     547             : 
     548           0 :                                 slept = CONNECT_TIMEOUT;
     549           0 :                                 break;
     550           0 :                         case EAGAIN:
     551           0 :                                 slept = rand() % 3 + 1;
     552           0 :                                 sleep(slept);
     553           0 :                                 break;
     554           0 :                         default:
     555           0 :                                 goto error_out;
     556             :                 }
     557             : 
     558             :         }
     559             : 
     560       43776 :   out:
     561             : 
     562       42235 :         return fd;
     563             : 
     564           0 :   error_out:
     565             : 
     566           0 :         close(fd);
     567           0 :         return -1;
     568             : }
     569             : 
     570      656937 : static const char *winbindd_socket_dir(void)
     571             : {
     572      656937 :         if (nss_wrapper_enabled()) {
     573        4925 :                 const char *env_dir;
     574             : 
     575      656787 :                 env_dir = getenv("SELFTEST_WINBINDD_SOCKET_DIR");
     576      656787 :                 if (env_dir != NULL) {
     577      655475 :                         return env_dir;
     578             :                 }
     579             :         }
     580             : 
     581        1400 :         return WINBINDD_SOCKET_DIR;
     582             : }
     583             : 
     584             : /* Connect to winbindd socket */
     585             : 
     586     1772542 : static int winbind_open_pipe_sock(struct winbindd_context *ctx,
     587             :                                   int recursing, int need_priv)
     588             : {
     589             : #ifdef HAVE_UNIXSOCKET
     590       11376 :         struct winbindd_request request;
     591       11376 :         struct winbindd_response response;
     592             : 
     593     1772542 :         ZERO_STRUCT(request);
     594     1772542 :         ZERO_STRUCT(response);
     595             : 
     596     1772542 :         if (!ctx) {
     597           0 :                 return -1;
     598             :         }
     599             : 
     600     1772542 :         if (ctx->our_pid != getpid()) {
     601       85917 :                 winbind_close_sock(ctx);
     602       85917 :                 ctx->our_pid = getpid();
     603             :         }
     604             : 
     605     1772542 :         if ((need_priv != 0) && !ctx->is_privileged) {
     606        3720 :                 winbind_close_sock(ctx);
     607             :         }
     608             : 
     609     1772542 :         if (ctx->winbindd_fd != -1) {
     610     1109200 :                 return ctx->winbindd_fd;
     611             :         }
     612             : 
     613      656937 :         if (recursing) {
     614           0 :                 return -1;
     615             :         }
     616             : 
     617      656937 :         ctx->winbindd_fd = winbind_named_pipe_sock(winbindd_socket_dir());
     618             : 
     619      656937 :         if (ctx->winbindd_fd == -1) {
     620      613451 :                 return -1;
     621             :         }
     622             : 
     623       40056 :         ctx->is_privileged = false;
     624             : 
     625             :         /* version-check the socket */
     626             : 
     627       40056 :         request.wb_flags = WBFLAG_RECURSE;
     628       40056 :         if ((winbindd_request_response(ctx, WINBINDD_INTERFACE_VERSION, &request,
     629       40056 :                                        &response) != NSS_STATUS_SUCCESS) ||
     630       40056 :             (response.data.interface_version != WINBIND_INTERFACE_VERSION)) {
     631           0 :                 winbind_close_sock(ctx);
     632           0 :                 return -1;
     633             :         }
     634             : 
     635       40056 :         if (need_priv == 0) {
     636       36336 :                 return ctx->winbindd_fd;
     637             :         }
     638             : 
     639             :         /* try and get priv pipe */
     640             : 
     641        3720 :         request.wb_flags = WBFLAG_RECURSE;
     642             : 
     643             :         /* Note that response needs to be initialized to avoid
     644             :          * crashing on clean up after WINBINDD_PRIV_PIPE_DIR call failed
     645             :          * as interface version (from the first request) returned as a fstring,
     646             :          * thus response.extra_data.data will not be NULL even though
     647             :          * winbindd response did not write over it due to a failure */
     648        3720 :         ZERO_STRUCT(response);
     649        3720 :         if (winbindd_request_response(ctx, WINBINDD_PRIV_PIPE_DIR, &request,
     650             :                                       &response) == NSS_STATUS_SUCCESS) {
     651           0 :                 int fd;
     652        3720 :                 fd = winbind_named_pipe_sock((char *)response.extra_data.data);
     653        3720 :                 if (fd != -1) {
     654        3720 :                         close(ctx->winbindd_fd);
     655        3720 :                         ctx->winbindd_fd = fd;
     656        3720 :                         ctx->is_privileged = true;
     657             :                 }
     658             : 
     659        3720 :                 SAFE_FREE(response.extra_data.data);
     660             :         }
     661             : 
     662        3720 :         if (!ctx->is_privileged) {
     663           0 :                 return -1;
     664             :         }
     665             : 
     666        3720 :         return ctx->winbindd_fd;
     667             : #else
     668             :         return -1;
     669             : #endif /* HAVE_UNIXSOCKET */
     670             : }
     671             : 
     672             : /* Write data to winbindd socket */
     673             : 
     674     1193459 : static int winbind_write_sock(struct winbindd_context *ctx, void *buffer,
     675             :                               int count, int recursing, int need_priv)
     676             : {
     677        7402 :         int fd, result, nwritten;
     678             : 
     679             :         /* Open connection to winbind daemon */
     680             : 
     681     1193462 :  restart:
     682             : 
     683     1193462 :         fd = winbind_open_pipe_sock(ctx, recursing, need_priv);
     684     1193462 :         if (fd == -1) {
     685      616881 :                 errno = ENOENT;
     686      616881 :                 return -1;
     687             :         }
     688             : 
     689             :         /* Write data to socket */
     690             : 
     691      572609 :         nwritten = 0;
     692             : 
     693     1152508 :         while(nwritten < count) {
     694        3972 :                 struct pollfd pfd;
     695        3972 :                 int ret;
     696             : 
     697             :                 /* Catch pipe close on other end by checking if a read()
     698             :                    call would not block by calling poll(). */
     699             : 
     700      576581 :                 pfd.fd = fd;
     701      576581 :                 pfd.events = POLLIN|POLLOUT|POLLHUP;
     702             : 
     703      576581 :                 ret = poll(&pfd, 1, -1);
     704      576581 :                 if (ret == -1) {
     705           0 :                         winbind_close_sock(ctx);
     706           0 :                         return -1;                   /* poll error */
     707             :                 }
     708             : 
     709             :                 /* Write should be OK if fd not available for reading */
     710             : 
     711      576581 :                 if ((ret == 1) && (pfd.revents & (POLLIN|POLLHUP|POLLERR))) {
     712             : 
     713             :                         /* Pipe has closed on remote end */
     714             : 
     715         654 :                         winbind_close_sock(ctx);
     716         654 :                         goto restart;
     717             :                 }
     718             : 
     719             :                 /* Do the write */
     720             : 
     721     1151854 :                 result = write(fd, (char *)buffer + nwritten,
     722      575927 :                                count - nwritten);
     723             : 
     724      575927 :                 if ((result == -1) || (result == 0)) {
     725             : 
     726             :                         /* Write failed */
     727             : 
     728           0 :                         winbind_close_sock(ctx);
     729           0 :                         return -1;
     730             :                 }
     731             : 
     732      575927 :                 nwritten += result;
     733             :         }
     734             : 
     735      571955 :         return nwritten;
     736             : }
     737             : 
     738             : /* Read data from winbindd socket */
     739             : 
     740      579080 : static int winbind_read_sock(struct winbindd_context *ctx,
     741             :                              void *buffer, int count)
     742             : {
     743        3974 :         int fd;
     744      579080 :         int nread = 0;
     745      579080 :         int total_time = 0;
     746             : 
     747      579080 :         fd = winbind_open_pipe_sock(ctx, false, false);
     748      579080 :         if (fd == -1) {
     749           0 :                 return -1;
     750             :         }
     751             : 
     752             :         /* Read data from socket */
     753     1158160 :         while(nread < count) {
     754        3974 :                 struct pollfd pfd;
     755        3974 :                 int ret;
     756             : 
     757             :                 /* Catch pipe close on other end by checking if a read()
     758             :                    call would not block by calling poll(). */
     759             : 
     760      579080 :                 pfd.fd = fd;
     761      579080 :                 pfd.events = POLLIN|POLLHUP;
     762             : 
     763             :                 /* Wait for 5 seconds for a reply. May need to parameterise this... */
     764             : 
     765      579080 :                 ret = poll(&pfd, 1, 5000);
     766      579080 :                 if (ret == -1) {
     767           0 :                         winbind_close_sock(ctx);
     768           0 :                         return -1;                   /* poll error */
     769             :                 }
     770             : 
     771      579080 :                 if (ret == 0) {
     772             :                         /* Not ready for read yet... */
     773           0 :                         if (total_time >= 300) {
     774             :                                 /* Timeout */
     775           0 :                                 winbind_close_sock(ctx);
     776           0 :                                 return -1;
     777             :                         }
     778           0 :                         total_time += 5;
     779           0 :                         continue;
     780             :                 }
     781             : 
     782      579080 :                 if ((ret == 1) && (pfd.revents & (POLLIN|POLLHUP|POLLERR))) {
     783             : 
     784             :                         /* Do the Read */
     785             : 
     786     1158160 :                         int result = read(fd, (char *)buffer + nread,
     787      579080 :                               count - nread);
     788             : 
     789      579080 :                         if ((result == -1) || (result == 0)) {
     790             : 
     791             :                                 /* Read failed.  I think the only useful thing we
     792             :                                    can do here is just return -1 and fail since the
     793             :                                    transaction has failed half way through. */
     794             : 
     795           0 :                                 winbind_close_sock(ctx);
     796           0 :                                 return -1;
     797             :                         }
     798             : 
     799      579080 :                         nread += result;
     800             : 
     801             :                 }
     802             :         }
     803             : 
     804      575106 :         return nread;
     805             : }
     806             : 
     807             : /* Read reply */
     808             : 
     809      521022 : static int winbindd_read_reply(struct winbindd_context *ctx,
     810             :                                struct winbindd_response *response)
     811             : {
     812      521022 :         int result1, result2 = 0;
     813             : 
     814      521022 :         if (!response) {
     815           0 :                 return -1;
     816             :         }
     817             : 
     818             :         /* Read fixed length response */
     819             : 
     820      521022 :         result1 = winbind_read_sock(ctx, response,
     821             :                                     sizeof(struct winbindd_response));
     822             : 
     823             :         /* We actually send the pointer value of the extra_data field from
     824             :            the server.  This has no meaning in the client's address space
     825             :            so we clear it out. */
     826             : 
     827      521022 :         response->extra_data.data = NULL;
     828             : 
     829      521022 :         if (result1 == -1) {
     830           0 :                 return -1;
     831             :         }
     832             : 
     833      521022 :         if (response->length < sizeof(struct winbindd_response)) {
     834           0 :                 return -1;
     835             :         }
     836             : 
     837             :         /* Read variable length response */
     838             : 
     839      521022 :         if (response->length > sizeof(struct winbindd_response)) {
     840       58058 :                 int extra_data_len = response->length -
     841             :                         sizeof(struct winbindd_response);
     842             : 
     843             :                 /* Mallocate memory for extra data */
     844             : 
     845       58058 :                 if (!(response->extra_data.data = malloc(extra_data_len))) {
     846           0 :                         return -1;
     847             :                 }
     848             : 
     849       58058 :                 result2 = winbind_read_sock(ctx, response->extra_data.data,
     850             :                                             extra_data_len);
     851       58058 :                 if (result2 == -1) {
     852           0 :                         winbindd_free_response(response);
     853           0 :                         return -1;
     854             :                 }
     855             :         }
     856             : 
     857             :         /* Return total amount of data read */
     858             : 
     859      521022 :         return result1 + result2;
     860             : }
     861             : 
     862             : /*
     863             :  * send simple types of requests
     864             :  */
     865             : 
     866     1198696 : static NSS_STATUS winbindd_send_request(
     867             :         struct winbindd_context *ctx,
     868             :         int req_type,
     869             :         int need_priv,
     870             :         struct winbindd_request *request)
     871             : {
     872        6600 :         struct winbindd_request lrequest;
     873             : 
     874             :         /* Check for our tricky environment variable */
     875             : 
     876     1198696 :         if (winbind_env_set()) {
     877       60793 :                 return NSS_STATUS_NOTFOUND;
     878             :         }
     879             : 
     880     1137903 :         if (!request) {
     881      194044 :                 ZERO_STRUCT(lrequest);
     882      194044 :                 request = &lrequest;
     883             :         }
     884             : 
     885             :         /* Fill in request and send down pipe */
     886             : 
     887     1137903 :         winbindd_init_request(request, req_type);
     888             : 
     889     1137903 :         if (winbind_write_sock(ctx, request, sizeof(*request),
     890     1137903 :                                request->wb_flags & WBFLAG_RECURSE,
     891             :                                need_priv) == -1)
     892             :         {
     893             :                 /* Set ENOENT for consistency.  Required by some apps */
     894      616881 :                 errno = ENOENT;
     895             : 
     896      616881 :                 return NSS_STATUS_UNAVAIL;
     897             :         }
     898             : 
     899      575927 :         if ((request->extra_len != 0) &&
     900       54905 :             (winbind_write_sock(ctx, request->extra_data.data,
     901       54103 :                                 request->extra_len,
     902       54905 :                                 request->wb_flags & WBFLAG_RECURSE,
     903             :                                 need_priv) == -1))
     904             :         {
     905             :                 /* Set ENOENT for consistency.  Required by some apps */
     906           0 :                 errno = ENOENT;
     907             : 
     908           0 :                 return NSS_STATUS_UNAVAIL;
     909             :         }
     910             : 
     911      517852 :         return NSS_STATUS_SUCCESS;
     912             : }
     913             : 
     914             : /*
     915             :  * Get results from winbindd request
     916             :  */
     917             : 
     918      521022 : static NSS_STATUS winbindd_get_response(struct winbindd_context *ctx,
     919             :                                         struct winbindd_response *response)
     920             : {
     921        3170 :         struct winbindd_response lresponse;
     922             : 
     923      521022 :         if (!response) {
     924      184335 :                 ZERO_STRUCT(lresponse);
     925      184335 :                 response = &lresponse;
     926             :         }
     927             : 
     928      521022 :         init_response(response);
     929             : 
     930             :         /* Wait for reply */
     931      521022 :         if (winbindd_read_reply(ctx, response) == -1) {
     932             :                 /* Set ENOENT for consistency.  Required by some apps */
     933           0 :                 errno = ENOENT;
     934             : 
     935           0 :                 return NSS_STATUS_UNAVAIL;
     936             :         }
     937             : 
     938             :         /* Throw away extra data if client didn't request it */
     939      521022 :         if (response == &lresponse) {
     940      184335 :                 winbindd_free_response(response);
     941             :         }
     942             : 
     943             :         /* Copy reply data from socket */
     944      521022 :         if (response->result != WINBINDD_OK) {
     945        9862 :                 return NSS_STATUS_NOTFOUND;
     946             :         }
     947             : 
     948      508006 :         return NSS_STATUS_SUCCESS;
     949             : }
     950             : 
     951             : /* Handle simple types of requests */
     952             : 
     953     1186429 : NSS_STATUS winbindd_request_response(struct winbindd_context *ctx,
     954             :                                      int req_type,
     955             :                                      struct winbindd_request *request,
     956             :                                      struct winbindd_response *response)
     957             : {
     958     1186429 :         NSS_STATUS status = NSS_STATUS_UNAVAIL;
     959             : 
     960     1186429 :         if (ctx == NULL) {
     961     1141429 :                 ctx = get_wb_global_ctx();
     962             :         }
     963             : 
     964     1186429 :         status = winbindd_send_request(ctx, req_type, 0, request);
     965     1186429 :         if (status != NSS_STATUS_SUCCESS) {
     966      677674 :                 goto out;
     967             :         }
     968      508755 :         status = winbindd_get_response(ctx, response);
     969             : 
     970     1186429 : out:
     971     1186429 :         return status;
     972             : }
     973             : 
     974       12267 : NSS_STATUS winbindd_priv_request_response(struct winbindd_context *ctx,
     975             :                                           int req_type,
     976             :                                           struct winbindd_request *request,
     977             :                                           struct winbindd_response *response)
     978             : {
     979       12267 :         NSS_STATUS status = NSS_STATUS_UNAVAIL;
     980             : 
     981       12267 :         if (ctx == NULL) {
     982       12267 :                 ctx = get_wb_global_ctx();
     983             :         }
     984             : 
     985       12267 :         status = winbindd_send_request(ctx, req_type, 1, request);
     986       12267 :         if (status != NSS_STATUS_SUCCESS) {
     987           0 :                 goto out;
     988             :         }
     989       12267 :         status = winbindd_get_response(ctx, response);
     990             : 
     991       12267 : out:
     992       12267 :         return status;
     993             : }
     994             : 
     995             : /* Create and free winbindd context */
     996             : 
     997         266 : struct winbindd_context *winbindd_ctx_create(void)
     998             : {
     999           0 :         struct winbindd_context *ctx;
    1000             : 
    1001         266 :         ctx = calloc(1, sizeof(struct winbindd_context));
    1002             : 
    1003         266 :         if (!ctx) {
    1004           0 :                 return NULL;
    1005             :         }
    1006             : 
    1007         266 :         ctx->winbindd_fd = -1;
    1008             : 
    1009         266 :         WB_GLOBAL_LIST_LOCK;
    1010         266 :         DLIST_ADD_END(wb_global_ctx.list, ctx);
    1011         266 :         WB_GLOBAL_LIST_UNLOCK;
    1012             : 
    1013         266 :         return ctx;
    1014             : }
    1015             : 
    1016         270 : void winbindd_ctx_free(struct winbindd_context *ctx)
    1017             : {
    1018         270 :         WB_GLOBAL_LIST_LOCK;
    1019         270 :         winbind_ctx_free_locked(ctx);
    1020         270 :         WB_GLOBAL_LIST_UNLOCK;
    1021         270 : }

Generated by: LCOV version 1.14