Coverage Report

Created: 2026-02-14 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/nsswitch/wb_common.c
Line
Count
Source
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
0
#define WB_GLOBAL_MUTEX_INITIALIZER PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
55
#else
56
#define WB_GLOBAL_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
57
#endif
58
0
#define WB_GLOBAL_LIST_LOCK do { \
59
0
  int __pret = pthread_mutex_lock(&wb_global_ctx.list_mutex); \
60
0
  assert(__pret == 0); \
61
0
} while(0)
62
0
#define WB_GLOBAL_LIST_UNLOCK do { \
63
0
  int __pret = pthread_mutex_unlock(&wb_global_ctx.list_mutex); \
64
0
  assert(__pret == 0); \
65
0
} 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
static void wb_atfork_prepare(void)
88
0
{
89
0
  WB_GLOBAL_LIST_LOCK;
90
0
}
91
92
static void wb_atfork_parent(void)
93
0
{
94
0
  WB_GLOBAL_LIST_UNLOCK;
95
0
}
96
97
static void wb_atfork_child(void)
98
0
{
99
0
  wb_global_ctx.list_mutex = (pthread_mutex_t)WB_GLOBAL_MUTEX_INITIALIZER;
100
101
0
  if (wb_global_ctx.key_initialized) {
102
0
    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
0
    ret = pthread_setspecific(wb_global_ctx.key, NULL);
116
0
    assert(ret == 0);
117
0
  }
118
119
  /*
120
   * But we need to close/cleanup the global state
121
   * of the parents threads.
122
   */
123
0
  winbind_cleanup_list();
124
0
}
125
126
static void wb_thread_ctx_destructor(void *p)
127
0
{
128
0
  struct winbindd_context *ctx = (struct winbindd_context *)p;
129
130
0
  winbindd_ctx_free(ctx);
131
0
}
132
133
static void wb_thread_ctx_initialize(void)
134
0
{
135
0
  int ret;
136
137
0
  ret = pthread_atfork(wb_atfork_prepare,
138
0
           wb_atfork_parent,
139
0
           wb_atfork_child);
140
0
  assert(ret == 0);
141
142
0
  ret = pthread_key_create(&wb_global_ctx.key,
143
0
         wb_thread_ctx_destructor);
144
0
  assert(ret == 0);
145
146
0
  wb_global_ctx.key_initialized = true;
147
0
}
148
149
static struct winbindd_context *get_wb_thread_ctx(void)
150
0
{
151
0
  struct winbindd_context *ctx = NULL;
152
0
  int ret;
153
154
0
  ret = pthread_once(&wb_global_ctx.control,
155
0
         wb_thread_ctx_initialize);
156
0
  assert(ret == 0);
157
158
0
  ctx = (struct winbindd_context *)pthread_getspecific(
159
0
    wb_global_ctx.key);
160
0
  if (ctx != NULL) {
161
0
    return ctx;
162
0
  }
163
164
0
  ctx = malloc(sizeof(struct winbindd_context));
165
0
  if (ctx == NULL) {
166
0
    return NULL;
167
0
  }
168
169
0
  *ctx = (struct winbindd_context) {
170
0
    .winbindd_fd = -1,
171
0
    .is_privileged = false,
172
0
    .our_pid = 0,
173
0
    .autofree = true,
174
0
  };
175
176
0
  WB_GLOBAL_LIST_LOCK;
177
0
  DLIST_ADD_END(wb_global_ctx.list, ctx);
178
0
  WB_GLOBAL_LIST_UNLOCK;
179
180
0
  ret = pthread_setspecific(wb_global_ctx.key, ctx);
181
0
  if (ret != 0) {
182
0
    free(ctx);
183
0
    return NULL;
184
0
  }
185
0
  return ctx;
186
0
}
187
#endif /* HAVE_PTHREAD */
188
189
static struct winbindd_context *get_wb_global_ctx(void)
190
0
{
191
0
  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
0
#ifdef HAVE_PTHREAD
202
0
  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
0
  return ctx;
211
0
}
212
213
void winbind_set_client_name(const char *name)
214
0
{
215
0
  if (name == NULL || strlen(name) == 0) {
216
0
    return;
217
0
  }
218
219
0
  (void)snprintf(client_name, sizeof(client_name), "%s", name);
220
0
}
221
222
static const char *winbind_get_client_name(void)
223
0
{
224
0
  if (client_name[0] == '\0') {
225
0
    const char *progname = getprogname();
226
0
    int len;
227
228
0
    if (progname == NULL) {
229
0
      progname = "<unknown>";
230
0
    }
231
232
0
    len = snprintf(client_name,
233
0
             sizeof(client_name),
234
0
             "%s",
235
0
             progname);
236
0
    if (len <= 0) {
237
0
      return progname;
238
0
    }
239
0
  }
240
241
0
  return client_name;
242
0
}
243
244
/* Initialise a request structure */
245
246
static void winbindd_init_request(struct winbindd_request *request,
247
          int request_type)
