Merge branch 'master' of ssh://git.samba.org/data/git/samba into wspp-schema
[samba.git] / source3 / lib / wbclient.c
1 /*
2    Unix SMB/CIFS implementation.
3    Infrastructure for async winbind requests
4    Copyright (C) Volker Lendecke 2008
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "wbc_async.h"
22
23 struct wb_context {
24         struct tevent_queue *queue;
25         int fd;
26         bool is_priv;
27 };
28
29 static int make_nonstd_fd(int fd)
30 {
31         int i;
32         int sys_errno = 0;
33         int fds[3];
34         int num_fds = 0;
35
36         if (fd == -1) {
37                 return -1;
38         }
39         while (fd < 3) {
40                 fds[num_fds++] = fd;
41                 fd = dup(fd);
42                 if (fd == -1) {
43                         sys_errno = errno;
44                         break;
45                 }
46         }
47         for (i=0; i<num_fds; i++) {
48                 close(fds[i]);
49         }
50         if (fd == -1) {
51                 errno = sys_errno;
52         }
53         return fd;
54 }
55
56 /****************************************************************************
57  Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
58  else
59  if SYSV use O_NDELAY
60  if BSD use FNDELAY
61  Set close on exec also.
62 ****************************************************************************/
63
64 static int make_safe_fd(int fd)
65 {
66         int result, flags;
67         int new_fd = make_nonstd_fd(fd);
68
69         if (new_fd == -1) {
70                 goto fail;
71         }
72
73         /* Socket should be nonblocking. */
74
75 #ifdef O_NONBLOCK
76 #define FLAG_TO_SET O_NONBLOCK
77 #else
78 #ifdef SYSV
79 #define FLAG_TO_SET O_NDELAY
80 #else /* BSD */
81 #define FLAG_TO_SET FNDELAY
82 #endif
83 #endif
84
85         if ((flags = fcntl(new_fd, F_GETFL)) == -1) {
86                 goto fail;
87         }
88
89         flags |= FLAG_TO_SET;
90         if (fcntl(new_fd, F_SETFL, flags) == -1) {
91                 goto fail;
92         }
93
94 #undef FLAG_TO_SET
95
96         /* Socket should be closed on exec() */
97 #ifdef FD_CLOEXEC
98         result = flags = fcntl(new_fd, F_GETFD, 0);
99         if (flags >= 0) {
100                 flags |= FD_CLOEXEC;
101                 result = fcntl( new_fd, F_SETFD, flags );
102         }
103         if (result < 0) {
104                 goto fail;
105         }
106 #endif
107         return new_fd;
108
109  fail:
110         if (new_fd != -1) {
111                 int sys_errno = errno;
112                 close(new_fd);
113                 errno = sys_errno;
114         }
115         return -1;
116 }
117
118 static bool winbind_closed_fd(int fd)
119 {
120         struct timeval tv;
121         fd_set r_fds;
122
123         if (fd == -1) {
124                 return true;
125         }
126
127         FD_ZERO(&r_fds);
128         FD_SET(fd, &r_fds);
129         ZERO_STRUCT(tv);
130
131         if ((select(fd+1, &r_fds, NULL, NULL, &tv) == -1)
132             || FD_ISSET(fd, &r_fds)) {
133                 return true;
134         }
135
136         return false;
137 }
138
139 struct wb_context *wb_context_init(TALLOC_CTX *mem_ctx)
140 {
141         struct wb_context *result;
142
143         result = talloc(mem_ctx, struct wb_context);
144         if (result == NULL) {
145                 return NULL;
146         }
147         result->queue = tevent_queue_create(result, "wb_trans");
148         if (result->queue == NULL) {
149                 TALLOC_FREE(result);
150                 return NULL;
151         }
152         result->fd = -1;
153         return result;
154 }
155
156 struct wb_connect_state {
157         int dummy;
158 };
159
160 static void wbc_connect_connected(struct tevent_req *subreq);
161
162 static struct tevent_req *wb_connect_send(TALLOC_CTX *mem_ctx,
163                                           struct tevent_context *ev,
164                                           struct wb_context *wb_ctx,
165                                           const char *dir)
166 {
167         struct tevent_req *result, *subreq;
168         struct wb_connect_state *state;
169         struct sockaddr_un sunaddr;
170         struct stat st;
171         char *path = NULL;
172         wbcErr wbc_err;
173
174         result = tevent_req_create(mem_ctx, &state, struct wb_connect_state);
175         if (result == NULL) {
176                 return NULL;
177         }
178
179         if (wb_ctx->fd != -1) {
180                 close(wb_ctx->fd);
181                 wb_ctx->fd = -1;
182         }
183
184         /* Check permissions on unix socket directory */
185
186         if (lstat(dir, &st) == -1) {
187                 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
188                 goto post_status;
189         }
190
191         if (!S_ISDIR(st.st_mode) ||
192             (st.st_uid != 0 && st.st_uid != geteuid())) {
193                 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
194                 goto post_status;
195         }
196
197         /* Connect to socket */
198
199         path = talloc_asprintf(talloc_tos(), "%s/%s", dir,
200                                WINBINDD_SOCKET_NAME);
201         if (path == NULL) {
202                 goto nomem;
203         }
204
205         sunaddr.sun_family = AF_UNIX;
206         strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path));
207         TALLOC_FREE(path);
208
209         /* If socket file doesn't exist, don't bother trying to connect
210            with retry.  This is an attempt to make the system usable when
211            the winbindd daemon is not running. */
212
213         if ((lstat(sunaddr.sun_path, &st) == -1)
214             || !S_ISSOCK(st.st_mode)
215             || (st.st_uid != 0 && st.st_uid != geteuid())) {
216                 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
217                 goto post_status;
218         }
219
220         wb_ctx->fd = make_safe_fd(socket(AF_UNIX, SOCK_STREAM, 0));
221         if (wb_ctx->fd == -1) {
222                 wbc_err = map_wbc_err_from_errno(errno);
223                 goto post_status;
224         }
225
226         subreq = async_connect_send(mem_ctx, ev, wb_ctx->fd,
227                                     (struct sockaddr *)&sunaddr,
228                                     sizeof(sunaddr));
229         if (subreq == NULL) {
230                 goto nomem;
231         }
232         tevent_req_set_callback(subreq, wbc_connect_connected, result);
233
234         if (!tevent_req_set_endtime(subreq, ev, timeval_current_ofs(30, 0))) {
235                 goto nomem;
236         }
237
238         return result;
239
240  post_status:
241         tevent_req_error(result, wbc_err);
242         return tevent_req_post(result, ev);
243  nomem:
244         TALLOC_FREE(result);
245         return NULL;
246 }
247
248 static void wbc_connect_connected(struct tevent_req *subreq)
249 {
250         struct tevent_req *req = tevent_req_callback_data(
251                 subreq, struct tevent_req);
252         int res, err;
253
254         res = async_connect_recv(subreq, &err);
255         TALLOC_FREE(subreq);
256         if (res == -1) {
257                 tevent_req_error(req, map_wbc_err_from_errno(err));
258                 return;
259         }
260         tevent_req_done(req);
261 }
262
263 static wbcErr wb_connect_recv(struct tevent_req *req)
264 {
265         return tevent_req_simple_recv_wbcerr(req);
266 }
267
268 struct wb_int_trans_state {
269         struct tevent_context *ev;
270         int fd;
271         struct winbindd_request *wb_req;
272         struct winbindd_response *wb_resp;
273 };
274
275 static void wb_int_trans_write_done(struct tevent_req *subreq);
276 static void wb_int_trans_read_done(struct tevent_req *subreq);
277
278 static struct tevent_req *wb_int_trans_send(TALLOC_CTX *mem_ctx,
279                                             struct tevent_context *ev,
280                                             struct tevent_queue *queue, int fd,
281                                             struct winbindd_request *wb_req)
282 {
283         struct tevent_req *result, *subreq;
284         struct wb_int_trans_state *state;
285
286         result = tevent_req_create(mem_ctx, &state,
287                                    struct wb_int_trans_state);
288         if (result == NULL) {
289                 return NULL;
290         }
291
292         if (winbind_closed_fd(fd)) {
293                 tevent_req_error(result, WBC_ERR_WINBIND_NOT_AVAILABLE);
294                 return tevent_req_post(result, ev);
295         }
296
297         state->ev = ev;
298         state->fd = fd;
299         state->wb_req = wb_req;
300         state->wb_req->length = sizeof(struct winbindd_request);
301         state->wb_req->pid = getpid();
302
303         subreq = wb_req_write_send(state, state->ev, queue, state->fd,
304                                    state->wb_req);
305         if (subreq == NULL) {
306                 goto fail;
307         }
308         tevent_req_set_callback(subreq, wb_int_trans_write_done, result);
309
310         return result;
311
312  fail:
313         TALLOC_FREE(result);
314         return NULL;
315 }
316
317 static void wb_int_trans_write_done(struct tevent_req *subreq)
318 {
319         struct tevent_req *req = tevent_req_callback_data(
320                 subreq, struct tevent_req);
321         struct wb_int_trans_state *state = tevent_req_data(
322                 req, struct wb_int_trans_state);
323         wbcErr wbc_err;
324
325         wbc_err = wb_req_write_recv(subreq);
326         TALLOC_FREE(subreq);
327         if (!WBC_ERROR_IS_OK(wbc_err)) {
328                 tevent_req_error(req, wbc_err);
329                 return;
330         }
331
332         subreq = wb_resp_read_send(state, state->ev, state->fd);
333         if (tevent_req_nomem(subreq, req)) {
334                 return;
335         }
336         tevent_req_set_callback(subreq, wb_int_trans_read_done, req);
337 }
338
339 static void wb_int_trans_read_done(struct tevent_req *subreq)
340 {
341         struct tevent_req *req = tevent_req_callback_data(
342                 subreq, struct tevent_req);
343         struct wb_int_trans_state *state = tevent_req_data(
344                 req, struct wb_int_trans_state);
345         wbcErr wbc_err;
346
347         wbc_err = wb_resp_read_recv(subreq, state, &state->wb_resp);
348         TALLOC_FREE(subreq);
349         if (!WBC_ERROR_IS_OK(wbc_err)) {
350                 tevent_req_error(req, wbc_err);
351                 return;
352         }
353
354         tevent_req_done(req);
355 }
356
357 static wbcErr wb_int_trans_recv(struct tevent_req *req,
358                                 TALLOC_CTX *mem_ctx,
359                                 struct winbindd_response **presponse)
360 {
361         struct wb_int_trans_state *state = tevent_req_data(
362                 req, struct wb_int_trans_state);
363         wbcErr wbc_err;
364
365         if (tevent_req_is_wbcerr(req, &wbc_err)) {
366                 return wbc_err;
367         }
368
369         *presponse = talloc_move(mem_ctx, &state->wb_resp);
370         return WBC_ERR_SUCCESS;
371 }
372
373 static const char *winbindd_socket_dir(void)
374 {
375 #ifdef SOCKET_WRAPPER
376         const char *env_dir;
377
378         env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR);
379         if (env_dir) {
380                 return env_dir;
381         }
382 #endif
383
384         return WINBINDD_SOCKET_DIR;
385 }
386
387 struct wb_open_pipe_state {
388         struct wb_context *wb_ctx;
389         struct tevent_context *ev;
390         bool need_priv;
391         struct winbindd_request wb_req;
392 };
393
394 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq);
395 static void wb_open_pipe_ping_done(struct tevent_req *subreq);
396 static void wb_open_pipe_getpriv_done(struct tevent_req *subreq);
397 static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq);
398
399 static struct tevent_req *wb_open_pipe_send(TALLOC_CTX *mem_ctx,
400                                             struct tevent_context *ev,
401                                             struct wb_context *wb_ctx,
402                                             bool need_priv)
403 {
404         struct tevent_req *result, *subreq;
405         struct wb_open_pipe_state *state;
406
407         result = tevent_req_create(mem_ctx, &state, struct wb_open_pipe_state);
408         if (result == NULL) {
409                 return NULL;
410         }
411         state->wb_ctx = wb_ctx;
412         state->ev = ev;
413         state->need_priv = need_priv;
414
415         if (wb_ctx->fd != -1) {
416                 close(wb_ctx->fd);
417                 wb_ctx->fd = -1;
418         }
419
420         subreq = wb_connect_send(state, ev, wb_ctx, winbindd_socket_dir());
421         if (subreq == NULL) {
422                 goto fail;
423         }
424         tevent_req_set_callback(subreq, wb_open_pipe_connect_nonpriv_done,
425                                 result);
426         return result;
427
428  fail:
429         TALLOC_FREE(result);
430         return NULL;
431 }
432
433 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq)
434 {
435         struct tevent_req *req = tevent_req_callback_data(
436                 subreq, struct tevent_req);
437         struct wb_open_pipe_state *state = tevent_req_data(
438                 req, struct wb_open_pipe_state);
439         wbcErr wbc_err;
440
441         wbc_err = wb_connect_recv(subreq);
442         TALLOC_FREE(subreq);
443         if (!WBC_ERROR_IS_OK(wbc_err)) {
444                 state->wb_ctx->is_priv = true;
445                 tevent_req_error(req, wbc_err);
446                 return;
447         }
448
449         ZERO_STRUCT(state->wb_req);
450         state->wb_req.cmd = WINBINDD_INTERFACE_VERSION;
451
452         subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
453                                    &state->wb_req);
454         if (tevent_req_nomem(subreq, req)) {
455                 return;
456         }
457         tevent_req_set_callback(subreq, wb_open_pipe_ping_done, req);
458 }
459
460 static void wb_open_pipe_ping_done(struct tevent_req *subreq)
461 {
462         struct tevent_req *req = tevent_req_callback_data(
463                 subreq, struct tevent_req);
464         struct wb_open_pipe_state *state = tevent_req_data(
465                 req, struct wb_open_pipe_state);
466         struct winbindd_response *wb_resp;
467         wbcErr wbc_err;
468
469         wbc_err = wb_int_trans_recv(subreq, state, &wb_resp);
470         TALLOC_FREE(subreq);
471         if (!WBC_ERROR_IS_OK(wbc_err)) {
472                 tevent_req_error(req, wbc_err);
473                 return;
474         }
475
476         if (!state->need_priv) {
477                 tevent_req_done(req);
478                 return;
479         }
480
481         state->wb_req.cmd = WINBINDD_PRIV_PIPE_DIR;
482
483         subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
484                                    &state->wb_req);
485         if (tevent_req_nomem(subreq, req)) {
486                 return;
487         }
488         tevent_req_set_callback(subreq, wb_open_pipe_getpriv_done, req);
489 }
490
491 static void wb_open_pipe_getpriv_done(struct tevent_req *subreq)
492 {
493         struct tevent_req *req = tevent_req_callback_data(
494                 subreq, struct tevent_req);
495         struct wb_open_pipe_state *state = tevent_req_data(
496                 req, struct wb_open_pipe_state);
497         struct winbindd_response *wb_resp = NULL;
498         wbcErr wbc_err;
499
500         wbc_err = wb_int_trans_recv(subreq, state, &wb_resp);
501         TALLOC_FREE(subreq);
502         if (!WBC_ERROR_IS_OK(wbc_err)) {
503                 tevent_req_error(req, wbc_err);
504                 return;
505         }
506
507         close(state->wb_ctx->fd);
508         state->wb_ctx->fd = -1;
509
510         subreq = wb_connect_send(state, state->ev, state->wb_ctx,
511                                   (char *)wb_resp->extra_data.data);
512         TALLOC_FREE(wb_resp);
513         if (tevent_req_nomem(subreq, req)) {
514                 return;
515         }
516         tevent_req_set_callback(subreq, wb_open_pipe_connect_priv_done, req);
517 }
518
519 static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq)
520 {
521         struct tevent_req *req = tevent_req_callback_data(
522                 subreq, struct tevent_req);
523         struct wb_open_pipe_state *state = tevent_req_data(
524                 req, struct wb_open_pipe_state);
525         wbcErr wbc_err;
526
527         wbc_err = wb_connect_recv(subreq);
528         TALLOC_FREE(subreq);
529         if (!WBC_ERROR_IS_OK(wbc_err)) {
530                 tevent_req_error(req, wbc_err);
531                 return;
532         }
533         state->wb_ctx->is_priv = true;
534         tevent_req_done(req);
535 }
536
537 static wbcErr wb_open_pipe_recv(struct tevent_req *req)
538 {
539         return tevent_req_simple_recv_wbcerr(req);
540 }
541
542 struct wb_trans_state {
543         struct wb_trans_state *prev, *next;
544         struct wb_context *wb_ctx;
545         struct tevent_context *ev;
546         struct winbindd_request *wb_req;
547         struct winbindd_response *wb_resp;
548         int num_retries;
549         bool need_priv;
550 };
551
552 static void wb_trans_connect_done(struct tevent_req *subreq);
553 static void wb_trans_done(struct tevent_req *subreq);
554 static void wb_trans_retry_wait_done(struct tevent_req *subreq);
555
556 struct tevent_req *wb_trans_send(TALLOC_CTX *mem_ctx,
557                                  struct tevent_context *ev,
558                                  struct wb_context *wb_ctx, bool need_priv,
559                                  struct winbindd_request *wb_req)
560 {
561         struct tevent_req *req, *subreq;
562         struct wb_trans_state *state;
563
564         req = tevent_req_create(mem_ctx, &state, struct wb_trans_state);
565         if (req == NULL) {
566                 return NULL;
567         }
568         state->wb_ctx = wb_ctx;
569         state->ev = ev;
570         state->wb_req = wb_req;
571         state->num_retries = 10;
572         state->need_priv = need_priv;
573
574         if ((wb_ctx->fd == -1) || (need_priv && !wb_ctx->is_priv)) {
575                 subreq = wb_open_pipe_send(state, ev, wb_ctx, need_priv);
576                 if (subreq == NULL) {
577                         goto fail;
578                 }
579                 tevent_req_set_callback(subreq, wb_trans_connect_done, req);
580                 return req;
581         }
582
583         subreq = wb_int_trans_send(state, ev, wb_ctx->queue, wb_ctx->fd,
584                                    wb_req);
585         if (subreq == NULL) {
586                 goto fail;
587         }
588         tevent_req_set_callback(subreq, wb_trans_done, req);
589         return req;
590  fail:
591         TALLOC_FREE(req);
592         return NULL;
593 }
594
595 static bool wb_trans_retry(struct tevent_req *req,
596                            struct wb_trans_state *state,
597                            wbcErr wbc_err)
598 {
599         struct tevent_req *subreq;
600
601         if (WBC_ERROR_IS_OK(wbc_err)) {
602                 return false;
603         }
604
605         if (wbc_err == WBC_ERR_WINBIND_NOT_AVAILABLE) {
606                 /*
607                  * Winbind not around or we can't connect to the pipe. Fail
608                  * immediately.
609                  */
610                 tevent_req_error(req, wbc_err);
611                 return true;
612         }
613
614         state->num_retries -= 1;
615         if (state->num_retries == 0) {
616                 tevent_req_error(req, wbc_err);
617                 return true;
618         }
619
620         /*
621          * The transfer as such failed, retry after one second
622          */
623
624         if (state->wb_ctx->fd != -1) {
625                 close(state->wb_ctx->fd);
626                 state->wb_ctx->fd = -1;
627         }
628
629         subreq = tevent_wakeup_send(state, state->ev,
630                                     timeval_current_ofs(1, 0));
631         if (tevent_req_nomem(subreq, req)) {
632                 return true;
633         }
634         tevent_req_set_callback(subreq, wb_trans_retry_wait_done, req);
635         return true;
636 }
637
638 static void wb_trans_retry_wait_done(struct tevent_req *subreq)
639 {
640         struct tevent_req *req = tevent_req_callback_data(
641                 subreq, struct tevent_req);
642         struct wb_trans_state *state = tevent_req_data(
643                 req, struct wb_trans_state);
644         bool ret;
645
646         ret = tevent_wakeup_recv(subreq);
647         TALLOC_FREE(subreq);
648         if (!ret) {
649                 tevent_req_error(req, WBC_ERR_UNKNOWN_FAILURE);
650                 return;
651         }
652
653         subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
654                                    state->need_priv);
655         if (tevent_req_nomem(subreq, req)) {
656                 return;
657         }
658         tevent_req_set_callback(subreq, wb_trans_connect_done, req);
659 }
660
661 static void wb_trans_connect_done(struct tevent_req *subreq)
662 {
663         struct tevent_req *req = tevent_req_callback_data(
664                 subreq, struct tevent_req);
665         struct wb_trans_state *state = tevent_req_data(
666                 req, struct wb_trans_state);
667         wbcErr wbc_err;
668
669         wbc_err = wb_open_pipe_recv(subreq);
670         TALLOC_FREE(subreq);
671
672         if (wb_trans_retry(req, state, wbc_err)) {
673                 return;
674         }
675
676         subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
677                                    state->wb_req);
678         if (tevent_req_nomem(subreq, req)) {
679                 return;
680         }
681         tevent_req_set_callback(subreq, wb_trans_done, req);
682 }
683
684 static void wb_trans_done(struct tevent_req *subreq)
685 {
686         struct tevent_req *req = tevent_req_callback_data(
687                 subreq, struct tevent_req);
688         struct wb_trans_state *state = tevent_req_data(
689                 req, struct wb_trans_state);
690         wbcErr wbc_err;
691
692         wbc_err = wb_int_trans_recv(subreq, state, &state->wb_resp);
693         TALLOC_FREE(subreq);
694
695         if (wb_trans_retry(req, state, wbc_err)) {
696                 return;
697         }
698
699         tevent_req_done(req);
700 }
701
702 wbcErr wb_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
703                      struct winbindd_response **presponse)
704 {
705         struct wb_trans_state *state = tevent_req_data(
706                 req, struct wb_trans_state);
707         wbcErr wbc_err;
708
709         if (tevent_req_is_wbcerr(req, &wbc_err)) {
710                 return wbc_err;
711         }
712
713         *presponse = talloc_move(mem_ctx, &state->wb_resp);
714         return WBC_ERR_SUCCESS;
715 }