async_sock: Use unix errnos instead of NTSTATUS
[kai/samba-autobuild/.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 static struct async_req *wb_connect_send(TALLOC_CTX *mem_ctx,
151                                          struct tevent_context *ev,
152                                          struct wb_context *wb_ctx,
153                                          const char *dir)
154 {
155         struct async_req *req;
156         struct sockaddr_un sunaddr;
157         struct stat st;
158         char *path = NULL;
159         wbcErr wbc_err;
160
161         if (wb_ctx->fd != -1) {
162                 close(wb_ctx->fd);
163                 wb_ctx->fd = -1;
164         }
165
166         /* Check permissions on unix socket directory */
167
168         if (lstat(dir, &st) == -1) {
169                 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
170                 goto post_status;
171         }
172
173         if (!S_ISDIR(st.st_mode) ||
174             (st.st_uid != 0 && st.st_uid != geteuid())) {
175                 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
176                 goto post_status;
177         }
178
179         /* Connect to socket */
180
181         path = talloc_asprintf(talloc_tos(), "%s/%s", dir,
182                                WINBINDD_SOCKET_NAME);
183         if (path == NULL) {
184                 goto nomem;
185         }
186
187         sunaddr.sun_family = AF_UNIX;
188         strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path));
189         TALLOC_FREE(path);
190
191         /* If socket file doesn't exist, don't bother trying to connect
192            with retry.  This is an attempt to make the system usable when
193            the winbindd daemon is not running. */
194
195         if ((lstat(sunaddr.sun_path, &st) == -1)
196             || !S_ISSOCK(st.st_mode)
197             || (st.st_uid != 0 && st.st_uid != geteuid())) {
198                 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
199                 goto post_status;
200         }
201
202         wb_ctx->fd = make_safe_fd(socket(AF_UNIX, SOCK_STREAM, 0));
203         if (wb_ctx->fd == -1) {
204                 wbc_err = map_wbc_err_from_errno(errno);
205                 goto post_status;
206         }
207
208         req = async_connect_send(mem_ctx, ev, wb_ctx->fd,
209                                  (struct sockaddr *)&sunaddr,
210                                  sizeof(sunaddr));
211         if (req == NULL) {
212                 goto nomem;
213         }
214         if (!async_req_set_timeout(req, ev, timeval_set(30, 0))) {
215                 TALLOC_FREE(req);
216                 goto nomem;
217         }
218
219         return req;
220
221  nomem:
222         wbc_err = WBC_ERR_NO_MEMORY;
223  post_status:
224         req = async_req_new(mem_ctx);
225         if (req == NULL) {
226                 return NULL;
227         }
228         if (async_post_error(req, ev, wbc_err)) {
229                 return req;
230         }
231         TALLOC_FREE(req);
232         return NULL;
233 }
234
235 static wbcErr wb_connect_recv(struct async_req *req)
236 {
237         return async_req_simple_recv_wbcerr(req);
238 }
239
240 static struct winbindd_request *winbindd_request_copy(
241         TALLOC_CTX *mem_ctx,
242         const struct winbindd_request *req)
243 {
244         struct winbindd_request *result;
245
246         result = (struct winbindd_request *)TALLOC_MEMDUP(
247                 mem_ctx, req, sizeof(struct winbindd_request));
248         if (result == NULL) {
249                 return NULL;
250         }
251
252         if (result->extra_len == 0) {
253                 return result;
254         }
255
256         result->extra_data.data = (char *)TALLOC_MEMDUP(
257                 result, result->extra_data.data, result->extra_len);
258         if (result->extra_data.data == NULL) {
259                 TALLOC_FREE(result);
260                 return NULL;
261         }
262         return result;
263 }
264
265 struct wb_int_trans_state {
266         struct tevent_context *ev;
267         int fd;
268         struct winbindd_request *wb_req;
269         struct winbindd_response *wb_resp;
270 };
271
272 static void wb_int_trans_write_done(struct async_req *subreq);
273 static void wb_int_trans_read_done(struct async_req *subreq);
274
275 static struct async_req *wb_int_trans_send(TALLOC_CTX *mem_ctx,
276                                            struct tevent_context *ev, int fd,
277                                            struct winbindd_request *wb_req)
278 {
279         struct async_req *result;
280         struct async_req *subreq;
281         struct wb_int_trans_state *state;
282
283         if (!async_req_setup(mem_ctx, &result, &state,
284                              struct wb_int_trans_state)) {
285                 return NULL;
286         }
287
288         if (winbind_closed_fd(fd)) {
289                 if (!async_post_error(result, ev,
290                                       WBC_ERR_WINBIND_NOT_AVAILABLE)) {
291                         goto fail;
292                 }
293                 return result;
294         }
295
296         state->ev = ev;
297         state->fd = fd;
298         state->wb_req = wb_req;
299
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, state->fd, state->wb_req);
304         if (subreq == NULL) {
305                 goto fail;
306         }
307         subreq->async.fn = wb_int_trans_write_done;
308         subreq->async.priv = 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 async_req *subreq)
318 {
319         struct async_req *req = talloc_get_type_abort(
320                 subreq->async.priv, struct async_req);
321         struct wb_int_trans_state *state = talloc_get_type_abort(
322                 req->private_data, 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                 async_req_error(req, wbc_err);
329                 return;
330         }
331
332         subreq = wb_resp_read_send(state, state->ev, state->fd);
333         if (subreq == NULL) {
334                 async_req_error(req, WBC_ERR_NO_MEMORY);
335         }
336         subreq->async.fn = wb_int_trans_read_done;
337         subreq->async.priv = req;
338 }
339
340 static void wb_int_trans_read_done(struct async_req *subreq)
341 {
342         struct async_req *req = talloc_get_type_abort(
343                 subreq->async.priv, struct async_req);
344         struct wb_int_trans_state *state = talloc_get_type_abort(
345                 req->private_data, struct wb_int_trans_state);
346         wbcErr wbc_err;
347
348         wbc_err = wb_resp_read_recv(subreq, state, &state->wb_resp);
349         TALLOC_FREE(subreq);
350         if (!WBC_ERROR_IS_OK(wbc_err)) {
351                 async_req_error(req, wbc_err);
352                 return;
353         }
354
355         async_req_done(req);
356 }
357
358 static wbcErr wb_int_trans_recv(struct async_req *req,
359                                 TALLOC_CTX *mem_ctx,
360                                 struct winbindd_response **presponse)
361 {
362         struct wb_int_trans_state *state = talloc_get_type_abort(
363                 req->private_data, struct wb_int_trans_state);
364         wbcErr wbc_err;
365
366         if (async_req_is_wbcerr(req, &wbc_err)) {
367                 return wbc_err;
368         }
369
370         *presponse = talloc_move(mem_ctx, &state->wb_resp);
371         return WBC_ERR_SUCCESS;
372 }
373
374 static const char *winbindd_socket_dir(void)
375 {
376 #ifdef SOCKET_WRAPPER
377         const char *env_dir;
378
379         env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR);
380         if (env_dir) {
381                 return env_dir;
382         }
383 #endif
384
385         return WINBINDD_SOCKET_DIR;
386 }
387
388 struct wb_open_pipe_state {
389         struct wb_context *wb_ctx;
390         struct tevent_context *ev;
391         bool need_priv;
392         struct winbindd_request wb_req;
393 };
394
395 static void wb_open_pipe_connect_nonpriv_done(struct async_req *subreq);
396 static void wb_open_pipe_ping_done(struct async_req *subreq);
397 static void wb_open_pipe_getpriv_done(struct async_req *subreq);
398 static void wb_open_pipe_connect_priv_done(struct async_req *subreq);
399
400 static struct async_req *wb_open_pipe_send(TALLOC_CTX *mem_ctx,
401                                            struct tevent_context *ev,
402                                            struct wb_context *wb_ctx,
403                                            bool need_priv)
404 {
405         struct async_req *result;
406         struct async_req *subreq;
407         struct wb_open_pipe_state *state;
408
409         if (!async_req_setup(mem_ctx, &result, &state,
410                              struct wb_open_pipe_state)) {
411                 return NULL;
412         }
413         state->wb_ctx = wb_ctx;
414         state->ev = ev;
415         state->need_priv = need_priv;
416
417         if (wb_ctx->fd != -1) {
418                 close(wb_ctx->fd);
419                 wb_ctx->fd = -1;
420         }
421
422         subreq = wb_connect_send(state, ev, wb_ctx, winbindd_socket_dir());
423         if (subreq == NULL) {
424                 goto fail;
425         }
426
427         subreq->async.fn = wb_open_pipe_connect_nonpriv_done;
428         subreq->async.priv = result;
429         return result;
430
431  fail:
432         TALLOC_FREE(result);
433         return NULL;
434 }
435
436 static void wb_open_pipe_connect_nonpriv_done(struct async_req *subreq)
437 {
438         struct async_req *req = talloc_get_type_abort(
439                 subreq->async.priv, struct async_req);
440         struct wb_open_pipe_state *state = talloc_get_type_abort(
441                 req->private_data, struct wb_open_pipe_state);
442         wbcErr wbc_err;
443
444         wbc_err = wb_connect_recv(subreq);
445         TALLOC_FREE(subreq);
446         if (!WBC_ERROR_IS_OK(wbc_err)) {
447                 state->wb_ctx->is_priv = true;
448                 async_req_error(req, wbc_err);
449                 return;
450         }
451
452         ZERO_STRUCT(state->wb_req);
453         state->wb_req.cmd = WINBINDD_INTERFACE_VERSION;
454
455         subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
456                                    &state->wb_req);
457         if (async_req_nomem(subreq, req)) {
458                 return;
459         }
460
461         subreq->async.fn = wb_open_pipe_ping_done;
462         subreq->async.priv = req;
463 }
464
465 static void wb_open_pipe_ping_done(struct async_req *subreq)
466 {
467         struct async_req *req = talloc_get_type_abort(
468                 subreq->async.priv, struct async_req);
469         struct wb_open_pipe_state *state = talloc_get_type_abort(
470                 req->private_data, struct wb_open_pipe_state);
471         struct winbindd_response *wb_resp;
472         wbcErr wbc_err;
473
474         wbc_err = wb_int_trans_recv(subreq, state, &wb_resp);
475         TALLOC_FREE(subreq);
476         if (!WBC_ERROR_IS_OK(wbc_err)) {
477                 async_req_error(req, wbc_err);
478                 return;
479         }
480
481         if (!state->need_priv) {
482                 async_req_done(req);
483                 return;
484         }
485
486         state->wb_req.cmd = WINBINDD_PRIV_PIPE_DIR;
487
488         subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
489                                    &state->wb_req);
490         if (async_req_nomem(subreq, req)) {
491                 return;
492         }
493
494         subreq->async.fn = wb_open_pipe_getpriv_done;
495         subreq->async.priv = req;
496 }
497
498 static void wb_open_pipe_getpriv_done(struct async_req *subreq)
499 {
500         struct async_req *req = talloc_get_type_abort(
501                 subreq->async.priv, struct async_req);
502         struct wb_open_pipe_state *state = talloc_get_type_abort(
503                 req->private_data, struct wb_open_pipe_state);
504         struct winbindd_response *wb_resp = NULL;
505         wbcErr wbc_err;
506
507         wbc_err = wb_int_trans_recv(subreq, state, &wb_resp);
508         TALLOC_FREE(subreq);
509         if (!WBC_ERROR_IS_OK(wbc_err)) {
510                 async_req_error(req, wbc_err);
511                 return;
512         }
513
514         close(state->wb_ctx->fd);
515         state->wb_ctx->fd = -1;
516
517         subreq = wb_connect_send(state, state->ev, state->wb_ctx,
518                                  (char *)wb_resp->extra_data.data);
519         TALLOC_FREE(wb_resp);
520         if (async_req_nomem(subreq, req)) {
521                 return;
522         }
523
524         subreq->async.fn = wb_open_pipe_connect_priv_done;
525         subreq->async.priv = req;
526 }
527
528 static void wb_open_pipe_connect_priv_done(struct async_req *subreq)
529 {
530         struct async_req *req = talloc_get_type_abort(
531                 subreq->async.priv, struct async_req);
532         struct wb_open_pipe_state *state = talloc_get_type_abort(
533                 req->private_data, struct wb_open_pipe_state);
534         wbcErr wbc_err;
535
536         wbc_err = wb_connect_recv(subreq);
537         TALLOC_FREE(subreq);
538         if (!WBC_ERROR_IS_OK(wbc_err)) {
539                 async_req_error(req, wbc_err);
540                 return;
541         }
542         state->wb_ctx->is_priv = true;
543         async_req_done(req);
544 }
545
546 static wbcErr wb_open_pipe_recv(struct async_req *req)
547 {
548         return async_req_simple_recv_wbcerr(req);
549 }
550
551 struct wb_trans_state {
552         struct wb_trans_state *prev, *next;
553         struct wb_context *wb_ctx;
554         struct tevent_context *ev;
555         struct winbindd_request *wb_req;
556         struct winbindd_response *wb_resp;
557         int num_retries;
558         bool need_priv;
559 };
560
561 static void wb_trans_connect_done(struct async_req *subreq);
562 static void wb_trans_done(struct async_req *subreq);
563 static void wb_trans_retry_wait_done(struct async_req *subreq);
564
565 static void wb_trigger_trans(struct async_req *req)
566 {
567         struct wb_trans_state *state = talloc_get_type_abort(
568                 req->private_data, struct wb_trans_state);
569         struct async_req *subreq;
570
571         if ((state->wb_ctx->fd == -1)
572             || (state->need_priv && !state->wb_ctx->is_priv)) {
573
574                 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
575                                            state->need_priv);
576                 if (async_req_nomem(subreq, req)) {
577                         return;
578                 }
579                 subreq->async.fn = wb_trans_connect_done;
580                 subreq->async.priv = req;
581                 return;
582         }
583
584         subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
585                                    state->wb_req);
586         if (async_req_nomem(subreq, req)) {
587                 return;
588         }
589         subreq->async.fn = wb_trans_done;
590         subreq->async.priv = req;
591 }
592
593 struct async_req *wb_trans_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
594                                 struct wb_context *wb_ctx, bool need_priv,
595                                 const struct winbindd_request *wb_req)
596 {
597         struct async_req *result;
598         struct wb_trans_state *state;
599
600         if (!async_req_setup(mem_ctx, &result, &state,
601                              struct wb_trans_state)) {
602                 return NULL;
603         }
604         state->wb_ctx = wb_ctx;
605         state->ev = ev;
606         state->wb_req = winbindd_request_copy(state, wb_req);
607         if (state->wb_req == NULL) {
608                 goto fail;
609         }
610         state->num_retries = 10;
611         state->need_priv = need_priv;
612
613         if (!async_req_enqueue(wb_ctx->queue, ev, result, wb_trigger_trans)) {
614                 goto fail;
615         }
616         return result;
617
618  fail:
619         TALLOC_FREE(result);
620         return NULL;
621 }
622
623 static bool wb_trans_retry(struct async_req *req,
624                            struct wb_trans_state *state,
625                            wbcErr wbc_err)
626 {
627         struct async_req *subreq;
628
629         if (WBC_ERROR_IS_OK(wbc_err)) {
630                 return false;
631         }
632
633         if (wbc_err == WBC_ERR_WINBIND_NOT_AVAILABLE) {
634                 /*
635                  * Winbind not around or we can't connect to the pipe. Fail
636                  * immediately.
637                  */
638                 async_req_error(req, wbc_err);
639                 return true;
640         }
641
642         state->num_retries -= 1;
643         if (state->num_retries == 0) {
644                 async_req_error(req, wbc_err);
645                 return true;
646         }
647
648         /*
649          * The transfer as such failed, retry after one second
650          */
651
652         if (state->wb_ctx->fd != -1) {
653                 close(state->wb_ctx->fd);
654                 state->wb_ctx->fd = -1;
655         }
656
657         subreq = async_wait_send(state, state->ev, timeval_set(1, 0));
658         if (async_req_nomem(subreq, req)) {
659                 return true;
660         }
661
662         subreq->async.fn = wb_trans_retry_wait_done;
663         subreq->async.priv = req;
664         return true;
665 }
666
667 static void wb_trans_retry_wait_done(struct async_req *subreq)
668 {
669         struct async_req *req = talloc_get_type_abort(
670                 subreq->async.priv, struct async_req);
671         struct wb_trans_state *state = talloc_get_type_abort(
672                 req->private_data, struct wb_trans_state);
673         bool ret;
674
675         ret = async_wait_recv(subreq);
676         TALLOC_FREE(subreq);
677         if (ret) {
678                 async_req_error(req, WBC_ERR_UNKNOWN_FAILURE);
679                 return;
680         }
681
682         subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
683                                    state->need_priv);
684         if (async_req_nomem(subreq, req)) {
685                 return;
686         }
687         subreq->async.fn = wb_trans_connect_done;
688         subreq->async.priv = req;
689 }
690
691 static void wb_trans_connect_done(struct async_req *subreq)
692 {
693         struct async_req *req = talloc_get_type_abort(
694                 subreq->async.priv, struct async_req);
695         struct wb_trans_state *state = talloc_get_type_abort(
696                 req->private_data, struct wb_trans_state);
697         wbcErr wbc_err;
698
699         wbc_err = wb_open_pipe_recv(subreq);
700         TALLOC_FREE(subreq);
701
702         if (wb_trans_retry(req, state, wbc_err)) {
703                 return;
704         }
705
706         subreq = wb_int_trans_send(state, state->ev, state->wb_ctx->fd,
707                                    state->wb_req);
708         if (async_req_nomem(subreq, req)) {
709                 return;
710         }
711
712         subreq->async.fn = wb_trans_done;
713         subreq->async.priv = req;
714 }
715
716 static void wb_trans_done(struct async_req *subreq)
717 {
718         struct async_req *req = talloc_get_type_abort(
719                 subreq->async.priv, struct async_req);
720         struct wb_trans_state *state = talloc_get_type_abort(
721                 req->private_data, struct wb_trans_state);
722         wbcErr wbc_err;
723
724         wbc_err = wb_int_trans_recv(subreq, state, &state->wb_resp);
725         TALLOC_FREE(subreq);
726
727         if (wb_trans_retry(req, state, wbc_err)) {
728                 return;
729         }
730
731         async_req_done(req);
732 }
733
734 wbcErr wb_trans_recv(struct async_req *req, TALLOC_CTX *mem_ctx,
735                      struct winbindd_response **presponse)
736 {
737         struct wb_trans_state *state = talloc_get_type_abort(
738                 req->private_data, struct wb_trans_state);
739         wbcErr wbc_err;
740
741         if (async_req_is_wbcerr(req, &wbc_err)) {
742                 return wbc_err;
743         }
744
745         *presponse = talloc_move(mem_ctx, &state->wb_resp);
746         return WBC_ERR_SUCCESS;
747 }