| 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 |
|
|---|
| 37 | extern BOOL override_logfile;
|
|---|
| 38 |
|
|---|
| 39 | /* Read some data from a client connection */
|
|---|
| 40 |
|
|---|
| 41 | static 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 |
|
|---|
| 92 | struct 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 | struct timed_event *reply_timeout_event;
|
|---|
| 100 | pid_t child_pid; /* pid of the child we're waiting on. Used to detect
|
|---|
| 101 | a restart of the child (child->pid != child_pid). */
|
|---|
| 102 | void *private_data;
|
|---|
| 103 | };
|
|---|
| 104 |
|
|---|
| 105 | static void async_request_fail(struct winbindd_async_request *state);
|
|---|
| 106 | static void async_main_request_sent(void *private_data, BOOL success);
|
|---|
| 107 | static void async_request_sent(void *private_data, BOOL success);
|
|---|
| 108 | static void async_reply_recv(void *private_data, BOOL success);
|
|---|
| 109 | static void schedule_async_request(struct winbindd_child *child);
|
|---|
| 110 |
|
|---|
| 111 | void async_request(TALLOC_CTX *mem_ctx, struct winbindd_child *child,
|
|---|
| 112 | struct winbindd_request *request,
|
|---|
| 113 | struct winbindd_response *response,
|
|---|
| 114 | void (*continuation)(void *private_data, BOOL success),
|
|---|
| 115 | void *private_data)
|
|---|
| 116 | {
|
|---|
| 117 | struct winbindd_async_request *state;
|
|---|
| 118 |
|
|---|
| 119 | SMB_ASSERT(continuation != NULL);
|
|---|
| 120 |
|
|---|
| 121 | state = TALLOC_P(mem_ctx, struct winbindd_async_request);
|
|---|
| 122 |
|
|---|
| 123 | if (state == NULL) {
|
|---|
| 124 | DEBUG(0, ("talloc failed\n"));
|
|---|
| 125 | continuation(private_data, False);
|
|---|
| 126 | return;
|
|---|
| 127 | }
|
|---|
| 128 |
|
|---|
| 129 | state->mem_ctx = mem_ctx;
|
|---|
| 130 | state->child = child;
|
|---|
| 131 | state->reply_timeout_event = NULL;
|
|---|
| 132 | state->request = request;
|
|---|
| 133 | state->response = response;
|
|---|
| 134 | state->continuation = continuation;
|
|---|
| 135 | state->private_data = private_data;
|
|---|
| 136 |
|
|---|
| 137 | DLIST_ADD_END(child->requests, state, struct winbindd_async_request *);
|
|---|
| 138 |
|
|---|
| 139 | schedule_async_request(child);
|
|---|
| 140 |
|
|---|
| 141 | return;
|
|---|
| 142 | }
|
|---|
| 143 |
|
|---|
| 144 | static void async_main_request_sent(void *private_data, BOOL success)
|
|---|
| 145 | {
|
|---|
| 146 | struct winbindd_async_request *state =
|
|---|
| 147 | talloc_get_type_abort(private_data, struct winbindd_async_request);
|
|---|
| 148 |
|
|---|
| 149 | if (!success) {
|
|---|
| 150 | DEBUG(5, ("Could not send async request\n"));
|
|---|
| 151 | async_request_fail(state);
|
|---|
| 152 | return;
|
|---|
| 153 | }
|
|---|
| 154 |
|
|---|
| 155 | if (state->request->extra_len == 0) {
|
|---|
| 156 | async_request_sent(private_data, True);
|
|---|
| 157 | return;
|
|---|
| 158 | }
|
|---|
| 159 |
|
|---|
| 160 | setup_async_write(&state->child->event, state->request->extra_data.data,
|
|---|
| 161 | state->request->extra_len,
|
|---|
| 162 | async_request_sent, state);
|
|---|
| 163 | }
|
|---|
| 164 |
|
|---|
| 165 | /****************************************************************
|
|---|
| 166 | Handler triggered if the child winbindd doesn't respond within
|
|---|
| 167 | a given timeout.
|
|---|
| 168 | ****************************************************************/
|
|---|
| 169 |
|
|---|
| 170 | static void async_request_timeout_handler(struct event_context *ctx,
|
|---|
| 171 | struct timed_event *te,
|
|---|
| 172 | const struct timeval *now,
|
|---|
| 173 | void *private_data)
|
|---|
| 174 | {
|
|---|
| 175 | struct winbindd_async_request *state =
|
|---|
| 176 | talloc_get_type_abort(private_data, struct winbindd_async_request);
|
|---|
| 177 |
|
|---|
| 178 | DEBUG(0,("async_request_timeout_handler: child pid %u is not responding. "
|
|---|
| 179 | "Closing connection to it.\n",
|
|---|
| 180 | state->child_pid ));
|
|---|
| 181 |
|
|---|
| 182 | /* Deal with the reply - set to error. */
|
|---|
| 183 | async_reply_recv(private_data, False);
|
|---|
| 184 | }
|
|---|
| 185 |
|
|---|
| 186 | /**************************************************************
|
|---|
| 187 | Common function called on both async send and recv fail.
|
|---|
| 188 | Cleans up the child and schedules the next request.
|
|---|
| 189 | **************************************************************/
|
|---|
| 190 |
|
|---|
| 191 | static void async_request_fail(struct winbindd_async_request *state)
|
|---|
| 192 | {
|
|---|
| 193 | DLIST_REMOVE(state->child->requests, state);
|
|---|
| 194 |
|
|---|
| 195 | TALLOC_FREE(state->reply_timeout_event);
|
|---|
| 196 |
|
|---|
| 197 | /* If child exists and is not already reaped,
|
|---|
| 198 | send kill signal to child. */
|
|---|
| 199 |
|
|---|
| 200 | if ((state->child->pid != (pid_t)0) &&
|
|---|
| 201 | (state->child->pid != (pid_t)-1) &&
|
|---|
| 202 | (state->child->pid == state->child_pid)) {
|
|---|
| 203 | kill(state->child_pid, SIGTERM);
|
|---|
| 204 |
|
|---|
| 205 | /*
|
|---|
| 206 | * Close the socket to the child.
|
|---|
| 207 | */
|
|---|
| 208 | winbind_child_died(state->child_pid);
|
|---|
| 209 | }
|
|---|
| 210 |
|
|---|
| 211 | state->response->length = sizeof(struct winbindd_response);
|
|---|
| 212 | state->response->result = WINBINDD_ERROR;
|
|---|
| 213 | state->continuation(state->private_data, False);
|
|---|
| 214 | }
|
|---|
| 215 |
|
|---|
| 216 | static void async_request_sent(void *private_data_data, BOOL success)
|
|---|
| 217 | {
|
|---|
| 218 | struct winbindd_async_request *state =
|
|---|
| 219 | talloc_get_type_abort(private_data_data, struct winbindd_async_request);
|
|---|
| 220 |
|
|---|
| 221 | if (!success) {
|
|---|
| 222 | DEBUG(5, ("Could not send async request to child pid %u\n",
|
|---|
| 223 | (unsigned int)state->child_pid ));
|
|---|
| 224 | async_request_fail(state);
|
|---|
| 225 | return;
|
|---|
| 226 | }
|
|---|
| 227 |
|
|---|
| 228 | /* Request successfully sent to the child, setup the wait for reply */
|
|---|
| 229 |
|
|---|
| 230 | setup_async_read(&state->child->event,
|
|---|
| 231 | &state->response->result,
|
|---|
| 232 | sizeof(state->response->result),
|
|---|
| 233 | async_reply_recv, state);
|
|---|
| 234 |
|
|---|
| 235 | /*
|
|---|
| 236 | * Set up a timeout of 300 seconds for the response.
|
|---|
| 237 | * If we don't get it close the child socket and
|
|---|
| 238 | * report failure.
|
|---|
| 239 | */
|
|---|
| 240 |
|
|---|
| 241 | state->reply_timeout_event = event_add_timed(winbind_event_context(),
|
|---|
| 242 | NULL,
|
|---|
| 243 | timeval_current_ofs(300,0),
|
|---|
| 244 | "async_request_timeout",
|
|---|
| 245 | async_request_timeout_handler,
|
|---|
| 246 | state);
|
|---|
| 247 | if (!state->reply_timeout_event) {
|
|---|
| 248 | smb_panic("async_request_sent: failed to add timeout handler.\n");
|
|---|
| 249 | }
|
|---|
| 250 | }
|
|---|
| 251 |
|
|---|
| 252 | static void async_reply_recv(void *private_data, BOOL success)
|
|---|
| 253 | {
|
|---|
| 254 | struct winbindd_async_request *state =
|
|---|
| 255 | talloc_get_type_abort(private_data, struct winbindd_async_request);
|
|---|
| 256 | struct winbindd_child *child = state->child;
|
|---|
| 257 |
|
|---|
| 258 | TALLOC_FREE(state->reply_timeout_event);
|
|---|
| 259 |
|
|---|
| 260 | state->response->length = sizeof(struct winbindd_response);
|
|---|
| 261 |
|
|---|
| 262 | if (!success) {
|
|---|
| 263 | DEBUG(5, ("Could not receive async reply from child pid %u\n",
|
|---|
| 264 | (unsigned int)state->child_pid ));
|
|---|
| 265 |
|
|---|
| 266 | cache_cleanup_response(state->child_pid);
|
|---|
| 267 | async_request_fail(state);
|
|---|
| 268 | return;
|
|---|
| 269 | }
|
|---|
| 270 |
|
|---|
| 271 | SMB_ASSERT(cache_retrieve_response(state->child_pid,
|
|---|
| 272 | state->response));
|
|---|
| 273 |
|
|---|
| 274 | cache_cleanup_response(state->child_pid);
|
|---|
| 275 |
|
|---|
| 276 | DLIST_REMOVE(child->requests, state);
|
|---|
| 277 |
|
|---|
| 278 | schedule_async_request(child);
|
|---|
| 279 |
|
|---|
| 280 | state->continuation(state->private_data, True);
|
|---|
| 281 | }
|
|---|
| 282 |
|
|---|
| 283 | static BOOL fork_domain_child(struct winbindd_child *child);
|
|---|
| 284 |
|
|---|
| 285 | static void schedule_async_request(struct winbindd_child *child)
|
|---|
| 286 | {
|
|---|
| 287 | struct winbindd_async_request *request = child->requests;
|
|---|
| 288 |
|
|---|
| 289 | if (request == NULL) {
|
|---|
| 290 | return;
|
|---|
| 291 | }
|
|---|
| 292 |
|
|---|
| 293 | if (child->event.flags != 0) {
|
|---|
| 294 | return; /* Busy */
|
|---|
| 295 | }
|
|---|
| 296 |
|
|---|
| 297 | /*
|
|---|
| 298 | * This may be a reschedule, so we might
|
|---|
| 299 | * have an existing timeout event pending on
|
|---|
| 300 | * the first entry in the child->requests list
|
|---|
| 301 | * (we only send one request at a time).
|
|---|
| 302 | * Ensure we free it before we reschedule.
|
|---|
| 303 | * Bug #5814, from hargagan <[email protected]>.
|
|---|
| 304 | * JRA.
|
|---|
| 305 | */
|
|---|
| 306 |
|
|---|
| 307 | TALLOC_FREE(request->reply_timeout_event);
|
|---|
| 308 |
|
|---|
| 309 | if ((child->pid == 0) && (!fork_domain_child(child))) {
|
|---|
| 310 | /* fork_domain_child failed.
|
|---|
| 311 | Cancel all outstanding requests */
|
|---|
| 312 |
|
|---|
| 313 | while (request != NULL) {
|
|---|
| 314 | /* request might be free'd in the continuation */
|
|---|
| 315 | struct winbindd_async_request *next = request->next;
|
|---|
| 316 |
|
|---|
| 317 | async_request_fail(request);
|
|---|
| 318 | request = next;
|
|---|
| 319 | }
|
|---|
| 320 | return;
|
|---|
| 321 | }
|
|---|
| 322 |
|
|---|
| 323 | /* Now we know who we're sending to - remember the pid. */
|
|---|
| 324 | request->child_pid = child->pid;
|
|---|
| 325 |
|
|---|
| 326 | setup_async_write(&child->event, request->request,
|
|---|
| 327 | sizeof(*request->request),
|
|---|
| 328 | async_main_request_sent, request);
|
|---|
| 329 |
|
|---|
| 330 | return;
|
|---|
| 331 | }
|
|---|
| 332 |
|
|---|
| 333 | struct domain_request_state {
|
|---|
| 334 | TALLOC_CTX *mem_ctx;
|
|---|
| 335 | struct winbindd_domain *domain;
|
|---|
| 336 | struct winbindd_request *request;
|
|---|
| 337 | struct winbindd_response *response;
|
|---|
| 338 | void (*continuation)(void *private_data_data, BOOL success);
|
|---|
| 339 | void *private_data_data;
|
|---|
| 340 | };
|
|---|
| 341 |
|
|---|
| 342 | static void domain_init_recv(void *private_data_data, BOOL success);
|
|---|
| 343 |
|
|---|
| 344 | void async_domain_request(TALLOC_CTX *mem_ctx,
|
|---|
| 345 | struct winbindd_domain *domain,
|
|---|
| 346 | struct winbindd_request *request,
|
|---|
| 347 | struct winbindd_response *response,
|
|---|
| 348 | void (*continuation)(void *private_data_data, BOOL success),
|
|---|
| 349 | void *private_data_data)
|
|---|
| 350 | {
|
|---|
| 351 | struct domain_request_state *state;
|
|---|
| 352 |
|
|---|
| 353 | if (domain->initialized) {
|
|---|
| 354 | async_request(mem_ctx, &domain->child, request, response,
|
|---|
| 355 | continuation, private_data_data);
|
|---|
| 356 | return;
|
|---|
| 357 | }
|
|---|
| 358 |
|
|---|
| 359 | state = TALLOC_P(mem_ctx, struct domain_request_state);
|
|---|
| 360 | if (state == NULL) {
|
|---|
| 361 | DEBUG(0, ("talloc failed\n"));
|
|---|
| 362 | continuation(private_data_data, False);
|
|---|
| 363 | return;
|
|---|
| 364 | }
|
|---|
| 365 |
|
|---|
| 366 | state->mem_ctx = mem_ctx;
|
|---|
| 367 | state->domain = domain;
|
|---|
| 368 | state->request = request;
|
|---|
| 369 | state->response = response;
|
|---|
| 370 | state->continuation = continuation;
|
|---|
| 371 | state->private_data_data = private_data_data;
|
|---|
| 372 |
|
|---|
| 373 | init_child_connection(domain, domain_init_recv, state);
|
|---|
| 374 | }
|
|---|
| 375 |
|
|---|
| 376 | static void recvfrom_child(void *private_data_data, BOOL success)
|
|---|
| 377 | {
|
|---|
| 378 | struct winbindd_cli_state *state =
|
|---|
| 379 | talloc_get_type_abort(private_data_data, struct winbindd_cli_state);
|
|---|
| 380 | enum winbindd_result result = state->response.result;
|
|---|
| 381 |
|
|---|
| 382 | /* This is an optimization: The child has written directly to the
|
|---|
| 383 | * response buffer. The request itself is still in pending state,
|
|---|
| 384 | * state that in the result code. */
|
|---|
| 385 |
|
|---|
| 386 | state->response.result = WINBINDD_PENDING;
|
|---|
| 387 |
|
|---|
| 388 | if ((!success) || (result != WINBINDD_OK)) {
|
|---|
| 389 | request_error(state);
|
|---|
| 390 | return;
|
|---|
| 391 | }
|
|---|
| 392 |
|
|---|
| 393 | request_ok(state);
|
|---|
| 394 | }
|
|---|
| 395 |
|
|---|
| 396 | void sendto_child(struct winbindd_cli_state *state,
|
|---|
| 397 | struct winbindd_child *child)
|
|---|
| 398 | {
|
|---|
| 399 | async_request(state->mem_ctx, child, &state->request,
|
|---|
| 400 | &state->response, recvfrom_child, state);
|
|---|
| 401 | }
|
|---|
| 402 |
|
|---|
| 403 | void sendto_domain(struct winbindd_cli_state *state,
|
|---|
| 404 | struct winbindd_domain *domain)
|
|---|
| 405 | {
|
|---|
| 406 | async_domain_request(state->mem_ctx, domain,
|
|---|
| 407 | &state->request, &state->response,
|
|---|
| 408 | recvfrom_child, state);
|
|---|
| 409 | }
|
|---|
| 410 |
|
|---|
| 411 | static void domain_init_recv(void *private_data_data, BOOL success)
|
|---|
| 412 | {
|
|---|
| 413 | struct domain_request_state *state =
|
|---|
| 414 | talloc_get_type_abort(private_data_data, struct domain_request_state);
|
|---|
| 415 |
|
|---|
| 416 | if (!success) {
|
|---|
| 417 | DEBUG(5, ("Domain init returned an error\n"));
|
|---|
| 418 | state->continuation(state->private_data_data, False);
|
|---|
| 419 | return;
|
|---|
| 420 | }
|
|---|
| 421 |
|
|---|
| 422 | async_request(state->mem_ctx, &state->domain->child,
|
|---|
| 423 | state->request, state->response,
|
|---|
| 424 | state->continuation, state->private_data_data);
|
|---|
| 425 | }
|
|---|
| 426 |
|
|---|
| 427 | struct winbindd_child_dispatch_table {
|
|---|
| 428 | enum winbindd_cmd cmd;
|
|---|
| 429 | enum winbindd_result (*fn)(struct winbindd_domain *domain,
|
|---|
| 430 | struct winbindd_cli_state *state);
|
|---|
| 431 | const char *winbindd_cmd_name;
|
|---|
| 432 | };
|
|---|
| 433 |
|
|---|
| 434 | static struct winbindd_child_dispatch_table child_dispatch_table[] = {
|
|---|
| 435 |
|
|---|
| 436 | { WINBINDD_LOOKUPSID, winbindd_dual_lookupsid, "LOOKUPSID" },
|
|---|
| 437 | { WINBINDD_LOOKUPNAME, winbindd_dual_lookupname, "LOOKUPNAME" },
|
|---|
| 438 | { WINBINDD_LOOKUPRIDS, winbindd_dual_lookuprids, "LOOKUPRIDS" },
|
|---|
| 439 | { WINBINDD_LIST_TRUSTDOM, winbindd_dual_list_trusted_domains, "LIST_TRUSTDOM" },
|
|---|
| 440 | { WINBINDD_INIT_CONNECTION, winbindd_dual_init_connection, "INIT_CONNECTION" },
|
|---|
| 441 | { WINBINDD_GETDCNAME, winbindd_dual_getdcname, "GETDCNAME" },
|
|---|
| 442 | { WINBINDD_SHOW_SEQUENCE, winbindd_dual_show_sequence, "SHOW_SEQUENCE" },
|
|---|
| 443 | { WINBINDD_PAM_AUTH, winbindd_dual_pam_auth, "PAM_AUTH" },
|
|---|
| 444 | { WINBINDD_PAM_AUTH_CRAP, winbindd_dual_pam_auth_crap, "AUTH_CRAP" },
|
|---|
| 445 | { WINBINDD_PAM_LOGOFF, winbindd_dual_pam_logoff, "PAM_LOGOFF" },
|
|---|
| 446 | { WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP,winbindd_dual_pam_chng_pswd_auth_crap,"CHNG_PSWD_AUTH_CRAP" },
|
|---|
| 447 | { WINBINDD_PAM_CHAUTHTOK, winbindd_dual_pam_chauthtok, "PAM_CHAUTHTOK" },
|
|---|
| 448 | { WINBINDD_CHECK_MACHACC, winbindd_dual_check_machine_acct, "CHECK_MACHACC" },
|
|---|
| 449 | { WINBINDD_DUAL_SID2UID, winbindd_dual_sid2uid, "DUAL_SID2UID" },
|
|---|
| 450 | { WINBINDD_DUAL_SID2GID, winbindd_dual_sid2gid, "DUAL_SID2GID" },
|
|---|
| 451 | #if 0 /* DISABLED until we fix the interface in Samba 3.0.26 --jerry */
|
|---|
| 452 | { WINBINDD_DUAL_SIDS2XIDS, winbindd_dual_sids2xids, "DUAL_SIDS2XIDS" },
|
|---|
| 453 | #endif /* end DISABLED */
|
|---|
| 454 | { WINBINDD_DUAL_UID2SID, winbindd_dual_uid2sid, "DUAL_UID2SID" },
|
|---|
| 455 | { WINBINDD_DUAL_GID2SID, winbindd_dual_gid2sid, "DUAL_GID2SID" },
|
|---|
| 456 | { WINBINDD_DUAL_UID2NAME, winbindd_dual_uid2name, "DUAL_UID2NAME" },
|
|---|
| 457 | { WINBINDD_DUAL_NAME2UID, winbindd_dual_name2uid, "DUAL_NAME2UID" },
|
|---|
| 458 | { WINBINDD_DUAL_GID2NAME, winbindd_dual_gid2name, "DUAL_GID2NAME" },
|
|---|
| 459 | { WINBINDD_DUAL_NAME2GID, winbindd_dual_name2gid, "DUAL_NAME2GID" },
|
|---|
| 460 | { WINBINDD_DUAL_SET_MAPPING, winbindd_dual_set_mapping, "DUAL_SET_MAPPING" },
|
|---|
| 461 | { WINBINDD_DUAL_SET_HWM, winbindd_dual_set_hwm, "DUAL_SET_HWMS" },
|
|---|
| 462 | { WINBINDD_DUAL_DUMP_MAPS, winbindd_dual_dump_maps, "DUAL_DUMP_MAPS" },
|
|---|
| 463 | { WINBINDD_DUAL_USERINFO, winbindd_dual_userinfo, "DUAL_USERINFO" },
|
|---|
| 464 | { WINBINDD_ALLOCATE_UID, winbindd_dual_allocate_uid, "ALLOCATE_UID" },
|
|---|
| 465 | { WINBINDD_ALLOCATE_GID, winbindd_dual_allocate_gid, "ALLOCATE_GID" },
|
|---|
| 466 | { WINBINDD_GETUSERDOMGROUPS, winbindd_dual_getuserdomgroups, "GETUSERDOMGROUPS" },
|
|---|
| 467 | { WINBINDD_DUAL_GETSIDALIASES, winbindd_dual_getsidaliases, "GETSIDALIASES" },
|
|---|
| 468 | { WINBINDD_CCACHE_NTLMAUTH, winbindd_dual_ccache_ntlm_auth, "CCACHE_NTLM_AUTH" },
|
|---|
| 469 | /* End of list */
|
|---|
| 470 |
|
|---|
| 471 | { WINBINDD_NUM_CMDS, NULL, "NONE" }
|
|---|
| 472 | };
|
|---|
| 473 |
|
|---|
| 474 | static void child_process_request(struct winbindd_domain *domain,
|
|---|
| 475 | struct winbindd_cli_state *state)
|
|---|
| 476 | {
|
|---|
| 477 | struct winbindd_child_dispatch_table *table;
|
|---|
| 478 |
|
|---|
| 479 | /* Free response data - we may be interrupted and receive another
|
|---|
| 480 | command before being able to send this data off. */
|
|---|
| 481 |
|
|---|
| 482 | state->response.result = WINBINDD_ERROR;
|
|---|
| 483 | state->response.length = sizeof(struct winbindd_response);
|
|---|
| 484 |
|
|---|
| 485 | state->mem_ctx = talloc_init("winbind request");
|
|---|
| 486 | if (state->mem_ctx == NULL)
|
|---|
| 487 | return;
|
|---|
| 488 |
|
|---|
| 489 | /* Process command */
|
|---|
| 490 |
|
|---|
| 491 | for (table = child_dispatch_table; table->fn; table++) {
|
|---|
| 492 | if (state->request.cmd == table->cmd) {
|
|---|
| 493 | DEBUG(10,("process_request: request fn %s\n",
|
|---|
| 494 | table->winbindd_cmd_name ));
|
|---|
| 495 | state->response.result = table->fn(domain, state);
|
|---|
| 496 | break;
|
|---|
| 497 | }
|
|---|
| 498 | }
|
|---|
| 499 |
|
|---|
| 500 | if (!table->fn) {
|
|---|
| 501 | DEBUG(10,("process_request: unknown request fn number %d\n",
|
|---|
| 502 | (int)state->request.cmd ));
|
|---|
| 503 | state->response.result = WINBINDD_ERROR;
|
|---|
| 504 | }
|
|---|
| 505 |
|
|---|
| 506 | talloc_destroy(state->mem_ctx);
|
|---|
| 507 | }
|
|---|
| 508 |
|
|---|
| 509 | void setup_domain_child(struct winbindd_domain *domain,
|
|---|
| 510 | struct winbindd_child *child,
|
|---|
| 511 | const char *explicit_logfile)
|
|---|
| 512 | {
|
|---|
| 513 | if (explicit_logfile != NULL) {
|
|---|
| 514 | pstr_sprintf(child->logfilename, "%s/log.winbindd-%s",
|
|---|
| 515 | dyn_LOGFILEBASE, explicit_logfile);
|
|---|
| 516 | } else if (domain != NULL) {
|
|---|
| 517 | pstr_sprintf(child->logfilename, "%s/log.wb-%s",
|
|---|
| 518 | dyn_LOGFILEBASE, domain->name);
|
|---|
| 519 | } else {
|
|---|
| 520 | smb_panic("Internal error: domain == NULL && "
|
|---|
| 521 | "explicit_logfile == NULL");
|
|---|
| 522 | }
|
|---|
| 523 |
|
|---|
| 524 | child->domain = domain;
|
|---|
| 525 | }
|
|---|
| 526 |
|
|---|
| 527 | struct winbindd_child *children = NULL;
|
|---|
| 528 |
|
|---|
| 529 | void winbind_child_died(pid_t pid)
|
|---|
| 530 | {
|
|---|
| 531 | struct winbindd_child *child;
|
|---|
| 532 |
|
|---|
| 533 | for (child = children; child != NULL; child = child->next) {
|
|---|
| 534 | if (child->pid == pid) {
|
|---|
| 535 | break;
|
|---|
| 536 | }
|
|---|
| 537 | }
|
|---|
| 538 |
|
|---|
| 539 | if (child == NULL) {
|
|---|
| 540 | DEBUG(5, ("Already reaped child %u died\n", (unsigned int)pid));
|
|---|
| 541 | return;
|
|---|
| 542 | }
|
|---|
| 543 |
|
|---|
| 544 | /* This will be re-added in fork_domain_child() */
|
|---|
| 545 |
|
|---|
| 546 | DLIST_REMOVE(children, child);
|
|---|
| 547 |
|
|---|
| 548 | remove_fd_event(&child->event);
|
|---|
| 549 | close(child->event.fd);
|
|---|
| 550 | child->event.fd = 0;
|
|---|
| 551 | child->event.flags = 0;
|
|---|
| 552 | child->pid = 0;
|
|---|
| 553 |
|
|---|
| 554 | if (child->requests) {
|
|---|
| 555 | /*
|
|---|
| 556 | * schedule_async_request() will also
|
|---|
| 557 | * clear this event but the call is
|
|---|
| 558 | * idempotent so it doesn't hurt to
|
|---|
| 559 | * cover all possible future code
|
|---|
| 560 | * paths. JRA.
|
|---|
| 561 | */
|
|---|
| 562 | TALLOC_FREE(child->requests->reply_timeout_event);
|
|---|
| 563 | }
|
|---|
| 564 |
|
|---|
| 565 | schedule_async_request(child);
|
|---|
| 566 | }
|
|---|
| 567 |
|
|---|
| 568 | /* Ensure any negative cache entries with the netbios or realm names are removed. */
|
|---|
| 569 |
|
|---|
| 570 | void winbindd_flush_negative_conn_cache(struct winbindd_domain *domain)
|
|---|
| 571 | {
|
|---|
| 572 | flush_negative_conn_cache_for_domain(domain->name);
|
|---|
| 573 | if (*domain->alt_name) {
|
|---|
| 574 | flush_negative_conn_cache_for_domain(domain->alt_name);
|
|---|
| 575 | }
|
|---|
| 576 | }
|
|---|
| 577 |
|
|---|
| 578 | /* Set our domains as offline and forward the offline message to our children. */
|
|---|
| 579 |
|
|---|
| 580 | void winbind_msg_offline(int msg_type, struct process_id src,
|
|---|
| 581 | void *buf, size_t len, void *private_data)
|
|---|
| 582 | {
|
|---|
| 583 | struct winbindd_child *child;
|
|---|
| 584 | struct winbindd_domain *domain;
|
|---|
| 585 |
|
|---|
| 586 | DEBUG(10,("winbind_msg_offline: got offline message.\n"));
|
|---|
| 587 |
|
|---|
| 588 | if (!lp_winbind_offline_logon()) {
|
|---|
| 589 | DEBUG(10,("winbind_msg_offline: rejecting offline message.\n"));
|
|---|
| 590 | return;
|
|---|
| 591 | }
|
|---|
| 592 |
|
|---|
| 593 | /* Set our global state as offline. */
|
|---|
| 594 | if (!set_global_winbindd_state_offline()) {
|
|---|
| 595 | DEBUG(10,("winbind_msg_offline: offline request failed.\n"));
|
|---|
| 596 | return;
|
|---|
| 597 | }
|
|---|
| 598 |
|
|---|
| 599 | /* Set all our domains as offline. */
|
|---|
| 600 | for (domain = domain_list(); domain; domain = domain->next) {
|
|---|
| 601 | if (domain->internal) {
|
|---|
| 602 | continue;
|
|---|
| 603 | }
|
|---|
| 604 | DEBUG(5,("winbind_msg_offline: marking %s offline.\n", domain->name));
|
|---|
| 605 | set_domain_offline(domain);
|
|---|
| 606 |
|
|---|
| 607 | /* Send an offline message to the idmap child when our
|
|---|
| 608 | primary domain goes offline */
|
|---|
| 609 |
|
|---|
| 610 | if ( domain->primary ) {
|
|---|
| 611 | struct winbindd_child *idmap = idmap_child();
|
|---|
| 612 |
|
|---|
| 613 | if ( idmap->pid != 0 ) {
|
|---|
| 614 | message_send_pid(pid_to_procid(idmap->pid),
|
|---|
| 615 | MSG_WINBIND_OFFLINE,
|
|---|
| 616 | domain->name,
|
|---|
| 617 | strlen(domain->name)+1,
|
|---|
| 618 | False);
|
|---|
| 619 | }
|
|---|
| 620 | }
|
|---|
| 621 | }
|
|---|
| 622 |
|
|---|
| 623 | for (child = children; child != NULL; child = child->next) {
|
|---|
| 624 | /* Don't send message to idmap child. We've already
|
|---|
| 625 | done so above. */
|
|---|
| 626 | if (!child->domain || (child == idmap_child())) {
|
|---|
| 627 | continue;
|
|---|
| 628 | }
|
|---|
| 629 |
|
|---|
| 630 | /* Or internal domains (this should not be possible....) */
|
|---|
| 631 | if (child->domain->internal) {
|
|---|
| 632 | continue;
|
|---|
| 633 | }
|
|---|
| 634 |
|
|---|
| 635 | /* Each winbindd child should only process requests for one domain - make sure
|
|---|
| 636 | we only set it online / offline for that domain. */
|
|---|
| 637 |
|
|---|
| 638 | DEBUG(10,("winbind_msg_offline: sending message to pid %u for domain %s.\n",
|
|---|
| 639 | (unsigned int)child->pid, domain->name ));
|
|---|
| 640 |
|
|---|
| 641 | message_send_pid(pid_to_procid(child->pid), MSG_WINBIND_OFFLINE, child->domain->name,
|
|---|
| 642 | strlen(child->domain->name)+1, False);
|
|---|
| 643 | }
|
|---|
| 644 | }
|
|---|
| 645 |
|
|---|
| 646 | /* Set our domains as online and forward the online message to our children. */
|
|---|
| 647 |
|
|---|
| 648 | void winbind_msg_online(int msg_type, struct process_id src,
|
|---|
| 649 | void *buf, size_t len, void *private_data)
|
|---|
| 650 | {
|
|---|
| 651 | struct winbindd_child *child;
|
|---|
| 652 | struct winbindd_domain *domain;
|
|---|
| 653 |
|
|---|
| 654 | DEBUG(10,("winbind_msg_online: got online message.\n"));
|
|---|
| 655 |
|
|---|
| 656 | if (!lp_winbind_offline_logon()) {
|
|---|
| 657 | DEBUG(10,("winbind_msg_online: rejecting online message.\n"));
|
|---|
| 658 | return;
|
|---|
| 659 | }
|
|---|
| 660 |
|
|---|
| 661 | /* Set our global state as online. */
|
|---|
| 662 | set_global_winbindd_state_online();
|
|---|
| 663 |
|
|---|
| 664 | smb_nscd_flush_user_cache();
|
|---|
| 665 | smb_nscd_flush_group_cache();
|
|---|
| 666 |
|
|---|
| 667 | /* Set all our domains as online. */
|
|---|
| 668 | for (domain = domain_list(); domain; domain = domain->next) {
|
|---|
| 669 | if (domain->internal) {
|
|---|
| 670 | continue;
|
|---|
| 671 | }
|
|---|
| 672 | DEBUG(5,("winbind_msg_online: requesting %s to go online.\n", domain->name));
|
|---|
| 673 |
|
|---|
| 674 | winbindd_flush_negative_conn_cache(domain);
|
|---|
| 675 | set_domain_online_request(domain);
|
|---|
| 676 |
|
|---|
| 677 | /* Send an online message to the idmap child when our
|
|---|
| 678 | primary domain comes back online */
|
|---|
| 679 |
|
|---|
| 680 | if ( domain->primary ) {
|
|---|
| 681 | struct winbindd_child *idmap = idmap_child();
|
|---|
| 682 |
|
|---|
| 683 | if ( idmap->pid != 0 ) {
|
|---|
| 684 | message_send_pid(pid_to_procid(idmap->pid),
|
|---|
| 685 | MSG_WINBIND_ONLINE,
|
|---|
| 686 | domain->name,
|
|---|
| 687 | strlen(domain->name)+1,
|
|---|
| 688 | False);
|
|---|
| 689 | }
|
|---|
| 690 |
|
|---|
| 691 | }
|
|---|
| 692 | }
|
|---|
| 693 |
|
|---|
| 694 | for (child = children; child != NULL; child = child->next) {
|
|---|
| 695 | /* Don't send message to idmap child. */
|
|---|
| 696 | if (!child->domain || (child == idmap_child())) {
|
|---|
| 697 | continue;
|
|---|
| 698 | }
|
|---|
| 699 |
|
|---|
| 700 | /* Or internal domains (this should not be possible....) */
|
|---|
| 701 | if (child->domain->internal) {
|
|---|
| 702 | continue;
|
|---|
| 703 | }
|
|---|
| 704 |
|
|---|
| 705 | /* Each winbindd child should only process requests for one domain - make sure
|
|---|
| 706 | we only set it online / offline for that domain. */
|
|---|
| 707 |
|
|---|
| 708 | DEBUG(10,("winbind_msg_online: sending message to pid %u for domain %s.\n",
|
|---|
| 709 | (unsigned int)child->pid, child->domain->name ));
|
|---|
| 710 |
|
|---|
| 711 | message_send_pid(pid_to_procid(child->pid), MSG_WINBIND_ONLINE, child->domain->name,
|
|---|
| 712 | strlen(child->domain->name)+1, False);
|
|---|
| 713 | }
|
|---|
| 714 | }
|
|---|
| 715 |
|
|---|
| 716 | /* Forward the online/offline messages to our children. */
|
|---|
| 717 | void winbind_msg_onlinestatus(int msg_type, struct process_id src,
|
|---|
| 718 | void *buf, size_t len, void *private_data)
|
|---|
| 719 | {
|
|---|
| 720 | struct winbindd_child *child;
|
|---|
| 721 |
|
|---|
| 722 | DEBUG(10,("winbind_msg_onlinestatus: got onlinestatus message.\n"));
|
|---|
| 723 |
|
|---|
| 724 | for (child = children; child != NULL; child = child->next) {
|
|---|
| 725 | if (child->domain && child->domain->primary) {
|
|---|
| 726 | DEBUG(10,("winbind_msg_onlinestatus: "
|
|---|
| 727 | "sending message to pid %u of primary domain.\n",
|
|---|
| 728 | (unsigned int)child->pid));
|
|---|
| 729 | message_send_pid(pid_to_procid(child->pid),
|
|---|
| 730 | MSG_WINBIND_ONLINESTATUS, buf, len, False);
|
|---|
| 731 | break;
|
|---|
| 732 | }
|
|---|
| 733 | }
|
|---|
| 734 | }
|
|---|
| 735 |
|
|---|
| 736 |
|
|---|
| 737 | static void account_lockout_policy_handler(struct event_context *ctx,
|
|---|
| 738 | struct timed_event *te,
|
|---|
| 739 | const struct timeval *now,
|
|---|
| 740 | void *private_data)
|
|---|
| 741 | {
|
|---|
| 742 | struct winbindd_child *child =
|
|---|
| 743 | (struct winbindd_child *)private_data;
|
|---|
| 744 | TALLOC_CTX *mem_ctx = NULL;
|
|---|
| 745 | struct winbindd_methods *methods;
|
|---|
| 746 | SAM_UNK_INFO_12 lockout_policy;
|
|---|
| 747 | NTSTATUS result;
|
|---|
| 748 |
|
|---|
| 749 | DEBUG(10,("account_lockout_policy_handler called\n"));
|
|---|
| 750 |
|
|---|
| 751 | TALLOC_FREE(child->lockout_policy_event);
|
|---|
| 752 |
|
|---|
| 753 | methods = child->domain->methods;
|
|---|
| 754 |
|
|---|
| 755 | mem_ctx = talloc_init("account_lockout_policy_handler ctx");
|
|---|
| 756 | if (!mem_ctx) {
|
|---|
| 757 | result = NT_STATUS_NO_MEMORY;
|
|---|
| 758 | } else {
|
|---|
| 759 | result = methods->lockout_policy(child->domain, mem_ctx, &lockout_policy);
|
|---|
| 760 | }
|
|---|
| 761 |
|
|---|
| 762 | talloc_destroy(mem_ctx);
|
|---|
| 763 |
|
|---|
| 764 | if (!NT_STATUS_IS_OK(result)) {
|
|---|
| 765 | DEBUG(10,("account_lockout_policy_handler: lockout_policy failed error %s\n",
|
|---|
| 766 | nt_errstr(result)));
|
|---|
| 767 | }
|
|---|
| 768 |
|
|---|
| 769 | child->lockout_policy_event = event_add_timed(winbind_event_context(), NULL,
|
|---|
| 770 | timeval_current_ofs(3600, 0),
|
|---|
| 771 | "account_lockout_policy_handler",
|
|---|
| 772 | account_lockout_policy_handler,
|
|---|
| 773 | child);
|
|---|
| 774 | }
|
|---|
| 775 |
|
|---|
| 776 | /* Deal with a request to go offline. */
|
|---|
| 777 |
|
|---|
| 778 | static void child_msg_offline(int msg_type, struct process_id src,
|
|---|
| 779 | void *buf, size_t len, void *private_data)
|
|---|
| 780 | {
|
|---|
| 781 | struct winbindd_domain *domain;
|
|---|
| 782 | struct winbindd_domain *primary_domain = NULL;
|
|---|
| 783 | const char *domainname = (const char *)buf;
|
|---|
| 784 |
|
|---|
| 785 | if (buf == NULL || len == 0) {
|
|---|
| 786 | return;
|
|---|
| 787 | }
|
|---|
| 788 |
|
|---|
| 789 | DEBUG(5,("child_msg_offline received for domain %s.\n", domainname));
|
|---|
| 790 |
|
|---|
| 791 | if (!lp_winbind_offline_logon()) {
|
|---|
| 792 | DEBUG(10,("child_msg_offline: rejecting offline message.\n"));
|
|---|
| 793 | return;
|
|---|
| 794 | }
|
|---|
| 795 |
|
|---|
| 796 | /* Set our global state as offline. */
|
|---|
| 797 | if (!set_global_winbindd_state_offline()) {
|
|---|
| 798 | DEBUG(10,("child_msg_offline: offline request failed.\n"));
|
|---|
| 799 | return;
|
|---|
| 800 | }
|
|---|
| 801 |
|
|---|
| 802 | primary_domain = find_our_domain();
|
|---|
| 803 |
|
|---|
| 804 | /* Mark the requested domain offline. */
|
|---|
| 805 |
|
|---|
| 806 | for (domain = domain_list(); domain; domain = domain->next) {
|
|---|
| 807 | if (domain->internal) {
|
|---|
| 808 | continue;
|
|---|
| 809 | }
|
|---|
| 810 | if (strequal(domain->name, domainname)) {
|
|---|
| 811 | DEBUG(5,("child_msg_offline: marking %s offline.\n", domain->name));
|
|---|
| 812 | set_domain_offline(domain);
|
|---|
| 813 | /* we are in the trusted domain, set the primary domain
|
|---|
| 814 | * offline too */
|
|---|
| 815 | if (domain != primary_domain) {
|
|---|
| 816 | set_domain_offline(primary_domain);
|
|---|
| 817 | }
|
|---|
| 818 | }
|
|---|
| 819 | }
|
|---|
| 820 | }
|
|---|
| 821 |
|
|---|
| 822 | /* Deal with a request to go online. */
|
|---|
| 823 |
|
|---|
| 824 | static void child_msg_online(int msg_type, struct process_id src,
|
|---|
| 825 | void *buf, size_t len, void *private_data)
|
|---|
| 826 | {
|
|---|
| 827 | struct winbindd_domain *domain;
|
|---|
| 828 | struct winbindd_domain *primary_domain = NULL;
|
|---|
| 829 | const char *domainname = (const char *)buf;
|
|---|
| 830 |
|
|---|
| 831 | if (buf == NULL || len == 0) {
|
|---|
| 832 | return;
|
|---|
| 833 | }
|
|---|
| 834 |
|
|---|
| 835 | DEBUG(5,("child_msg_online received for domain %s.\n", domainname));
|
|---|
| 836 |
|
|---|
| 837 | if (!lp_winbind_offline_logon()) {
|
|---|
| 838 | DEBUG(10,("child_msg_online: rejecting online message.\n"));
|
|---|
| 839 | return;
|
|---|
| 840 | }
|
|---|
| 841 |
|
|---|
| 842 | primary_domain = find_our_domain();
|
|---|
| 843 |
|
|---|
| 844 | /* Set our global state as online. */
|
|---|
| 845 | set_global_winbindd_state_online();
|
|---|
| 846 |
|
|---|
| 847 | /* Try and mark everything online - delete any negative cache entries
|
|---|
| 848 | to force a reconnect now. */
|
|---|
| 849 |
|
|---|
| 850 | for (domain = domain_list(); domain; domain = domain->next) {
|
|---|
| 851 | if (domain->internal) {
|
|---|
| 852 | continue;
|
|---|
| 853 | }
|
|---|
| 854 | if (strequal(domain->name, domainname)) {
|
|---|
| 855 | DEBUG(5,("child_msg_online: requesting %s to go online.\n", domain->name));
|
|---|
| 856 | winbindd_flush_negative_conn_cache(domain);
|
|---|
| 857 | set_domain_online_request(domain);
|
|---|
| 858 |
|
|---|
| 859 | /* we can be in trusted domain, which will contact primary domain
|
|---|
| 860 | * we have to bring primary domain online in trusted domain process
|
|---|
| 861 | * see, winbindd_dual_pam_auth() --> winbindd_dual_pam_auth_samlogon()
|
|---|
| 862 | * --> contact_domain = find_our_domain()
|
|---|
| 863 | * */
|
|---|
| 864 | if (domain != primary_domain) {
|
|---|
| 865 | winbindd_flush_negative_conn_cache(primary_domain);
|
|---|
| 866 | set_domain_online_request(primary_domain);
|
|---|
| 867 | }
|
|---|
| 868 | }
|
|---|
| 869 | }
|
|---|
| 870 | }
|
|---|
| 871 |
|
|---|
| 872 | static const char *collect_onlinestatus(TALLOC_CTX *mem_ctx)
|
|---|
| 873 | {
|
|---|
| 874 | struct winbindd_domain *domain;
|
|---|
| 875 | char *buf = NULL;
|
|---|
| 876 |
|
|---|
| 877 | if ((buf = talloc_asprintf(mem_ctx, "global:%s ",
|
|---|
| 878 | get_global_winbindd_state_offline() ?
|
|---|
| 879 | "Offline":"Online")) == NULL) {
|
|---|
| 880 | return NULL;
|
|---|
| 881 | }
|
|---|
| 882 |
|
|---|
| 883 | for (domain = domain_list(); domain; domain = domain->next) {
|
|---|
| 884 | if ((buf = talloc_asprintf_append(buf, "%s:%s ",
|
|---|
| 885 | domain->name,
|
|---|
| 886 | domain->online ?
|
|---|
| 887 | "Online":"Offline")) == NULL) {
|
|---|
| 888 | return NULL;
|
|---|
| 889 | }
|
|---|
| 890 | }
|
|---|
| 891 |
|
|---|
| 892 | buf = talloc_asprintf_append(buf, "\n");
|
|---|
| 893 |
|
|---|
| 894 | DEBUG(5,("collect_onlinestatus: %s", buf));
|
|---|
| 895 |
|
|---|
| 896 | return buf;
|
|---|
| 897 | }
|
|---|
| 898 |
|
|---|
| 899 | static void child_msg_onlinestatus(int msg_type, struct process_id src,
|
|---|
| 900 | void *buf, size_t len, void *private_data)
|
|---|
| 901 | {
|
|---|
| 902 | TALLOC_CTX *mem_ctx;
|
|---|
| 903 | const char *message;
|
|---|
| 904 | struct process_id *sender;
|
|---|
| 905 |
|
|---|
| 906 | DEBUG(5,("winbind_msg_onlinestatus received.\n"));
|
|---|
| 907 |
|
|---|
| 908 | if (!buf) {
|
|---|
| 909 | return;
|
|---|
| 910 | }
|
|---|
| 911 |
|
|---|
| 912 | sender = (struct process_id *)buf;
|
|---|
| 913 |
|
|---|
| 914 | mem_ctx = talloc_init("winbind_msg_onlinestatus");
|
|---|
| 915 | if (mem_ctx == NULL) {
|
|---|
| 916 | return;
|
|---|
| 917 | }
|
|---|
| 918 |
|
|---|
| 919 | message = collect_onlinestatus(mem_ctx);
|
|---|
| 920 | if (message == NULL) {
|
|---|
| 921 | talloc_destroy(mem_ctx);
|
|---|
| 922 | return;
|
|---|
| 923 | }
|
|---|
| 924 |
|
|---|
| 925 | message_send_pid(*sender, MSG_WINBIND_ONLINESTATUS,
|
|---|
| 926 | message, strlen(message) + 1, True);
|
|---|
| 927 |
|
|---|
| 928 | talloc_destroy(mem_ctx);
|
|---|
| 929 | }
|
|---|
| 930 |
|
|---|
| 931 | bool reinit_after_fork(struct messaging_context *msg_ctx,
|
|---|
| 932 | struct event_context *ev_ctx,
|
|---|
| 933 | bool parent_longlived);
|
|---|
| 934 | void ccache_remove_all_after_fork(void);
|
|---|
| 935 |
|
|---|
| 936 | bool winbindd_reinit_after_fork(const char *logfile)
|
|---|
| 937 | {
|
|---|
| 938 | struct winbindd_domain *dom;
|
|---|
| 939 | struct winbindd_child *cl;
|
|---|
| 940 |
|
|---|
| 941 | if (!reinit_after_fork(NULL,
|
|---|
| 942 | winbind_event_context(), true)) {
|
|---|
| 943 | DEBUG(0, ("reinit_after_fork failed.\n"));
|
|---|
| 944 | return false;
|
|---|
| 945 | }
|
|---|
| 946 |
|
|---|
| 947 | close_conns_after_fork();
|
|---|
| 948 |
|
|---|
| 949 | if (!override_logfile && logfile) {
|
|---|
| 950 | lp_set_logfile(logfile);
|
|---|
| 951 | reopen_logs();
|
|---|
| 952 | }
|
|---|
| 953 |
|
|---|
| 954 | /* Don't handle the same messages as our parent. */
|
|---|
| 955 | message_deregister(MSG_SMB_CONF_UPDATED);
|
|---|
| 956 | message_deregister(MSG_SHUTDOWN);
|
|---|
| 957 | message_deregister(MSG_WINBIND_OFFLINE);
|
|---|
| 958 | message_deregister(MSG_WINBIND_ONLINE);
|
|---|
| 959 | message_deregister(MSG_WINBIND_ONLINESTATUS);
|
|---|
| 960 |
|
|---|
| 961 | ccache_remove_all_after_fork();
|
|---|
| 962 |
|
|---|
| 963 | for (dom = domain_list(); dom; dom = dom->next) {
|
|---|
| 964 | TALLOC_FREE(dom->check_online_event);
|
|---|
| 965 | }
|
|---|
| 966 |
|
|---|
| 967 | for (cl = children; cl; cl = cl->next) {
|
|---|
| 968 | struct winbindd_async_request *request;
|
|---|
| 969 |
|
|---|
| 970 | for (request = cl->requests; request; request = request->next) {
|
|---|
| 971 | TALLOC_FREE(request->reply_timeout_event);
|
|---|
| 972 | }
|
|---|
| 973 | TALLOC_FREE(cl->lockout_policy_event);
|
|---|
| 974 | }
|
|---|
| 975 |
|
|---|
| 976 | return true;
|
|---|
| 977 | }
|
|---|
| 978 |
|
|---|
| 979 | static BOOL fork_domain_child(struct winbindd_child *child)
|
|---|
| 980 | {
|
|---|
| 981 | int fdpair[2];
|
|---|
| 982 | struct winbindd_cli_state state;
|
|---|
| 983 | struct winbindd_domain *primary_domain = NULL;
|
|---|
| 984 |
|
|---|
| 985 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair) != 0) {
|
|---|
| 986 | DEBUG(0, ("Could not open child pipe: %s\n",
|
|---|
| 987 | strerror(errno)));
|
|---|
| 988 | return False;
|
|---|
| 989 | }
|
|---|
| 990 |
|
|---|
| 991 | ZERO_STRUCT(state);
|
|---|
| 992 | state.pid = sys_getpid();
|
|---|
| 993 |
|
|---|
| 994 | /* Ensure we don't process messages whilst we're
|
|---|
| 995 | changing the disposition for the child. */
|
|---|
| 996 | message_block();
|
|---|
| 997 |
|
|---|
| 998 | child->pid = sys_fork();
|
|---|
| 999 |
|
|---|
| 1000 | if (child->pid == -1) {
|
|---|
| 1001 | DEBUG(0, ("Could not fork: %s\n", strerror(errno)));
|
|---|
| 1002 | message_unblock();
|
|---|
| 1003 | return False;
|
|---|
| 1004 | }
|
|---|
| 1005 |
|
|---|
| 1006 | if (child->pid != 0) {
|
|---|
| 1007 | /* Parent */
|
|---|
| 1008 | close(fdpair[0]);
|
|---|
| 1009 | child->next = child->prev = NULL;
|
|---|
| 1010 | DLIST_ADD(children, child);
|
|---|
| 1011 | child->event.fd = fdpair[1];
|
|---|
| 1012 | child->event.flags = 0;
|
|---|
| 1013 | add_fd_event(&child->event);
|
|---|
| 1014 | /* We're ok with online/offline messages now. */
|
|---|
| 1015 | message_unblock();
|
|---|
| 1016 | return True;
|
|---|
| 1017 | }
|
|---|
| 1018 |
|
|---|
| 1019 | /* Child */
|
|---|
| 1020 |
|
|---|
| 1021 | /* Stop zombies in children */
|
|---|
| 1022 | CatchChild();
|
|---|
| 1023 |
|
|---|
| 1024 | state.sock = fdpair[0];
|
|---|
| 1025 | close(fdpair[1]);
|
|---|
| 1026 |
|
|---|
| 1027 | /* tdb needs special fork handling */
|
|---|
| 1028 | if (!winbindd_reinit_after_fork(child->logfilename)) {
|
|---|
| 1029 | DEBUG(0, ("winbindd_reinit_after_fork failed.\n"));
|
|---|
| 1030 | _exit(0);
|
|---|
| 1031 | }
|
|---|
| 1032 |
|
|---|
| 1033 | /* The child is ok with online/offline messages now. */
|
|---|
| 1034 | message_unblock();
|
|---|
| 1035 |
|
|---|
| 1036 | /* Handle online/offline messages. */
|
|---|
| 1037 | message_register(MSG_WINBIND_OFFLINE, child_msg_offline, NULL);
|
|---|
| 1038 | message_register(MSG_WINBIND_ONLINE, child_msg_online, NULL);
|
|---|
| 1039 | message_register(MSG_WINBIND_ONLINESTATUS, child_msg_onlinestatus,
|
|---|
| 1040 | NULL);
|
|---|
| 1041 |
|
|---|
| 1042 | primary_domain = find_our_domain();
|
|---|
| 1043 |
|
|---|
| 1044 | if (primary_domain == NULL) {
|
|---|
| 1045 | smb_panic("no primary domain found");
|
|---|
| 1046 | }
|
|---|
| 1047 |
|
|---|
| 1048 | /* It doesn't matter if we allow cache login,
|
|---|
| 1049 | * try to bring domain online after fork. */
|
|---|
| 1050 | if ( child->domain ) {
|
|---|
| 1051 | child->domain->startup = True;
|
|---|
| 1052 | child->domain->startup_time = time(NULL);
|
|---|
| 1053 | /* we can be in primary domain or in trusted domain
|
|---|
| 1054 | * If we are in trusted domain, set the primary domain
|
|---|
| 1055 | * in start-up mode */
|
|---|
| 1056 | if (!(child->domain->internal)) {
|
|---|
| 1057 | set_domain_online_request(child->domain);
|
|---|
| 1058 | if (!(child->domain->primary)) {
|
|---|
| 1059 | primary_domain->startup = True;
|
|---|
| 1060 | primary_domain->startup_time = time(NULL);
|
|---|
| 1061 | set_domain_online_request(primary_domain);
|
|---|
| 1062 | }
|
|---|
| 1063 | }
|
|---|
| 1064 | }
|
|---|
| 1065 |
|
|---|
| 1066 | /* We might be in the idmap child...*/
|
|---|
| 1067 | if (child->domain && !(child->domain->internal) &&
|
|---|
| 1068 | lp_winbind_offline_logon()) {
|
|---|
| 1069 |
|
|---|
| 1070 | set_domain_online_request(child->domain);
|
|---|
| 1071 |
|
|---|
| 1072 | if (primary_domain != child->domain) {
|
|---|
| 1073 | /* We need to talk to the primary
|
|---|
| 1074 | * domain as well as the trusted
|
|---|
| 1075 | * domain inside a trusted domain
|
|---|
| 1076 | * child.
|
|---|
| 1077 | * See the code in :
|
|---|
| 1078 | * winbindd_dual_pam_auth_samlogon()
|
|---|
| 1079 | * especially the calling of
|
|---|
| 1080 | * contact_domain = find_our_domain()
|
|---|
| 1081 | * in the non-DC case for details.
|
|---|
| 1082 | */
|
|---|
| 1083 | set_domain_online_request(primary_domain);
|
|---|
| 1084 | }
|
|---|
| 1085 |
|
|---|
| 1086 | child->lockout_policy_event = event_add_timed(
|
|---|
| 1087 | winbind_event_context(), NULL, timeval_zero(),
|
|---|
| 1088 | "account_lockout_policy_handler",
|
|---|
| 1089 | account_lockout_policy_handler,
|
|---|
| 1090 | child);
|
|---|
| 1091 | }
|
|---|
| 1092 |
|
|---|
| 1093 | while (1) {
|
|---|
| 1094 |
|
|---|
| 1095 | int ret;
|
|---|
| 1096 | fd_set read_fds;
|
|---|
| 1097 | struct timeval t;
|
|---|
| 1098 | struct timeval *tp;
|
|---|
| 1099 | struct timeval now;
|
|---|
| 1100 |
|
|---|
| 1101 | /* free up any talloc memory */
|
|---|
| 1102 | lp_TALLOC_FREE();
|
|---|
| 1103 | main_loop_TALLOC_FREE();
|
|---|
| 1104 |
|
|---|
| 1105 | /* check for signals */
|
|---|
| 1106 | winbind_check_sigterm(false);
|
|---|
| 1107 | winbind_check_sighup(override_logfile ? NULL :
|
|---|
| 1108 | child->logfilename);
|
|---|
| 1109 |
|
|---|
| 1110 | run_events(winbind_event_context(), 0, NULL, NULL);
|
|---|
| 1111 |
|
|---|
| 1112 | GetTimeOfDay(&now);
|
|---|
| 1113 |
|
|---|
| 1114 | if (child->domain && child->domain->startup &&
|
|---|
| 1115 | (now.tv_sec > child->domain->startup_time + 30)) {
|
|---|
| 1116 | /* No longer in "startup" mode. */
|
|---|
| 1117 | DEBUG(10,("fork_domain_child: domain %s no longer in 'startup' mode.\n",
|
|---|
| 1118 | child->domain->name ));
|
|---|
| 1119 | child->domain->startup = False;
|
|---|
| 1120 | }
|
|---|
| 1121 |
|
|---|
| 1122 | tp = get_timed_events_timeout(winbind_event_context(), &t);
|
|---|
| 1123 | if (tp) {
|
|---|
| 1124 | DEBUG(11,("select will use timeout of %u.%u seconds\n",
|
|---|
| 1125 | (unsigned int)tp->tv_sec, (unsigned int)tp->tv_usec ));
|
|---|
| 1126 | }
|
|---|
| 1127 |
|
|---|
| 1128 | /* Handle messages */
|
|---|
| 1129 |
|
|---|
| 1130 | message_dispatch();
|
|---|
| 1131 |
|
|---|
| 1132 | FD_ZERO(&read_fds);
|
|---|
| 1133 | FD_SET(state.sock, &read_fds);
|
|---|
| 1134 |
|
|---|
| 1135 | ret = sys_select(state.sock + 1, &read_fds, NULL, NULL, tp);
|
|---|
| 1136 |
|
|---|
| 1137 | if (ret == 0) {
|
|---|
| 1138 | DEBUG(11,("nothing is ready yet, continue\n"));
|
|---|
| 1139 | continue;
|
|---|
| 1140 | }
|
|---|
| 1141 |
|
|---|
| 1142 | if (ret == -1 && errno == EINTR) {
|
|---|
| 1143 | /* We got a signal - continue. */
|
|---|
| 1144 | continue;
|
|---|
| 1145 | }
|
|---|
| 1146 |
|
|---|
| 1147 | if (ret == -1 && errno != EINTR) {
|
|---|
| 1148 | DEBUG(0,("select error occured\n"));
|
|---|
| 1149 | perror("select");
|
|---|
| 1150 | return False;
|
|---|
| 1151 | }
|
|---|
| 1152 |
|
|---|
| 1153 | /* fetch a request from the main daemon */
|
|---|
| 1154 | child_read_request(&state);
|
|---|
| 1155 |
|
|---|
| 1156 | if (state.finished) {
|
|---|
| 1157 | /* we lost contact with our parent */
|
|---|
| 1158 | exit(0);
|
|---|
| 1159 | }
|
|---|
| 1160 |
|
|---|
| 1161 | DEBUG(4,("child daemon request %d\n", (int)state.request.cmd));
|
|---|
| 1162 |
|
|---|
| 1163 | ZERO_STRUCT(state.response);
|
|---|
| 1164 | state.request.null_term = '\0';
|
|---|
| 1165 | child_process_request(child->domain, &state);
|
|---|
| 1166 |
|
|---|
| 1167 | SAFE_FREE(state.request.extra_data.data);
|
|---|
| 1168 |
|
|---|
| 1169 | cache_store_response(sys_getpid(), &state.response);
|
|---|
| 1170 |
|
|---|
| 1171 | SAFE_FREE(state.response.extra_data.data);
|
|---|
| 1172 |
|
|---|
| 1173 | /* We just send the result code back, the result
|
|---|
| 1174 | * structure needs to be fetched via the
|
|---|
| 1175 | * winbindd_cache. Hmm. That needs fixing... */
|
|---|
| 1176 |
|
|---|
| 1177 | if (write_data(state.sock,
|
|---|
| 1178 | (const char *)&state.response.result,
|
|---|
| 1179 | sizeof(state.response.result)) !=
|
|---|
| 1180 | sizeof(state.response.result)) {
|
|---|
| 1181 | DEBUG(0, ("Could not write result\n"));
|
|---|
| 1182 | exit(1);
|
|---|
| 1183 | }
|
|---|
| 1184 | }
|
|---|
| 1185 | }
|
|---|