/* nslcd.c - ldap local connection daemon Copyright (C) 2006 West Consulting Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Arthur de Jong This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #ifdef HAVE_STDINT_H #include #endif /* HAVE_STDINT_H */ #include #include #include #ifdef HAVE_GETOPT_H #include #endif /* HAVE_GETOPT_H */ #include #include #include #include #include #include #include #include #include #ifdef HAVE_NSS_H #include #endif /* HAVE_NSS_H */ #include #ifdef HAVE_PTHREAD_NP_H #include #endif /* HAVE_PTHREAD_NP_H */ #ifndef HAVE_GETOPT_LONG #include "compat/getopt_long.h" #endif /* not HAVE_GETOPT_LONG */ #include "compat/daemon.h" #include #include #include #include "nslcd.h" #include "log.h" #include "cfg.h" #include "common.h" #include "compat/attrs.h" #include "compat/getpeercred.h" #include "compat/socket.h" /* read timeout is half a second because clients should send their request quickly, write timeout is 60 seconds because clients could be taking some time to process the results */ #define READ_TIMEOUT 500 #define WRITE_TIMEOUT 60*1000 /* buffer sizes for I/O */ #define READBUFFER_MINSIZE 32 #define READBUFFER_MAXSIZE 64 #define WRITEBUFFER_MINSIZE 64 #define WRITEBUFFER_MAXSIZE 1*1024*1024 /* flag to indicate if we are in debugging mode */ static int nslcd_debugging=0; /* flag to indicate user requested the --check option */ static int nslcd_checkonly=0; /* the exit flag to indicate that a signal was received */ static volatile int nslcd_exitsignal=0; /* the server socket used for communication */ static int nslcd_serversocket=-1; /* thread ids of all running threads */ static pthread_t *nslcd_threads; /* if we don't have clearenv() we have to do this the hard way */ #ifndef HAVE_CLEARENV /* the definition of the environment */ extern char **environ; /* the environment we want to use */ static char *sane_environment[] = { "HOME=/", "TMPDIR=/tmp", "LDAPNOINIT=1", NULL }; #endif /* not HAVE_CLEARENV */ /* display version information */ static void display_version(FILE *fp) { fprintf(fp,"%s\n",PACKAGE_STRING); fprintf(fp,"Written by Luke Howard and Arthur de Jong.\n\n"); fprintf(fp,"Copyright (C) 1997-2013 Luke Howard, Arthur de Jong and West Consulting\n" "This is free software; see the source for copying conditions. There is NO\n" "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"); } /* display usage information */ static void display_usage(FILE *fp,const char *program_name) { fprintf(fp,"Usage: %s [OPTION]...\n",program_name); fprintf(fp,"Name Service LDAP connection daemon.\n"); fprintf(fp," -c, --check check if the daemon already is running\n"); fprintf(fp," -d, --debug don't fork and print debugging to stderr\n"); fprintf(fp," --help display this help and exit\n"); fprintf(fp," --version output version information and exit\n"); fprintf(fp,"\n" "Report bugs to <%s>.\n",PACKAGE_BUGREPORT); } /* the definition of options for getopt(). see getopt(2) */ static struct option const nslcd_options[] = { { "check", no_argument, NULL, 'c' }, { "debug", no_argument, NULL, 'd' }, { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, { NULL, 0, NULL, 0 } }; #define NSLCD_OPTIONSTRING "cdhV" /* parse command line options and save settings in struct */ static void parse_cmdline(int argc,char *argv[]) { int optc; while ((optc=getopt_long(argc,argv,NSLCD_OPTIONSTRING,nslcd_options,NULL))!=-1) { switch (optc) { case 'c': /* -c, --check check if the daemon already is running */ nslcd_checkonly=1; break; case 'd': /* -d, --debug don't fork and print debugging to stderr */ nslcd_debugging++; log_setdefaultloglevel(LOG_DEBUG); break; case 'h': /* --help display this help and exit */ display_usage(stdout,argv[0]); exit(EXIT_SUCCESS); case 'V': /* --version output version information and exit */ display_version(stdout); exit(EXIT_SUCCESS); case ':': /* missing required parameter */ case '?': /* unknown option character or extraneous parameter */ default: fprintf(stderr,"Try '%s --help' for more information.\n", argv[0]); exit(EXIT_FAILURE); } } /* check for remaining arguments */ if (optind= 0) { if (close(nslcd_serversocket)) log_log(LOG_WARNING,"problem closing server socket (ignored): %s",strerror(errno)); } /* remove existing named socket */ if (unlink(NSLCD_SOCKET)<0) { log_log(LOG_DEBUG,"unlink() of "NSLCD_SOCKET" failed (ignored): %s", strerror(errno)); } /* remove pidfile */ if (unlink(NSLCD_PIDFILE)<0) { log_log(LOG_DEBUG,"unlink() of "NSLCD_PIDFILE" failed (ignored): %s", strerror(errno)); } /* log exit */ log_log(LOG_INFO,"version %s bailing out",VERSION); } /* create the directory for the specified file to reside in */ static void mkdirname(const char *filename) { char *tmpname,*path; tmpname=strdup(filename); if (tmpname==NULL) return; path=dirname(tmpname); if (mkdir(path,(mode_t)0755)==0) { /* if directory was just created, set correct ownership */ if (lchown(path,nslcd_cfg->ldc_uid,nslcd_cfg->ldc_gid)<0) log_log(LOG_WARNING,"problem setting permissions for %s: %s",path,strerror(errno)); } free(tmpname); } /* returns a socket ready to answer requests from the client, exit()s on error */ static int create_socket(const char *filename) { int sock; int i; struct sockaddr_un addr; /* create a socket */ if ( (sock=socket(PF_UNIX,SOCK_STREAM,0))<0 ) { log_log(LOG_ERR,"cannot create socket: %s",strerror(errno)); exit(EXIT_FAILURE); } if (sock>=FD_SETSIZE) { log_log(LOG_ERR,"socket file descriptor number too high (%d)",sock); exit(EXIT_FAILURE); } /* remove existing named socket */ if (unlink(filename)<0) { log_log(LOG_DEBUG,"unlink() of %s failed (ignored): %s", filename,strerror(errno)); } /* do not block on accept() */ if ((i=fcntl(sock,F_GETFL,0))<0) { log_log(LOG_ERR,"fctnl(F_GETFL) failed: %s",strerror(errno)); if (close(sock)) log_log(LOG_WARNING,"problem closing socket: %s",strerror(errno)); exit(EXIT_FAILURE); } if (fcntl(sock,F_SETFL,i|O_NONBLOCK)<0) { log_log(LOG_ERR,"fctnl(F_SETFL,O_NONBLOCK) failed: %s",strerror(errno)); if (close(sock)) log_log(LOG_WARNING,"problem closing socket: %s",strerror(errno)); exit(EXIT_FAILURE); } /* create the directory if needed */ mkdirname(filename); /* create socket address structure */ memset(&addr,0,sizeof(struct sockaddr_un)); addr.sun_family=AF_UNIX; strncpy(addr.sun_path,filename,sizeof(addr.sun_path)); addr.sun_path[sizeof(addr.sun_path)-1]='\0'; /* bind to the named socket */ if (bind(sock,(struct sockaddr *)&addr,SUN_LEN(&addr))) { log_log(LOG_ERR,"bind() to %s failed: %s",filename,strerror(errno)); if (close(sock)) log_log(LOG_WARNING,"problem closing socket: %s",strerror(errno)); exit(EXIT_FAILURE); } /* close the file descriptor on exec */ if (fcntl(sock,F_SETFD,FD_CLOEXEC)<0) { log_log(LOG_ERR,"fctnl(F_SETFL,FD_CLOEXEC) on %s failed: %s",filename,strerror(errno)); if (close(sock)) log_log(LOG_WARNING,"problem closing socket: %s",strerror(errno)); exit(EXIT_FAILURE); } /* set permissions of socket so anybody can do requests */ /* Note: we use chmod() here instead of fchmod() because fchmod does not work on sockets http://www.opengroup.org/onlinepubs/009695399/functions/fchmod.html http://lkml.org/lkml/2005/5/16/11 */ if (chmod(filename,(mode_t)0666)) { log_log(LOG_ERR,"chmod(0666) of %s failed: %s",filename,strerror(errno)); if (close(sock)) log_log(LOG_WARNING,"problem closing socket: %s",strerror(errno)); exit(EXIT_FAILURE); } /* start listening for connections */ if (listen(sock,SOMAXCONN)<0) { log_log(LOG_ERR,"listen() failed: %s",strerror(errno)); if (close(sock)) log_log(LOG_WARNING,"problem closing socket: %s",strerror(errno)); exit(EXIT_FAILURE); } /* we're done */ return sock; } /* read the version information and action from the stream this function returns the read action in location pointer to by action */ static int read_header(TFILE *fp,int32_t *action) { int32_t tmpint32; /* read the protocol version */ READ_TYPE(fp,tmpint32,int32_t); if (tmpint32 != (int32_t)NSLCD_VERSION) { log_log(LOG_DEBUG,"wrong nslcd version id (%d)",(int)tmpint32); return -1; } /* read the request type */ READ(fp,action,sizeof(int32_t)); return 0; } /* read a request message, returns <0 in case of errors, this function closes the socket */ static void handleconnection(int sock,MYLDAP_SESSION *session) { TFILE *fp; int32_t action; uid_t uid=(uid_t)-1; gid_t gid=(gid_t)-1; pid_t pid=(pid_t)-1; /* log connection */ if (getpeercred(sock,&uid,&gid,&pid)) log_log(LOG_DEBUG,"connection from unknown client: %s",strerror(errno)); else log_log(LOG_DEBUG,"connection from pid=%d uid=%d gid=%d", (int)pid,(int)uid,(int)gid); /* create a stream object */ if ((fp=tio_fdopen(sock,READ_TIMEOUT,WRITE_TIMEOUT, READBUFFER_MINSIZE,READBUFFER_MAXSIZE, WRITEBUFFER_MINSIZE,WRITEBUFFER_MAXSIZE))==NULL) { log_log(LOG_WARNING,"cannot create stream for writing: %s",strerror(errno)); (void)close(sock); return; } /* read request */ if (read_header(fp,&action)) { (void)tio_close(fp); return; } /* handle request */ switch (action) { case NSLCD_ACTION_CONFIG_GET: (void)nslcd_config_get(fp,session); break; case NSLCD_ACTION_ALIAS_BYNAME: (void)nslcd_alias_byname(fp,session); break; case NSLCD_ACTION_ALIAS_ALL: (void)nslcd_alias_all(fp,session); break; case NSLCD_ACTION_ETHER_BYNAME: (void)nslcd_ether_byname(fp,session); break; case NSLCD_ACTION_ETHER_BYETHER: (void)nslcd_ether_byether(fp,session); break; case NSLCD_ACTION_ETHER_ALL: (void)nslcd_ether_all(fp,session); break; case NSLCD_ACTION_GROUP_BYNAME: (void)nslcd_group_byname(fp,session); break; case NSLCD_ACTION_GROUP_BYGID: (void)nslcd_group_bygid(fp,session); break; case NSLCD_ACTION_GROUP_BYMEMBER: (void)nslcd_group_bymember(fp,session); break; case NSLCD_ACTION_GROUP_ALL: (void)nslcd_group_all(fp,session); break; case NSLCD_ACTION_HOST_BYNAME: (void)nslcd_host_byname(fp,session); break; case NSLCD_ACTION_HOST_BYADDR: (void)nslcd_host_byaddr(fp,session); break; case NSLCD_ACTION_HOST_ALL: (void)nslcd_host_all(fp,session); break; case NSLCD_ACTION_NETGROUP_BYNAME: (void)nslcd_netgroup_byname(fp,session); break; case NSLCD_ACTION_NETWORK_BYNAME: (void)nslcd_network_byname(fp,session); break; case NSLCD_ACTION_NETWORK_BYADDR: (void)nslcd_network_byaddr(fp,session); break; case NSLCD_ACTION_NETWORK_ALL: (void)nslcd_network_all(fp,session); break; case NSLCD_ACTION_PASSWD_BYNAME: (void)nslcd_passwd_byname(fp,session,uid); break; case NSLCD_ACTION_PASSWD_BYUID: (void)nslcd_passwd_byuid(fp,session,uid); break; case NSLCD_ACTION_PASSWD_ALL: (void)nslcd_passwd_all(fp,session,uid); break; case NSLCD_ACTION_PROTOCOL_BYNAME: (void)nslcd_protocol_byname(fp,session); break; case NSLCD_ACTION_PROTOCOL_BYNUMBER:(void)nslcd_protocol_bynumber(fp,session); break; case NSLCD_ACTION_PROTOCOL_ALL: (void)nslcd_protocol_all(fp,session); break; case NSLCD_ACTION_RPC_BYNAME: (void)nslcd_rpc_byname(fp,session); break; case NSLCD_ACTION_RPC_BYNUMBER: (void)nslcd_rpc_bynumber(fp,session); break; case NSLCD_ACTION_RPC_ALL: (void)nslcd_rpc_all(fp,session); break; case NSLCD_ACTION_SERVICE_BYNAME: (void)nslcd_service_byname(fp,session); break; case NSLCD_ACTION_SERVICE_BYNUMBER: (void)nslcd_service_bynumber(fp,session); break; case NSLCD_ACTION_SERVICE_ALL: (void)nslcd_service_all(fp,session); break; case NSLCD_ACTION_SHADOW_BYNAME: if (uid==0) (void)nslcd_shadow_byname(fp,session); else log_log(LOG_DEBUG,"denied shadow request by non-root user"); break; case NSLCD_ACTION_SHADOW_ALL: if (uid==0) (void)nslcd_shadow_all(fp,session); else log_log(LOG_DEBUG,"denied shadow request by non-root user"); break; case NSLCD_ACTION_PAM_AUTHC: (void)nslcd_pam_authc(fp,session,uid); break; case NSLCD_ACTION_PAM_AUTHZ: (void)nslcd_pam_authz(fp,session); break; case NSLCD_ACTION_PAM_SESS_O: (void)nslcd_pam_sess_o(fp,session); break; case NSLCD_ACTION_PAM_SESS_C: (void)nslcd_pam_sess_c(fp,session); break; case NSLCD_ACTION_PAM_PWMOD: (void)nslcd_pam_pwmod(fp,session,uid); break; default: log_log(LOG_WARNING,"invalid request id: %d",(int)action); break; } /* we're done with the request */ myldap_session_cleanup(session); (void)tio_close(fp); return; } /* test to see if we can lock the specified file */ static int is_locked(const char* filename) { int fd; if (filename!=NULL) { errno=0; if ((fd=open(filename,O_RDWR,0644))<0) { if (errno==ENOENT) return 0; /* if file doesn't exist it cannot be locked */ log_log(LOG_ERR,"cannot open lock file (%s): %s",filename,strerror(errno)); exit(EXIT_FAILURE); } if (lockf(fd,F_TEST,0)<0) { if (close(fd)) log_log(LOG_WARNING,"problem closing fd: %s",strerror(errno)); return -1; } if (close(fd)) log_log(LOG_WARNING,"problem closing fd: %s",strerror(errno)); } return 0; } /* write the current process id to the specified file */ static void create_pidfile(const char *filename) { int fd; char buffer[20]; if (filename!=NULL) { mkdirname(filename); if ((fd=open(filename,O_RDWR|O_CREAT,0644))<0) { log_log(LOG_ERR,"cannot create pid file (%s): %s",filename,strerror(errno)); exit(EXIT_FAILURE); } if (lockf(fd,F_TLOCK,0)<0) { log_log(LOG_ERR,"cannot lock pid file (%s): %s",filename,strerror(errno)); exit(EXIT_FAILURE); } if (ftruncate(fd,0)<0) { log_log(LOG_ERR,"cannot truncate pid file (%s): %s",filename,strerror(errno)); exit(EXIT_FAILURE); } mysnprintf(buffer,sizeof(buffer),"%d\n",(int)getpid()); if (write(fd,buffer,strlen(buffer))!=(int)strlen(buffer)) { log_log(LOG_ERR,"error writing pid file (%s): %s",filename,strerror(errno)); exit(EXIT_FAILURE); } /* we keep the pidfile open so the lock remains valid */ } } /* try to install signal handler and check result */ static void install_sighandler(int signum,void (*handler) (int)) { struct sigaction act; memset(&act,0,sizeof(struct sigaction)); act.sa_handler=handler; sigemptyset(&act.sa_mask); act.sa_flags=SA_RESTART|SA_NOCLDSTOP; if (sigaction(signum,&act,NULL)!=0) { log_log(LOG_ERR,"error installing signal handler for '%s': %s",signame(signum),strerror(errno)); exit(EXIT_FAILURE); } } static void worker_cleanup(void *arg) { MYLDAP_SESSION *session=(MYLDAP_SESSION *)arg; myldap_session_close(session); } static void *worker(void UNUSED(*arg)) { MYLDAP_SESSION *session; int csock; int j; struct sockaddr_storage addr; socklen_t alen; fd_set fds; struct timeval tv; /* create a new LDAP session */ session=myldap_create_session(); /* clean up the session if we're done */ pthread_cleanup_push(worker_cleanup,session); /* start waiting for incoming connections */ while (1) { /* time out connection to LDAP server if needed */ myldap_session_check(session); /* set up the set of fds to wait on */ FD_ZERO(&fds); FD_SET(nslcd_serversocket,&fds); /* set up our timeout value */ tv.tv_sec=nslcd_cfg->ldc_idle_timelimit; tv.tv_usec=0; /* wait for a new connection */ j=select(nslcd_serversocket+1,&fds,NULL,NULL,nslcd_cfg->ldc_idle_timelimit>0?&tv:NULL); /* check result of select() */ if (j<0) { if (errno==EINTR) log_log(LOG_DEBUG,"select() failed (ignored): %s",strerror(errno)); else log_log(LOG_ERR,"select() failed: %s",strerror(errno)); continue; } /* see if our file descriptor is actually ready */ if (!FD_ISSET(nslcd_serversocket,&fds)) continue; /* wait for a new connection */ alen=(socklen_t)sizeof(struct sockaddr_storage); csock=accept(nslcd_serversocket,(struct sockaddr *)&addr,&alen); if (csock<0) { if ((errno==EINTR)||(errno==EAGAIN)||(errno==EWOULDBLOCK)) log_log(LOG_DEBUG,"accept() failed (ignored): %s",strerror(errno)); else log_log(LOG_ERR,"accept() failed: %s",strerror(errno)); continue; } /* make sure O_NONBLOCK is not inherited */ if ((j=fcntl(csock,F_GETFL,0))<0) { log_log(LOG_ERR,"fctnl(F_GETFL) failed: %s",strerror(errno)); if (close(csock)) log_log(LOG_WARNING,"problem closing socket: %s",strerror(errno)); continue; } if (fcntl(csock,F_SETFL,j&~O_NONBLOCK)<0) { log_log(LOG_ERR,"fctnl(F_SETFL,~O_NONBLOCK) failed: %s",strerror(errno)); if (close(csock)) log_log(LOG_WARNING,"problem closing socket: %s",strerror(errno)); continue; } /* indicate new connection to logging module (generates unique id) */ log_newsession(); /* handle the connection */ handleconnection(csock,session); /* indicate end of session in log messages */ log_clearsession(); } pthread_cleanup_pop(1); return NULL; } /* function to disable lookups through the nss_ldap module to avoid lookup loops */ static void disable_nss_ldap(void) { void *handle; char *error; int *enable_flag; /* try to load the NSS module */ #ifdef RTLD_NODELETE handle=dlopen(NSS_LDAP_SONAME,RTLD_LAZY|RTLD_NODELETE); #else /* not RTLD_NODELETE */ handle=dlopen(NSS_LDAP_SONAME,RTLD_LAZY); #endif /* RTLD_NODELETE */ if (handle==NULL) { log_log(LOG_WARNING,"Warning: LDAP NSS module not loaded: %s",dlerror()); return; } /* clear any existing errors */ dlerror(); /* try to look up the flag */ enable_flag=(int *)dlsym(handle,"_nss_ldap_enablelookups"); error=dlerror(); if (error!=NULL) { log_log(LOG_WARNING,"Warning: %s (probably older NSS module loaded)",error); /* fall back to changing the way host lookup is done */ #ifdef HAVE___NSS_CONFIGURE_LOOKUP if (__nss_configure_lookup("hosts","files dns")) log_log(LOG_ERR,"unable to override hosts lookup method: %s",strerror(errno)); #endif /* HAVE___NSS_CONFIGURE_LOOKUP */ dlclose(handle); return; } /* disable nss_ldap */ *enable_flag=0; #ifdef RTLD_NODELETE /* only close the handle if RTLD_NODELETE was used */ dlclose(handle); #endif /* RTLD_NODELETE */ } /* the main program... */ int main(int argc,char *argv[]) { int i; sigset_t signalmask,oldmask; #ifdef HAVE_PTHREAD_TIMEDJOIN_NP struct timespec ts; #endif /* HAVE_PTHREAD_TIMEDJOIN_NP */ /* parse the command line */ parse_cmdline(argc,argv); /* clean the environment */ #ifdef HAVE_CLEARENV if ( clearenv() || putenv("HOME=/") || putenv("TMPDIR=/tmp") || putenv("LDAPNOINIT=1") ) { log_log(LOG_ERR,"clearing environment failed"); exit(EXIT_FAILURE); } #else /* not HAVE_CLEARENV */ /* this is a bit ugly */ environ=sane_environment; #endif /* not HAVE_CLEARENV */ /* disable the nss_ldap module for this process */ disable_nss_ldap(); /* set LDAP log level */ if (myldap_set_debuglevel(nslcd_debugging)!=LDAP_SUCCESS) exit(EXIT_FAILURE); /* read configuration file */ cfg_init(NSLCD_CONF_PATH); /* set default mode for pidfile and socket */ (void)umask((mode_t)0022); /* see if someone already locked the pidfile if --check option was given: exit TRUE if daemon runs (pidfile locked), FALSE otherwise */ if (nslcd_checkonly) { if (is_locked(NSLCD_PIDFILE)) { log_log(LOG_DEBUG,"pidfile (%s) is locked",NSLCD_PIDFILE); exit(EXIT_SUCCESS); } else { log_log(LOG_DEBUG,"pidfile (%s) is not locked",NSLCD_PIDFILE); exit(EXIT_FAILURE); } } /* normal check for pidfile locked */ if (is_locked(NSLCD_PIDFILE)) { log_log(LOG_ERR,"daemon may already be active, cannot acquire lock (%s): %s",NSLCD_PIDFILE,strerror(errno)); exit(EXIT_FAILURE); } /* close all file descriptors (except stdin/out/err) */ i=sysconf(_SC_OPEN_MAX)-1; /* if the system does not have OPEN_MAX just close the first 32 and hope we closed enough */ if (i<0) i=32; for (;i>3;i--) close(i); /* daemonize */ if ((!nslcd_debugging)&&(daemon(0,0)<0)) { log_log(LOG_ERR,"unable to daemonize: %s",strerror(errno)); exit(EXIT_FAILURE); } /* intilialize logging */ if (!nslcd_debugging) log_startlogging(); log_log(LOG_INFO,"version %s starting",VERSION); /* write pidfile */ create_pidfile(NSLCD_PIDFILE); /* install handler to close stuff off on exit and log notice */ if (atexit(exithandler)) { log_log(LOG_ERR,"atexit() failed: %s",strerror(errno)); exit(EXIT_FAILURE); } /* create socket */ nslcd_serversocket=create_socket(NSLCD_SOCKET); if ((nslcd_cfg->ldc_gid!=NOGID)&&(nslcd_cfg->ldc_uidname!=NULL)) { #ifdef HAVE_INITGROUPS /* load supplementary groups */ if (initgroups(nslcd_cfg->ldc_uidname,nslcd_cfg->ldc_gid)<0) log_log(LOG_WARNING,"cannot initgroups(\"%s\",%d) (ignored): %s", nslcd_cfg->ldc_uidname,(int)nslcd_cfg->ldc_gid,strerror(errno)); else log_log(LOG_DEBUG,"initgroups(\"%s\",%d) done", nslcd_cfg->ldc_uidname,(int)nslcd_cfg->ldc_gid); #else /* not HAVE_INITGROUPS */ #ifdef HAVE_SETGROUPS /* just drop all supplemental groups */ if (setgroups(0,NULL)<0) log_log(LOG_WARNING,"cannot setgroups(0,NULL) (ignored): %s",strerror(errno)); else log_log(LOG_DEBUG,"setgroups(0,NULL) done"); #else /* not HAVE_SETGROUPS */ log_log(LOG_DEBUG,"neither initgroups() or setgroups() available"); #endif /* not HAVE_SETGROUPS */ #endif /* not HAVE_INITGROUPS */ } /* change to nslcd gid */ if (nslcd_cfg->ldc_gid!=NOGID) { if (setgid(nslcd_cfg->ldc_gid)!=0) { log_log(LOG_ERR,"cannot setgid(%d): %s",(int)nslcd_cfg->ldc_gid,strerror(errno)); exit(EXIT_FAILURE); } log_log(LOG_DEBUG,"setgid(%d) done",(int)nslcd_cfg->ldc_gid); } /* change to nslcd uid */ if (nslcd_cfg->ldc_uid!=NOUID) { if (setuid(nslcd_cfg->ldc_uid)!=0) { log_log(LOG_ERR,"cannot setuid(%d): %s",(int)nslcd_cfg->ldc_uid,strerror(errno)); exit(EXIT_FAILURE); } log_log(LOG_DEBUG,"setuid(%d) done",(int)nslcd_cfg->ldc_uid); } /* block all these signals so our worker threads won't handle them */ sigemptyset(&signalmask); sigaddset(&signalmask,SIGHUP); sigaddset(&signalmask,SIGINT); sigaddset(&signalmask,SIGQUIT); sigaddset(&signalmask,SIGABRT); sigaddset(&signalmask,SIGPIPE); sigaddset(&signalmask,SIGTERM); sigaddset(&signalmask,SIGUSR1); sigaddset(&signalmask,SIGUSR2); pthread_sigmask(SIG_BLOCK,&signalmask,&oldmask); /* start worker threads */ log_log(LOG_INFO,"accepting connections"); nslcd_threads=(pthread_t *)malloc(nslcd_cfg->ldc_threads*sizeof(pthread_t)); if (nslcd_threads==NULL) { log_log(LOG_CRIT,"main(): malloc() failed to allocate memory"); exit(EXIT_FAILURE); } for (i=0;ildc_threads;i++) { if (pthread_create(&nslcd_threads[i],NULL,worker,NULL)) { log_log(LOG_ERR,"unable to start worker thread %d: %s",i,strerror(errno)); exit(EXIT_FAILURE); } } pthread_sigmask(SIG_SETMASK,&oldmask,NULL); /* install signalhandlers for some signals */ install_sighandler(SIGHUP, sigexit_handler); install_sighandler(SIGINT, sigexit_handler); install_sighandler(SIGQUIT,sigexit_handler); install_sighandler(SIGABRT,sigexit_handler); install_sighandler(SIGPIPE,SIG_IGN); install_sighandler(SIGTERM,sigexit_handler); install_sighandler(SIGUSR1,sigexit_handler); install_sighandler(SIGUSR2,sigexit_handler); /* wait until we received a signal */ while (nslcd_exitsignal==0) { sleep(INT_MAX); /* sleep as long as we can or until we receive a signal */ } /* print something about received signal */ log_log(LOG_INFO,"caught signal %s (%d), shutting down", signame(nslcd_exitsignal),nslcd_exitsignal); /* cancel all running threads */ for (i=0;ildc_threads;i++) if (pthread_cancel(nslcd_threads[i])) log_log(LOG_WARNING,"failed to stop thread %d (ignored): %s",i,strerror(errno)); /* close server socket to trigger failures in threads waiting on accept() */ close(nslcd_serversocket); nslcd_serversocket=-1; /* if we can, wait a few seconds for the threads to finish */ #ifdef HAVE_PTHREAD_TIMEDJOIN_NP ts.tv_sec=time(NULL)+3; ts.tv_nsec=0; #endif /* HAVE_PTHREAD_TIMEDJOIN_NP */ for (i=0;ildc_threads;i++) { #ifdef HAVE_PTHREAD_TIMEDJOIN_NP pthread_timedjoin_np(nslcd_threads[i],NULL,&ts); #endif /* HAVE_PTHREAD_TIMEDJOIN_NP */ if (pthread_kill(nslcd_threads[i],0)==0) log_log(LOG_ERR,"thread %d is still running, shutting down anyway",i); } /* we're done */ return EXIT_FAILURE; }