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