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