1/* 2 Unix SMB/CIFS implementation. 3 4 Windows NT Domain nsswitch module 5 6 Copyright (C) Tim Potter 2000 7 Copyright (C) James Peach 2006 8 9 This library is free software; you can redistribute it and/or 10 modify it under the terms of the GNU Library General Public 11 License as published by the Free Software Foundation; either 12 version 2 of the License, or (at your option) any later version. 13 14 This library 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 GNU 17 Library General Public License for more details. 18 19 You should have received a copy of the GNU Library General Public 20 License along with this library; if not, write to the 21 Free Software Foundation, Inc., 59 Temple Place - Suite 330, 22 Boston, MA 02111-1307, USA. 23*/ 24 25#include "winbind_client.h" 26 27#ifndef PRINTF_ATTRIBUTE 28#define PRINTF_ATTRIBUTE(m, n) 29#endif 30 31#ifndef HAVE_ASPRINTF_DECL 32/*PRINTFLIKE2 */ 33int asprintf(char **,const char *, ...) PRINTF_ATTRIBUTE(2,3); 34#endif 35 36#ifdef HAVE_NS_API_H 37#undef VOLATILE 38#undef STATIC 39#undef DYNAMIC 40#include <ns_daemon.h> 41#endif 42 43/* Maximum number of users to pass back over the unix domain socket 44 per call. This is not a static limit on the total number of users 45 or groups returned in total. */ 46 47#define MAX_GETPWENT_USERS 250 48#define MAX_GETGRENT_USERS 250 49 50/* Prototypes from wb_common.c */ 51 52extern int winbindd_fd; 53 54#ifdef HAVE_NS_API_H 55 56/* IRIX version */ 57 58static int send_next_request(nsd_file_t *, struct winbindd_request *); 59static int do_list(int state, nsd_file_t *rq); 60 61static nsd_file_t *current_rq = NULL; 62static int current_winbind_xid = 0; 63static int next_winbind_xid = 0; 64 65typedef struct winbind_xid { 66 int xid; 67 nsd_file_t *rq; 68 struct winbindd_request *request; 69 struct winbind_xid *next; 70} winbind_xid_t; 71 72static winbind_xid_t *winbind_xids = (winbind_xid_t *)0; 73 74static int 75winbind_xid_new(int xid, nsd_file_t *rq, struct winbindd_request *request) 76{ 77 winbind_xid_t *new; 78 79 nsd_logprintf(NSD_LOG_LOW, 80 "entering winbind_xid_new xid = %d rq = 0x%x, request = 0x%x\n", 81 xid, rq, request); 82 new = (winbind_xid_t *)nsd_calloc(1,sizeof(winbind_xid_t)); 83 if (!new) { 84 nsd_logprintf(NSD_LOG_RESOURCE,"winbind_xid_new: failed malloc\n"); 85 return NSD_ERROR; 86 } 87 88 new->xid = xid; 89 new->rq = rq; 90 new->request = request; 91 new->next = winbind_xids; 92 winbind_xids = new; 93 94 return NSD_CONTINUE; 95} 96 97/* 98** This routine will look down the xid list and return the request 99** associated with an xid. We remove the record if it is found. 100*/ 101nsd_file_t * 102winbind_xid_lookup(int xid, struct winbindd_request **requestp) 103{ 104 winbind_xid_t **last, *dx; 105 nsd_file_t *result=0; 106 107 for (last = &winbind_xids, dx = winbind_xids; dx && (dx->xid != xid); 108 last = &dx->next, dx = dx->next); 109 if (dx) { 110 *last = dx->next; 111 result = dx->rq; 112 *requestp = dx->request; 113 SAFE_FREE(dx); 114 } 115 nsd_logprintf(NSD_LOG_LOW, 116 "entering winbind_xid_lookup xid = %d rq = 0x%x, request = 0x%x\n", 117 xid, result, dx->request); 118 119 return result; 120} 121 122static int 123winbind_startnext_timeout(nsd_file_t **rqp, nsd_times_t *to) 124{ 125 nsd_file_t *rq; 126 struct winbindd_request *request; 127 128 nsd_logprintf(NSD_LOG_MIN, "timeout (winbind startnext)\n"); 129 rq = to->t_file; 130 *rqp = rq; 131 nsd_timeout_remove(rq); 132 request = to->t_clientdata; 133 return(send_next_request(rq, request)); 134} 135 136static void 137dequeue_request(void) 138{ 139 nsd_file_t *rq; 140 struct winbindd_request *request; 141 142 /* 143 * Check for queued requests 144 */ 145 if (winbind_xids) { 146 nsd_logprintf(NSD_LOG_MIN, "timeout (winbind) unqueue xid %d\n", 147 current_winbind_xid); 148 rq = winbind_xid_lookup(current_winbind_xid++, &request); 149 /* cause a timeout on the queued request so we can send it */ 150 nsd_timeout_new(rq,1,winbind_startnext_timeout,request); 151 } 152} 153 154static int 155do_request(nsd_file_t *rq, struct winbindd_request *request) 156{ 157 if (winbind_xids == NULL) { 158 /* 159 * No outstanding requests. 160 * Send off the request to winbindd 161 */ 162 nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) sending request\n"); 163 return(send_next_request(rq, request)); 164 } else { 165 /* 166 * Just queue it up for now - previous callout or timout 167 * will start it up 168 */ 169 nsd_logprintf(NSD_LOG_MIN, 170 "lookup (winbind): queue request xid = %d\n", 171 next_winbind_xid); 172 return(winbind_xid_new(next_winbind_xid++, rq, request)); 173 } 174} 175 176static int 177winbind_callback(nsd_file_t **rqp, int fd) 178{ 179 struct winbindd_response response; 180 nsd_file_t *rq; 181 NSS_STATUS status; 182 char * result = NULL; 183 size_t rlen; 184 185 dequeue_request(); 186 187 nsd_logprintf(NSD_LOG_MIN, "entering callback (winbind)\n"); 188 189 rq = current_rq; 190 *rqp = rq; 191 192 nsd_timeout_remove(rq); 193 nsd_callback_remove(fd); 194 195 ZERO_STRUCT(response); 196 status = winbindd_get_response(&response); 197 198 if (status != NSS_STATUS_SUCCESS) { 199 /* free any extra data area in response structure */ 200 free_response(&response); 201 nsd_logprintf(NSD_LOG_MIN, 202 "callback (winbind) returning not found, status = %d\n", 203 status); 204 205 switch (status) { 206 case NSS_STATUS_UNAVAIL: 207 rq->f_status = NS_UNAVAIL; 208 break; 209 case NSS_STATUS_TRYAGAIN: 210 rq->f_status = NS_TRYAGAIN; 211 break; 212 case NSS_STATUS_NOTFOUND: 213 /* FALLTHRU */ 214 default: 215 rq->f_status = NS_NOTFOUND; 216 } 217 218 return NSD_NEXT; 219 } 220 221 switch ((int)rq->f_cmd_data) { 222 case WINBINDD_WINS_BYNAME: 223 case WINBINDD_WINS_BYIP: 224 nsd_logprintf(NSD_LOG_MIN, 225 "callback (winbind) WINS_BYNAME | WINS_BYIP\n"); 226 227 rlen = asprintf(&result, "%s\n", response.data.winsresp); 228 if (rlen == 0 || result == NULL) { 229 return NSD_ERROR; 230 } 231 232 free_response(&response); 233 234 nsd_logprintf(NSD_LOG_MIN, " %s\n", result); 235 nsd_set_result(rq, NS_SUCCESS, result, rlen, DYNAMIC); 236 return NSD_OK; 237 238 case WINBINDD_GETPWUID: 239 case WINBINDD_GETPWNAM: 240 { 241 struct winbindd_pw *pw = &response.data.pw; 242 243 nsd_logprintf(NSD_LOG_MIN, 244 "callback (winbind) GETPWUID | GETPWUID\n"); 245 246 rlen = asprintf(&result,"%s:%s:%d:%d:%s:%s:%s\n", 247 pw->pw_name, 248 pw->pw_passwd, 249 pw->pw_uid, 250 pw->pw_gid, 251 pw->pw_gecos, 252 pw->pw_dir, 253 pw->pw_shell); 254 if (rlen == 0 || result == NULL) 255 return NSD_ERROR; 256 257 free_response(&response); 258 259 nsd_logprintf(NSD_LOG_MIN, " %s\n", result); 260 nsd_set_result(rq, NS_SUCCESS, result, rlen, DYNAMIC); 261 return NSD_OK; 262 } 263 264 case WINBINDD_GETGRNAM: 265 case WINBINDD_GETGRGID: 266 { 267 const struct winbindd_gr *gr = &response.data.gr; 268 const char * members; 269 270 nsd_logprintf(NSD_LOG_MIN, 271 "callback (winbind) GETGRNAM | GETGRGID\n"); 272 273 if (gr->num_gr_mem && response.extra_data.data) { 274 members = response.extra_data.data; 275 } else { 276 members = ""; 277 } 278 279 rlen = asprintf(&result, "%s:%s:%d:%s\n", 280 gr->gr_name, gr->gr_passwd, gr->gr_gid, members); 281 if (rlen == 0 || result == NULL) 282 return NSD_ERROR; 283 284 free_response(&response); 285 286 nsd_logprintf(NSD_LOG_MIN, " %s\n", result); 287 nsd_set_result(rq, NS_SUCCESS, result, rlen, DYNAMIC); 288 return NSD_OK; 289 } 290 291 case WINBINDD_SETGRENT: 292 case WINBINDD_SETPWENT: 293 nsd_logprintf(NSD_LOG_MIN, 294 "callback (winbind) SETGRENT | SETPWENT\n"); 295 free_response(&response); 296 return(do_list(1,rq)); 297 298 case WINBINDD_GETGRENT: 299 case WINBINDD_GETGRLST: 300 { 301 int entries; 302 303 nsd_logprintf(NSD_LOG_MIN, 304 "callback (winbind) GETGRENT | GETGRLIST %d responses\n", 305 response.data.num_entries); 306 307 if (response.data.num_entries) { 308 const struct winbindd_gr *gr = &response.data.gr; 309 const char * members; 310 fstring grp_name; 311 int i; 312 313 gr = (struct winbindd_gr *)response.extra_data.data; 314 if (! gr ) { 315 nsd_logprintf(NSD_LOG_MIN, " no extra_data\n"); 316 free_response(&response); 317 return NSD_ERROR; 318 } 319 320 members = (char *)response.extra_data.data + 321 (response.data.num_entries * sizeof(struct winbindd_gr)); 322 323 for (i = 0; i < response.data.num_entries; i++) { 324 snprintf(grp_name, sizeof(grp_name) - 1, "%s:%s:%d:", 325 gr->gr_name, gr->gr_passwd, gr->gr_gid); 326 327 nsd_append_element(rq, NS_SUCCESS, result, rlen); 328 nsd_append_result(rq, NS_SUCCESS, 329 &members[gr->gr_mem_ofs], 330 strlen(&members[gr->gr_mem_ofs])); 331 332 /* Don't log the whole list, because it might be 333 * _really_ long and we probably don't want to clobber 334 * the log with it. 335 */ 336 nsd_logprintf(NSD_LOG_MIN, " %s (...)\n", grp_name); 337 338 gr++; 339 } 340 } 341 342 entries = response.data.num_entries; 343 free_response(&response); 344 if (entries < MAX_GETPWENT_USERS) 345 return(do_list(2,rq)); 346 else 347 return(do_list(1,rq)); 348 } 349 350 case WINBINDD_GETPWENT: 351 { 352 int entries; 353 354 nsd_logprintf(NSD_LOG_MIN, 355 "callback (winbind) GETPWENT %d responses\n", 356 response.data.num_entries); 357 358 if (response.data.num_entries) { 359 struct winbindd_pw *pw = &response.data.pw; 360 int i; 361 362 pw = (struct winbindd_pw *)response.extra_data.data; 363 if (! pw ) { 364 nsd_logprintf(NSD_LOG_MIN, " no extra_data\n"); 365 free_response(&response); 366 return NSD_ERROR; 367 } 368 for (i = 0; i < response.data.num_entries; i++) { 369 result = NULL; 370 rlen = asprintf(&result, "%s:%s:%d:%d:%s:%s:%s", 371 pw->pw_name, 372 pw->pw_passwd, 373 pw->pw_uid, 374 pw->pw_gid, 375 pw->pw_gecos, 376 pw->pw_dir, 377 pw->pw_shell); 378 379 if (rlen != 0 && result != NULL) { 380 nsd_logprintf(NSD_LOG_MIN, " %s\n",result); 381 nsd_append_element(rq, NS_SUCCESS, result, rlen); 382 free(result); 383 } 384 385 pw++; 386 } 387 } 388 389 entries = response.data.num_entries; 390 free_response(&response); 391 if (entries < MAX_GETPWENT_USERS) 392 return(do_list(2,rq)); 393 else 394 return(do_list(1,rq)); 395 } 396 397 case WINBINDD_ENDGRENT: 398 case WINBINDD_ENDPWENT: 399 nsd_logprintf(NSD_LOG_MIN, "callback (winbind) ENDGRENT | ENDPWENT\n"); 400 nsd_append_element(rq, NS_SUCCESS, "\n", 1); 401 free_response(&response); 402 return NSD_NEXT; 403 404 default: 405 free_response(&response); 406 nsd_logprintf(NSD_LOG_MIN, "callback (winbind) invalid command %d\n", (int)rq->f_cmd_data); 407 return NSD_NEXT; 408 } 409} 410 411static int 412winbind_timeout(nsd_file_t **rqp, nsd_times_t *to) 413{ 414 nsd_file_t *rq; 415 416 dequeue_request(); 417 418 nsd_logprintf(NSD_LOG_MIN, "timeout (winbind)\n"); 419 420 rq = to->t_file; 421 *rqp = rq; 422 423 /* Remove the callback and timeout */ 424 nsd_callback_remove(winbindd_fd); 425 nsd_timeout_remove(rq); 426 427 rq->f_status = NS_NOTFOUND; 428 return NSD_NEXT; 429} 430 431static int 432send_next_request(nsd_file_t *rq, struct winbindd_request *request) 433{ 434 NSS_STATUS status; 435 long timeout; 436 437 switch (rq->f_index) { 438 case LOOKUP: 439 timeout = nsd_attr_fetch_long(rq->f_attrs, 440 "lookup_timeout", 10, 10); 441 break; 442 case LIST: 443 timeout = nsd_attr_fetch_long(rq->f_attrs, 444 "list_timeout", 10, 10); 445 break; 446 default: 447 nsd_logprintf(NSD_LOG_OPER, 448 "send_next_request (winbind) " 449 "invalid request type %d\n", rq->f_index); 450 rq->f_status = NS_BADREQ; 451 return NSD_NEXT; 452 } 453 454 nsd_logprintf(NSD_LOG_MIN, 455 "send_next_request (winbind) %d, timeout = %d sec\n", 456 rq->f_cmd_data, timeout); 457 status = winbindd_send_request((int)rq->f_cmd_data,0,request); 458 SAFE_FREE(request); 459 460 if (status != NSS_STATUS_SUCCESS) { 461 nsd_logprintf(NSD_LOG_MIN, 462 "send_next_request (winbind) error status = %d\n", 463 status); 464 rq->f_status = status; 465 return NSD_NEXT; 466 } 467 468 current_rq = rq; 469 470 /* 471 * Set up callback and timeouts 472 */ 473 nsd_logprintf(NSD_LOG_MIN, "send_next_request (winbind) fd = %d\n", 474 winbindd_fd); 475 476 nsd_callback_new(winbindd_fd, winbind_callback, NSD_READ); 477 nsd_timeout_new(rq, timeout * 1000, winbind_timeout, NULL); 478 return NSD_CONTINUE; 479} 480 481int init(void) 482{ 483 nsd_logprintf(NSD_LOG_MIN, "entering init (winbind)\n"); 484 return(NSD_OK); 485} 486 487int lookup(nsd_file_t *rq) 488{ 489 char *map; 490 char *key; 491 struct winbindd_request *request; 492 493 nsd_logprintf(NSD_LOG_MIN, "entering lookup (winbind)\n"); 494 if (! rq) 495 return NSD_ERROR; 496 497 map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0); 498 key = nsd_attr_fetch_string(rq->f_attrs, "key", (char*)0); 499 if (! map || ! key) { 500 nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) table or key not defined\n"); 501 rq->f_status = NS_BADREQ; 502 return NSD_ERROR; 503 } 504 505 nsd_logprintf(NSD_LOG_MIN, "lookup (winbind %s)\n",map); 506 507 request = (struct winbindd_request *)nsd_calloc(1,sizeof(struct winbindd_request)); 508 if (! request) { 509 nsd_logprintf(NSD_LOG_RESOURCE, 510 "lookup (winbind): failed malloc\n"); 511 return NSD_ERROR; 512 } 513 514 if (strcasecmp(map,"passwd.byuid") == 0) { 515 request->data.uid = atoi(key); 516 rq->f_cmd_data = (void *)WINBINDD_GETPWUID; 517 } else if (strcasecmp(map,"passwd.byname") == 0) { 518 strncpy(request->data.username, key, 519 sizeof(request->data.username) - 1); 520 request->data.username[sizeof(request->data.username) - 1] = '\0'; 521 rq->f_cmd_data = (void *)WINBINDD_GETPWNAM; 522 } else if (strcasecmp(map,"group.byname") == 0) { 523 strncpy(request->data.groupname, key, 524 sizeof(request->data.groupname) - 1); 525 request->data.groupname[sizeof(request->data.groupname) - 1] = '\0'; 526 rq->f_cmd_data = (void *)WINBINDD_GETGRNAM; 527 } else if (strcasecmp(map,"group.bygid") == 0) { 528 request->data.gid = atoi(key); 529 rq->f_cmd_data = (void *)WINBINDD_GETGRGID; 530 } else if (strcasecmp(map,"hosts.byname") == 0) { 531 strncpy(request->data.winsreq, key, sizeof(request->data.winsreq) - 1); 532 request->data.winsreq[sizeof(request->data.winsreq) - 1] = '\0'; 533 rq->f_cmd_data = (void *)WINBINDD_WINS_BYNAME; 534 } else if (strcasecmp(map,"hosts.byaddr") == 0) { 535 strncpy(request->data.winsreq, key, sizeof(request->data.winsreq) - 1); 536 request->data.winsreq[sizeof(request->data.winsreq) - 1] = '\0'; 537 rq->f_cmd_data = (void *)WINBINDD_WINS_BYIP; 538 } else { 539 /* 540 * Don't understand this map - just return not found 541 */ 542 nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) unknown table\n"); 543 SAFE_FREE(request); 544 rq->f_status = NS_NOTFOUND; 545 return NSD_NEXT; 546 } 547 548 return(do_request(rq, request)); 549} 550 551int list(nsd_file_t *rq) 552{ 553 char *map; 554 555 nsd_logprintf(NSD_LOG_MIN, "entering list (winbind)\n"); 556 if (! rq) 557 return NSD_ERROR; 558 559 map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0); 560 if (! map ) { 561 nsd_logprintf(NSD_LOG_MIN, "list (winbind) table not defined\n"); 562 rq->f_status = NS_BADREQ; 563 return NSD_ERROR; 564 } 565 566 nsd_logprintf(NSD_LOG_MIN, "list (winbind %s)\n",map); 567 568 return (do_list(0,rq)); 569} 570 571static int 572do_list(int state, nsd_file_t *rq) 573{ 574 char *map; 575 struct winbindd_request *request; 576 577 nsd_logprintf(NSD_LOG_MIN, "entering do_list (winbind) state = %d\n",state); 578 579 map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0); 580 request = (struct winbindd_request *)nsd_calloc(1,sizeof(struct winbindd_request)); 581 if (! request) { 582 nsd_logprintf(NSD_LOG_RESOURCE, 583 "do_list (winbind): failed malloc\n"); 584 return NSD_ERROR; 585 } 586 587 if (strcasecmp(map,"passwd.byname") == 0) { 588 switch (state) { 589 case 0: 590 rq->f_cmd_data = (void *)WINBINDD_SETPWENT; 591 break; 592 case 1: 593 request->data.num_entries = MAX_GETPWENT_USERS; 594 rq->f_cmd_data = (void *)WINBINDD_GETPWENT; 595 break; 596 case 2: 597 rq->f_cmd_data = (void *)WINBINDD_ENDPWENT; 598 break; 599 default: 600 nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown state\n"); 601 SAFE_FREE(request); 602 rq->f_status = NS_NOTFOUND; 603 return NSD_NEXT; 604 } 605 } else if (strcasecmp(map,"group.byname") == 0) { 606 switch (state) { 607 case 0: 608 rq->f_cmd_data = (void *)WINBINDD_SETGRENT; 609 break; 610 case 1: 611 request->data.num_entries = MAX_GETGRENT_USERS; 612 rq->f_cmd_data = (void *)WINBINDD_GETGRENT; 613 break; 614 case 2: 615 rq->f_cmd_data = (void *)WINBINDD_ENDGRENT; 616 break; 617 default: 618 nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown state\n"); 619 SAFE_FREE(request); 620 rq->f_status = NS_NOTFOUND; 621 return NSD_NEXT; 622 } 623 } else { 624 /* 625 * Don't understand this map - just return not found 626 */ 627 nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown table\n"); 628 SAFE_FREE(request); 629 rq->f_status = NS_NOTFOUND; 630 return NSD_NEXT; 631 } 632 633 return(do_request(rq, request)); 634} 635 636#endif /* HAVE_NS_API_H */ 637