2
Unix SMB/CIFS implementation.
3
Infrastructure for async winbind requests
4
Copyright (C) Volker Lendecke 2008
6
** NOTE! The following LGPL license applies to the wbclient
7
** library. This does NOT imply that all of Samba is released
10
This library is free software; you can redistribute it and/or
11
modify it under the terms of the GNU Lesser General Public
12
License as published by the Free Software Foundation; either
13
version 3 of the License, or (at your option) any later version.
15
This library is distributed in the hope that it will be useful,
16
but WITHOUT ANY WARRANTY; without even the implied warranty of
17
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18
Library General Public License for more details.
20
You should have received a copy of the GNU Lesser General Public License
21
along with this program. If not, see <http://www.gnu.org/licenses/>.
25
#include "system/filesys.h"
26
#include "system/network.h"
29
#include "lib/async_req/async_sock.h"
30
#include "nsswitch/winbind_struct_protocol.h"
31
#include "nsswitch/libwbclient/wbclient.h"
32
#include "nsswitch/libwbclient/wbc_async.h"
34
wbcErr map_wbc_err_from_errno(int error)
39
return WBC_ERR_AUTH_ERROR;
41
return WBC_ERR_NO_MEMORY;
44
return WBC_ERR_UNKNOWN_FAILURE;
48
bool tevent_req_is_wbcerr(struct tevent_req *req, wbcErr *pwbc_err)
50
enum tevent_req_state state;
52
if (!tevent_req_is_error(req, &state, &error)) {
53
*pwbc_err = WBC_ERR_SUCCESS;
58
case TEVENT_REQ_USER_ERROR:
61
case TEVENT_REQ_TIMED_OUT:
62
*pwbc_err = WBC_ERR_UNKNOWN_FAILURE;
64
case TEVENT_REQ_NO_MEMORY:
65
*pwbc_err = WBC_ERR_NO_MEMORY;
68
*pwbc_err = WBC_ERR_UNKNOWN_FAILURE;
74
wbcErr tevent_req_simple_recv_wbcerr(struct tevent_req *req)
78
if (tevent_req_is_wbcerr(req, &wbc_err)) {
82
return WBC_ERR_SUCCESS;
85
struct wbc_debug_ops {
86
void (*debug)(void *context, enum wbcDebugLevel level,
87
const char *fmt, va_list ap) PRINTF_ATTRIBUTE(3,0);
92
struct tevent_queue *queue;
96
struct wbc_debug_ops debug_ops;
99
static int make_nonstd_fd(int fd)
117
for (i=0; i<num_fds; i++) {
126
/****************************************************************************
127
Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
131
Set close on exec also.
132
****************************************************************************/
134
static int make_safe_fd(int fd)
137
int new_fd = make_nonstd_fd(fd);
143
/* Socket should be nonblocking. */
146
#define FLAG_TO_SET O_NONBLOCK
149
#define FLAG_TO_SET O_NDELAY
151
#define FLAG_TO_SET FNDELAY
155
if ((flags = fcntl(new_fd, F_GETFL)) == -1) {
159
flags |= FLAG_TO_SET;
160
if (fcntl(new_fd, F_SETFL, flags) == -1) {
166
/* Socket should be closed on exec() */
168
result = flags = fcntl(new_fd, F_GETFD, 0);
171
result = fcntl( new_fd, F_SETFD, flags );
181
int sys_errno = errno;
188
/* Just put a prototype to avoid moving the whole function around */
189
static const char *winbindd_socket_dir(void);
191
struct wb_context *wb_context_init(TALLOC_CTX *mem_ctx, const char* dir)
193
struct wb_context *result;
195
result = talloc(mem_ctx, struct wb_context);
196
if (result == NULL) {
199
result->queue = tevent_queue_create(result, "wb_trans");
200
if (result->queue == NULL) {
205
result->is_priv = false;
208
result->dir = talloc_strdup(result, dir);
210
result->dir = winbindd_socket_dir();
212
if (result->dir == NULL) {
219
struct wb_connect_state {
223
static void wbc_connect_connected(struct tevent_req *subreq);
225
static struct tevent_req *wb_connect_send(TALLOC_CTX *mem_ctx,
226
struct tevent_context *ev,
227
struct wb_context *wb_ctx,
230
struct tevent_req *result, *subreq;
231
struct wb_connect_state *state;
232
struct sockaddr_un sunaddr;
237
result = tevent_req_create(mem_ctx, &state, struct wb_connect_state);
238
if (result == NULL) {
242
if (wb_ctx->fd != -1) {
247
/* Check permissions on unix socket directory */
249
if (lstat(dir, &st) == -1) {
250
wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
254
if (!S_ISDIR(st.st_mode) ||
255
(st.st_uid != 0 && st.st_uid != geteuid())) {
256
wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
260
/* Connect to socket */
262
path = talloc_asprintf(mem_ctx, "%s/%s", dir,
263
WINBINDD_SOCKET_NAME);
268
sunaddr.sun_family = AF_UNIX;
269
strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path));
272
/* If socket file doesn't exist, don't bother trying to connect
273
with retry. This is an attempt to make the system usable when
274
the winbindd daemon is not running. */
276
if ((lstat(sunaddr.sun_path, &st) == -1)
277
|| !S_ISSOCK(st.st_mode)
278
|| (st.st_uid != 0 && st.st_uid != geteuid())) {
279
wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
283
wb_ctx->fd = make_safe_fd(socket(AF_UNIX, SOCK_STREAM, 0));
284
if (wb_ctx->fd == -1) {
285
wbc_err = map_wbc_err_from_errno(errno);
289
subreq = async_connect_send(mem_ctx, ev, wb_ctx->fd,
290
(struct sockaddr *)(void *)&sunaddr,
292
if (subreq == NULL) {
295
tevent_req_set_callback(subreq, wbc_connect_connected, result);
299
tevent_req_error(result, wbc_err);
300
return tevent_req_post(result, ev);
306
static void wbc_connect_connected(struct tevent_req *subreq)
308
struct tevent_req *req = tevent_req_callback_data(
309
subreq, struct tevent_req);
312
res = async_connect_recv(subreq, &err);
315
tevent_req_error(req, map_wbc_err_from_errno(err));
318
tevent_req_done(req);
321
static wbcErr wb_connect_recv(struct tevent_req *req)
323
return tevent_req_simple_recv_wbcerr(req);
326
static const char *winbindd_socket_dir(void)
328
#ifdef SOCKET_WRAPPER
331
env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR);
337
return WINBINDD_SOCKET_DIR;
340
struct wb_open_pipe_state {
341
struct wb_context *wb_ctx;
342
struct tevent_context *ev;
344
struct winbindd_request wb_req;
347
static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq);
348
static void wb_open_pipe_ping_done(struct tevent_req *subreq);
349
static void wb_open_pipe_getpriv_done(struct tevent_req *subreq);
350
static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq);
352
static struct tevent_req *wb_open_pipe_send(TALLOC_CTX *mem_ctx,
353
struct tevent_context *ev,
354
struct wb_context *wb_ctx,
357
struct tevent_req *result, *subreq;
358
struct wb_open_pipe_state *state;
360
result = tevent_req_create(mem_ctx, &state, struct wb_open_pipe_state);
361
if (result == NULL) {
364
state->wb_ctx = wb_ctx;
366
state->need_priv = need_priv;
368
if (wb_ctx->fd != -1) {
373
subreq = wb_connect_send(state, ev, wb_ctx, wb_ctx->dir);
374
if (subreq == NULL) {
377
tevent_req_set_callback(subreq, wb_open_pipe_connect_nonpriv_done,
386
static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq)
388
struct tevent_req *req = tevent_req_callback_data(
389
subreq, struct tevent_req);
390
struct wb_open_pipe_state *state = tevent_req_data(
391
req, struct wb_open_pipe_state);
394
wbc_err = wb_connect_recv(subreq);
396
if (!WBC_ERROR_IS_OK(wbc_err)) {
397
state->wb_ctx->is_priv = true;
398
tevent_req_error(req, wbc_err);
402
ZERO_STRUCT(state->wb_req);
403
state->wb_req.cmd = WINBINDD_INTERFACE_VERSION;
404
state->wb_req.pid = getpid();
406
subreq = wb_simple_trans_send(state, state->ev, NULL,
407
state->wb_ctx->fd, &state->wb_req);
408
if (tevent_req_nomem(subreq, req)) {
411
tevent_req_set_callback(subreq, wb_open_pipe_ping_done, req);
414
static void wb_open_pipe_ping_done(struct tevent_req *subreq)
416
struct tevent_req *req = tevent_req_callback_data(
417
subreq, struct tevent_req);
418
struct wb_open_pipe_state *state = tevent_req_data(
419
req, struct wb_open_pipe_state);
420
struct winbindd_response *wb_resp;
423
ret = wb_simple_trans_recv(subreq, state, &wb_resp, &err);
426
tevent_req_error(req, map_wbc_err_from_errno(err));
430
if (!state->need_priv) {
431
tevent_req_done(req);
435
state->wb_req.cmd = WINBINDD_PRIV_PIPE_DIR;
436
state->wb_req.pid = getpid();
438
subreq = wb_simple_trans_send(state, state->ev, NULL,
439
state->wb_ctx->fd, &state->wb_req);
440
if (tevent_req_nomem(subreq, req)) {
443
tevent_req_set_callback(subreq, wb_open_pipe_getpriv_done, req);
446
static void wb_open_pipe_getpriv_done(struct tevent_req *subreq)
448
struct tevent_req *req = tevent_req_callback_data(
449
subreq, struct tevent_req);
450
struct wb_open_pipe_state *state = tevent_req_data(
451
req, struct wb_open_pipe_state);
452
struct winbindd_response *wb_resp = NULL;
455
ret = wb_simple_trans_recv(subreq, state, &wb_resp, &err);
458
tevent_req_error(req, map_wbc_err_from_errno(err));
462
close(state->wb_ctx->fd);
463
state->wb_ctx->fd = -1;
465
subreq = wb_connect_send(state, state->ev, state->wb_ctx,
466
(char *)wb_resp->extra_data.data);
467
TALLOC_FREE(wb_resp);
468
if (tevent_req_nomem(subreq, req)) {
471
tevent_req_set_callback(subreq, wb_open_pipe_connect_priv_done, req);
474
static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq)
476
struct tevent_req *req = tevent_req_callback_data(
477
subreq, struct tevent_req);
478
struct wb_open_pipe_state *state = tevent_req_data(
479
req, struct wb_open_pipe_state);
482
wbc_err = wb_connect_recv(subreq);
484
if (!WBC_ERROR_IS_OK(wbc_err)) {
485
tevent_req_error(req, wbc_err);
488
state->wb_ctx->is_priv = true;
489
tevent_req_done(req);
492
static wbcErr wb_open_pipe_recv(struct tevent_req *req)
494
return tevent_req_simple_recv_wbcerr(req);
497
struct wb_trans_state {
498
struct wb_trans_state *prev, *next;
499
struct wb_context *wb_ctx;
500
struct tevent_context *ev;
501
struct winbindd_request *wb_req;
502
struct winbindd_response *wb_resp;
506
static bool closed_fd(int fd)
520
selret = select(fd+1, &r_fds, NULL, NULL, &tv);
527
return (FD_ISSET(fd, &r_fds));
530
static void wb_trans_trigger(struct tevent_req *req, void *private_data);
531
static void wb_trans_connect_done(struct tevent_req *subreq);
532
static void wb_trans_done(struct tevent_req *subreq);
533
static void wb_trans_retry_wait_done(struct tevent_req *subreq);
535
struct tevent_req *wb_trans_send(TALLOC_CTX *mem_ctx,
536
struct tevent_context *ev,
537
struct wb_context *wb_ctx, bool need_priv,
538
struct winbindd_request *wb_req)
540
struct tevent_req *req;
541
struct wb_trans_state *state;
543
req = tevent_req_create(mem_ctx, &state, struct wb_trans_state);
547
state->wb_ctx = wb_ctx;
549
state->wb_req = wb_req;
550
state->need_priv = need_priv;
552
if (!tevent_queue_add(wb_ctx->queue, ev, req, wb_trans_trigger,
554
tevent_req_nomem(NULL, req);
555
return tevent_req_post(req, ev);
560
static void wb_trans_trigger(struct tevent_req *req, void *private_data)
562
struct wb_trans_state *state = tevent_req_data(
563
req, struct wb_trans_state);
564
struct tevent_req *subreq;
566
if ((state->wb_ctx->fd != -1) && closed_fd(state->wb_ctx->fd)) {
567
close(state->wb_ctx->fd);
568
state->wb_ctx->fd = -1;
571
if ((state->wb_ctx->fd == -1)
572
|| (state->need_priv && !state->wb_ctx->is_priv)) {
573
subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
575
if (tevent_req_nomem(subreq, req)) {
578
tevent_req_set_callback(subreq, wb_trans_connect_done, req);
582
state->wb_req->pid = getpid();
584
subreq = wb_simple_trans_send(state, state->ev, NULL,
585
state->wb_ctx->fd, state->wb_req);
586
if (tevent_req_nomem(subreq, req)) {
589
tevent_req_set_callback(subreq, wb_trans_done, req);
592
static bool wb_trans_retry(struct tevent_req *req,
593
struct wb_trans_state *state,
596
struct tevent_req *subreq;
598
if (WBC_ERROR_IS_OK(wbc_err)) {
602
if (wbc_err == WBC_ERR_WINBIND_NOT_AVAILABLE) {
604
* Winbind not around or we can't connect to the pipe. Fail
607
tevent_req_error(req, wbc_err);
612
* The transfer as such failed, retry after one second
615
if (state->wb_ctx->fd != -1) {
616
close(state->wb_ctx->fd);
617
state->wb_ctx->fd = -1;
620
subreq = tevent_wakeup_send(state, state->ev,
621
tevent_timeval_current_ofs(1, 0));
622
if (tevent_req_nomem(subreq, req)) {
625
tevent_req_set_callback(subreq, wb_trans_retry_wait_done, req);
629
static void wb_trans_retry_wait_done(struct tevent_req *subreq)
631
struct tevent_req *req = tevent_req_callback_data(
632
subreq, struct tevent_req);
633
struct wb_trans_state *state = tevent_req_data(
634
req, struct wb_trans_state);
637
ret = tevent_wakeup_recv(subreq);
640
tevent_req_error(req, WBC_ERR_UNKNOWN_FAILURE);
644
subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
646
if (tevent_req_nomem(subreq, req)) {
649
tevent_req_set_callback(subreq, wb_trans_connect_done, req);
652
static void wb_trans_connect_done(struct tevent_req *subreq)
654
struct tevent_req *req = tevent_req_callback_data(
655
subreq, struct tevent_req);
656
struct wb_trans_state *state = tevent_req_data(
657
req, struct wb_trans_state);
660
wbc_err = wb_open_pipe_recv(subreq);
663
if (wb_trans_retry(req, state, wbc_err)) {
667
subreq = wb_simple_trans_send(state, state->ev, NULL,
668
state->wb_ctx->fd, state->wb_req);
669
if (tevent_req_nomem(subreq, req)) {
672
tevent_req_set_callback(subreq, wb_trans_done, req);
675
static void wb_trans_done(struct tevent_req *subreq)
677
struct tevent_req *req = tevent_req_callback_data(
678
subreq, struct tevent_req);
679
struct wb_trans_state *state = tevent_req_data(
680
req, struct wb_trans_state);
683
ret = wb_simple_trans_recv(subreq, state, &state->wb_resp, &err);
686
&& wb_trans_retry(req, state, map_wbc_err_from_errno(err))) {
690
tevent_req_done(req);
693
wbcErr wb_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
694
struct winbindd_response **presponse)
696
struct wb_trans_state *state = tevent_req_data(
697
req, struct wb_trans_state);
700
if (tevent_req_is_wbcerr(req, &wbc_err)) {
704
*presponse = talloc_move(mem_ctx, &state->wb_resp);
705
return WBC_ERR_SUCCESS;
708
/********************************************************************
709
* Debug wrapper functions, modeled (with lot's of code copied as is)
710
* after the tevent debug wrapper functions
711
********************************************************************/
714
this allows the user to choose their own debug function
716
int wbcSetDebug(struct wb_context *wb_ctx,
717
void (*debug)(void *context,
718
enum wbcDebugLevel level,
720
va_list ap) PRINTF_ATTRIBUTE(3,0),
723
wb_ctx->debug_ops.debug = debug;
724
wb_ctx->debug_ops.context = context;
729
debug function for wbcSetDebugStderr
731
static void wbcDebugStderr(void *private_data,
732
enum wbcDebugLevel level,
734
va_list ap) PRINTF_ATTRIBUTE(3,0);
735
static void wbcDebugStderr(void *private_data,
736
enum wbcDebugLevel level,
737
const char *fmt, va_list ap)
739
if (level <= WBC_DEBUG_WARNING) {
740
vfprintf(stderr, fmt, ap);
745
convenience function to setup debug messages on stderr
746
messages of level WBC_DEBUG_WARNING and higher are printed
748
int wbcSetDebugStderr(struct wb_context *wb_ctx)
750
return wbcSetDebug(wb_ctx, wbcDebugStderr, wb_ctx);
756
* The default debug action is to ignore debugging messages.
757
* This is the most appropriate action for a library.
758
* Applications using the library must decide where to
759
* redirect debugging messages
761
void wbcDebug(struct wb_context *wb_ctx, enum wbcDebugLevel level,
762
const char *fmt, ...)
768
if (wb_ctx->debug_ops.debug == NULL) {
772
wb_ctx->debug_ops.debug(wb_ctx->debug_ops.context, level, fmt, ap);