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