Convert wb_open_pipe 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 tevent_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 tevent_req *result, *subreq;
424         struct wb_open_pipe_state *state;
425
426         result = tevent_req_create(mem_ctx, &state, struct wb_open_pipe_state);
427         if (result == NULL) {
428                 return NULL;
429         }
430         state->wb_ctx = wb_ctx;
431         state->ev = ev;
432         state->need_priv = need_priv;
433
434         if (wb_ctx->fd != -1) {
435                 close(wb_ctx->fd);
436                 wb_ctx->fd = -1;
437         }
438
439         subreq = wb_connect_send(state, ev, wb_ctx, winbindd_socket_dir());
440         if (subreq == NULL) {
441                 goto fail;
442         }
443         tevent_req_set_callback(subreq, wb_open_pipe_connect_nonpriv_done,
444                                 result);
445         return result;
446
447  fail:
448         TALLOC_FREE(result);
449         return NULL;
450 }
451
452 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq)
453 {
454         struct tevent_req *req = tevent_req_callback_data(
455                 subreq, struct tevent_req);
456         struct wb_open_pipe_state *state = tevent_req_data(
457                 req, struct wb_open_pipe_state);
458         wbcErr wbc_err;
459
460         wbc_err = wb_connect_recv(subreq);
461         TALLOC_FREE(subreq);
462         if (!WBC_ERROR_IS_OK(wbc_err)) {
463                 state->wb_ctx->is_priv = true;
464                 tevent_req_error(req, wbc_err);
465                 return;
466         }
467
468         ZERO_STRUCT(state->wb_req);
469         state->wb_req.cmd = WINBINDD_INTERFACE_VERSION;
470
471         subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
472                                    &state->wb_req);
473         if (tevent_req_nomem(subreq, req)) {
474                 return;
475         }
476         tevent_req_set_callback(subreq, wb_open_pipe_ping_done, req);
477 }
478
479 static void wb_open_pipe_ping_done(struct tevent_req *subreq)
480 {
481         struct tevent_req *req = tevent_req_callback_data(
482                 subreq, struct tevent_req);
483         struct wb_open_pipe_state *state = tevent_req_data(
484                 req, struct wb_open_pipe_state);
485         struct winbindd_response *wb_resp;
486         wbcErr wbc_err;
487
488         wbc_err = wb_int_trans_recv(subreq, state, &wb_resp);
489         TALLOC_FREE(subreq);
490         if (!WBC_ERROR_IS_OK(wbc_err)) {
491                 tevent_req_error(req, wbc_err);
492                 return;
493         }
494
495         if (!state->need_priv) {
496                 tevent_req_done(req);
497                 return;
498         }
499
500         state->wb_req.cmd = WINBINDD_PRIV_PIPE_DIR;
501
502         subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
503                                    &state->wb_req);
504         if (tevent_req_nomem(subreq, req)) {
505                 return;
506         }
507         tevent_req_set_callback(subreq, wb_open_pipe_getpriv_done, req);
508 }
509
510 static void wb_open_pipe_getpriv_done(struct tevent_req *subreq)
511 {
512         struct tevent_req *req = tevent_req_callback_data(
513                 subreq, struct tevent_req);
514         struct wb_open_pipe_state *state = tevent_req_data(
515                 req, struct wb_open_pipe_state);
516         struct winbindd_response *wb_resp = NULL;
517         wbcErr wbc_err;
518
519         wbc_err = wb_int_trans_recv(subreq, state, &wb_resp);
520         TALLOC_FREE(subreq);
521         if (!WBC_ERROR_IS_OK(wbc_err)) {
522                 tevent_req_error(req, wbc_err);
523                 return;
524         }
525
526         close(state->wb_ctx->fd);
527         state->wb_ctx->fd = -1;
528
529         subreq = wb_connect_send(state, state->ev, state->wb_ctx,
530                                   (char *)wb_resp->extra_data.data);
531         TALLOC_FREE(wb_resp);
532         if (tevent_req_nomem(subreq, req)) {
533                 return;
534         }
535         tevent_req_set_callback(subreq, wb_open_pipe_connect_priv_done, req);
536 }
537
538 static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq)
539 {
540         struct tevent_req *req = tevent_req_callback_data(
541                 subreq, struct tevent_req);
542         struct wb_open_pipe_state *state = tevent_req_data(
543                 req, struct wb_open_pipe_state);
544         wbcErr wbc_err;
545
546         wbc_err = wb_connect_recv(subreq);
547         TALLOC_FREE(subreq);
548         if (!WBC_ERROR_IS_OK(wbc_err)) {
549                 tevent_req_error(req, wbc_err);
550                 return;
551         }
552         state->wb_ctx->is_priv = true;
553         tevent_req_done(req);
554 }
555
556 static wbcErr wb_open_pipe_recv(struct tevent_req *req)
557 {
558         return tevent_req_simple_recv_wbcerr(req);
559 }
560
561 struct wb_trans_state {
562         struct wb_trans_state *prev, *next;
563         struct wb_context *wb_ctx;
564         struct tevent_context *ev;
565         struct winbindd_request *wb_req;
566         struct winbindd_response *wb_resp;
567         int num_retries;
568         bool need_priv;
569 };
570
571 static void wb_trans_connect_done(struct tevent_req *subreq);
572 static void wb_trans_done(struct tevent_req *subreq);
573 static void wb_trans_retry_wait_done(struct async_req *subreq);
574
575 static void wb_trigger_trans(struct async_req *req)
576 {
577         struct wb_trans_state *state = talloc_get_type_abort(
578                 req->private_data, struct wb_trans_state);
579         struct tevent_req *subreq;
580
581         if ((state->wb_ctx->fd == -1)
582             || (state->need_priv && !state->wb_ctx->is_priv)) {
583
584                 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
585                                            state->need_priv);
586                 if (async_req_nomem(subreq, req)) {
587                         return;
588                 }
589                 tevent_req_set_callback(subreq, wb_trans_connect_done, req);
590                 return;
591         }
592
593         subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
594                                    state->wb_req);
595         if (async_req_nomem(subreq, req)) {
596                 return;
597         }
598         tevent_req_set_callback(subreq, wb_trans_done, req);
599 }
600
601 struct async_req *wb_trans_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
602                                 struct wb_context *wb_ctx, bool need_priv,
603                                 const struct winbindd_request *wb_req)
604 {
605         struct async_req *result;
606         struct wb_trans_state *state;
607
608         if (!async_req_setup(mem_ctx, &result, &state,
609                              struct wb_trans_state)) {
610                 return NULL;
611         }
612         state->wb_ctx = wb_ctx;
613         state->ev = ev;
614         state->wb_req = winbindd_request_copy(state, wb_req);
615         if (state->wb_req == NULL) {
616                 goto fail;
617         }
618         state->num_retries = 10;
619         state->need_priv = need_priv;
620
621         if (!async_req_enqueue(wb_ctx->queue, ev, result, wb_trigger_trans)) {
622                 goto fail;
623         }
624         return result;
625
626  fail:
627         TALLOC_FREE(result);
628         return NULL;
629 }
630
631 static bool wb_trans_retry(struct async_req *req,
632                            struct wb_trans_state *state,
633                            wbcErr wbc_err)
634 {
635         struct async_req *subreq;
636
637         if (WBC_ERROR_IS_OK(wbc_err)) {
638                 return false;
639         }
640
641         if (wbc_err == WBC_ERR_WINBIND_NOT_AVAILABLE) {
642                 /*
643                  * Winbind not around or we can't connect to the pipe. Fail
644                  * immediately.
645                  */
646                 async_req_error(req, wbc_err);
647                 return true;
648         }
649
650         state->num_retries -= 1;
651         if (state->num_retries == 0) {
652                 async_req_error(req, wbc_err);
653                 return true;
654         }
655
656         /*
657          * The transfer as such failed, retry after one second
658          */
659
660         if (state->wb_ctx->fd != -1) {
661                 close(state->wb_ctx->fd);
662                 state->wb_ctx->fd = -1;
663         }
664
665         subreq = async_wait_send(state, state->ev, timeval_set(1, 0));
666         if (async_req_nomem(subreq, req)) {
667                 return true;
668         }
669
670         subreq->async.fn = wb_trans_retry_wait_done;
671         subreq->async.priv = req;
672         return true;
673 }
674
675 static void wb_trans_retry_wait_done(struct async_req *subreq)
676 {
677         struct async_req *req = talloc_get_type_abort(
678                 subreq->async.priv, struct async_req);
679         struct wb_trans_state *state = talloc_get_type_abort(
680                 req->private_data, struct wb_trans_state);
681         struct tevent_req *subreq2;
682         bool ret;
683
684         ret = async_wait_recv(subreq);
685         TALLOC_FREE(subreq);
686         if (ret) {
687                 async_req_error(req, WBC_ERR_UNKNOWN_FAILURE);
688                 return;
689         }
690
691         subreq2 = wb_open_pipe_send(state, state->ev, state->wb_ctx,
692                                     state->need_priv);
693         if (async_req_nomem(subreq2, req)) {
694                 return;
695         }
696         tevent_req_set_callback(subreq2, wb_trans_connect_done, req);
697 }
698
699 static void wb_trans_connect_done(struct tevent_req *subreq)
700 {
701         struct async_req *req = tevent_req_callback_data(
702                 subreq, struct async_req);
703         struct wb_trans_state *state = talloc_get_type_abort(
704                 req->private_data, struct wb_trans_state);
705         struct tevent_req *subreq2;
706         wbcErr wbc_err;
707
708         wbc_err = wb_open_pipe_recv(subreq);
709         TALLOC_FREE(subreq);
710
711         if (wb_trans_retry(req, state, wbc_err)) {
712                 return;
713         }
714
715         subreq2 = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
716                                     state->wb_req);
717         if (async_req_nomem(subreq2, req)) {
718                 return;
719         }
720         tevent_req_set_callback(subreq2, wb_trans_done, req);
721 }
722
723 static void wb_trans_done(struct tevent_req *subreq)
724 {
725         struct async_req *req = tevent_req_callback_data(
726                 subreq, struct async_req);
727         struct wb_trans_state *state = talloc_get_type_abort(
728                 req->private_data, struct wb_trans_state);
729         wbcErr wbc_err;
730
731         wbc_err = wb_int_trans_recv(subreq, state, &state->wb_resp);
732         TALLOC_FREE(subreq);
733
734         if (wb_trans_retry(req, state, wbc_err)) {
735                 return;
736         }
737
738         async_req_done(req);
739 }
740
741 wbcErr wb_trans_recv(struct async_req *req, TALLOC_CTX *mem_ctx,
742                      struct winbindd_response **presponse)
743 {
744         struct wb_trans_state *state = talloc_get_type_abort(
745                 req->private_data, struct wb_trans_state);
746         wbcErr wbc_err;
747
748         if (async_req_is_wbcerr(req, &wbc_err)) {
749                 return wbc_err;
750         }
751
752         *presponse = talloc_move(mem_ctx, &state->wb_resp);
753         return WBC_ERR_SUCCESS;
754 }