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 34842203 : static void wb_atfork_prepare(void)
88 : {
89 34842203 : WB_GLOBAL_LIST_LOCK;
90 34842203 : }
91 :
92 34801966 : static void wb_atfork_parent(void)
93 : {
94 34801966 : WB_GLOBAL_LIST_UNLOCK;
95 34801966 : }
96 :
97 40237 : static void wb_atfork_child(void)
98 : {
99 40237 : wb_global_ctx.list_mutex = (pthread_mutex_t)WB_GLOBAL_MUTEX_INITIALIZER;
100 :
101 40237 : 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 40237 : ret = pthread_setspecific(wb_global_ctx.key, NULL);
116 40237 : assert(ret == 0);
117 : }
118 :
119 : /*
120 : * But we need to close/cleanup the global state
121 : * of the parents threads.
122 : */
123 40237 : winbind_cleanup_list();
124 40237 : }
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 47692 : static void wb_thread_ctx_initialize(void)
134 : {
135 912 : int ret;
136 :
137 47692 : ret = pthread_atfork(wb_atfork_prepare,
138 : wb_atfork_parent,
139 : wb_atfork_child);
140 47692 : assert(ret == 0);
141 :
142 47692 : ret = pthread_key_create(&wb_global_ctx.key,
143 : wb_thread_ctx_destructor);
144 47692 : assert(ret == 0);
145 :
146 47692 : wb_global_ctx.key_initialized = true;
147 47692 : }
148 :
149 1034889 : static struct winbindd_context *get_wb_thread_ctx(void)
150 : {
151 1034889 : struct winbindd_context *ctx = NULL;
152 5059 : int ret;
153 :
154 1034889 : ret = pthread_once(&wb_global_ctx.control,
155 : wb_thread_ctx_initialize);
156 1034889 : assert(ret == 0);
157 :
158 1034889 : ctx = (struct winbindd_context *)pthread_getspecific(
159 : wb_global_ctx.key);
160 1034889 : if (ctx != NULL) {
161 934835 : return ctx;
162 : }
163 :
164 97389 : ctx = malloc(sizeof(struct winbindd_context));
165 97389 : if (ctx == NULL) {
166 0 : return NULL;
167 : }
168 :
169 97389 : *ctx = (struct winbindd_context) {
170 : .winbindd_fd = -1,
171 : .is_privileged = false,
172 : .our_pid = 0,
173 : .autofree = true,
174 : };
175 :
176 97389 : WB_GLOBAL_LIST_LOCK;
177 97389 : DLIST_ADD_END(wb_global_ctx.list, ctx);
178 97389 : WB_GLOBAL_LIST_UNLOCK;
179 :
180 97389 : ret = pthread_setspecific(wb_global_ctx.key, ctx);
181 97389 : if (ret != 0) {
182 0 : free(ctx);
183 0 : return NULL;
184 : }
185 94995 : return ctx;
186 : }
187 : #endif /* HAVE_PTHREAD */
188 :
189 1034889 : static struct winbindd_context *get_wb_global_ctx(void)
190 : {
191 1034889 : 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 1034889 : 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 1034889 : return ctx;
211 : }
212 :
213 135190 : void winbind_set_client_name(const char *name)
214 : {
215 135190 : if (name == NULL || strlen(name) == 0) {
216 0 : return;
217 : }
218 :
219 135190 : (void)snprintf(client_name, sizeof(client_name), "%s", name);
220 : }
221 :
222 1037964 : static const char *winbind_get_client_name(void)
223 : {
224 1037964 : if (client_name[0] == '\0') {
225 34453 : const char *progname = getprogname();
226 854 : int len;
227 :
228 34453 : if (progname == NULL) {
229 0 : progname = "<unknown>";
230 : }
231 :
232 34453 : len = snprintf(client_name,
233 : sizeof(client_name),
234 : "%s",
235 : progname);
236 34453 : if (len <= 0) {
237 0 : return progname;
238 : }
239 : }
240 :
241 1031364 : return client_name;
242 : }
243 :
244 : /* Initialise a request structure */
245 :
246 1037964 : static void winbindd_init_request(struct winbindd_request *request,
247 : int request_type)
248 : {
249 1037964 : request->length = sizeof(struct winbindd_request);
250 :
251 1037964 : request->cmd = (enum winbindd_cmd)request_type;
252 1037964 : request->pid = getpid();
253 :
254 1037964 : (void)snprintf(request->client_name,
255 : sizeof(request->client_name),
256 : "%s",
257 : winbind_get_client_name());
258 1037964 : }
259 :
260 : /* Initialise a response structure */
261 :
262 575469 : static void init_response(struct winbindd_response *response)
263 : {
264 : /* Initialise return value */
265 :
266 575469 : response->result = WINBINDD_ERROR;
267 572299 : }
268 :
269 : /* Close established socket */
270 :
271 183061 : static void winbind_close_sock(struct winbindd_context *ctx)
272 : {
273 183061 : if (!ctx) {
274 0 : return;
275 : }
276 :
277 180667 : if (ctx->winbindd_fd != -1) {
278 47813 : close(ctx->winbindd_fd);
279 47813 : ctx->winbindd_fd = -1;
280 : }
281 : }
282 :
283 97655 : static void winbind_ctx_free_locked(struct winbindd_context *ctx)
284 : {
285 97655 : winbind_close_sock(ctx);
286 97655 : DLIST_REMOVE(wb_global_ctx.list, ctx);
287 97655 : free(ctx);
288 97655 : }
289 :
290 217451 : static void winbind_cleanup_list(void)
291 : {
292 217451 : struct winbindd_context *ctx = NULL, *next = NULL;
293 :
294 217451 : WB_GLOBAL_LIST_LOCK;
295 312442 : for (ctx = wb_global_ctx.list; ctx != NULL; ctx = next) {
296 97385 : next = ctx->next;
297 :
298 97385 : if (ctx->autofree) {
299 97385 : winbind_ctx_free_locked(ctx);
300 : } else {
301 6261 : winbind_close_sock(ctx);
302 : }
303 : }
304 217451 : WB_GLOBAL_LIST_UNLOCK;
305 217451 : }
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 177214 : static void winbind_destructor(void)
315 : {
316 : #ifdef HAVE_PTHREAD
317 177214 : if (wb_global_ctx.key_initialized) {
318 1756 : int ret;
319 79270 : ret = pthread_key_delete(wb_global_ctx.key);
320 79270 : assert(ret == 0);
321 79270 : wb_global_ctx.key_initialized = false;
322 : }
323 :
324 177214 : wb_global_ctx.control = (pthread_once_t)PTHREAD_ONCE_INIT;
325 : #endif /* HAVE_PTHREAD */
326 :
327 177214 : winbind_cleanup_list();
328 177214 : }
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 54361 : static int make_nonstd_fd_internals(int fd, int limit /* Recursion limiter */)
336 : {
337 1541 : int new_fd;
338 54361 : 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 52820 : 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 54361 : static int make_safe_fd(int fd)
376 : {
377 1541 : int result, flags;
378 54361 : int new_fd = make_nonstd_fd_internals(fd, RECURSION_LIMIT);
379 54361 : 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 54361 : if ((flags = fcntl(new_fd, F_GETFL)) == -1) {
396 0 : close(new_fd);
397 0 : return -1;
398 : }
399 :
400 54361 : flags |= FLAG_TO_SET;
401 54361 : 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 54361 : result = flags = fcntl(new_fd, F_GETFD, 0);
411 54361 : if (flags >= 0) {
412 54361 : flags |= FD_CLOEXEC;
413 54361 : result = fcntl( new_fd, F_SETFD, flags );
414 : }
415 54361 : if (result < 0) {
416 0 : close(new_fd);
417 0 : return -1;
418 : }
419 : #endif
420 52820 : 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 108726 : static bool winbind_privileged_pipe_is_root(uid_t uid)
436 : {
437 108726 : if (uid == 0) {
438 0 : return true;
439 : }
440 :
441 108726 : if (uid_wrapper_enabled()) {
442 105644 : return true;
443 : }
444 :
445 0 : return false;
446 : }
447 :
448 : /* Connect to winbindd socket */
449 :
450 516856 : 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 516856 : if (lstat(dir, &st) == -1) {
462 462491 : errno = ENOENT;
463 462491 : 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 54365 : if (!S_ISDIR(st.st_mode) ||
471 54365 : !winbind_privileged_pipe_is_root(st.st_uid)) {
472 0 : errno = ENOENT;
473 0 : return -1;
474 : }
475 :
476 : /* Connect to socket */
477 :
478 54365 : sunaddr = (struct sockaddr_un) { .sun_family = AF_UNIX };
479 :
480 54365 : ret = snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path),
481 : "%s/%s", dir, WINBINDD_SOCKET_NAME);
482 54365 : 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 54365 : if (lstat(sunaddr.sun_path, &st) == -1) {
492 4 : errno = ENOENT;
493 4 : 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 54361 : if (!S_ISSOCK(st.st_mode) ||
503 54361 : !winbind_privileged_pipe_is_root(st.st_uid)) {
504 0 : errno = ENOENT;
505 0 : return -1;
506 : }
507 :
508 : /* Connect to socket */
509 :
510 54361 : 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 54361 : if ((fd = make_safe_fd( fd)) == -1) {
517 0 : return fd;
518 : }
519 :
520 54361 : 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 54361 : out:
561 :
562 52820 : return fd;
563 :
564 0 : error_out:
565 :
566 0 : close(fd);
567 0 : return -1;
568 : }
569 :
570 510308 : static const char *winbindd_socket_dir(void)
571 : {
572 510308 : if (nss_wrapper_enabled()) {
573 4925 : const char *env_dir;
574 :
575 510158 : env_dir = getenv("SELFTEST_WINBINDD_SOCKET_DIR");
576 510158 : if (env_dir != NULL) {
577 508866 : return env_dir;
578 : }
579 : }
580 :
581 1380 : return WINBINDD_SOCKET_DIR;
582 : }
583 :
584 : /* Connect to winbindd socket */
585 :
586 1767396 : 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 1767396 : ZERO_STRUCT(request);
594 1767396 : ZERO_STRUCT(response);
595 :
596 1767396 : if (!ctx) {
597 0 : return -1;
598 : }
599 :
600 1767396 : if (ctx->our_pid != getpid()) {
601 77688 : winbind_close_sock(ctx);
602 77688 : ctx->our_pid = getpid();
603 : }
604 :
605 1767396 : if ((need_priv != 0) && !ctx->is_privileged) {
606 6548 : winbind_close_sock(ctx);
607 : }
608 :
609 1767396 : if (ctx->winbindd_fd != -1) {
610 1250683 : return ctx->winbindd_fd;
611 : }
612 :
613 510308 : if (recursing) {
614 0 : return -1;
615 : }
616 :
617 510308 : ctx->winbindd_fd = winbind_named_pipe_sock(winbindd_socket_dir());
618 :
619 510308 : if (ctx->winbindd_fd == -1) {
620 459065 : return -1;
621 : }
622 :
623 47813 : ctx->is_privileged = false;
624 :
625 : /* version-check the socket */
626 :
627 47813 : request.wb_flags = WBFLAG_RECURSE;
628 47813 : if ((winbindd_request_response(ctx, WINBINDD_INTERFACE_VERSION, &request,
629 47813 : &response) != NSS_STATUS_SUCCESS) ||
630 47813 : (response.data.interface_version != WINBIND_INTERFACE_VERSION)) {
631 0 : winbind_close_sock(ctx);
632 0 : return -1;
633 : }
634 :
635 47813 : if (need_priv == 0) {
636 41265 : return ctx->winbindd_fd;
637 : }
638 :
639 : /* try and get priv pipe */
640 :
641 6548 : 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 6548 : ZERO_STRUCT(response);
649 6548 : if (winbindd_request_response(ctx, WINBINDD_PRIV_PIPE_DIR, &request,
650 : &response) == NSS_STATUS_SUCCESS) {
651 0 : int fd;
652 6548 : fd = winbind_named_pipe_sock((char *)response.extra_data.data);
653 6548 : if (fd != -1) {
654 6548 : close(ctx->winbindd_fd);
655 6548 : ctx->winbindd_fd = fd;
656 6548 : ctx->is_privileged = true;
657 : }
658 :
659 6548 : SAFE_FREE(response.extra_data.data);
660 : }
661 :
662 6548 : if (!ctx->is_privileged) {
663 0 : return -1;
664 : }
665 :
666 6548 : return ctx->winbindd_fd;
667 : #else
668 : return -1;
669 : #endif /* HAVE_UNIXSOCKET */
670 : }
671 :
672 : /* Write data to winbindd socket */
673 :
674 1112133 : 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 1112136 : restart:
682 :
683 1112136 : fd = winbind_open_pipe_sock(ctx, recursing, need_priv);
684 1112136 : if (fd == -1) {
685 462495 : errno = ENOENT;
686 462495 : return -1;
687 : }
688 :
689 : /* Write data to socket */
690 :
691 645669 : nwritten = 0;
692 :
693 1298112 : 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 649641 : pfd.fd = fd;
701 649641 : pfd.events = POLLIN|POLLOUT|POLLHUP;
702 :
703 649641 : ret = poll(&pfd, 1, -1);
704 649641 : 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 649641 : if ((ret == 1) && (pfd.revents & (POLLIN|POLLHUP|POLLERR))) {
712 :
713 : /* Pipe has closed on remote end */
714 :
715 1170 : winbind_close_sock(ctx);
716 1170 : goto restart;
717 : }
718 :
719 : /* Do the write */
720 :
721 1296942 : result = write(fd, (char *)buffer + nwritten,
722 648471 : count - nwritten);
723 :
724 648471 : if ((result == -1) || (result == 0)) {
725 :
726 : /* Write failed */
727 :
728 0 : winbind_close_sock(ctx);
729 0 : return -1;
730 : }
731 :
732 648471 : nwritten += result;
733 : }
734 :
735 644499 : return nwritten;
736 : }
737 :
738 : /* Read data from winbindd socket */
739 :
740 655260 : static int winbind_read_sock(struct winbindd_context *ctx,
741 : void *buffer, int count)
742 : {
743 3974 : int fd;
744 655260 : int nread = 0;
745 655260 : int total_time = 0;
746 :
747 655260 : fd = winbind_open_pipe_sock(ctx, false, false);
748 655260 : if (fd == -1) {
749 0 : return -1;
750 : }
751 :
752 : /* Read data from socket */
753 1310521 : 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 655262 : pfd.fd = fd;
761 655262 : pfd.events = POLLIN|POLLHUP;
762 :
763 : /* Wait for 5 seconds for a reply. May need to parameterise this... */
764 :
765 655262 : ret = poll(&pfd, 1, 5000);
766 655261 : if (ret == -1) {
767 0 : winbind_close_sock(ctx);
768 0 : return -1; /* poll error */
769 : }
770 :
771 655261 : if (ret == 0) {
772 : /* Not ready for read yet... */
773 2 : if (total_time >= 300) {
774 : /* Timeout */
775 0 : winbind_close_sock(ctx);
776 0 : return -1;
777 : }
778 2 : total_time += 5;
779 2 : continue;
780 : }
781 :
782 655259 : if ((ret == 1) && (pfd.revents & (POLLIN|POLLHUP|POLLERR))) {
783 :
784 : /* Do the Read */
785 :
786 1310518 : int result = read(fd, (char *)buffer + nread,
787 655259 : count - nread);
788 :
789 655259 : 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 655259 : nread += result;
800 :
801 : }
802 : }
803 :
804 651285 : return nread;
805 : }
806 :
807 : /* Read reply */
808 :
809 575469 : static int winbindd_read_reply(struct winbindd_context *ctx,
810 : struct winbindd_response *response)
811 : {
812 575469 : int result1, result2 = 0;
813 :
814 575469 : if (!response) {
815 0 : return -1;
816 : }
817 :
818 : /* Read fixed length response */
819 :
820 575469 : 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 575468 : response->extra_data.data = NULL;
828 :
829 575468 : if (result1 == -1) {
830 0 : return -1;
831 : }
832 :
833 575468 : if (response->length < sizeof(struct winbindd_response)) {
834 0 : return -1;
835 : }
836 :
837 : /* Read variable length response */
838 :
839 575468 : if (response->length > sizeof(struct winbindd_response)) {
840 79791 : int extra_data_len = response->length -
841 : sizeof(struct winbindd_response);
842 :
843 : /* Mallocate memory for extra data */
844 :
845 79791 : if (!(response->extra_data.data = malloc(extra_data_len))) {
846 0 : return -1;
847 : }
848 :
849 79791 : result2 = winbind_read_sock(ctx, response->extra_data.data,
850 : extra_data_len);
851 79791 : 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 575468 : return result1 + result2;
860 : }
861 :
862 : /*
863 : * send simple types of requests
864 : */
865 :
866 1090474 : 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 1090474 : if (winbind_env_set()) {
877 52510 : return NSS_STATUS_NOTFOUND;
878 : }
879 :
880 1037964 : if (!request) {
881 192411 : ZERO_STRUCT(lrequest);
882 192411 : request = &lrequest;
883 : }
884 :
885 : /* Fill in request and send down pipe */
886 :
887 1037964 : winbindd_init_request(request, req_type);
888 :
889 1037964 : if (winbind_write_sock(ctx, request, sizeof(*request),
890 1037964 : request->wb_flags & WBFLAG_RECURSE,
891 : need_priv) == -1)
892 : {
893 : /* Set ENOENT for consistency. Required by some apps */
894 462495 : errno = ENOENT;
895 :
896 462495 : return NSS_STATUS_UNAVAIL;
897 : }
898 :
899 648471 : if ((request->extra_len != 0) &&
900 73002 : (winbind_write_sock(ctx, request->extra_data.data,
901 72200 : request->extra_len,
902 73002 : 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 572299 : return NSS_STATUS_SUCCESS;
912 : }
913 :
914 : /*
915 : * Get results from winbindd request
916 : */
917 :
918 575469 : static NSS_STATUS winbindd_get_response(struct winbindd_context *ctx,
919 : struct winbindd_response *response)
920 : {
921 3170 : struct winbindd_response lresponse;
922 :
923 575469 : if (!response) {
924 183310 : ZERO_STRUCT(lresponse);
925 183310 : response = &lresponse;
926 : }
927 :
928 575469 : init_response(response);
929 :
930 : /* Wait for reply */
931 575469 : 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 575468 : if (response == &lresponse) {
940 183310 : winbindd_free_response(response);
941 : }
942 :
943 : /* Copy reply data from socket */
944 575468 : if (response->result != WINBINDD_OK) {
945 9056 : return NSS_STATUS_NOTFOUND;
946 : }
947 :
948 563258 : return NSS_STATUS_SUCCESS;
949 : }
950 :
951 : /* Handle simple types of requests */
952 :
953 1070510 : 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 1070510 : NSS_STATUS status = NSS_STATUS_UNAVAIL;
959 :
960 1070510 : if (ctx == NULL) {
961 1014925 : ctx = get_wb_global_ctx();
962 : }
963 :
964 1070510 : status = winbindd_send_request(ctx, req_type, 0, request);
965 1070510 : if (status != NSS_STATUS_SUCCESS) {
966 515005 : goto out;
967 : }
968 555505 : status = winbindd_get_response(ctx, response);
969 :
970 1070509 : out:
971 1070509 : return status;
972 : }
973 :
974 19964 : 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 19964 : NSS_STATUS status = NSS_STATUS_UNAVAIL;
980 :
981 19964 : if (ctx == NULL) {
982 19964 : ctx = get_wb_global_ctx();
983 : }
984 :
985 19964 : status = winbindd_send_request(ctx, req_type, 1, request);
986 19964 : if (status != NSS_STATUS_SUCCESS) {
987 0 : goto out;
988 : }
989 19964 : status = winbindd_get_response(ctx, response);
990 :
991 19964 : out:
992 19964 : 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 : }
|