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