248
0
{
249
0
  request->length = sizeof(struct winbindd_request);
250
251
0
  request->cmd = (enum winbindd_cmd)request_type;
252
0
  request->pid = getpid();
253
254
0
  (void)snprintf(request->client_name,
255
0
           sizeof(request->client_name),
256
0
           "%s",
257
0
           winbind_get_client_name());
258
0
}
259
260
/* Initialise a response structure */
261
262
static void init_response(struct winbindd_response *response)
263
0
{
264
  /* Initialise return value */
265
266
0
  response->result = WINBINDD_ERROR;
267
0
}
268
269
/* Close established socket */
270
271
static void winbind_close_sock(struct winbindd_context *ctx)
272
0
{
273
0
  if (!ctx) {
274
0
    return;
275
0
  }
276
277
0
  if (ctx->winbindd_fd != -1) {
278
0
    close(ctx->winbindd_fd);
279
0
    ctx->winbindd_fd = -1;
280
0
  }
281
0
}
282
283
static void winbind_ctx_free_locked(struct winbindd_context *ctx)
284
0
{
285
0
  winbind_close_sock(ctx);
286
0
  DLIST_REMOVE(wb_global_ctx.list, ctx);
287
0
  free(ctx);
288
0
}
289
290
static void winbind_cleanup_list(void)
291
0
{
292
0
  struct winbindd_context *ctx = NULL, *next = NULL;
293
294
0
  WB_GLOBAL_LIST_LOCK;
295
0
  for (ctx = wb_global_ctx.list; ctx != NULL; ctx = next) {
296
0
    next = ctx->next;
297
298
0
    if (ctx->autofree) {
299
0
      winbind_ctx_free_locked(ctx);
300
0
    } else {
301
0
      winbind_close_sock(ctx);
302
0
    }
303
0
  }
304
0
  WB_GLOBAL_LIST_UNLOCK;
305
0
}
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
static void winbind_destructor(void)
315
0
{
316
0
#ifdef HAVE_PTHREAD
317
0
  if (wb_global_ctx.key_initialized) {
318
0
    int ret;
319
0
    ret = pthread_key_delete(wb_global_ctx.key);
320
0
    assert(ret == 0);
321
0
    wb_global_ctx.key_initialized = false;
322
0
  }
323
324
0
  wb_global_ctx.control = (pthread_once_t)PTHREAD_ONCE_INIT;
325
0
#endif /* HAVE_PTHREAD */
326
327
0
  winbind_cleanup_list();
328
0
}
329
330
0
#define CONNECT_TIMEOUT 30
331
332
/* Make sure socket handle isn't stdin, stdout or stderr */
333
0
#define RECURSION_LIMIT 3
334
335
static int make_nonstd_fd_internals(int fd, int limit /* Recursion limiter */)
336
0
{
337
0
  int new_fd;
338
0
  if (fd >= 0 && fd <= 2) {
339
0
#ifdef F_DUPFD
340
0
    if ((new_fd = fcntl(fd, F_DUPFD, 3)) == -1) {
341
0
      return -1;
342
0
    }
343
    /* Paranoia */
344
0
    if (new_fd < 3) {
345
0
      close(new_fd);
346
0
      return -1;
347
0
    }
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
0
  }
364
0
  return fd;
365
0
}
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
static int make_safe_fd(int fd)
376
0
{
377
0
  int result, flags;
378
0
  int new_fd = make_nonstd_fd_internals(fd, RECURSION_LIMIT);
379
0
  if (new_fd == -1) {
380
0
    close(fd);
381
0
    return -1;
382
0
  }
383
384
  /* Socket should be nonblocking. */
385
0
#ifdef O_NONBLOCK
386
0
#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
0
  if ((flags = fcntl(new_fd, F_GETFL)) == -1) {
396
0
    close(new_fd);
397
0
    return -1;
398
0
  }
399
400
0
  flags |= FLAG_TO_SET;
401
0
  if (fcntl(new_fd, F_SETFL, flags) == -1) {
402
0
    close(new_fd);
403
0
    return -1;
404
0
  }
405
406
0
#undef FLAG_TO_SET
407
408
  /* Socket should be closed on exec() */
409
0
#ifdef FD_CLOEXEC
410
0
  result = flags = fcntl(new_fd, F_GETFD, 0);
411
0
  if (flags >= 0) {
412
0
    flags |= FD_CLOEXEC;
413
0
    result = fcntl( new_fd, F_SETFD, flags );
414
0
  }
415
0
  if (result < 0) {
416
0
    close(new_fd);
417
0
    return -1;
418
0
  }
419
0
#endif
420
0
  return new_fd;
421
0
}
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
static bool winbind_privileged_pipe_is_root(uid_t uid)
436
0
{
437
0
  if (uid == 0) {
438
0
    return true;
439
0
  }
440
441
0
  if (uid_wrapper_enabled()) {
442
0
    return true;
443
0
  }
444
445
0
  return false;
446
0
}
447
448
/* Connect to winbindd socket */
449
450
static int winbind_named_pipe_sock(const char *dir)
451
0
{
452
0
  struct sockaddr_un sunaddr;
453
0
  struct stat st;
454
0
  int fd;
455
0
  int wait_time;
456
0
  int slept;
457
0
  int ret;
458
459
  /* Check permissions on unix socket directory */
460
461
0
  if (lstat(dir, &st) == -1) {
462
0
    errno = ENOENT;
463
0
    return -1;
464
0
  }
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
0
  if (!S_ISDIR(st.st_mode) ||
471
0
      !winbind_privileged_pipe_is_root(st.st_uid)) {
472
0
    errno = ENOENT;
473
0
    return -1;
474
0
  }
475
476
  /* Connect to socket */
477
478
0
  sunaddr = (struct sockaddr_un) { .sun_family = AF_UNIX };
479
480
0
  ret = snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path),
