891a2144e6c3655a867dce9ce08bb626c144cee5
[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         return result;
184 }
185
186 struct wb_connect_state {
187         int dummy;
188 };
189
190 static void wbc_connect_connected(struct tevent_req *subreq);
191
192 static struct tevent_req *wb_connect_send(TALLOC_CTX *mem_ctx,
193                                           struct tevent_context *ev,
194                                           struct wb_context *wb_ctx,
195                                           const char *dir)
196 {
197         struct tevent_req *result, *subreq;
198         struct wb_connect_state *state;
199         struct sockaddr_un sunaddr;
200         struct stat st;
201         char *path = NULL;
202         wbcErr wbc_err;
203
204         result = tevent_req_create(mem_ctx, &state, struct wb_connect_state);
205         if (result == NULL) {
206                 return NULL;
207         }
208
209         if (wb_ctx->fd != -1) {
210                 close(wb_ctx->fd);
211                 wb_ctx->fd = -1;
212         }
213
214         /* Check permissions on unix socket directory */
215
216         if (lstat(dir, &st) == -1) {
217                 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
218                 goto post_status;
219         }
220
221         if (!S_ISDIR(st.st_mode) ||
222             (st.st_uid != 0 && st.st_uid != geteuid())) {
223                 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
224                 goto post_status;
225         }
226
227         /* Connect to socket */
228
229         path = talloc_asprintf(talloc_tos(), "%s/%s", dir,
230                                WINBINDD_SOCKET_NAME);
231         if (path == NULL) {
232                 goto nomem;
233         }
234
235         sunaddr.sun_family = AF_UNIX;
236         strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path));
237         TALLOC_FREE(path);
238
239         /* If socket file doesn't exist, don't bother trying to connect
240            with retry.  This is an attempt to make the system usable when
241            the winbindd daemon is not running. */
242
243         if ((lstat(sunaddr.sun_path, &st) == -1)
244             || !S_ISSOCK(st.st_mode)
245             || (st.st_uid != 0 && st.st_uid != geteuid())) {
246                 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
247                 goto post_status;
248         }
249
250         wb_ctx->fd = make_safe_fd(socket(AF_UNIX, SOCK_STREAM, 0));
251         if (wb_ctx->fd == -1) {
252                 wbc_err = map_wbc_err_from_errno(errno);
253                 goto post_status;
254         }
255
256         subreq = async_connect_send(mem_ctx, ev, wb_ctx->fd,
257                                     (struct sockaddr *)(void *)&sunaddr,
258                                     sizeof(sunaddr));
259         if (subreq == NULL) {
260                 goto nomem;
261         }
262         tevent_req_set_callback(subreq, wbc_connect_connected, result);
263
264         if (!tevent_req_set_endtime(subreq, ev, timeval_current_ofs(30, 0))) {
265                 goto nomem;
266         }
267
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, state->wb_ctx->queue,
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, state->wb_ctx->queue,
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         int num_retries;
476         bool need_priv;
477 };
478
479 static void wb_trans_connect_done(struct tevent_req *subreq);
480 static void wb_trans_done(struct tevent_req *subreq);
481 static void wb_trans_retry_wait_done(struct tevent_req *subreq);
482
483 struct tevent_req *wb_trans_send(TALLOC_CTX *mem_ctx,
484                                  struct tevent_context *ev,
485                                  struct wb_context *wb_ctx, bool need_priv,
486                                  struct winbindd_request *wb_req)
487 {
488         struct tevent_req *req, *subreq;
489         struct wb_trans_state *state;
490
491         req = tevent_req_create(mem_ctx, &state, struct wb_trans_state);
492         if (req == NULL) {
493                 return NULL;
494         }
495         state->wb_ctx = wb_ctx;
496         state->ev = ev;
497         state->wb_req = wb_req;
498         state->num_retries = 10;
499         state->need_priv = need_priv;
500
501         if ((wb_ctx->fd == -1) || (need_priv && !wb_ctx->is_priv)) {
502                 subreq = wb_open_pipe_send(state, ev, wb_ctx, need_priv);
503                 if (subreq == NULL) {
504                         goto fail;
505                 }
506                 tevent_req_set_callback(subreq, wb_trans_connect_done, req);
507                 return req;
508         }
509
510         state->wb_req->pid = getpid();
511
512         subreq = wb_simple_trans_send(state, ev, wb_ctx->queue, wb_ctx->fd,
513                                       wb_req);
514         if (subreq == NULL) {
515                 goto fail;
516         }
517         tevent_req_set_callback(subreq, wb_trans_done, req);
518         return req;
519  fail:
520         TALLOC_FREE(req);
521         return NULL;
522 }
523
524 static bool wb_trans_retry(struct tevent_req *req,
525                            struct wb_trans_state *state,
526                            wbcErr wbc_err)
527 {
528         struct tevent_req *subreq;
529
530         if (WBC_ERROR_IS_OK(wbc_err)) {
531                 return false;
532         }
533
534         if (wbc_err == WBC_ERR_WINBIND_NOT_AVAILABLE) {
535                 /*
536                  * Winbind not around or we can't connect to the pipe. Fail
537                  * immediately.
538                  */
539                 tevent_req_error(req, wbc_err);
540                 return true;
541         }
542
543         state->num_retries -= 1;
544         if (state->num_retries == 0) {
545                 tevent_req_error(req, wbc_err);
546                 return true;
547         }
548
549         /*
550          * The transfer as such failed, retry after one second
551          */
552
553         if (state->wb_ctx->fd != -1) {
554                 close(state->wb_ctx->fd);
555                 state->wb_ctx->fd = -1;
556         }
557
558         subreq = tevent_wakeup_send(state, state->ev,
559                                     timeval_current_ofs(1, 0));
560         if (tevent_req_nomem(subreq, req)) {
561                 return true;
562         }
563         tevent_req_set_callback(subreq, wb_trans_retry_wait_done, req);
564         return true;
565 }
566
567 static void wb_trans_retry_wait_done(struct tevent_req *subreq)
568 {
569         struct tevent_req *req = tevent_req_callback_data(
570                 subreq, struct tevent_req);
571         struct wb_trans_state *state = tevent_req_data(
572                 req, struct wb_trans_state);
573         bool ret;
574
575         ret = tevent_wakeup_recv(subreq);
576         TALLOC_FREE(subreq);
577         if (!ret) {
578                 tevent_req_error(req, WBC_ERR_UNKNOWN_FAILURE);
579                 return;
580         }
581
582         subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
583                                    state->need_priv);
584         if (tevent_req_nomem(subreq, req)) {
585                 return;
586         }
587         tevent_req_set_callback(subreq, wb_trans_connect_done, req);
588 }
589
590 static void wb_trans_connect_done(struct tevent_req *subreq)
591 {
592         struct tevent_req *req = tevent_req_callback_data(
593                 subreq, struct tevent_req);
594         struct wb_trans_state *state = tevent_req_data(
595                 req, struct wb_trans_state);
596         wbcErr wbc_err;
597
598         wbc_err = wb_open_pipe_recv(subreq);
599         TALLOC_FREE(subreq);
600
601         if (wb_trans_retry(req, state, wbc_err)) {
602                 return;
603         }
604
605         subreq = wb_simple_trans_send(state, state->ev, state->wb_ctx->queue,
606                                       state->wb_ctx->fd, state->wb_req);
607         if (tevent_req_nomem(subreq, req)) {
608                 return;
609         }
610         tevent_req_set_callback(subreq, wb_trans_done, req);
611 }
612
613 static void wb_trans_done(struct tevent_req *subreq)
614 {
615         struct tevent_req *req = tevent_req_callback_data(
616                 subreq, struct tevent_req);
617         struct wb_trans_state *state = tevent_req_data(
618                 req, struct wb_trans_state);
619         int ret, err;
620
621         ret = wb_simple_trans_recv(subreq, state, &state->wb_resp, &err);
622         TALLOC_FREE(subreq);
623
624         if (wb_trans_retry(req, state, map_wbc_err_from_errno(err))) {
625                 return;
626         }
627
628         tevent_req_done(req);
629 }
630
631 wbcErr wb_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
632                      struct winbindd_response **presponse)
633 {
634         struct wb_trans_state *state = tevent_req_data(
635                 req, struct wb_trans_state);
636         wbcErr wbc_err;
637
638         if (tevent_req_is_wbcerr(req, &wbc_err)) {
639                 return wbc_err;
640         }
641
642         *presponse = talloc_move(mem_ctx, &state->wb_resp);
643         return WBC_ERR_SUCCESS;
644 }