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