source: trunk/samba/source/nsswitch/winbindd_dual.c @ 30

Last change on this file since 30 was 30, checked in by Paul Smedley, 14 years ago

Code updated to Samba 3.0.25rc2 level

File size: 28.8 KB
Line 
1/*
2   Unix SMB/CIFS implementation.
3
4   Winbind child daemons
5
6   Copyright (C) Andrew Tridgell 2002
7   Copyright (C) Volker Lendecke 2004,2005
8   
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18   
19   You should have received a copy of the GNU General Public License
20   along with this program; if not, write to the Free Software
21   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22*/
23
24/*
25 * We fork a child per domain to be able to act non-blocking in the main
26 * winbind daemon. A domain controller thousands of miles away being being
27 * slow replying with a 10.000 user list should not hold up netlogon calls
28 * that can be handled locally.
29 */
30
31#include "includes.h"
32#include "winbindd.h"
33
34#undef DBGC_CLASS
35#define DBGC_CLASS DBGC_WINBIND
36
37extern BOOL override_logfile;
38
39/* Read some data from a client connection */
40
41static void child_read_request(struct winbindd_cli_state *state)
42{
43        ssize_t len;
44
45        /* Read data */
46
47        len = read_data(state->sock, (char *)&state->request,
48                        sizeof(state->request));
49
50        if (len != sizeof(state->request)) {
51                DEBUG(len > 0 ? 0 : 3, ("Got invalid request length: %d\n", (int)len));
52                state->finished = True;
53                return;
54        }
55
56        if (state->request.extra_len == 0) {
57                state->request.extra_data.data = NULL;
58                return;
59        }
60
61        DEBUG(10, ("Need to read %d extra bytes\n", (int)state->request.extra_len));
62
63        state->request.extra_data.data =
64                SMB_MALLOC_ARRAY(char, state->request.extra_len + 1);
65
66        if (state->request.extra_data.data == NULL) {
67                DEBUG(0, ("malloc failed\n"));
68                state->finished = True;
69                return;
70        }
71
72        /* Ensure null termination */
73        state->request.extra_data.data[state->request.extra_len] = '\0';
74
75        len = read_data(state->sock, state->request.extra_data.data,
76                        state->request.extra_len);
77
78        if (len != state->request.extra_len) {
79                DEBUG(0, ("Could not read extra data\n"));
80                state->finished = True;
81                return;
82        }
83}
84
85/*
86 * Machinery for async requests sent to children. You set up a
87 * winbindd_request, select a child to query, and issue a async_request
88 * call. When the request is completed, the callback function you specified is
89 * called back with the private pointer you gave to async_request.
90 */
91
92struct winbindd_async_request {
93        struct winbindd_async_request *next, *prev;
94        TALLOC_CTX *mem_ctx;
95        struct winbindd_child *child;
96        struct winbindd_request *request;
97        struct winbindd_response *response;
98        void (*continuation)(void *private_data, BOOL success);
99        void *private_data;
100};
101
102static void async_main_request_sent(void *private_data, BOOL success);
103static void async_request_sent(void *private_data, BOOL success);
104static void async_reply_recv(void *private_data, BOOL success);
105static void schedule_async_request(struct winbindd_child *child);
106
107void async_request(TALLOC_CTX *mem_ctx, struct winbindd_child *child,
108                   struct winbindd_request *request,
109                   struct winbindd_response *response,
110                   void (*continuation)(void *private_data, BOOL success),
111                   void *private_data)
112{
113        struct winbindd_async_request *state;
114
115        SMB_ASSERT(continuation != NULL);
116
117        state = TALLOC_P(mem_ctx, struct winbindd_async_request);
118
119        if (state == NULL) {
120                DEBUG(0, ("talloc failed\n"));
121                continuation(private_data, False);
122                return;
123        }
124
125        state->mem_ctx = mem_ctx;
126        state->child = child;
127        state->request = request;
128        state->response = response;
129        state->continuation = continuation;
130        state->private_data = private_data;
131
132        DLIST_ADD_END(child->requests, state, struct winbindd_async_request *);
133
134        schedule_async_request(child);
135
136        return;
137}
138
139static void async_main_request_sent(void *private_data, BOOL success)
140{
141        struct winbindd_async_request *state =
142                talloc_get_type_abort(private_data, struct winbindd_async_request);
143
144        if (!success) {
145                DEBUG(5, ("Could not send async request\n"));
146
147                state->response->length = sizeof(struct winbindd_response);
148                state->response->result = WINBINDD_ERROR;
149                state->continuation(state->private_data, False);
150                return;
151        }
152
153        if (state->request->extra_len == 0) {
154                async_request_sent(private_data, True);
155                return;
156        }
157
158        setup_async_write(&state->child->event, state->request->extra_data.data,
159                          state->request->extra_len,
160                          async_request_sent, state);
161}
162
163static void async_request_sent(void *private_data_data, BOOL success)
164{
165        struct winbindd_async_request *state =
166                talloc_get_type_abort(private_data_data, struct winbindd_async_request);
167
168        if (!success) {
169                DEBUG(5, ("Could not send async request\n"));
170
171                state->response->length = sizeof(struct winbindd_response);
172                state->response->result = WINBINDD_ERROR;
173                state->continuation(state->private_data, False);
174                return;
175        }
176
177        /* Request successfully sent to the child, setup the wait for reply */
178
179        setup_async_read(&state->child->event,
180                         &state->response->result,
181                         sizeof(state->response->result),
182                         async_reply_recv, state);
183}
184
185static void async_reply_recv(void *private_data, BOOL success)
186{
187        struct winbindd_async_request *state =
188                talloc_get_type_abort(private_data, struct winbindd_async_request);
189        struct winbindd_child *child = state->child;
190
191        state->response->length = sizeof(struct winbindd_response);
192
193        if (!success) {
194                DEBUG(5, ("Could not receive async reply\n"));
195
196                cache_cleanup_response(child->pid);
197                DLIST_REMOVE(child->requests, state);
198
199                state->response->result = WINBINDD_ERROR;
200                state->continuation(state->private_data, False);
201                return;
202        }
203
204        SMB_ASSERT(cache_retrieve_response(child->pid,
205                                           state->response));
206
207        cache_cleanup_response(child->pid);
208       
209        DLIST_REMOVE(child->requests, state);
210
211        schedule_async_request(child);
212
213        state->continuation(state->private_data, True);
214}
215
216static BOOL fork_domain_child(struct winbindd_child *child);
217
218static void schedule_async_request(struct winbindd_child *child)
219{
220        struct winbindd_async_request *request = child->requests;
221
222        if (request == NULL) {
223                return;
224        }
225
226        if (child->event.flags != 0) {
227                return;         /* Busy */
228        }
229
230        if ((child->pid == 0) && (!fork_domain_child(child))) {
231                /* Cancel all outstanding requests */
232
233                while (request != NULL) {
234                        /* request might be free'd in the continuation */
235                        struct winbindd_async_request *next = request->next;
236                        request->continuation(request->private_data, False);
237                        request = next;
238                }
239                return;
240        }
241
242        setup_async_write(&child->event, request->request,
243                          sizeof(*request->request),
244                          async_main_request_sent, request);
245
246        return;
247}
248
249struct domain_request_state {
250        TALLOC_CTX *mem_ctx;
251        struct winbindd_domain *domain;
252        struct winbindd_request *request;
253        struct winbindd_response *response;
254        void (*continuation)(void *private_data_data, BOOL success);
255        void *private_data_data;
256};
257
258static void domain_init_recv(void *private_data_data, BOOL success);
259
260void async_domain_request(TALLOC_CTX *mem_ctx,
261                          struct winbindd_domain *domain,
262                          struct winbindd_request *request,
263                          struct winbindd_response *response,
264                          void (*continuation)(void *private_data_data, BOOL success),
265                          void *private_data_data)
266{
267        struct domain_request_state *state;
268
269        if (domain->initialized) {
270                async_request(mem_ctx, &domain->child, request, response,
271                              continuation, private_data_data);
272                return;
273        }
274
275        state = TALLOC_P(mem_ctx, struct domain_request_state);
276        if (state == NULL) {
277                DEBUG(0, ("talloc failed\n"));
278                continuation(private_data_data, False);
279                return;
280        }
281
282        state->mem_ctx = mem_ctx;
283        state->domain = domain;
284        state->request = request;
285        state->response = response;
286        state->continuation = continuation;
287        state->private_data_data = private_data_data;
288
289        init_child_connection(domain, domain_init_recv, state);
290}
291
292static void recvfrom_child(void *private_data_data, BOOL success)
293{
294        struct winbindd_cli_state *state =
295                talloc_get_type_abort(private_data_data, struct winbindd_cli_state);
296        enum winbindd_result result = state->response.result;
297
298        /* This is an optimization: The child has written directly to the
299         * response buffer. The request itself is still in pending state,
300         * state that in the result code. */
301
302        state->response.result = WINBINDD_PENDING;
303
304        if ((!success) || (result != WINBINDD_OK)) {
305                request_error(state);
306                return;
307        }
308
309        request_ok(state);
310}
311
312void sendto_child(struct winbindd_cli_state *state,
313                  struct winbindd_child *child)
314{
315        async_request(state->mem_ctx, child, &state->request,
316                      &state->response, recvfrom_child, state);
317}
318
319void sendto_domain(struct winbindd_cli_state *state,
320                   struct winbindd_domain *domain)
321{
322        async_domain_request(state->mem_ctx, domain,
323                             &state->request, &state->response,
324                             recvfrom_child, state);
325}
326
327static void domain_init_recv(void *private_data_data, BOOL success)
328{
329        struct domain_request_state *state =
330                talloc_get_type_abort(private_data_data, struct domain_request_state);
331
332        if (!success) {
333                DEBUG(5, ("Domain init returned an error\n"));
334                state->continuation(state->private_data_data, False);
335                return;
336        }
337
338        async_request(state->mem_ctx, &state->domain->child,
339                      state->request, state->response,
340                      state->continuation, state->private_data_data);
341}
342
343struct winbindd_child_dispatch_table {
344        enum winbindd_cmd cmd;
345        enum winbindd_result (*fn)(struct winbindd_domain *domain,
346                                   struct winbindd_cli_state *state);
347        const char *winbindd_cmd_name;
348};
349
350static struct winbindd_child_dispatch_table child_dispatch_table[] = {
351       
352        { WINBINDD_LOOKUPSID,            winbindd_dual_lookupsid,             "LOOKUPSID" },
353        { WINBINDD_LOOKUPNAME,           winbindd_dual_lookupname,            "LOOKUPNAME" },
354        { WINBINDD_LOOKUPRIDS,           winbindd_dual_lookuprids,            "LOOKUPRIDS" },
355        { WINBINDD_LIST_TRUSTDOM,        winbindd_dual_list_trusted_domains,  "LIST_TRUSTDOM" },
356        { WINBINDD_INIT_CONNECTION,      winbindd_dual_init_connection,       "INIT_CONNECTION" },
357        { WINBINDD_GETDCNAME,            winbindd_dual_getdcname,             "GETDCNAME" },
358        { WINBINDD_SHOW_SEQUENCE,        winbindd_dual_show_sequence,         "SHOW_SEQUENCE" },
359        { WINBINDD_PAM_AUTH,             winbindd_dual_pam_auth,              "PAM_AUTH" },
360        { WINBINDD_PAM_AUTH_CRAP,        winbindd_dual_pam_auth_crap,         "AUTH_CRAP" },
361        { WINBINDD_PAM_LOGOFF,           winbindd_dual_pam_logoff,            "PAM_LOGOFF" },
362        { WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP,winbindd_dual_pam_chng_pswd_auth_crap,"CHNG_PSWD_AUTH_CRAP" },
363        { WINBINDD_PAM_CHAUTHTOK,        winbindd_dual_pam_chauthtok,         "PAM_CHAUTHTOK" },
364        { WINBINDD_CHECK_MACHACC,        winbindd_dual_check_machine_acct,    "CHECK_MACHACC" },
365        { WINBINDD_DUAL_SID2UID,         winbindd_dual_sid2uid,               "DUAL_SID2UID" },
366        { WINBINDD_DUAL_SID2GID,         winbindd_dual_sid2gid,               "DUAL_SID2GID" },
367#if 0   /* DISABLED until we fix the interface in Samba 3.0.26 --jerry */
368        { WINBINDD_DUAL_SIDS2XIDS,       winbindd_dual_sids2xids,             "DUAL_SIDS2XIDS" },
369#endif  /* end DISABLED */
370        { WINBINDD_DUAL_UID2SID,         winbindd_dual_uid2sid,               "DUAL_UID2SID" },
371        { WINBINDD_DUAL_GID2SID,         winbindd_dual_gid2sid,               "DUAL_GID2SID" },
372        { WINBINDD_DUAL_UID2NAME,        winbindd_dual_uid2name,              "DUAL_UID2NAME" },
373        { WINBINDD_DUAL_NAME2UID,        winbindd_dual_name2uid,              "DUAL_NAME2UID" },
374        { WINBINDD_DUAL_GID2NAME,        winbindd_dual_gid2name,              "DUAL_GID2NAME" },
375        { WINBINDD_DUAL_NAME2GID,        winbindd_dual_name2gid,              "DUAL_NAME2GID" },
376        { WINBINDD_DUAL_SET_MAPPING,     winbindd_dual_set_mapping,           "DUAL_SET_MAPPING" },
377        { WINBINDD_DUAL_SET_HWM,         winbindd_dual_set_hwm,               "DUAL_SET_HWMS" },
378        { WINBINDD_DUAL_DUMP_MAPS,       winbindd_dual_dump_maps,             "DUAL_DUMP_MAPS" },
379        { WINBINDD_DUAL_USERINFO,        winbindd_dual_userinfo,              "DUAL_USERINFO" },
380        { WINBINDD_ALLOCATE_UID,         winbindd_dual_allocate_uid,          "ALLOCATE_UID" },
381        { WINBINDD_ALLOCATE_GID,         winbindd_dual_allocate_gid,          "ALLOCATE_GID" },
382        { WINBINDD_GETUSERDOMGROUPS,     winbindd_dual_getuserdomgroups,      "GETUSERDOMGROUPS" },
383        { WINBINDD_DUAL_GETSIDALIASES,   winbindd_dual_getsidaliases,         "GETSIDALIASES" },
384        { WINBINDD_CCACHE_NTLMAUTH,      winbindd_dual_ccache_ntlm_auth,      "CCACHE_NTLM_AUTH" },
385        /* End of list */
386
387        { WINBINDD_NUM_CMDS, NULL, "NONE" }
388};
389
390static void child_process_request(struct winbindd_domain *domain,
391                                  struct winbindd_cli_state *state)
392{
393        struct winbindd_child_dispatch_table *table;
394
395        /* Free response data - we may be interrupted and receive another
396           command before being able to send this data off. */
397
398        state->response.result = WINBINDD_ERROR;
399        state->response.length = sizeof(struct winbindd_response);
400
401        state->mem_ctx = talloc_init("winbind request");
402        if (state->mem_ctx == NULL)
403                return;
404
405        /* Process command */
406
407        for (table = child_dispatch_table; table->fn; table++) {
408                if (state->request.cmd == table->cmd) {
409                        DEBUG(10,("process_request: request fn %s\n",
410                                  table->winbindd_cmd_name ));
411                        state->response.result = table->fn(domain, state);
412                        break;
413                }
414        }
415
416        if (!table->fn) {
417                DEBUG(10,("process_request: unknown request fn number %d\n",
418                          (int)state->request.cmd ));
419                state->response.result = WINBINDD_ERROR;
420        }
421
422        talloc_destroy(state->mem_ctx);
423}
424
425void setup_domain_child(struct winbindd_domain *domain,
426                        struct winbindd_child *child,
427                        const char *explicit_logfile)
428{
429        if (explicit_logfile != NULL) {
430                pstr_sprintf(child->logfilename, "%s/log.winbindd-%s",
431                             dyn_LOGFILEBASE, explicit_logfile);
432        } else if (domain != NULL) {
433                pstr_sprintf(child->logfilename, "%s/log.wb-%s",
434                             dyn_LOGFILEBASE, domain->name);
435        } else {
436                smb_panic("Internal error: domain == NULL && "
437                          "explicit_logfile == NULL");
438        }
439
440        child->domain = domain;
441}
442
443struct winbindd_child *children = NULL;
444
445void winbind_child_died(pid_t pid)
446{
447        struct winbindd_child *child;
448
449        for (child = children; child != NULL; child = child->next) {
450                if (child->pid == pid) {
451                        break;
452                }
453        }
454
455        if (child == NULL) {
456                DEBUG(0, ("Unknown child %d died!\n", pid));
457                return;
458        }
459
460        remove_fd_event(&child->event);
461        close(child->event.fd);
462        child->event.fd = 0;
463        child->event.flags = 0;
464        child->pid = 0;
465
466        schedule_async_request(child);
467}
468
469/* Ensure any negative cache entries with the netbios or realm names are removed. */
470
471void winbindd_flush_negative_conn_cache(struct winbindd_domain *domain)
472{
473        flush_negative_conn_cache_for_domain(domain->name);
474        if (*domain->alt_name) {
475                flush_negative_conn_cache_for_domain(domain->alt_name);
476        }
477}
478
479/* Set our domains as offline and forward the offline message to our children. */
480
481void winbind_msg_offline(int msg_type, struct process_id src,
482                         void *buf, size_t len, void *private_data)
483{
484        struct winbindd_child *child;
485        struct winbindd_domain *domain;
486
487        DEBUG(10,("winbind_msg_offline: got offline message.\n"));
488
489        if (!lp_winbind_offline_logon()) {
490                DEBUG(10,("winbind_msg_offline: rejecting offline message.\n"));
491                return;
492        }
493
494        /* Set our global state as offline. */
495        if (!set_global_winbindd_state_offline()) {
496                DEBUG(10,("winbind_msg_offline: offline request failed.\n"));
497                return;
498        }
499
500        /* Set all our domains as offline. */
501        for (domain = domain_list(); domain; domain = domain->next) {
502                if (domain->internal) {
503                        continue;
504                }
505                DEBUG(5,("winbind_msg_offline: marking %s offline.\n", domain->name));
506                set_domain_offline(domain);
507
508                /* Send an offline message to the idmap child when our
509                   primary domain goes offline */
510
511                if ( domain->primary ) {
512                        struct winbindd_child *idmap = idmap_child();
513
514                        if ( idmap->pid != 0 ) {
515                                message_send_pid(pid_to_procid(idmap->pid), 
516                                                 MSG_WINBIND_OFFLINE, 
517                                                 domain->name, 
518                                                 strlen(domain->name)+1, 
519                                                 False);
520                        }                       
521                }
522        }
523
524        for (child = children; child != NULL; child = child->next) {
525                /* Don't send message to idmap child.  We've already
526                   done so above. */
527                if (!child->domain || (child == idmap_child())) {
528                        continue;
529                }
530
531                /* Or internal domains (this should not be possible....) */
532                if (child->domain->internal) {
533                        continue;
534                }
535
536                /* Each winbindd child should only process requests for one domain - make sure
537                   we only set it online / offline for that domain. */
538
539                DEBUG(10,("winbind_msg_offline: sending message to pid %u for domain %s.\n",
540                        (unsigned int)child->pid, domain->name ));
541
542                message_send_pid(pid_to_procid(child->pid), MSG_WINBIND_OFFLINE, child->domain->name,
543                        strlen(child->domain->name)+1, False);
544        }
545}
546
547/* Set our domains as online and forward the online message to our children. */
548
549void winbind_msg_online(int msg_type, struct process_id src,
550                        void *buf, size_t len, void *private_data)
551{
552        struct winbindd_child *child;
553        struct winbindd_domain *domain;
554
555        DEBUG(10,("winbind_msg_online: got online message.\n"));
556
557        if (!lp_winbind_offline_logon()) {
558                DEBUG(10,("winbind_msg_online: rejecting online message.\n"));
559                return;
560        }
561
562        /* Set our global state as online. */
563        set_global_winbindd_state_online();
564
565        smb_nscd_flush_user_cache();
566        smb_nscd_flush_group_cache();
567
568        /* Set all our domains as online. */
569        for (domain = domain_list(); domain; domain = domain->next) {
570                if (domain->internal) {
571                        continue;
572                }
573                DEBUG(5,("winbind_msg_online: requesting %s to go online.\n", domain->name));
574
575                winbindd_flush_negative_conn_cache(domain);
576                set_domain_online_request(domain);
577
578                /* Send an online message to the idmap child when our
579                   primary domain comes back online */
580
581                if ( domain->primary ) {
582                        struct winbindd_child *idmap = idmap_child();
583                       
584                        if ( idmap->pid != 0 ) {
585                                message_send_pid(pid_to_procid(idmap->pid), 
586                                                 MSG_WINBIND_ONLINE,
587                                                 domain->name,
588                                                 strlen(domain->name)+1, 
589                                                 False);
590                        }
591                       
592                }
593        }
594
595        for (child = children; child != NULL; child = child->next) {
596                /* Don't send message to idmap child. */
597                if (!child->domain || (child == idmap_child())) {
598                        continue;
599                }
600
601                /* Or internal domains (this should not be possible....) */
602                if (child->domain->internal) {
603                        continue;
604                }
605
606                /* Each winbindd child should only process requests for one domain - make sure
607                   we only set it online / offline for that domain. */
608
609                DEBUG(10,("winbind_msg_online: sending message to pid %u for domain %s.\n",
610                        (unsigned int)child->pid, child->domain->name ));
611
612                message_send_pid(pid_to_procid(child->pid), MSG_WINBIND_ONLINE, child->domain->name,
613                        strlen(child->domain->name)+1, False);
614        }
615}
616
617/* Forward the online/offline messages to our children. */
618void winbind_msg_onlinestatus(int msg_type, struct process_id src,
619                              void *buf, size_t len, void *private_data)
620{
621        struct winbindd_child *child;
622
623        DEBUG(10,("winbind_msg_onlinestatus: got onlinestatus message.\n"));
624
625        for (child = children; child != NULL; child = child->next) {
626                if (child->domain && child->domain->primary) {
627                        DEBUG(10,("winbind_msg_onlinestatus: "
628                                  "sending message to pid %u of primary domain.\n",
629                                  (unsigned int)child->pid));
630                        message_send_pid(pid_to_procid(child->pid), 
631                                         MSG_WINBIND_ONLINESTATUS, buf, len, False);
632                        break;
633                }
634        }
635}
636
637
638static void account_lockout_policy_handler(struct event_context *ctx,
639                                           struct timed_event *te,
640                                           const struct timeval *now,
641                                           void *private_data)
642{
643        struct winbindd_child *child =
644                (struct winbindd_child *)private_data;
645        TALLOC_CTX *mem_ctx = NULL;
646        struct winbindd_methods *methods;
647        SAM_UNK_INFO_12 lockout_policy;
648        NTSTATUS result;
649
650        DEBUG(10,("account_lockout_policy_handler called\n"));
651
652        if (child->lockout_policy_event) {
653                TALLOC_FREE(child->lockout_policy_event);
654        }
655
656        methods = child->domain->methods;
657
658        mem_ctx = talloc_init("account_lockout_policy_handler ctx");
659        if (!mem_ctx) {
660                result = NT_STATUS_NO_MEMORY;
661        } else {
662                result = methods->lockout_policy(child->domain, mem_ctx, &lockout_policy);
663        }
664
665        talloc_destroy(mem_ctx);
666
667        if (!NT_STATUS_IS_OK(result)) {
668                DEBUG(10,("account_lockout_policy_handler: lockout_policy failed error %s\n",
669                         nt_errstr(result)));
670        }
671
672        child->lockout_policy_event = event_add_timed(winbind_event_context(), NULL,
673                                                      timeval_current_ofs(3600, 0),
674                                                      "account_lockout_policy_handler",
675                                                      account_lockout_policy_handler,
676                                                      child);
677}
678
679/* Deal with a request to go offline. */
680
681static void child_msg_offline(int msg_type, struct process_id src,
682                              void *buf, size_t len, void *private_data)
683{
684        struct winbindd_domain *domain;
685        const char *domainname = (const char *)buf;
686
687        if (buf == NULL || len == 0) {
688                return;
689        }
690
691        DEBUG(5,("child_msg_offline received for domain %s.\n", domainname));
692
693        if (!lp_winbind_offline_logon()) {
694                DEBUG(10,("child_msg_offline: rejecting offline message.\n"));
695                return;
696        }
697
698        /* Set our global state as offline. */
699        if (!set_global_winbindd_state_offline()) {
700                DEBUG(10,("child_msg_offline: offline request failed.\n"));
701                return;
702        }
703
704        /* Mark the requested domain offline. */
705
706        for (domain = domain_list(); domain; domain = domain->next) {
707                if (domain->internal) {
708                        continue;
709                }
710                if (strequal(domain->name, domainname)) {
711                        DEBUG(5,("child_msg_offline: marking %s offline.\n", domain->name));
712                        set_domain_offline(domain);
713                }
714        }
715}
716
717/* Deal with a request to go online. */
718
719static void child_msg_online(int msg_type, struct process_id src,
720                             void *buf, size_t len, void *private_data)
721{
722        struct winbindd_domain *domain;
723        const char *domainname = (const char *)buf;
724
725        if (buf == NULL || len == 0) {
726                return;
727        }
728
729        DEBUG(5,("child_msg_online received for domain %s.\n", domainname));
730
731        if (!lp_winbind_offline_logon()) {
732                DEBUG(10,("child_msg_online: rejecting online message.\n"));
733                return;
734        }
735
736        /* Set our global state as online. */
737        set_global_winbindd_state_online();
738
739        /* Try and mark everything online - delete any negative cache entries
740           to force a reconnect now. */
741
742        for (domain = domain_list(); domain; domain = domain->next) {
743                if (domain->internal) {
744                        continue;
745                }
746                if (strequal(domain->name, domainname)) {
747                        DEBUG(5,("child_msg_online: requesting %s to go online.\n", domain->name));
748                        winbindd_flush_negative_conn_cache(domain);
749                        set_domain_online_request(domain);
750                }
751        }
752}
753
754static const char *collect_onlinestatus(TALLOC_CTX *mem_ctx)
755{
756        struct winbindd_domain *domain;
757        char *buf = NULL;
758
759        if ((buf = talloc_asprintf(mem_ctx, "global:%s ", 
760                                   get_global_winbindd_state_offline() ? 
761                                   "Offline":"Online")) == NULL) {
762                return NULL;
763        }
764
765        for (domain = domain_list(); domain; domain = domain->next) {
766                if ((buf = talloc_asprintf_append(buf, "%s:%s ", 
767                                                  domain->name, 
768                                                  domain->online ?
769                                                  "Online":"Offline")) == NULL) {
770                        return NULL;
771                }
772        }
773
774        buf = talloc_asprintf_append(buf, "\n");
775
776        DEBUG(5,("collect_onlinestatus: %s", buf));
777
778        return buf;
779}
780
781static void child_msg_onlinestatus(int msg_type, struct process_id src,
782                                   void *buf, size_t len, void *private_data)
783{
784        TALLOC_CTX *mem_ctx;
785        const char *message;
786        struct process_id *sender;
787       
788        DEBUG(5,("winbind_msg_onlinestatus received.\n"));
789
790        if (!buf) {
791                return;
792        }
793
794        sender = (struct process_id *)buf;
795
796        mem_ctx = talloc_init("winbind_msg_onlinestatus");
797        if (mem_ctx == NULL) {
798                return;
799        }
800       
801        message = collect_onlinestatus(mem_ctx);
802        if (message == NULL) {
803                talloc_destroy(mem_ctx);
804                return;
805        }
806
807        message_send_pid(*sender, MSG_WINBIND_ONLINESTATUS, 
808                         message, strlen(message) + 1, True);
809
810        talloc_destroy(mem_ctx);
811}
812
813static BOOL fork_domain_child(struct winbindd_child *child)
814{
815        int fdpair[2];
816        struct winbindd_cli_state state;
817        struct winbindd_domain *domain;
818
819        if (socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair) != 0) {
820                DEBUG(0, ("Could not open child pipe: %s\n",
821                          strerror(errno)));
822                return False;
823        }
824
825        ZERO_STRUCT(state);
826        state.pid = sys_getpid();
827
828        /* Stop zombies */
829        CatchChild();
830
831        /* Ensure we don't process messages whilst we're
832           changing the disposition for the child. */
833        message_block();
834
835        child->pid = sys_fork();
836
837        if (child->pid == -1) {
838                DEBUG(0, ("Could not fork: %s\n", strerror(errno)));
839                message_unblock();
840                return False;
841        }
842
843        if (child->pid != 0) {
844                /* Parent */
845                close(fdpair[0]);
846                child->next = child->prev = NULL;
847                DLIST_ADD(children, child);
848                child->event.fd = fdpair[1];
849                child->event.flags = 0;
850                child->requests = NULL;
851                add_fd_event(&child->event);
852                /* We're ok with online/offline messages now. */
853                message_unblock();
854                return True;
855        }
856
857        /* Child */
858
859        state.sock = fdpair[0];
860        close(fdpair[1]);
861
862        /* tdb needs special fork handling */
863        if (tdb_reopen_all(1) == -1) {
864                DEBUG(0,("tdb_reopen_all failed.\n"));
865                _exit(0);
866        }
867
868        close_conns_after_fork();
869
870        if (!override_logfile) {
871                lp_set_logfile(child->logfilename);
872                reopen_logs();
873        }
874
875        /* Don't handle the same messages as our parent. */
876        message_deregister(MSG_SMB_CONF_UPDATED);
877        message_deregister(MSG_SHUTDOWN);
878        message_deregister(MSG_WINBIND_OFFLINE);
879        message_deregister(MSG_WINBIND_ONLINE);
880        message_deregister(MSG_WINBIND_ONLINESTATUS);
881
882        /* The child is ok with online/offline messages now. */
883        message_unblock();
884
885        /* Handle online/offline messages. */
886        message_register(MSG_WINBIND_OFFLINE, child_msg_offline, NULL);
887        message_register(MSG_WINBIND_ONLINE, child_msg_online, NULL);
888        message_register(MSG_WINBIND_ONLINESTATUS, child_msg_onlinestatus,
889                         NULL);
890
891        if ( child->domain ) {
892                child->domain->startup = True;
893                child->domain->startup_time = time(NULL);
894        }
895
896        /* Ensure we have no pending check_online events other
897           than one for this domain. */
898
899        for (domain = domain_list(); domain; domain = domain->next) {
900                if (domain != child->domain) {
901                        if (domain->check_online_event) {
902                                TALLOC_FREE(domain->check_online_event);
903                        }
904                }
905        }
906
907        /* Ensure we're not handling an event inherited from
908           our parent. */
909
910        cancel_named_event(winbind_event_context(),
911                           "krb5_ticket_refresh_handler");
912
913        /* We might be in the idmap child...*/
914        if (child->domain && !(child->domain->internal) &&
915            lp_winbind_offline_logon()) {
916
917                set_domain_online_request(child->domain);
918
919                child->lockout_policy_event = event_add_timed(
920                        winbind_event_context(), NULL, timeval_zero(),
921                        "account_lockout_policy_handler",
922                        account_lockout_policy_handler,
923                        child);
924        }
925
926        while (1) {
927
928                int ret;
929                fd_set read_fds;
930                struct timeval t;
931                struct timeval *tp;
932                struct timeval now;
933
934                /* free up any talloc memory */
935                lp_TALLOC_FREE();
936                main_loop_TALLOC_FREE();
937
938                run_events(winbind_event_context(), 0, NULL, NULL);
939
940                GetTimeOfDay(&now);
941
942                if (child->domain && child->domain->startup &&
943                                (now.tv_sec > child->domain->startup_time + 30)) {
944                        /* No longer in "startup" mode. */
945                        DEBUG(10,("fork_domain_child: domain %s no longer in 'startup' mode.\n",
946                                child->domain->name ));
947                        child->domain->startup = False;
948                }
949
950                tp = get_timed_events_timeout(winbind_event_context(), &t);
951                if (tp) {
952                        DEBUG(11,("select will use timeout of %u.%u seconds\n",
953                                (unsigned int)tp->tv_sec, (unsigned int)tp->tv_usec ));
954                }
955
956                /* Handle messages */
957
958                message_dispatch();
959
960                FD_ZERO(&read_fds);
961                FD_SET(state.sock, &read_fds);
962
963                ret = sys_select(state.sock + 1, &read_fds, NULL, NULL, tp);
964
965                if (ret == 0) {
966                        DEBUG(11,("nothing is ready yet, continue\n"));
967                        continue;
968                }
969
970                if (ret == -1 && errno == EINTR) {
971                        /* We got a signal - continue. */
972                        continue;
973                }
974
975                if (ret == -1 && errno != EINTR) {
976                        DEBUG(0,("select error occured\n"));
977                        perror("select");
978                        return False;
979                }
980
981                /* fetch a request from the main daemon */
982                child_read_request(&state);
983
984                if (state.finished) {
985                        /* we lost contact with our parent */
986                        exit(0);
987                }
988
989                DEBUG(4,("child daemon request %d\n", (int)state.request.cmd));
990
991                ZERO_STRUCT(state.response);
992                state.request.null_term = '\0';
993                child_process_request(child->domain, &state);
994
995                SAFE_FREE(state.request.extra_data.data);
996
997                cache_store_response(sys_getpid(), &state.response);
998
999                SAFE_FREE(state.response.extra_data.data);
1000
1001                /* We just send the result code back, the result
1002                 * structure needs to be fetched via the
1003                 * winbindd_cache. Hmm. That needs fixing... */
1004
1005                if (write_data(state.sock,
1006                               (const char *)&state.response.result,
1007                               sizeof(state.response.result)) !=
1008                    sizeof(state.response.result)) {
1009                        DEBUG(0, ("Could not write result\n"));
1010                        exit(1);
1011                }
1012        }
1013}
Note: See TracBrowser for help on using the repository browser.