preparing for release of 2.2.3a
[Samba.git] / source / nsswitch / winbindd.c
blob5dc28a43695b0fdd28d33c87f054f9d353f28e54
1 /*
2 Unix SMB/Netbios implementation.
3 Version 3.0
5 Winbind daemon for ntdom nss module
7 Copyright (C) by Tim Potter 2000, 2001
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.
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.
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.
24 #include "winbindd.h"
26 pstring servicesf = CONFIGFILE;
28 /* List of all connected clients */
30 struct winbindd_cli_state *client_list;
31 static int num_clients;
33 /* Reload configuration */
35 static BOOL reload_services_file(BOOL test)
37 BOOL ret;
38 pstring logfile;
40 if (lp_loaded()) {
41 pstring fname;
43 pstrcpy(fname,lp_configfile());
44 if (file_exist(fname,NULL) && !strcsequal(fname,servicesf)) {
45 pstrcpy(servicesf,fname);
46 test = False;
50 snprintf(logfile, sizeof(logfile), "%s/log.winbindd", LOGFILEBASE);
51 lp_set_logfile(logfile);
52 reopen_logs();
54 ret = lp_load(servicesf,False,False,True);
56 snprintf(logfile, sizeof(logfile), "%s/log.winbindd", LOGFILEBASE);
57 lp_set_logfile(logfile);
58 reopen_logs();
59 load_interfaces();
61 return(ret);
64 #if DUMP_CORE
66 /**************************************************************************** **
67 Prepare to dump a core file - carefully!
68 **************************************************************************** */
70 static BOOL dump_core(void)
72 char *p;
73 pstring dname;
74 pstrcpy( dname, lp_logfile() );
75 if ((p=strrchr(dname,'/')))
76 *p=0;
77 pstrcat( dname, "/corefiles" );
78 mkdir( dname, 0700 );
79 sys_chown( dname, getuid(), getgid() );
80 chmod( dname, 0700 );
81 if ( chdir(dname) )
82 return( False );
83 umask( ~(0700) );
85 #ifdef HAVE_GETRLIMIT
86 #ifdef RLIMIT_CORE
88 struct rlimit rlp;
89 getrlimit( RLIMIT_CORE, &rlp );
90 rlp.rlim_cur = MAX( 4*1024*1024, rlp.rlim_cur );
91 setrlimit( RLIMIT_CORE, &rlp );
92 getrlimit( RLIMIT_CORE, &rlp );
93 DEBUG( 3, ( "Core limits now %d %d\n", (int)rlp.rlim_cur, (int)rlp.rlim_max ) );
95 #endif
96 #endif
98 DEBUG(0,("Dumping core in %s\n",dname));
99 abort();
100 return( True );
101 } /* dump_core */
102 #endif
104 /**************************************************************************** **
105 Handle a fault..
106 **************************************************************************** */
108 static void fault_quit(void)
110 #if DUMP_CORE
111 dump_core();
112 #endif
115 static void winbindd_status(void)
117 struct winbindd_cli_state *tmp;
119 DEBUG(0, ("winbindd status:\n"));
121 /* Print client state information */
123 DEBUG(0, ("\t%d clients currently active\n", num_clients));
125 if (DEBUGLEVEL >= 2 && num_clients) {
126 DEBUG(2, ("\tclient list:\n"));
127 for(tmp = client_list; tmp; tmp = tmp->next) {
128 DEBUG(2, ("\t\tpid %d, sock %d, rbl %d, wbl %d\n",
129 tmp->pid, tmp->sock, tmp->read_buf_len,
130 tmp->write_buf_len));
135 /* Print winbindd status to log file */
137 static void print_winbindd_status(void)
139 winbindd_status();
140 winbindd_idmap_status();
141 winbindd_cache_status();
142 winbindd_cm_status();
145 /* Flush client cache */
147 static void flush_caches(void)
149 /* Clear cached user and group enumation info */
151 winbindd_flush_cache();
154 /* Handle the signal by unlinking socket and exiting */
156 static void terminate(void)
158 pstring path;
160 /* Close idmap. */
161 winbindd_idmap_close();
163 /* Remove socket file */
164 snprintf(path, sizeof(path), "%s/%s",
165 WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME);
166 unlink(path);
167 exit(0);
170 static BOOL do_sigterm;
172 static void termination_handler(int signum)
174 do_sigterm = True;
175 sys_select_signal();
178 static BOOL do_sigusr1;
180 static void sigusr1_handler(int signum)
182 do_sigusr1 = True;
183 sys_select_signal();
186 static BOOL do_sighup;
188 static void sighup_handler(int signum)
190 do_sighup = True;
191 sys_select_signal();
194 /* Create winbindd socket */
196 static int create_sock(void)
198 struct sockaddr_un sunaddr;
199 struct stat st;
200 int sock;
201 mode_t old_umask;
202 pstring path;
204 /* Create the socket directory or reuse the existing one */
206 if (lstat(WINBINDD_SOCKET_DIR, &st) == -1) {
208 if (errno == ENOENT) {
210 /* Create directory */
212 if (mkdir(WINBINDD_SOCKET_DIR, 0755) == -1) {
213 DEBUG(0, ("error creating socket directory "
214 "%s: %s\n", WINBINDD_SOCKET_DIR,
215 strerror(errno)));
216 return -1;
219 } else {
221 DEBUG(0, ("lstat failed on socket directory %s: %s\n",
222 WINBINDD_SOCKET_DIR, strerror(errno)));
223 return -1;
226 } else {
228 /* Check ownership and permission on existing directory */
230 if (!S_ISDIR(st.st_mode)) {
231 DEBUG(0, ("socket directory %s isn't a directory\n",
232 WINBINDD_SOCKET_DIR));
233 return -1;
236 if ((st.st_uid != sec_initial_uid()) ||
237 ((st.st_mode & 0777) != 0755)) {
238 DEBUG(0, ("invalid permissions on socket directory "
239 "%s\n", WINBINDD_SOCKET_DIR));
240 return -1;
244 /* Create the socket file */
246 old_umask = umask(0);
248 sock = socket(AF_UNIX, SOCK_STREAM, 0);
250 if (sock == -1) {
251 perror("socket");
252 return -1;
255 snprintf(path, sizeof(path), "%s/%s",
256 WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME);
258 unlink(path);
259 memset(&sunaddr, 0, sizeof(sunaddr));
260 sunaddr.sun_family = AF_UNIX;
261 safe_strcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path)-1);
263 if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1) {
264 DEBUG(0, ("bind failed on winbind socket %s: %s\n",
265 path,
266 strerror(errno)));
267 close(sock);
268 return -1;
271 if (listen(sock, 5) == -1) {
272 DEBUG(0, ("listen failed on winbind socket %s: %s\n",
273 path,
274 strerror(errno)));
275 close(sock);
276 return -1;
279 umask(old_umask);
281 /* Success! */
283 return sock;
286 struct dispatch_table {
287 enum winbindd_cmd cmd;
288 enum winbindd_result (*fn)(struct winbindd_cli_state *state);
289 char *winbindd_cmd_name;
292 static struct dispatch_table dispatch_table[] = {
294 /* User functions */
296 { WINBINDD_GETPWNAM_FROM_USER, winbindd_getpwnam_from_user, "GETPWNAM_FROM_USER" },
297 { WINBINDD_GETPWNAM_FROM_UID, winbindd_getpwnam_from_uid, "GETPWNAM_FROM_UID" },
299 { WINBINDD_SETPWENT, winbindd_setpwent, "SETPWENT" },
300 { WINBINDD_ENDPWENT, winbindd_endpwent, "ENDPWENT" },
301 { WINBINDD_GETPWENT, winbindd_getpwent, "GETPWENT" },
303 { WINBINDD_GETGROUPS, winbindd_getgroups, "GETGROUPS" },
305 /* Group functions */
307 { WINBINDD_GETGRNAM_FROM_GROUP, winbindd_getgrnam_from_group, "GETGRNAM_FROM_GROUP" },
308 { WINBINDD_GETGRNAM_FROM_GID, winbindd_getgrnam_from_gid, "GETGRNAM_FROM_GID" },
309 { WINBINDD_SETGRENT, winbindd_setgrent, "SETGRENT" },
310 { WINBINDD_ENDGRENT, winbindd_endgrent, "ENDGRENT" },
311 { WINBINDD_GETGRENT, winbindd_getgrent, "GETGRENT" },
313 /* PAM auth functions */
315 { WINBINDD_PAM_AUTH, winbindd_pam_auth, "PAM_AUTH" },
316 #if ALLOW_WINBIND_AUTH_CRAP
317 { WINBINDD_PAM_AUTH_CRAP, winbindd_pam_auth_crap, "AUTH_CRAP" },
318 #endif
319 { WINBINDD_PAM_CHAUTHTOK, winbindd_pam_chauthtok, "CHAUTHTOK" },
321 /* Enumeration functions */
323 { WINBINDD_LIST_USERS, winbindd_list_users, "LIST_USERS" },
324 { WINBINDD_LIST_GROUPS, winbindd_list_groups, "LIST_GROUPS" },
325 { WINBINDD_LIST_TRUSTDOM, winbindd_list_trusted_domains, "LIST_TRUSTDOM" },
327 /* SID related functions */
329 { WINBINDD_LOOKUPSID, winbindd_lookupsid, "LOOKUPSID" },
330 { WINBINDD_LOOKUPNAME, winbindd_lookupname, "LOOKUPNAME" },
332 /* Lookup related functions */
334 { WINBINDD_SID_TO_UID, winbindd_sid_to_uid, "SID_TO_UID" },
335 { WINBINDD_SID_TO_GID, winbindd_sid_to_gid, "SID_TO_GID" },
336 { WINBINDD_GID_TO_SID, winbindd_gid_to_sid, "GID_TO_SID" },
337 { WINBINDD_UID_TO_SID, winbindd_uid_to_sid, "UID_TO_SID" },
339 /* Miscellaneous */
341 { WINBINDD_CHECK_MACHACC, winbindd_check_machine_acct, "CHECK_MACHACC" },
343 /* WINS functions */
345 { WINBINDD_WINS_BYNAME, winbindd_wins_byname, "WINS_BYNAME" },
346 { WINBINDD_WINS_BYIP, winbindd_wins_byip, "WINS_BYIP" },
348 /* End of list */
350 { WINBINDD_NUM_CMDS, NULL, "NONE" }
353 static void process_request(struct winbindd_cli_state *state)
355 struct dispatch_table *table = dispatch_table;
357 /* Free response data - we may be interrupted and receive another
358 command before being able to send this data off. */
360 SAFE_FREE(state->response.extra_data);
362 ZERO_STRUCT(state->response);
364 state->response.result = WINBINDD_ERROR;
365 state->response.length = sizeof(struct winbindd_response);
367 /* Process command */
369 for (table = dispatch_table; table->fn; table++) {
370 if (state->request.cmd == table->cmd) {
371 DEBUG(10,("process_request: request fn %s\n", table->winbindd_cmd_name ));
372 state->response.result = table->fn(state);
373 break;
377 if (!table->fn)
378 DEBUG(10,("process_request: unknown request fn number %d\n", (int)state->request.cmd ));
380 /* In case extra data pointer is NULL */
382 if (!state->response.extra_data)
383 state->response.length = sizeof(struct winbindd_response);
386 /* Process a new connection by adding it to the client connection list */
388 static void new_connection(int accept_sock)
390 struct sockaddr_un sunaddr;
391 struct winbindd_cli_state *state;
392 socklen_t len;
393 int sock;
395 /* Accept connection */
397 len = sizeof(sunaddr);
399 do {
400 sock = accept(accept_sock, (struct sockaddr *)&sunaddr, &len);
401 } while (sock == -1 && errno == EINTR);
403 if (sock == -1)
404 return;
406 DEBUG(6,("accepted socket %d\n", sock));
408 /* Create new connection structure */
410 if ((state = (struct winbindd_cli_state *)
411 malloc(sizeof(*state))) == NULL)
412 return;
414 ZERO_STRUCTP(state);
415 state->sock = sock;
417 /* Add to connection list */
419 DLIST_ADD(client_list, state);
420 num_clients++;
423 /* Remove a client connection from client connection list */
425 static void remove_client(struct winbindd_cli_state *state)
427 /* It's a dead client - hold a funeral */
429 if (state != NULL) {
431 /* Close socket */
433 close(state->sock);
435 /* Free any getent state */
437 free_getent_state(state->getpwent_state);
438 free_getent_state(state->getgrent_state);
440 /* We may have some extra data that was not freed if the
441 client was killed unexpectedly */
443 SAFE_FREE(state->response.extra_data);
445 /* Remove from list and free */
447 DLIST_REMOVE(client_list, state);
448 SAFE_FREE(state);
449 num_clients--;
453 /* Process a complete received packet from a client */
455 static void process_packet(struct winbindd_cli_state *state)
457 /* Process request */
459 state->pid = state->request.pid;
461 process_request(state);
463 /* Update client state */
465 state->read_buf_len = 0;
466 state->write_buf_len = sizeof(struct winbindd_response);
469 /* Read some data from a client connection */
471 static void client_read(struct winbindd_cli_state *state)
473 int n;
475 /* Read data */
477 do {
478 n = read(state->sock, state->read_buf_len + (char *)&state->request,
479 sizeof(state->request) - state->read_buf_len);
480 } while (n == -1 && errno == EINTR);
482 DEBUG(10,("client_read: read %d bytes. Need %d more for a full request.\n", n,
483 sizeof(state->request) - n - state->read_buf_len ));
485 /* Read failed, kill client */
487 if (n == -1 || n == 0) {
488 DEBUG(5,("read failed on sock %d, pid %d: %s\n",
489 state->sock, state->pid,
490 (n == -1) ? strerror(errno) : "EOF"));
492 state->finished = True;
493 return;
496 /* Update client state */
498 state->read_buf_len += n;
501 /* Write some data to a client connection */
503 static void client_write(struct winbindd_cli_state *state)
505 char *data;
506 int num_written;
508 /* Write some data */
510 if (!state->write_extra_data) {
512 /* Write response structure */
514 data = (char *)&state->response + sizeof(state->response) -
515 state->write_buf_len;
517 } else {
519 /* Write extra data */
521 data = (char *)state->response.extra_data +
522 state->response.length -
523 sizeof(struct winbindd_response) -
524 state->write_buf_len;
527 do {
528 num_written = write(state->sock, data, state->write_buf_len);
529 } while (num_written == -1 && errno == EINTR);
531 DEBUG(10,("client_write: wrote %d bytes.\n", num_written ));
533 /* Write failed, kill cilent */
535 if (num_written == -1 || num_written == 0) {
537 DEBUG(3,("write failed on sock %d, pid %d: %s\n",
538 state->sock, state->pid,
539 (num_written == -1) ? strerror(errno) : "EOF"));
541 state->finished = True;
543 SAFE_FREE(state->response.extra_data);
545 return;
548 /* Update client state */
550 state->write_buf_len -= num_written;
552 /* Have we written all data? */
554 if (state->write_buf_len == 0) {
556 /* Take care of extra data */
558 if (state->write_extra_data) {
560 SAFE_FREE(state->response.extra_data);
562 state->write_extra_data = False;
564 DEBUG(10,("client_write: client_write: complete response written.\n"));
566 } else if (state->response.length >
567 sizeof(struct winbindd_response)) {
569 /* Start writing extra data */
571 state->write_buf_len =
572 state->response.length -
573 sizeof(struct winbindd_response);
575 DEBUG(10,("client_write: need to write %d extra data bytes.\n", (int)state->write_buf_len));
577 state->write_extra_data = True;
582 /* Process incoming clients on accept_sock. We use a tricky non-blocking,
583 non-forking, non-threaded model which allows us to handle many
584 simultaneous connections while remaining impervious to many denial of
585 service attacks. */
587 static void process_loop(int accept_sock)
589 /* We'll be doing this a lot */
591 while (1) {
592 struct winbindd_cli_state *state;
593 fd_set r_fds, w_fds;
594 int maxfd = accept_sock, selret;
595 struct timeval timeout;
597 /* Free up temporary memory */
599 lp_talloc_free();
601 /* Initialise fd lists for select() */
603 FD_ZERO(&r_fds);
604 FD_ZERO(&w_fds);
605 FD_SET(accept_sock, &r_fds);
607 timeout.tv_sec = WINBINDD_ESTABLISH_LOOP;
608 timeout.tv_usec = 0;
610 /* Set up client readers and writers */
612 state = client_list;
614 while (state) {
616 /* Dispose of client connection if it is marked as
617 finished */
619 if (state->finished) {
620 struct winbindd_cli_state *next = state->next;
622 remove_client(state);
623 state = next;
624 continue;
627 /* Select requires we know the highest fd used */
629 if (state->sock > maxfd)
630 maxfd = state->sock;
632 /* Add fd for reading */
634 if (state->read_buf_len != sizeof(state->request))
635 FD_SET(state->sock, &r_fds);
637 /* Add fd for writing */
639 if (state->write_buf_len)
640 FD_SET(state->sock, &w_fds);
642 state = state->next;
645 /* Call select */
647 selret = sys_select(maxfd + 1, &r_fds, &w_fds, NULL, &timeout);
649 if (selret == 0)
650 continue;
652 if ((selret == -1 && errno != EINTR) || selret == 0) {
654 /* Select error, something is badly wrong */
656 perror("select");
657 exit(1);
660 /* Create a new connection if accept_sock readable */
662 if (selret > 0) {
664 if (FD_ISSET(accept_sock, &r_fds))
665 new_connection(accept_sock);
667 /* Process activity on client connections */
669 for (state = client_list; state; state = state->next) {
671 /* Data available for reading */
673 if (FD_ISSET(state->sock, &r_fds)) {
675 /* Read data */
677 client_read(state);
679 #if 0
680 /* JRA - currently there's no length field in the request... */
682 * If we have the start of a
683 * packet, then check the
684 * length field to make sure
685 * the client's not talking
686 * Mock Swedish.
689 if (state->read_buf_len >= sizeof(int)
690 && *(int *) state->buf != sizeof(state->request)) {
692 struct winbindd_cli_state *rem_state = state;
694 DEBUG(0,("process_loop: Invalid request size (%d) send, should be (%d)\n",
695 *(int *) rem_state->buf, sizeof(rem_state->request) ));
697 state = state_next;
698 remove_client(rem_state);
699 continue;
701 #endif
703 /* A request packet might be
704 complete */
706 if (state->read_buf_len ==
707 sizeof(state->request)) {
708 process_packet(state);
712 /* Data available for writing */
714 if (FD_ISSET(state->sock, &w_fds))
715 client_write(state);
719 /* Check signal handling things */
721 if (do_sigterm)
722 terminate();
724 if (do_sighup) {
726 /* Flush winbindd cache */
728 flush_caches();
729 reload_services_file(True);
730 do_sighup = False;
733 if (do_sigusr1) {
734 print_winbindd_status();
735 do_sigusr1 = False;
740 /* Main function */
742 struct winbindd_state server_state; /* Server state information */
744 int main(int argc, char **argv)
746 extern pstring global_myname;
747 extern fstring global_myworkgroup;
748 extern BOOL append_log;
749 pstring logfile;
750 int accept_sock;
751 BOOL interactive = False;
752 int opt, new_debuglevel = -1;
754 /* glibc (?) likes to print "User defined signal 1" and exit if a
755 SIGUSR1 is received before a handler is installed */
757 CatchSignal(SIGUSR1, SIG_IGN);
759 TimeInit();
761 charset_initialise(); /* For *&#^%'s sake don't remove this */
763 fault_setup((void (*)(void *))fault_quit );
765 /* Initialise for running in non-root mode */
767 sec_init();
769 /* Set environment variable so we don't recursively call ourselves.
770 This may also be useful interactively. */
772 SETENV(WINBINDD_DONT_ENV, "1", 1);
774 /* Initialise samba/rpc client stuff */
776 while ((opt = getopt(argc, argv, "id:s:")) != EOF) {
777 switch (opt) {
779 /* Don't become a daemon */
781 case 'i':
782 interactive = True;
783 break;
785 /* Run with specified debug level */
787 case 'd':
788 new_debuglevel = atoi(optarg);
789 break;
791 /* Load a different smb.conf file */
793 case 's':
794 pstrcpy(servicesf,optarg);
795 break;
797 default:
798 printf("Unknown option %c\n", (char)opt);
799 exit(1);
804 /* Append to log file by default as we are a single process daemon
805 program. */
807 append_log = True;
809 snprintf(logfile, sizeof(logfile), "%s/log.winbindd", LOGFILEBASE);
810 lp_set_logfile(logfile);
811 setup_logging("winbindd", interactive);
812 reopen_logs();
814 DEBUG(1, ("winbindd version %s started.\n", VERSION ) );
815 DEBUGADD( 1, ( "Copyright The Samba Team 2000-2001\n" ) );
817 if (!reload_services_file(False)) {
818 DEBUG(0, ("error opening config file\n"));
819 exit(1);
822 codepage_initialise(lp_client_code_page());
824 /* Setup names. */
825 if (!*global_myname) {
826 char *p;
828 fstrcpy(global_myname, myhostname());
829 p = strchr(global_myname, '.');
830 if (p)
831 *p = 0;
834 fstrcpy(global_myworkgroup, lp_workgroup());
836 if (new_debuglevel != -1)
837 DEBUGLEVEL = new_debuglevel;
839 if (!interactive)
840 become_daemon();
842 #if HAVE_SETPGID
844 * If we're interactive we want to set our own process group for
845 * signal management.
847 if (interactive)
848 setpgid( (pid_t)0, (pid_t)0);
849 #endif
851 load_interfaces();
853 secrets_init();
855 /* Get list of domains we look up requests for. This includes the
856 domain which we are a member of as well as any trusted
857 domains. */
859 get_domain_info();
861 ZERO_STRUCT(server_state);
863 /* Winbind daemon initialisation */
865 if (!winbindd_param_init())
866 return 1;
868 if (!winbindd_idmap_init())
869 return 1;
871 winbindd_cache_init();
873 /* Unblock all signals we are interested in as they may have been
874 blocked by the parent process. */
876 BlockSignals(False, SIGINT);
877 BlockSignals(False, SIGQUIT);
878 BlockSignals(False, SIGTERM);
879 BlockSignals(False, SIGUSR1);
880 BlockSignals(False, SIGHUP);
882 /* Setup signal handlers */
884 CatchSignal(SIGINT, termination_handler); /* Exit on these sigs */
885 CatchSignal(SIGQUIT, termination_handler);
886 CatchSignal(SIGTERM, termination_handler);
888 CatchSignal(SIGPIPE, SIG_IGN); /* Ignore sigpipe */
890 CatchSignal(SIGUSR1, sigusr1_handler); /* Debugging sigs */
891 CatchSignal(SIGHUP, sighup_handler);
893 /* Create UNIX domain socket */
895 if ((accept_sock = create_sock()) == -1) {
896 DEBUG(0, ("failed to create socket\n"));
897 return 1;
900 /* Loop waiting for requests */
902 process_loop(accept_sock);
904 return 0;