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