CVE-2023-0614 ldb: Add function to filter message in place
[Samba.git] / nsswitch / wb_common.c
blobd569e761ebe4a0193124e49446042a7f793d1952
1 /*
2 Unix SMB/CIFS implementation.
4 winbind client common code
6 Copyright (C) Tim Potter 2000
7 Copyright (C) Andrew Tridgell 2000
8 Copyright (C) Andrew Bartlett 2002
9 Copyright (C) Matthew Newton 2015
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.
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.
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/>.
26 #include "replace.h"
27 #include "system/select.h"
28 #include "winbind_client.h"
29 #include <assert.h>
31 #ifdef HAVE_PTHREAD_H
32 #include <pthread.h>
33 #endif
35 static __thread char client_name[32];
37 /* Global context */
39 struct winbindd_context {
40 int winbindd_fd; /* winbind file descriptor */
41 bool is_privileged; /* using the privileged socket? */
42 pid_t our_pid; /* calling process pid */
45 static struct wb_global_ctx {
46 bool initialized;
47 #ifdef HAVE_PTHREAD
48 pthread_once_t control;
49 pthread_key_t key;
50 #else
51 bool dummy;
52 #endif
53 } wb_global_ctx = {
54 #ifdef HAVE_PTHREAD
55 .control = PTHREAD_ONCE_INIT,
56 #endif
59 static void winbind_close_sock(struct winbindd_context *ctx);
61 #ifdef HAVE_PTHREAD
62 static void wb_thread_ctx_initialize(void);
64 static void wb_atfork_child(void)
66 struct winbindd_context *ctx = NULL;
67 int ret;
69 ctx = (struct winbindd_context *)pthread_getspecific(wb_global_ctx.key);
70 if (ctx == NULL) {
71 return;
74 ret = pthread_setspecific(wb_global_ctx.key, NULL);
75 assert(ret == 0);
77 winbind_close_sock(ctx);
78 free(ctx);
80 ret = pthread_key_delete(wb_global_ctx.key);
81 assert(ret == 0);
83 wb_global_ctx.control = (pthread_once_t)PTHREAD_ONCE_INIT;
86 static void wb_thread_ctx_destructor(void *p)
88 struct winbindd_context *ctx = (struct winbindd_context *)p;
90 winbind_close_sock(ctx);
91 free(ctx);
94 static void wb_thread_ctx_initialize(void)
96 int ret;
98 ret = pthread_atfork(NULL,
99 NULL,
100 wb_atfork_child);
101 assert(ret == 0);
103 ret = pthread_key_create(&wb_global_ctx.key,
104 wb_thread_ctx_destructor);
105 assert(ret == 0);
107 #endif
109 static struct winbindd_context *get_wb_thread_ctx(void)
111 struct winbindd_context *ctx = NULL;
112 int ret;
114 ret = pthread_once(&wb_global_ctx.control,
115 wb_thread_ctx_initialize);
116 assert(ret == 0);
118 ctx = (struct winbindd_context *)pthread_getspecific(
119 wb_global_ctx.key);
120 if (ctx != NULL) {
121 return ctx;
124 ctx = malloc(sizeof(struct winbindd_context));
125 if (ctx == NULL) {
126 return NULL;
129 *ctx = (struct winbindd_context) {
130 .winbindd_fd = -1,
131 .is_privileged = false,
132 .our_pid = 0
135 ret = pthread_setspecific(wb_global_ctx.key, ctx);
136 if (ret != 0) {
137 free(ctx);
138 return NULL;
140 return ctx;
143 static struct winbindd_context *get_wb_global_ctx(void)
145 struct winbindd_context *ctx = NULL;
146 #ifndef HAVE_PTHREAD
147 static struct winbindd_context _ctx = {
148 .winbindd_fd = -1,
149 .is_privileged = false,
150 .our_pid = 0
152 #endif
154 #ifdef HAVE_PTHREAD
155 ctx = get_wb_thread_ctx();
156 #else
157 ctx = &_ctx;
158 #endif
160 wb_global_ctx.initialized = true;
161 return ctx;
164 void winbind_set_client_name(const char *name)
166 if (name == NULL || strlen(name) == 0) {
167 return;
170 (void)snprintf(client_name, sizeof(client_name), "%s", name);
173 static const char *winbind_get_client_name(void)
175 if (client_name[0] == '\0') {
176 const char *progname = getprogname();
177 int len;
179 if (progname == NULL) {
180 progname = "<unknown>";
183 len = snprintf(client_name,
184 sizeof(client_name),
185 "%s",
186 progname);
187 if (len <= 0) {
188 return progname;
192 return client_name;
195 /* Initialise a request structure */
197 static void winbindd_init_request(struct winbindd_request *request,
198 int request_type)
200 request->length = sizeof(struct winbindd_request);
202 request->cmd = (enum winbindd_cmd)request_type;
203 request->pid = getpid();
205 (void)snprintf(request->client_name,
206 sizeof(request->client_name),
207 "%s",
208 winbind_get_client_name());
211 /* Initialise a response structure */
213 static void init_response(struct winbindd_response *response)
215 /* Initialise return value */
217 response->result = WINBINDD_ERROR;
220 /* Close established socket */
222 static void winbind_close_sock(struct winbindd_context *ctx)
224 if (!ctx) {
225 return;
228 if (ctx->winbindd_fd != -1) {
229 close(ctx->winbindd_fd);
230 ctx->winbindd_fd = -1;
234 /* Destructor for global context to ensure fd is closed */
236 #ifdef HAVE_DESTRUCTOR_ATTRIBUTE
237 __attribute__((destructor))
238 #elif defined (HAVE_PRAGMA_FINI)
239 #pragma fini (winbind_destructor)
240 #endif
241 static void winbind_destructor(void)
243 struct winbindd_context *ctx;
245 if (!wb_global_ctx.initialized) {
246 return;
249 #ifdef HAVE_PTHREAD_H
250 ctx = (struct winbindd_context *)pthread_getspecific(wb_global_ctx.key);
251 if (ctx == NULL) {
252 return;
254 #else
255 ctx = get_wb_global_ctx();
256 #endif
258 winbind_close_sock(ctx);
261 #define CONNECT_TIMEOUT 30
263 /* Make sure socket handle isn't stdin, stdout or stderr */
264 #define RECURSION_LIMIT 3
266 static int make_nonstd_fd_internals(int fd, int limit /* Recursion limiter */)
268 int new_fd;
269 if (fd >= 0 && fd <= 2) {
270 #ifdef F_DUPFD
271 if ((new_fd = fcntl(fd, F_DUPFD, 3)) == -1) {
272 return -1;
274 /* Paranoia */
275 if (new_fd < 3) {
276 close(new_fd);
277 return -1;
279 close(fd);
280 return new_fd;
281 #else
282 if (limit <= 0)
283 return -1;
285 new_fd = dup(fd);
286 if (new_fd == -1)
287 return -1;
289 /* use the program stack to hold our list of FDs to close */
290 new_fd = make_nonstd_fd_internals(new_fd, limit - 1);
291 close(fd);
292 return new_fd;
293 #endif
295 return fd;
298 /****************************************************************************
299 Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
300 else
301 if SYSV use O_NDELAY
302 if BSD use FNDELAY
303 Set close on exec also.
304 ****************************************************************************/
306 static int make_safe_fd(int fd)
308 int result, flags;
309 int new_fd = make_nonstd_fd_internals(fd, RECURSION_LIMIT);
310 if (new_fd == -1) {
311 close(fd);
312 return -1;
315 /* Socket should be nonblocking. */
316 #ifdef O_NONBLOCK
317 #define FLAG_TO_SET O_NONBLOCK
318 #else
319 #ifdef SYSV
320 #define FLAG_TO_SET O_NDELAY
321 #else /* BSD */
322 #define FLAG_TO_SET FNDELAY
323 #endif
324 #endif
326 if ((flags = fcntl(new_fd, F_GETFL)) == -1) {
327 close(new_fd);
328 return -1;
331 flags |= FLAG_TO_SET;
332 if (fcntl(new_fd, F_SETFL, flags) == -1) {
333 close(new_fd);
334 return -1;
337 #undef FLAG_TO_SET
339 /* Socket should be closed on exec() */
340 #ifdef FD_CLOEXEC
341 result = flags = fcntl(new_fd, F_GETFD, 0);
342 if (flags >= 0) {
343 flags |= FD_CLOEXEC;
344 result = fcntl( new_fd, F_SETFD, flags );
346 if (result < 0) {
347 close(new_fd);
348 return -1;
350 #endif
351 return new_fd;
355 * @internal
357 * @brief Check if we talk to the privileged pipe which should be owned by root.
359 * This checks if we have uid_wrapper running and if this is the case it will
360 * allow one to connect to the winbind privileged pipe even it is not owned by root.
362 * @param[in] uid The uid to check if we can safely talk to the pipe.
364 * @return If we have access it returns true, else false.
366 static bool winbind_privileged_pipe_is_root(uid_t uid)
368 if (uid == 0) {
369 return true;
372 if (uid_wrapper_enabled()) {
373 return true;
376 return false;
379 /* Connect to winbindd socket */
381 static int winbind_named_pipe_sock(const char *dir)
383 struct sockaddr_un sunaddr;
384 struct stat st;
385 int fd;
386 int wait_time;
387 int slept;
388 int ret;
390 /* Check permissions on unix socket directory */
392 if (lstat(dir, &st) == -1) {
393 errno = ENOENT;
394 return -1;
398 * This tells us that the pipe is owned by a privileged
399 * process, as we will be sending passwords to it.
401 if (!S_ISDIR(st.st_mode) ||
402 !winbind_privileged_pipe_is_root(st.st_uid)) {
403 errno = ENOENT;
404 return -1;
407 /* Connect to socket */
409 sunaddr = (struct sockaddr_un) { .sun_family = AF_UNIX };
411 ret = snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path),
412 "%s/%s", dir, WINBINDD_SOCKET_NAME);
413 if ((ret == -1) || (ret >= sizeof(sunaddr.sun_path))) {
414 errno = ENAMETOOLONG;
415 return -1;
418 /* If socket file doesn't exist, don't bother trying to connect
419 with retry. This is an attempt to make the system usable when
420 the winbindd daemon is not running. */
422 if (lstat(sunaddr.sun_path, &st) == -1) {
423 errno = ENOENT;
424 return -1;
427 /* Check permissions on unix socket file */
430 * This tells us that the pipe is owned by a privileged
431 * process, as we will be sending passwords to it.
433 if (!S_ISSOCK(st.st_mode) ||
434 !winbind_privileged_pipe_is_root(st.st_uid)) {
435 errno = ENOENT;
436 return -1;
439 /* Connect to socket */
441 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
442 return -1;
445 /* Set socket non-blocking and close on exec. */
447 if ((fd = make_safe_fd( fd)) == -1) {
448 return fd;
451 for (wait_time = 0; connect(fd, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1;
452 wait_time += slept) {
453 struct pollfd pfd;
454 int connect_errno = 0;
455 socklen_t errnosize;
457 if (wait_time >= CONNECT_TIMEOUT)
458 goto error_out;
460 switch (errno) {
461 case EINPROGRESS:
462 pfd.fd = fd;
463 pfd.events = POLLOUT;
465 ret = poll(&pfd, 1, (CONNECT_TIMEOUT - wait_time) * 1000);
467 if (ret > 0) {
468 errnosize = sizeof(connect_errno);
470 ret = getsockopt(fd, SOL_SOCKET,
471 SO_ERROR, &connect_errno, &errnosize);
473 if (ret >= 0 && connect_errno == 0) {
474 /* Connect succeed */
475 goto out;
479 slept = CONNECT_TIMEOUT;
480 break;
481 case EAGAIN:
482 slept = rand() % 3 + 1;
483 sleep(slept);
484 break;
485 default:
486 goto error_out;
491 out:
493 return fd;
495 error_out:
497 close(fd);
498 return -1;
501 static const char *winbindd_socket_dir(void)
503 if (nss_wrapper_enabled()) {
504 const char *env_dir;
506 env_dir = getenv("SELFTEST_WINBINDD_SOCKET_DIR");
507 if (env_dir != NULL) {
508 return env_dir;
512 return WINBINDD_SOCKET_DIR;
515 /* Connect to winbindd socket */
517 static int winbind_open_pipe_sock(struct winbindd_context *ctx,
518 int recursing, int need_priv)
520 #ifdef HAVE_UNIXSOCKET
521 struct winbindd_request request;
522 struct winbindd_response response;
524 ZERO_STRUCT(request);
525 ZERO_STRUCT(response);
527 if (!ctx) {
528 return -1;
531 if (ctx->our_pid != getpid()) {
532 winbind_close_sock(ctx);
533 ctx->our_pid = getpid();
536 if ((need_priv != 0) && !ctx->is_privileged) {
537 winbind_close_sock(ctx);
540 if (ctx->winbindd_fd != -1) {
541 return ctx->winbindd_fd;
544 if (recursing) {
545 return -1;
548 ctx->winbindd_fd = winbind_named_pipe_sock(winbindd_socket_dir());
550 if (ctx->winbindd_fd == -1) {
551 return -1;
554 ctx->is_privileged = false;
556 /* version-check the socket */
558 request.wb_flags = WBFLAG_RECURSE;
559 if ((winbindd_request_response(ctx, WINBINDD_INTERFACE_VERSION, &request,
560 &response) != NSS_STATUS_SUCCESS) ||
561 (response.data.interface_version != WINBIND_INTERFACE_VERSION)) {
562 winbind_close_sock(ctx);
563 return -1;
566 if (need_priv == 0) {
567 return ctx->winbindd_fd;
570 /* try and get priv pipe */
572 request.wb_flags = WBFLAG_RECURSE;
574 /* Note that response needs to be initialized to avoid
575 * crashing on clean up after WINBINDD_PRIV_PIPE_DIR call failed
576 * as interface version (from the first request) returned as a fstring,
577 * thus response.extra_data.data will not be NULL even though
578 * winbindd response did not write over it due to a failure */
579 ZERO_STRUCT(response);
580 if (winbindd_request_response(ctx, WINBINDD_PRIV_PIPE_DIR, &request,
581 &response) == NSS_STATUS_SUCCESS) {
582 int fd;
583 fd = winbind_named_pipe_sock((char *)response.extra_data.data);
584 if (fd != -1) {
585 close(ctx->winbindd_fd);
586 ctx->winbindd_fd = fd;
587 ctx->is_privileged = true;
590 SAFE_FREE(response.extra_data.data);
593 if (!ctx->is_privileged) {
594 return -1;
597 return ctx->winbindd_fd;
598 #else
599 return -1;
600 #endif /* HAVE_UNIXSOCKET */
603 /* Write data to winbindd socket */
605 static int winbind_write_sock(struct winbindd_context *ctx, void *buffer,
606 int count, int recursing, int need_priv)
608 int fd, result, nwritten;
610 /* Open connection to winbind daemon */
612 restart:
614 fd = winbind_open_pipe_sock(ctx, recursing, need_priv);
615 if (fd == -1) {
616 errno = ENOENT;
617 return -1;
620 /* Write data to socket */
622 nwritten = 0;
624 while(nwritten < count) {
625 struct pollfd pfd;
626 int ret;
628 /* Catch pipe close on other end by checking if a read()
629 call would not block by calling poll(). */
631 pfd.fd = fd;
632 pfd.events = POLLIN|POLLOUT|POLLHUP;
634 ret = poll(&pfd, 1, -1);
635 if (ret == -1) {
636 winbind_close_sock(ctx);
637 return -1; /* poll error */
640 /* Write should be OK if fd not available for reading */
642 if ((ret == 1) && (pfd.revents & (POLLIN|POLLHUP|POLLERR))) {
644 /* Pipe has closed on remote end */
646 winbind_close_sock(ctx);
647 goto restart;
650 /* Do the write */
652 result = write(fd, (char *)buffer + nwritten,
653 count - nwritten);
655 if ((result == -1) || (result == 0)) {
657 /* Write failed */
659 winbind_close_sock(ctx);
660 return -1;
663 nwritten += result;
666 return nwritten;
669 /* Read data from winbindd socket */
671 static int winbind_read_sock(struct winbindd_context *ctx,
672 void *buffer, int count)
674 int fd;
675 int nread = 0;
676 int total_time = 0;
678 fd = winbind_open_pipe_sock(ctx, false, false);
679 if (fd == -1) {
680 return -1;
683 /* Read data from socket */
684 while(nread < count) {
685 struct pollfd pfd;
686 int ret;
688 /* Catch pipe close on other end by checking if a read()
689 call would not block by calling poll(). */
691 pfd.fd = fd;
692 pfd.events = POLLIN|POLLHUP;
694 /* Wait for 5 seconds for a reply. May need to parameterise this... */
696 ret = poll(&pfd, 1, 5000);
697 if (ret == -1) {
698 winbind_close_sock(ctx);
699 return -1; /* poll error */
702 if (ret == 0) {
703 /* Not ready for read yet... */
704 if (total_time >= 300) {
705 /* Timeout */
706 winbind_close_sock(ctx);
707 return -1;
709 total_time += 5;
710 continue;
713 if ((ret == 1) && (pfd.revents & (POLLIN|POLLHUP|POLLERR))) {
715 /* Do the Read */
717 int result = read(fd, (char *)buffer + nread,
718 count - nread);
720 if ((result == -1) || (result == 0)) {
722 /* Read failed. I think the only useful thing we
723 can do here is just return -1 and fail since the
724 transaction has failed half way through. */
726 winbind_close_sock(ctx);
727 return -1;
730 nread += result;
735 return nread;
738 /* Read reply */
740 static int winbindd_read_reply(struct winbindd_context *ctx,
741 struct winbindd_response *response)
743 int result1, result2 = 0;
745 if (!response) {
746 return -1;
749 /* Read fixed length response */
751 result1 = winbind_read_sock(ctx, response,
752 sizeof(struct winbindd_response));
754 /* We actually send the pointer value of the extra_data field from
755 the server. This has no meaning in the client's address space
756 so we clear it out. */
758 response->extra_data.data = NULL;
760 if (result1 == -1) {
761 return -1;
764 if (response->length < sizeof(struct winbindd_response)) {
765 return -1;
768 /* Read variable length response */
770 if (response->length > sizeof(struct winbindd_response)) {
771 int extra_data_len = response->length -
772 sizeof(struct winbindd_response);
774 /* Mallocate memory for extra data */
776 if (!(response->extra_data.data = malloc(extra_data_len))) {
777 return -1;
780 result2 = winbind_read_sock(ctx, response->extra_data.data,
781 extra_data_len);
782 if (result2 == -1) {
783 winbindd_free_response(response);
784 return -1;
788 /* Return total amount of data read */
790 return result1 + result2;
794 * send simple types of requests
797 static NSS_STATUS winbindd_send_request(
798 struct winbindd_context *ctx,
799 int req_type,
800 int need_priv,
801 struct winbindd_request *request)
803 struct winbindd_request lrequest;
805 /* Check for our tricky environment variable */
807 if (winbind_env_set()) {
808 return NSS_STATUS_NOTFOUND;
811 if (!request) {
812 ZERO_STRUCT(lrequest);
813 request = &lrequest;
816 /* Fill in request and send down pipe */
818 winbindd_init_request(request, req_type);
820 if (winbind_write_sock(ctx, request, sizeof(*request),
821 request->wb_flags & WBFLAG_RECURSE,
822 need_priv) == -1)
824 /* Set ENOENT for consistency. Required by some apps */
825 errno = ENOENT;
827 return NSS_STATUS_UNAVAIL;
830 if ((request->extra_len != 0) &&
831 (winbind_write_sock(ctx, request->extra_data.data,
832 request->extra_len,
833 request->wb_flags & WBFLAG_RECURSE,
834 need_priv) == -1))
836 /* Set ENOENT for consistency. Required by some apps */
837 errno = ENOENT;
839 return NSS_STATUS_UNAVAIL;
842 return NSS_STATUS_SUCCESS;
846 * Get results from winbindd request
849 static NSS_STATUS winbindd_get_response(struct winbindd_context *ctx,
850 struct winbindd_response *response)
852 struct winbindd_response lresponse;
854 if (!response) {
855 ZERO_STRUCT(lresponse);
856 response = &lresponse;
859 init_response(response);
861 /* Wait for reply */
862 if (winbindd_read_reply(ctx, response) == -1) {
863 /* Set ENOENT for consistency. Required by some apps */
864 errno = ENOENT;
866 return NSS_STATUS_UNAVAIL;
869 /* Throw away extra data if client didn't request it */
870 if (response == &lresponse) {
871 winbindd_free_response(response);
874 /* Copy reply data from socket */
875 if (response->result != WINBINDD_OK) {
876 return NSS_STATUS_NOTFOUND;
879 return NSS_STATUS_SUCCESS;
882 /* Handle simple types of requests */
884 NSS_STATUS winbindd_request_response(struct winbindd_context *ctx,
885 int req_type,
886 struct winbindd_request *request,
887 struct winbindd_response *response)
889 NSS_STATUS status = NSS_STATUS_UNAVAIL;
891 if (ctx == NULL) {
892 ctx = get_wb_global_ctx();
895 status = winbindd_send_request(ctx, req_type, 0, request);
896 if (status != NSS_STATUS_SUCCESS) {
897 goto out;
899 status = winbindd_get_response(ctx, response);
901 out:
902 return status;
905 NSS_STATUS winbindd_priv_request_response(struct winbindd_context *ctx,
906 int req_type,
907 struct winbindd_request *request,
908 struct winbindd_response *response)
910 NSS_STATUS status = NSS_STATUS_UNAVAIL;
912 if (ctx == NULL) {
913 ctx = get_wb_global_ctx();
916 status = winbindd_send_request(ctx, req_type, 1, request);
917 if (status != NSS_STATUS_SUCCESS) {
918 goto out;
920 status = winbindd_get_response(ctx, response);
922 out:
923 return status;
926 /* Create and free winbindd context */
928 struct winbindd_context *winbindd_ctx_create(void)
930 struct winbindd_context *ctx;
932 ctx = calloc(1, sizeof(struct winbindd_context));
934 if (!ctx) {
935 return NULL;
938 ctx->winbindd_fd = -1;
940 return ctx;
943 void winbindd_ctx_free(struct winbindd_context *ctx)
945 winbind_close_sock(ctx);
946 free(ctx);