481
0
           "%s/%s", dir, WINBINDD_SOCKET_NAME);
482
0
  if ((ret == -1) || (ret >= sizeof(sunaddr.sun_path))) {
483
0
    errno = ENAMETOOLONG;
484
0
    return -1;
485
0
  }
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
0
  if (lstat(sunaddr.sun_path, &st) == -1) {
492
0
    errno = ENOENT;
493
0
    return -1;
494
0
  }
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
0
  if (!S_ISSOCK(st.st_mode) ||
503
0
      !winbind_privileged_pipe_is_root(st.st_uid)) {
504
0
    errno = ENOENT;
505
0
    return -1;
506
0
  }
507
508
  /* Connect to socket */
509
510
0
  if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
511
0
    return -1;
512
0
  }
513
514
  /* Set socket non-blocking and close on exec. */
515
516
0
  if ((fd = make_safe_fd( fd)) == -1) {
517
0
    return fd;
518
0
  }
519
520
0
  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
0
              SO_ERROR, &connect_errno, &errnosize);
541
542
0
          if (ret >= 0 && connect_errno == 0) {
543
            /* Connect succeed */
544
0
            goto out;
545
0
          }
546
0
        }
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
0
    }
557
558
0
  }
559
560
0
  out:
561
562
0
  return fd;
563
564
0
  error_out:
565
566
0
  close(fd);
567
0
  return -1;
568
0
}
569
570
static const char *winbindd_socket_dir(void)
571
0
{
572
0
  if (nss_wrapper_enabled()) {
573
0
    const char *env_dir;
574
575
0
    env_dir = getenv("SELFTEST_WINBINDD_SOCKET_DIR");
576
0
    if (env_dir != NULL) {
577
0
      return env_dir;
578
0
    }
579
0
  }
580
581
0
  return WINBINDD_SOCKET_DIR;
582
0
}
583
584
/* Connect to winbindd socket */
585
586
static int winbind_open_pipe_sock(struct winbindd_context *ctx,
587
          int recursing, int need_priv)
