979aa94d40fc6a701b517d3961c1738f9117149e
[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 wbcErr map_wbc_err_from_errno(int error)
24 {
25         switch(error) {
26         case EPERM:
27         case EACCES:
28                 return WBC_ERR_AUTH_ERROR;
29         case ENOMEM:
30                 return WBC_ERR_NO_MEMORY;
31         case EIO:
32         default:
33                 return WBC_ERR_UNKNOWN_FAILURE;
34         }
35 }
36
37 bool tevent_req_is_wbcerr(struct tevent_req *req, wbcErr *pwbc_err)
38 {
39         enum tevent_req_state state;
40         uint64_t error;
41         if (!tevent_req_is_error(req, &state, &error)) {
42                 *pwbc_err = WBC_ERR_SUCCESS;
43                 return false;
44         }
45
46         switch (state) {
47         case TEVENT_REQ_USER_ERROR:
48                 *pwbc_err = error;
49                 break;
50         case TEVENT_REQ_TIMED_OUT:
51                 *pwbc_err = WBC_ERR_UNKNOWN_FAILURE;
52                 break;
53         case TEVENT_REQ_NO_MEMORY:
54                 *pwbc_err = WBC_ERR_NO_MEMORY;
55                 break;
56         default:
57                 *pwbc_err = WBC_ERR_UNKNOWN_FAILURE;
58                 break;
59         }
60         return true;
61 }
62
63 wbcErr tevent_req_simple_recv_wbcerr(struct tevent_req *req)
64 {
65         wbcErr wbc_err;
66
67         if (tevent_req_is_wbcerr(req, &wbc_err)) {
68                 return wbc_err;
69         }
70
71         return WBC_ERR_SUCCESS;
72 }
73
74 struct wb_context {
75         struct tevent_queue *queue;
76         int fd;
77         bool is_priv;
78 };
79
80 static int make_nonstd_fd(int fd)
81 {
82         int i;
83         int sys_errno = 0;
84         int fds[3];
85         int num_fds = 0;
86
87         if (fd == -1) {
88                 return -1;
89         }
90         while (fd < 3) {
91                 fds[num_fds++] = fd;
92                 fd = dup(fd);
93                 if (fd == -1) {
94                         sys_errno = errno;
95                         break;
96                 }
97         }
98         for (i=0; i<num_fds; i++) {
99                 close(fds[i]);
100         }
101         if (fd == -1) {
102                 errno = sys_errno;
103         }
104         return fd;
105 }
106
107 /****************************************************************************
108  Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
109  else
110  if SYSV use O_NDELAY
111  if BSD use FNDELAY
112  Set close on exec also.
113 ****************************************************************************/
114
115 static int make_safe_fd(int fd)
116 {
117         int result, flags;
118         int new_fd = make_nonstd_fd(fd);
119
120         if (new_fd == -1) {
121                 goto fail;
122         }
123
124         /* Socket should be nonblocking. */
125
126 #ifdef O_NONBLOCK
127 #define FLAG_TO_SET O_NONBLOCK
128 #else
129 #ifdef SYSV
130 #define FLAG_TO_SET O_NDELAY
131 #else /* BSD */
132 #define FLAG_TO_SET FNDELAY
133 #endif
134 #endif
135
136         if ((flags = fcntl(new_fd, F_GETFL)) == -1) {
137                 goto fail;
138         }
139
140         flags |= FLAG_TO_SET;
141         if (fcntl(new_fd, F_SETFL, flags) == -1) {
142                 goto fail;
143         }
144
145 #undef FLAG_TO_SET
146
147         /* Socket should be closed on exec() */
148 #ifdef FD_CLOEXEC
149         result = flags = fcntl(new_fd, F_GETFD, 0);
150         if (flags >= 0) {
151                 flags |= FD_CLOEXEC;
152                 result = fcntl( new_fd, F_SETFD, flags );
153         }
154         if (result < 0) {
155                 goto fail;
156         }
157 #endif
158         return new_fd;
159
160  fail:
161         if (new_fd != -1) {
162                 int sys_errno = errno;
163                 close(new_fd);
164                 errno = sys_errno;
165         }
166         return -1;
167 }
168
169 struct wb_context *wb_context_init(TALLOC_CTX *mem_ctx)
170 {
171         struct wb_context *result;
172
173         result = talloc(mem_ctx, struct wb_context);
174         if (result == NULL) {
175                 return NULL;
176         }
177         result->queue = tevent_queue_create(result, "wb_trans");
178         if (result->queue == NULL) {
179                 TALLOC_FREE(result);
180                 return NULL;
181         }
182         result->fd = -1;
183         result->is_priv = false;
184         return result;
185 }
186
187 struct wb_connect_state {
188         int dummy;
189 };
190
191 static void wbc_connect_connected(struct tevent_req *subreq);
192
193 static struct tevent_req *wb_connect_send(TALLOC_CTX *mem_ctx,
194                                           struct tevent_context *ev,
195                                           struct wb_context *wb_ctx,
196                                           const char *dir)
197 {
198         struct tevent_req *result, *subreq;
199         struct wb_connect_state *state;
200         struct sockaddr_un sunaddr;
201         struct stat st;
202         char *path = NULL;
203         wbcErr wbc_err;
204
205         result = tevent_req_create(mem_ctx, &state, struct wb_connect_state);
206         if (result == NULL) {
207                 return NULL;
208         }
209
210         if (wb_ctx->fd != -1) {
211                 close(wb_ctx->fd);
212                 wb_ctx->fd = -1;
213         }
214
215         /* Check permissions on unix socket directory */
216
217         if (lstat(dir, &st) == -1) {
218                 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
219                 goto post_status;
220         }
221
222         if (!S_ISDIR(st.st_mode) ||
223             (st.st_uid != 0 && st.st_uid != geteuid())) {
224                 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
225                 goto post_status;
226         }
227
228         /* Connect to socket */
229
230         path = talloc_asprintf(talloc_tos(), "%s/%s", dir,
231                                WINBINDD_SOCKET_NAME);
232         if (path == NULL) {
233                 goto nomem;
234         }
235
236         sunaddr.sun_family = AF_UNIX;
237         strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path));
238         TALLOC_FREE(path);
239
240         /* If socket file doesn't exist, don't bother trying to connect
241            with retry.  This is an attempt to make the system usable when
242            the winbindd daemon is not running. */
243
244         if ((lstat(sunaddr.sun_path, &st) == -1)
245             || !S_ISSOCK(st.st_mode)
246             || (st.st_uid != 0 && st.st_uid != geteuid())) {
247                 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
248                 goto post_status;
249         }
250
251         wb_ctx->fd = make_safe_fd(socket(AF_UNIX, SOCK_STREAM, 0));
252         if (wb_ctx->fd == -1) {
253                 wbc_err = map_wbc_err_from_errno(errno);
254                 goto post_status;
255         }
256
257         subreq = async_connect_send(mem_ctx, ev, wb_ctx->fd,
258                                     (struct sockaddr *)(void *)&sunaddr,
259                                     sizeof(sunaddr));
260         if (subreq == NULL) {
261                 goto nomem;
262         }
263         tevent_req_set_callback(subreq, wbc_connect_connected, result);
264
265         if (!tevent_req_set_endtime(subreq, ev, timeval_current_ofs(30, 0))) {
266                 goto nomem;
267         }
268
269         return result;
270
271  post_status:
272         tevent_req_error(result, wbc_err);
273         return tevent_req_post(result, ev);
274  nomem:
275         TALLOC_FREE(result);
276         return NULL;
277 }
278
279 static void wbc_connect_connected(struct tevent_req *subreq)
280 {
281         struct tevent_req *req = tevent_req_callback_data(
282                 subreq, struct tevent_req);
283         int res, err;
284
285         res = async_connect_recv(subreq, &err);
286         TALLOC_FREE(subreq);
287         if (res == -1) {
288                 tevent_req_error(req, map_wbc_err_from_errno(err));
289                 return;
290         }
291         tevent_req_done(req);
292 }
293
294 static wbcErr wb_connect_recv(struct tevent_req *req)
295 {
296         return tevent_req_simple_recv_wbcerr(req);
297 }
298
299 static const char *winbindd_socket_dir(void)
300 {
301 #ifdef SOCKET_WRAPPER
302         const char *env_dir;
303
304         env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR);
305         if (env_dir) {
306                 return env_dir;
307         }
308 #endif
309
310         return WINBINDD_SOCKET_DIR;
311 }
312
313 struct wb_open_pipe_state {
314         struct wb_context *wb_ctx;
315         struct tevent_context *ev;
316         bool need_priv;
317         struct winbindd_request wb_req;
318 };
319
320 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq);
321 static void wb_open_pipe_ping_done(struct tevent_req *subreq);
322 static void wb_open_pipe_getpriv_done(struct tevent_req *subreq);
323 static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq);
324
325 static struct tevent_req *wb_open_pipe_send(TALLOC_CTX *mem_ctx,
326                                             struct tevent_context *ev,
327                                             struct wb_context *wb_ctx,
328                                             bool need_priv)
329 {
330         struct tevent_req *result, *subreq;
331         struct wb_open_pipe_state *state;
332
333         result = tevent_req_create(mem_ctx, &state, struct wb_open_pipe_state);
334         if (result == NULL) {
335                 return NULL;
336         }
337         state->wb_ctx = wb_ctx;
338         state->ev = ev;
339         state->need_priv = need_priv;
340
341         if (wb_ctx->fd != -1) {
342                 close(wb_ctx->fd);
343                 wb_ctx->fd = -1;
344         }
345
346         subreq = wb_connect_send(state, ev, wb_ctx, winbindd_socket_dir());
347         if (subreq == NULL) {
348                 goto fail;
349         }
350         tevent_req_set_callback(subreq, wb_open_pipe_connect_nonpriv_done,
351                                 result);
352         return result;
353
354  fail:
355         TALLOC_FREE(result);
356         return NULL;
357 }
358
359 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq)
360 {
361         struct tevent_req *req = tevent_req_callback_data(
362                 subreq, struct tevent_req);
363         struct wb_open_pipe_state *state = tevent_req_data(
364                 req, struct wb_open_pipe_state);
365         wbcErr wbc_err;
366
367         wbc_err = wb_connect_recv(subreq);
368         TALLOC_FREE(subreq);
369         if (!WBC_ERROR_IS_OK(wbc_err)) {
370                 state->wb_ctx->is_priv = true;
371                 tevent_req_error(req, wbc_err);
372                 return;
373         }
374
375         ZERO_STRUCT(state->wb_req);
376         state->wb_req.cmd = WINBINDD_INTERFACE_VERSION;
377         state->wb_req.pid = getpid();
378
379         subreq = wb_simple_trans_send(state, state->ev, NULL,
380                                       state->wb_ctx->fd, &state->wb_req);
381         if (tevent_req_nomem(subreq, req)) {
382                 return;
383         }
384         tevent_req_set_callback(subreq, wb_open_pipe_ping_done, req);
385 }
386
387 static void wb_open_pipe_ping_done(struct tevent_req *subreq)
388 {
389         struct tevent_req *req = tevent_req_callback_data(
390                 subreq, struct tevent_req);
391         struct wb_open_pipe_state *state = tevent_req_data(
392                 req, struct wb_open_pipe_state);
393         struct winbindd_response *wb_resp;
394         int ret, err;
395
396         ret = wb_simple_trans_recv(subreq, state, &wb_resp, &err);
397         TALLOC_FREE(subreq);
398         if (ret == -1) {
399                 tevent_req_error(req, map_wbc_err_from_errno(err));
400                 return;
401         }
402
403         if (!state->need_priv) {
404                 tevent_req_done(req);
405                 return;
406         }
407
408         state->wb_req.cmd = WINBINDD_PRIV_PIPE_DIR;
409         state->wb_req.pid = getpid();
410
411         subreq = wb_simple_trans_send(state, state->ev, NULL,
412                                       state->wb_ctx->fd, &state->wb_req);
413         if (tevent_req_nomem(subreq, req)) {
414                 return;
415         }
416         tevent_req_set_callback(subreq, wb_open_pipe_getpriv_done, req);
417 }
418
419 static void wb_open_pipe_getpriv_done(struct tevent_req *subreq)
420 {
421         struct tevent_req *req = tevent_req_callback_data(
422                 subreq, struct tevent_req);
423         struct wb_open_pipe_state *state = tevent_req_data(
424                 req, struct wb_open_pipe_state);
425         struct winbindd_response *wb_resp = NULL;
426         int ret, err;
427
428         ret = wb_simple_trans_recv(subreq, state, &wb_resp, &err);
429         TALLOC_FREE(subreq);
430         if (ret == -1) {
431                 tevent_req_error(req, map_wbc_err_from_errno(err));
432                 return;
433         }
434
435         close(state->wb_ctx->fd);
436         state->wb_ctx->fd = -1;
437
438         subreq = wb_connect_send(state, state->ev, state->wb_ctx,
439                                   (char *)wb_resp->extra_data.data);
440         TALLOC_FREE(wb_resp);
441         if (tevent_req_nomem(subreq, req)) {
442                 return;
443         }
444         tevent_req_set_callback(subreq, wb_open_pipe_connect_priv_done, req);
445 }
446
447 static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq)
448 {
449         struct tevent_req *req = tevent_req_callback_data(
450                 subreq, struct tevent_req);
451         struct wb_open_pipe_state *state = tevent_req_data(
452                 req, struct wb_open_pipe_state);
453         wbcErr wbc_err;
454
455         wbc_err = wb_connect_recv(subreq);
456         TALLOC_FREE(subreq);
457         if (!WBC_ERROR_IS_OK(wbc_err)) {
458                 tevent_req_error(req, wbc_err);
459                 return;
460         }
461         state->wb_ctx->is_priv = true;
462         tevent_req_done(req);
463 }
464
465 static wbcErr wb_open_pipe_recv(struct tevent_req *req)
466 {
467         return tevent_req_simple_recv_wbcerr(req);
468 }
469
470 struct wb_trans_state {
471         struct wb_trans_state *prev, *next;
472         struct wb_context *wb_ctx;
473         struct tevent_context *ev;
474         struct winbindd_request *wb_req;
475         struct winbindd_response *wb_resp;
476         int num_retries;
477         bool need_priv;
478 };
479
480 static bool closed_fd(int fd)
481 {
482         struct timeval tv;
483         fd_set r_fds;
484         int selret;
485
486         if (fd == -1) {
487                 return true;
488         }
489
490         FD_ZERO(&r_fds);
491         FD_SET(fd, &r_fds);
492         ZERO_STRUCT(tv);
493
494         selret = select(fd+1, &r_fds, NULL, NULL, &tv);
495         if (selret == -1) {
496                 return true;
497         }
498         if (selret == 0) {
499                 return false;
500         }
501         return (FD_ISSET(fd, &r_fds));
502 }
503
504 static void wb_trans_trigger(struct tevent_req *req, void *private_data);
505 static void wb_trans_connect_done(struct tevent_req *subreq);
506 static void wb_trans_done(struct tevent_req *subreq);
507 static void wb_trans_retry_wait_done(struct tevent_req *subreq);
508
509 struct tevent_req *wb_trans_send(TALLOC_CTX *mem_ctx,
510                                  struct tevent_context *ev,
511                                  struct wb_context *wb_ctx, bool need_priv,
512                                  struct winbindd_request *wb_req)
513 {
514         struct tevent_req *req;
515         struct wb_trans_state *state;
516
517         req = tevent_req_create(mem_ctx, &state, struct wb_trans_state);
518         if (req == NULL) {
519                 return NULL;
520         }
521         state->wb_ctx = wb_ctx;
522         state->ev = ev;
523         state->wb_req = wb_req;
524         state->num_retries = 10;
525         state->need_priv = need_priv;
526
527         if (!tevent_queue_add(wb_ctx->queue, ev, req, wb_trans_trigger,
528                               NULL)) {
529                 tevent_req_nomem(NULL, req);
530                 return tevent_req_post(req, ev);
531         }
532         return req;
533 }
534
535 static void wb_trans_trigger(struct tevent_req *req, void *private_data)
536 {
537         struct wb_trans_state *state = tevent_req_data(
538                 req, struct wb_trans_state);
539         struct tevent_req *subreq;
540
541         if ((state->wb_ctx->fd != -1) && closed_fd(state->wb_ctx->fd)) {
542                 close(state->wb_ctx->fd);
543                 state->wb_ctx->fd = -1;
544         }
545
546         if ((state->wb_ctx->fd == -1)
547             || (state->need_priv && !state->wb_ctx->is_priv)) {
548                 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
549                                            state->need_priv);
550                 if (tevent_req_nomem(subreq, req)) {
551                         return;
552                 }
553                 tevent_req_set_callback(subreq, wb_trans_connect_done, req);
554                 return;
555         }
556
557         state->wb_req->pid = getpid();
558
559         subreq = wb_simple_trans_send(state, state->ev, NULL,
560                                       state->wb_ctx->fd, state->wb_req);
561         if (tevent_req_nomem(subreq, req)) {
562                 return;
563         }
564         tevent_req_set_callback(subreq, wb_trans_done, req);
565 }
566
567 static bool wb_trans_retry(struct tevent_req *req,
568                            struct wb_trans_state *state,
569                            wbcErr wbc_err)
570 {
571         struct tevent_req *subreq;
572
573         if (WBC_ERROR_IS_OK(wbc_err)) {
574                 return false;
575         }
576
577         if (wbc_err == WBC_ERR_WINBIND_NOT_AVAILABLE) {
578                 /*
579                  * Winbind not around or we can't connect to the pipe. Fail
580                  * immediately.
581                  */
582                 tevent_req_error(req, wbc_err);
583                 return true;
584         }
585
586         state->num_retries -= 1;
587         if (state->num_retries == 0) {
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 }