Make struct wb_context private to wbclient.c
[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 struct wb_context {
24         struct async_req_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 = async_req_queue_init(result);
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 static void wb_trigger_trans(struct async_req *req)
557 {
558         struct wb_trans_state *state = talloc_get_type_abort(
559                 req->private_data, struct wb_trans_state);
560         struct tevent_req *subreq;
561
562         if ((state->wb_ctx->fd == -1)
563             || (state->need_priv && !state->wb_ctx->is_priv)) {
564
565                 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
566                                            state->need_priv);
567                 if (async_req_nomem(subreq, req)) {
568                         return;
569                 }
570                 tevent_req_set_callback(subreq, wb_trans_connect_done, req);
571                 return;
572         }
573
574         subreq = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
575                                    state->wb_req);
576         if (async_req_nomem(subreq, req)) {
577                 return;
578         }
579         tevent_req_set_callback(subreq, wb_trans_done, req);
580 }
581
582 struct async_req *wb_trans_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
583                                 struct wb_context *wb_ctx, bool need_priv,
584                                 struct winbindd_request *wb_req)
585 {
586         struct async_req *result;
587         struct wb_trans_state *state;
588
589         if (!async_req_setup(mem_ctx, &result, &state,
590                              struct wb_trans_state)) {
591                 return NULL;
592         }
593         state->wb_ctx = wb_ctx;
594         state->ev = ev;
595         state->wb_req = wb_req;
596         state->num_retries = 10;
597         state->need_priv = need_priv;
598
599         if (!async_req_enqueue(wb_ctx->queue, ev, result, wb_trigger_trans)) {
600                 goto fail;
601         }
602         return result;
603
604  fail:
605         TALLOC_FREE(result);
606         return NULL;
607 }
608
609 static bool wb_trans_retry(struct async_req *req,
610                            struct wb_trans_state *state,
611                            wbcErr wbc_err)
612 {
613         struct tevent_req *subreq;
614
615         if (WBC_ERROR_IS_OK(wbc_err)) {
616                 return false;
617         }
618
619         if (wbc_err == WBC_ERR_WINBIND_NOT_AVAILABLE) {
620                 /*
621                  * Winbind not around or we can't connect to the pipe. Fail
622                  * immediately.
623                  */
624                 async_req_error(req, wbc_err);
625                 return true;
626         }
627
628         state->num_retries -= 1;
629         if (state->num_retries == 0) {
630                 async_req_error(req, wbc_err);
631                 return true;
632         }
633
634         /*
635          * The transfer as such failed, retry after one second
636          */
637
638         if (state->wb_ctx->fd != -1) {
639                 close(state->wb_ctx->fd);
640                 state->wb_ctx->fd = -1;
641         }
642
643         subreq = tevent_wakeup_send(state, state->ev,
644                                     timeval_current_ofs(1, 0));
645         if (async_req_nomem(subreq, req)) {
646                 return true;
647         }
648         tevent_req_set_callback(subreq, wb_trans_retry_wait_done, req);
649         return true;
650 }
651
652 static void wb_trans_retry_wait_done(struct tevent_req *subreq)
653 {
654         struct async_req *req = tevent_req_callback_data(
655                 subreq, struct async_req);
656         struct wb_trans_state *state = talloc_get_type_abort(
657                 req->private_data, struct wb_trans_state);
658         bool ret;
659
660         ret = tevent_wakeup_recv(subreq);
661         TALLOC_FREE(subreq);
662         if (!ret) {
663                 async_req_error(req, WBC_ERR_UNKNOWN_FAILURE);
664                 return;
665         }
666
667         subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
668                                     state->need_priv);
669         if (async_req_nomem(subreq, req)) {
670                 return;
671         }
672         tevent_req_set_callback(subreq, wb_trans_connect_done, req);
673 }
674
675 static void wb_trans_connect_done(struct tevent_req *subreq)
676 {
677         struct async_req *req = tevent_req_callback_data(
678                 subreq, 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         wbcErr wbc_err;
683
684         wbc_err = wb_open_pipe_recv(subreq);
685         TALLOC_FREE(subreq);
686
687         if (wb_trans_retry(req, state, wbc_err)) {
688                 return;
689         }
690
691         subreq2 = wb_int_trans_send(state, state->ev, NULL, state->wb_ctx->fd,
692                                     state->wb_req);
693         if (async_req_nomem(subreq2, req)) {
694                 return;
695         }
696         tevent_req_set_callback(subreq2, wb_trans_done, req);
697 }
698
699 static void wb_trans_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         wbcErr wbc_err;
706
707         wbc_err = wb_int_trans_recv(subreq, state, &state->wb_resp);
708         TALLOC_FREE(subreq);
709
710         if (wb_trans_retry(req, state, wbc_err)) {
711                 return;
712         }
713
714         async_req_done(req);
715 }
716
717 wbcErr wb_trans_recv(struct async_req *req, TALLOC_CTX *mem_ctx,
718                      struct winbindd_response **presponse)
719 {
720         struct wb_trans_state *state = talloc_get_type_abort(
721                 req->private_data, struct wb_trans_state);
722         wbcErr wbc_err;
723
724         if (async_req_is_wbcerr(req, &wbc_err)) {
725                 return wbc_err;
726         }
727
728         *presponse = talloc_move(mem_ctx, &state->wb_resp);
729         return WBC_ERR_SUCCESS;
730 }