588
0
{
589
0
#ifdef HAVE_UNIXSOCKET
590
0
  struct winbindd_request request;
591
0
  struct winbindd_response response;
592
593
0
  ZERO_STRUCT(request);
594
0
  ZERO_STRUCT(response);
595
596
0
  if (!ctx) {
597
0
    return -1;
598
0
  }
599
600
0
  if (ctx->our_pid != getpid()) {
601
0
    winbind_close_sock(ctx);
602
0
    ctx->our_pid = getpid();
603
0
  }
604
605
0
  if ((need_priv != 0) && !ctx->is_privileged) {
606
0
    winbind_close_sock(ctx);
607
0
  }
608
609
0
  if (ctx->winbindd_fd != -1) {
610
0
    return ctx->winbindd_fd;
611
0
  }
612
613
0
  if (recursing) {
614
0
    return -1;
615
0
  }
616
617
0
  ctx->winbindd_fd = winbind_named_pipe_sock(winbindd_socket_dir());
618
619
0
  if (ctx->winbindd_fd == -1) {
620
0
    return -1;
621
0
  }
622
623
0
  ctx->is_privileged = false;
624
625
  /* version-check the socket */
626
627
0
  request.wb_flags = WBFLAG_RECURSE;
628
0
  if ((winbindd_request_response(ctx, WINBINDD_INTERFACE_VERSION, &request,
629
0
               &response) != NSS_STATUS_SUCCESS) ||
630
0
      (response.data.interface_version != WINBIND_INTERFACE_VERSION)) {
631
0
    winbind_close_sock(ctx);
632
0
    return -1;
633
0
  }
634
635
0
  if (need_priv == 0) {
636
0
    return ctx->winbindd_fd;
637
0
  }
638
639
  /* try and get priv pipe */
640
641
0
  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
0
  ZERO_STRUCT(response);
649
0
  if (winbindd_request_response(ctx, WINBINDD_PRIV_PIPE_DIR, &request,
650
0
              &response) == NSS_STATUS_SUCCESS) {
651
0
    int fd;
652
0
    fd = winbind_named_pipe_sock((char *)response.extra_data.data);
653
0
    if (fd != -1) {
654
0
      close(ctx->winbindd_fd);
655
0
      ctx->winbindd_fd = fd;
656
0
      ctx->is_privileged = true;
657
0
    }
658
659
0
    SAFE_FREE(response.extra_data.data);
660
0
  }
661
662
0
  if (!ctx->is_privileged) {
663
0
    return -1;
664
0
  }
665
666
0
  return ctx->winbindd_fd;
667
#else
668
  return -1;
669
#endif /* HAVE_UNIXSOCKET */
670
0
}
671
672
/* Write data to winbindd socket */
673
674
static ssize_t winbind_write_sock(struct winbindd_context *ctx,
675
          void *buffer,
676
          size_t count,
677
          int recursing,
678
          int need_priv)
679
0
{
680
0
  int fd;
681
0
  ssize_t nwritten;
682
683
  /* Open connection to winbind daemon */
684
685
0
 restart:
686
687
0
  fd = winbind_open_pipe_sock(ctx, recursing, need_priv);
688
0
  if (fd == -1) {
689
0
    errno = ENOENT;
690
0
    return -1;
691
0
  }
692
693
  /* Write data to socket */
694
695
0
  nwritten = 0;
696
697
0
  while(nwritten < count) {
698
0
    struct pollfd pfd;
699
0
    ssize_t result;
700
0
    int ret;
701
702
    /* Catch pipe close on other end by checking if a read()
703
       call would not block by calling poll(). */
704
705
0
    pfd.fd = fd;
706
0
    pfd.events = POLLIN|POLLOUT|POLLHUP;
707
708
0
    ret = poll(&pfd, 1, -1);
709
0
    if (ret == -1) {
710
0
      winbind_close_sock(ctx);
711
0
      return -1;                   /* poll error */
712
0
    }
713
714
    /* Write should be OK if fd not available for reading */
715
716
0
    if ((ret == 1) && (pfd.revents & (POLLIN|POLLHUP|POLLERR))) {
717
718
      /* Pipe has closed on remote end */
719
720
0
      winbind_close_sock(ctx);
721
0
      goto restart;
722
0
    }
723
724
    /* Do the write */
725
726
0
    result = write(fd, (char *)buffer + nwritten,
727
0
             count - nwritten);
728
729
0
    if ((result == -1) || (result == 0)) {
730
731
      /* Write failed */
732
733
0
      winbind_close_sock(ctx);
734
0
      return -1;
735
0
    }
736
737
0
    nwritten += result;
738
0
  }
739
740
0
  return nwritten;
741
0
}
742
743
/* Read data from winbindd socket */
744
745
static int winbind_read_sock(struct winbindd_context *ctx,
746
           void *buffer, int count)
747
0
{
748
0
  int fd;
749
0
  int nread = 0;
750
0
  int total_time = 0;
751
752
0
  fd = winbind_open_pipe_sock(ctx, false, false);
753
0
  if (fd == -1) {
754
0
    return -1;
755
0
  }
756
757
  /* Read data from socket */
758
0
  while(nread < count) {
759
0
    struct pollfd pfd;
760
0
    int ret;
761
762
    /* Catch pipe close on other end by checking if a read()
763
       call would not block by calling poll(). */
764
765
0
    pfd.fd = fd;
766
0
    pfd.events = POLLIN|POLLHUP;
767
768
    /* Wait for 5 seconds for a reply. May need to parameterise this... */
769
770
0
    ret = poll(&pfd, 1, 5000);
771
0
    if (ret == -1) {
772
0
      winbind_close_sock(ctx);
773
0
      return -1;                   /* poll error */
774
0
    }
775
776
0
    if (ret == 0) {
777
      /* Not ready for read yet... */
778
0
      if (total_time >= 300) {
779
        /* Timeout */
780
0
        winbind_close_sock(ctx);
781
0
        return -1;
782
0
      }
783
0
      total_time += 5;
784
0
      continue;
785
0
    }
786
787
0
    if ((ret == 1) && (pfd.revents & (POLLIN|POLLHUP|POLLERR))) {
788
789
      /* Do the Read */
790
791
0
      int result = read(fd, (char *)buffer + nread,
792
0
            count - nread);
793
794
0
      if ((result == -1) || (result == 0)) {
795
796
        /* Read failed.  I think the only useful thing we
797
           can do here is just return -1 and fail since the
798
           transaction has failed half way through. */
799
800
0
        winbind_close_sock(ctx);
801
0
        return -1;
802
0
      }
803
804
0
      nread += result;
805
806
0
    }
807
0
  }
808
809
0
  return nread;
810
0
}
811
812
/* Read reply */
813
814
static int winbindd_read_reply(struct winbindd_context *ctx,
815
             struct winbindd_response *response)
816
0
{
817
0
  int result1, result2 = 0;
818
819
0
  if (!response) {
820
0
    return -1;
821
0
  }
822
823
  /* Read fixed length response */
824
825
0
  result1 = winbind_read_sock(ctx, response,
826
0
            sizeof(struct winbindd_response));
827
828
  /* We actually send the pointer value of the extra_data field from
829
     the server.  This has no meaning in the client's address space
830
     so we clear it out. */
831
832
0
  response->extra_data.data = NULL;
833
834
0
  if (result1 == -1) {
835
0
    return -1;
836
0
  }
837
838
0
  if (response->length < sizeof(struct winbindd_response)) {
839
0
    return -1;
840
0
  }
841
842
  /* Read variable length response */
843
844
0
  if (response->length > sizeof(struct winbindd_response)) {
845
0
    int extra_data_len = response->length -
846
0
      sizeof(struct winbindd_response);
847
848
    /* Mallocate memory for extra data */
849
850
0
    if (!(response->extra_data.data = malloc(extra_data_len))) {
851
0
      return -1;
852
0
    }
853
854
0
    result2 = winbind_read_sock(ctx, response->extra_data.data,
855
0
              extra_data_len);
856
0
    if (result2 == -1) {
857
0
      winbindd_free_response(response);
858
0
      return -1;
859
0
    }
860
0
  }
861
862
  /* Return total amount of data read */
863
864
0
  return result1 + result2;
865
0
}
866
867
/*
868
 * send simple types of requests
869
 */
870
871
static NSS_STATUS winbindd_send_request(
872
  struct winbindd_context *ctx,
873
  int req_type,
874
  int need_priv,
875
  struct winbindd_request *request)
876
0
{
877
0
  struct winbindd_request lrequest;
878
879
  /* Check for our tricky environment variable */
880
881
0
  if (winbind_env_set()) {
882
0
    return NSS_STATUS_NOTFOUND;
883
0
  }
884
885
0
  if (!request) {
886
0
    ZERO_STRUCT(lrequest);
887
0
    request = &lrequest;
888
0
  }
889
890
  /* Fill in request and send down pipe */
891
892
0
  winbindd_init_request(request, req_type);
893
894
0
  if (winbind_write_sock(ctx, request, sizeof(*request),
895
0
             request->wb_flags & WBFLAG_RECURSE,
896
0
             need_priv) == -1)
897
0
  {
898
    /* Set ENOENT for consistency.  Required by some apps */
899
0
    errno = ENOENT;
900
901
0
    return NSS_STATUS_UNAVAIL;
902
0
  }
903
904
0
  if ((request->extra_len != 0) &&
905
0
      (winbind_write_sock(ctx, request->extra_data.data,
906
0
        request->extra_len,
907
0
        request->wb_flags & WBFLAG_RECURSE,
908
0
        need_priv) == -1))
909
0
  {
910
    /* Set ENOENT for consistency.  Required by some apps */
911
0
    errno = ENOENT;
912
913
0
    return NSS_STATUS_UNAVAIL;
914
0
  }
915
916
0
  return NSS_STATUS_SUCCESS;
917
0
}
918
919
/*
920
 * Get results from winbindd request
921
 */
922
923
static NSS_STATUS winbindd_get_response(struct winbindd_context *ctx,
924
          struct winbindd_response *response)
925
0
{
926
0
  struct winbindd_response lresponse;
927
928
0
  if (!response) {
929
0
    ZERO_STRUCT(lresponse);
930
0
    response = &lresponse;
931
0
  }
932
933
0
  init_response(response);
934
935
  /* Wait for reply */
936
0
  if (winbindd_read_reply(ctx, response) == -1) {
937
    /* Set ENOENT for consistency.  Required by some apps */
938
0
    errno = ENOENT;
939
940
0
    return NSS_STATUS_UNAVAIL;
941
0
  }
942
943
  /* Throw away extra data if client didn't request it */
944
0
  if (response == &lresponse) {
945
0
    winbindd_free_response(response);
946
0
  }
947
948
  /* Copy reply data from socket */
949
0
  if (response->result != WINBINDD_OK) {
950
0
    return NSS_STATUS_NOTFOUND;
951
0
  }
952
953
0
  return NSS_STATUS_SUCCESS;
954
0
}
955
956
/* Handle simple types of requests */
957
958
NSS_STATUS winbindd_request_response(struct winbindd_context *ctx,
959
             int req_type,
960
             struct winbindd_request *request,
961
             struct winbindd_response *response)
962
0
{
963
0
  NSS_STATUS status = NSS_STATUS_UNAVAIL;
964
965
0
  if (ctx == NULL) {
966
0
    ctx = get_wb_global_ctx();
967
0
  }
968
969
0
  status = winbindd_send_request(ctx, req_type, 0, request);
970
0
  if (status != NSS_STATUS_SUCCESS) {
971
0
    goto out;
972
0
  }
973
0
  status = winbindd_get_response(ctx, response);
974
975
0
out:
976
0
  return status;
977
0
}
978
979
NSS_STATUS winbindd_priv_request_response(struct winbindd_context *ctx,
980
            int req_type,
981
            struct winbindd_request *request,
982
            struct winbindd_response *response)
983
0
{
984
0
  NSS_STATUS status = NSS_STATUS_UNAVAIL;
985
986
0
  if (ctx == NULL) {
987
0
    ctx = get_wb_global_ctx();
988
0
  }
989
990
0
  status = winbindd_send_request(ctx, req_type, 1, request);
991
0
  if (status != NSS_STATUS_SUCCESS) {
992
0
    goto out;
993
0
  }
994
0
  status = winbindd_get_response(ctx, response);
995
996
0
out:
997
0
  return status;
998
0
}
999
1000
/* Create and free winbindd context */
1001
1002
struct winbindd_context *winbindd_ctx_create(void)
1003
0
{
1004
0
  struct winbindd_context *ctx;
1005
1006
0
  ctx = calloc(1, sizeof(struct winbindd_context));
1007
1008
0
  if (!ctx) {
1009
0
    return NULL;
1010
0
  }
1011
1012
0
  ctx->winbindd_fd = -1;
1013
1014
0
  WB_GLOBAL_LIST_LOCK;
1015
0
  DLIST_ADD_END(wb_global_ctx.list, ctx);
1016
0
  WB_GLOBAL_LIST_UNLOCK;
1017
1018
0
  return ctx;
1019
0
}
1020
1021
void winbindd_ctx_free(struct winbindd_context *ctx)
1022
0
{
1023
0
  WB_GLOBAL_LIST_LOCK;
1024
0
  winbind_ctx_free_locked(ctx);
1025
  WB_GLOBAL_LIST_UNLOCK;
1026
0
}