1 /*
2    nslcd.c - ldap local connection daemon
3 
4    Copyright (C) 2006 West Consulting
5    Copyright (C) 2006-2019 Arthur de Jong
6 
7    This library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Lesser General Public
9    License as published by the Free Software Foundation; either
10    version 2.1 of the License, or (at your option) any later version.
11 
12    This library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16 
17    You should have received a copy of the GNU Lesser General Public
18    License along with this library; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20    02110-1301 USA
21 */
22 
23 #include "config.h"
24 
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #ifdef HAVE_STDINT_H
29 #include <stdint.h>
30 #endif /* HAVE_STDINT_H */
31 #include <sys/types.h>
32 #include <sys/param.h>
33 #include <sys/wait.h>
34 #ifdef HAVE_GETOPT_H
35 #include <getopt.h>
36 #endif /* HAVE_GETOPT_H */
37 #include <assert.h>
38 #include <signal.h>
39 #include <errno.h>
40 #include <unistd.h>
41 #include <fcntl.h>
42 #include <sys/stat.h>
43 #include <sys/socket.h>
44 #include <sys/un.h>
45 #include <grp.h>
46 #ifdef HAVE_NSS_H
47 #include <nss.h>
48 #endif /* HAVE_NSS_H */
49 #include <pthread.h>
50 #ifdef HAVE_PTHREAD_NP_H
51 #include <pthread_np.h>
52 #endif /* HAVE_PTHREAD_NP_H */
53 #ifndef HAVE_GETOPT_LONG
54 #include "compat/getopt_long.h"
55 #endif /* not HAVE_GETOPT_LONG */
56 #include <dlfcn.h>
57 #include <libgen.h>
58 #include <limits.h>
59 
60 #include "nslcd.h"
61 #include "log.h"
62 #include "cfg.h"
63 #include "common.h"
64 #include "compat/attrs.h"
65 #include "compat/getpeercred.h"
66 #include "compat/socket.h"
67 #include "daemonize.h"
68 
69 /* read timeout is half a second because clients should send their request
70    quickly, write timeout is 60 seconds because clients could be taking some
71    time to process the results */
72 #define READ_TIMEOUT 500
73 #define WRITE_TIMEOUT 60 * 1000
74 
75 /* buffer sizes for I/O */
76 #define READBUFFER_MINSIZE 32
77 #define READBUFFER_MAXSIZE 64
78 #define WRITEBUFFER_MINSIZE 1024
79 #define WRITEBUFFER_MAXSIZE 1 * 1024 * 1024
80 
81 /* adjust the oom killer score */
82 #define OOM_SCORE_ADJ_FILE "/proc/self/oom_score_adj"
83 #define OOM_SCORE_ADJ "-1000"
84 
85 /* flag to indicate if we are in debugging mode */
86 static int nslcd_debugging = 0;
87 
88 /* flag to indicate we shouldn't daemonize */
89 static int nslcd_nofork = 0;
90 
91 /* flag to indicate user requested the --check option */
92 static int nslcd_checkonly = 0;
93 
94 /* the flag to indicate that a signal was received */
95 static volatile int nslcd_receivedsignal = 0;
96 
97 /* the server socket used for communication */
98 static int nslcd_serversocket = -1;
99 
100 /* thread ids of all running threads */
101 static pthread_t *nslcd_threads;
102 
103 /* if we don't have clearenv() we have to do this the hard way */
104 #ifndef HAVE_CLEARENV
105 
106 /* the definition of the environment */
107 extern char **environ;
108 
109 /* the environment we want to use */
110 static char *sane_environment[] = {
111   "HOME=/",
112   "TMPDIR=/tmp",
113   "LDAPNOINIT=1",
114   NULL
115 };
116 
117 #endif /* not HAVE_CLEARENV */
118 
119 /* display version information */
display_version(FILE * fp)120 static void display_version(FILE *fp)
121 {
122   fprintf(fp, "%s\n", PACKAGE_STRING);
123   fprintf(fp, "Written by Luke Howard and Arthur de Jong.\n\n");
124   fprintf(fp, "Copyright (C) 1997-2019 Arthur de Jong and others\n"
125               "This is free software; see the source for copying conditions.  There is NO\n"
126               "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
127 }
128 
129 /* display usage information */
display_usage(FILE * fp,const char * program_name)130 static void display_usage(FILE *fp, const char *program_name)
131 {
132   fprintf(fp, "Usage: %s [OPTION]...\n", program_name);
133   fprintf(fp, "Name Service LDAP connection daemon.\n");
134   fprintf(fp, "  -c, --check        check if the daemon already is running\n");
135   fprintf(fp, "  -d, --debug        don't fork and print debugging to stderr\n");
136   fprintf(fp, "  -n, --nofork       don't fork\n");
137   fprintf(fp, "      --help         display this help and exit\n");
138   fprintf(fp, "      --version      output version information and exit\n");
139   fprintf(fp, "\n" "Report bugs to <%s>.\n", PACKAGE_BUGREPORT);
140 }
141 
142 /* the definition of options for getopt(). see getopt(2) */
143 static struct option const nslcd_options[] = {
144   {"check",   no_argument, NULL, 'c'},
145   {"debug",   no_argument, NULL, 'd'},
146   {"nofork",  no_argument, NULL, 'n'},
147   {"help",    no_argument, NULL, 'h'},
148   {"version", no_argument, NULL, 'V'},
149   {NULL,      0,           NULL, 0}
150 };
151 #define NSLCD_OPTIONSTRING "cndhV"
152 
153 /* parse command line options and save settings in struct  */
parse_cmdline(int argc,char * argv[])154 static void parse_cmdline(int argc, char *argv[])
155 {
156   int optc;
157   while ((optc = getopt_long(argc, argv, NSLCD_OPTIONSTRING, nslcd_options, NULL)) != -1)
158   {
159     switch (optc)
160     {
161       case 'c': /* -c, --check        check if the daemon already is running */
162         nslcd_checkonly = 1;
163         break;
164       case 'd': /* -d, --debug        don't fork and print debugging to stderr */
165         nslcd_debugging++;
166         log_setdefaultloglevel(LOG_DEBUG);
167         break;
168       case 'n': /* -n, --nofork       don't fork */
169         nslcd_nofork++;
170         break;
171       case 'h': /*     --help         display this help and exit */
172         display_usage(stdout, argv[0]);
173         exit(EXIT_SUCCESS);
174       case 'V': /*     --version      output version information and exit */
175         display_version(stdout);
176         exit(EXIT_SUCCESS);
177       case ':': /* missing required parameter */
178       case '?': /* unknown option character or extraneous parameter */
179       default:
180         fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]);
181         exit(EXIT_FAILURE);
182     }
183   }
184   /* check for remaining arguments */
185   if (optind < argc)
186   {
187     fprintf(stderr, "%s: unrecognized option '%s'\n", argv[0], argv[optind]);
188     fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]);
189     exit(EXIT_FAILURE);
190   }
191 }
192 
193 /* signal handler for storing information on received signals */
sig_handler(int signum)194 static void sig_handler(int signum)
195 {
196   /* just save the signal to indicate that we're stopping */
197   nslcd_receivedsignal = signum;
198 }
199 
200 /* do some cleaning up before terminating */
exithandler(void)201 static void exithandler(void)
202 {
203   /* close socket if it's still in use */
204   if (nslcd_serversocket >= 0)
205   {
206     if (close(nslcd_serversocket))
207       log_log(LOG_WARNING, "problem closing server socket (ignored): %s",
208               strerror(errno));
209   }
210   /* remove existing named socket */
211   if (unlink(NSLCD_SOCKET) < 0)
212   {
213     log_log(LOG_DEBUG, "unlink() of " NSLCD_SOCKET " failed (ignored): %s",
214             strerror(errno));
215   }
216   /* remove pidfile */
217   if (unlink(NSLCD_PIDFILE) < 0)
218   {
219     log_log(LOG_DEBUG, "unlink() of " NSLCD_PIDFILE " failed (ignored): %s",
220             strerror(errno));
221   }
222   /* log exit */
223   log_log(LOG_INFO, "version %s bailing out", VERSION);
224 }
225 
226 /* create the directory for the specified file to reside in */
mkdirname(const char * filename)227 static void mkdirname(const char *filename)
228 {
229   char *tmpname, *path;
230   tmpname = strdup(filename);
231   if (tmpname == NULL)
232     return;
233   path = dirname(tmpname);
234   if (mkdir(path, (mode_t)0755) == 0)
235   {
236     /* if directory was just created, set correct ownership */
237     if (lchown(path, nslcd_cfg->uid, nslcd_cfg->gid) < 0)
238       log_log(LOG_WARNING, "problem setting permissions for %s: %s",
239               path, strerror(errno));
240   }
241   free(tmpname);
242 }
243 
244 /* returns a socket ready to answer requests from the client,
245    exit()s on error */
create_socket(const char * filename)246 static int create_socket(const char *filename)
247 {
248   int sock;
249   int i;
250   struct sockaddr_un addr;
251   /* create a socket */
252   if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
253   {
254     log_log(LOG_ERR, "cannot create socket: %s", strerror(errno));
255     exit(EXIT_FAILURE);
256   }
257   if (sock >= (int)FD_SETSIZE)
258   {
259     log_log(LOG_ERR, "socket file descriptor number too high (%d)", sock);
260     exit(EXIT_FAILURE);
261   }
262   /* remove existing named socket */
263   if (unlink(filename) < 0)
264   {
265     log_log(LOG_DEBUG, "unlink() of %s failed (ignored): %s",
266             filename, strerror(errno));
267   }
268   /* do not block on accept() */
269   if ((i = fcntl(sock, F_GETFL, 0)) < 0)
270   {
271     log_log(LOG_ERR, "fctnl(F_GETFL) failed: %s", strerror(errno));
272     if (close(sock))
273       log_log(LOG_WARNING, "problem closing socket: %s", strerror(errno));
274     exit(EXIT_FAILURE);
275   }
276   if (fcntl(sock, F_SETFL, i | O_NONBLOCK) < 0)
277   {
278     log_log(LOG_ERR, "fctnl(F_SETFL,O_NONBLOCK) failed: %s", strerror(errno));
279     if (close(sock))
280       log_log(LOG_WARNING, "problem closing socket: %s", strerror(errno));
281     exit(EXIT_FAILURE);
282   }
283   /* create the directory if needed */
284   mkdirname(filename);
285   /* create socket address structure */
286   memset(&addr, 0, sizeof(struct sockaddr_un));
287   addr.sun_family = AF_UNIX;
288   strncpy(addr.sun_path, filename, sizeof(addr.sun_path));
289   addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
290   /* bind to the named socket */
291   if (bind(sock, (struct sockaddr *)&addr, SUN_LEN(&addr)))
292   {
293     log_log(LOG_ERR, "bind() to %s failed: %s", filename, strerror(errno));
294     if (close(sock))
295       log_log(LOG_WARNING, "problem closing socket: %s", strerror(errno));
296     exit(EXIT_FAILURE);
297   }
298   /* close the file descriptor on exec */
299   if (fcntl(sock, F_SETFD, FD_CLOEXEC) < 0)
300   {
301     log_log(LOG_ERR, "fctnl(F_SETFL,FD_CLOEXEC) on %s failed: %s",
302             filename, strerror(errno));
303     if (close(sock))
304       log_log(LOG_WARNING, "problem closing socket: %s", strerror(errno));
305     exit(EXIT_FAILURE);
306   }
307   /* set permissions of socket so anybody can do requests */
308   /* Note: we use chmod() here instead of fchmod() because
309      fchmod does not work on sockets
310      http://www.opengroup.org/onlinepubs/009695399/functions/fchmod.html
311      http://lkml.org/lkml/2005/5/16/11 */
312   if (chmod(filename, (mode_t)0666))
313   {
314     log_log(LOG_ERR, "chmod(0666) of %s failed: %s",
315             filename, strerror(errno));
316     if (close(sock))
317       log_log(LOG_WARNING, "problem closing socket: %s", strerror(errno));
318     exit(EXIT_FAILURE);
319   }
320   /* start listening for connections */
321   if (listen(sock, SOMAXCONN) < 0)
322   {
323     log_log(LOG_ERR, "listen() failed: %s", strerror(errno));
324     if (close(sock))
325       log_log(LOG_WARNING, "problem closing socket: %s", strerror(errno));
326     exit(EXIT_FAILURE);
327   }
328   /* we're done */
329   return sock;
330 }
331 
332 /* read the version information and action from the stream
333    this function returns the read action in location pointer to by action */
read_header(TFILE * fp,int32_t * action)334 static int read_header(TFILE *fp, int32_t *action)
335 {
336   int32_t tmpint32;
337   int32_t protocol;
338   /* read the protocol version */
339   READ_INT32(fp, protocol);
340   if (protocol != (int32_t)NSLCD_VERSION)
341   {
342     log_log(LOG_DEBUG, "invalid nslcd version id: 0x%08x", (unsigned int)protocol);
343     return -1;
344   }
345   /* read the request type */
346   READ_INT32(fp, *action);
347   return 0;
348 }
349 
350 /* read a request message, returns <0 in case of errors,
351    this function closes the socket */
handleconnection(int sock,MYLDAP_SESSION * session)352 static void handleconnection(int sock, MYLDAP_SESSION *session)
353 {
354   TFILE *fp;
355   int32_t action;
356   pid_t pid = (pid_t)-1;
357   uid_t uid = (uid_t)-1;
358   gid_t gid = (gid_t)-1;
359   char peerinfo[80];
360   /* log connection */
361   if (getpeercred(sock, &uid, &gid, &pid))
362     log_log(LOG_DEBUG, "connection from unknown client: %s", strerror(errno));
363   else
364   {
365     peerinfo[0] = '\0';
366     if (pid != (pid_t)-1)
367       mysnprintf(peerinfo + strlen(peerinfo), sizeof(peerinfo) - strlen(peerinfo) - 1,
368                  " pid=%lu", (unsigned long int)pid);
369     if (uid != (uid_t)-1)
370       mysnprintf(peerinfo + strlen(peerinfo), sizeof(peerinfo) - strlen(peerinfo) - 1,
371                  " uid=%lu", (unsigned long int)uid);
372     if (gid != (gid_t)-1)
373       mysnprintf(peerinfo + strlen(peerinfo), sizeof(peerinfo) - strlen(peerinfo) - 1,
374                  " gid=%lu", (unsigned long int)gid);
375     log_log(LOG_DEBUG, "connection from %s", (peerinfo[0] == '\0') ? "unknown" : peerinfo);
376   }
377   /* create a stream object */
378   if ((fp = tio_fdopen(sock, READ_TIMEOUT, WRITE_TIMEOUT,
379                        READBUFFER_MINSIZE, READBUFFER_MAXSIZE,
380                        WRITEBUFFER_MINSIZE, WRITEBUFFER_MAXSIZE)) == NULL)
381   {
382     log_log(LOG_WARNING, "cannot create stream for writing: %s",
383             strerror(errno));
384     (void)close(sock);
385     return;
386   }
387   /* read request */
388   if (read_header(fp, &action))
389   {
390     (void)tio_close(fp);
391     return;
392   }
393   /* handle request */
394   switch (action)
395   {
396     case NSLCD_ACTION_CONFIG_GET:       (void)nslcd_config_get(fp, session); break;
397     case NSLCD_ACTION_ALIAS_BYNAME:     (void)nslcd_alias_byname(fp, session); break;
398     case NSLCD_ACTION_ALIAS_ALL:        (void)nslcd_alias_all(fp, session); break;
399     case NSLCD_ACTION_ETHER_BYNAME:     (void)nslcd_ether_byname(fp, session); break;
400     case NSLCD_ACTION_ETHER_BYETHER:    (void)nslcd_ether_byether(fp, session); break;
401     case NSLCD_ACTION_ETHER_ALL:        (void)nslcd_ether_all(fp, session); break;
402     case NSLCD_ACTION_GROUP_BYNAME:     (void)nslcd_group_byname(fp, session); break;
403     case NSLCD_ACTION_GROUP_BYGID:      (void)nslcd_group_bygid(fp, session); break;
404     case NSLCD_ACTION_GROUP_BYMEMBER:   (void)nslcd_group_bymember(fp, session); break;
405     case NSLCD_ACTION_GROUP_ALL:
406       if (!nslcd_cfg->nss_disable_enumeration) (void)nslcd_group_all(fp, session);
407       break;
408     case NSLCD_ACTION_HOST_BYNAME:      (void)nslcd_host_byname(fp, session); break;
409     case NSLCD_ACTION_HOST_BYADDR:      (void)nslcd_host_byaddr(fp, session); break;
410     case NSLCD_ACTION_HOST_ALL:         (void)nslcd_host_all(fp, session); break;
411     case NSLCD_ACTION_NETGROUP_BYNAME:  (void)nslcd_netgroup_byname(fp, session); break;
412     case NSLCD_ACTION_NETGROUP_ALL:     (void)nslcd_netgroup_all(fp, session); break;
413     case NSLCD_ACTION_NETWORK_BYNAME:   (void)nslcd_network_byname(fp, session); break;
414     case NSLCD_ACTION_NETWORK_BYADDR:   (void)nslcd_network_byaddr(fp, session); break;
415     case NSLCD_ACTION_NETWORK_ALL:      (void)nslcd_network_all(fp, session); break;
416     case NSLCD_ACTION_PASSWD_BYNAME:    (void)nslcd_passwd_byname(fp, session, uid); break;
417     case NSLCD_ACTION_PASSWD_BYUID:     (void)nslcd_passwd_byuid(fp, session, uid); break;
418     case NSLCD_ACTION_PASSWD_ALL:
419       if (!nslcd_cfg->nss_disable_enumeration) (void)nslcd_passwd_all(fp, session, uid);
420       break;
421     case NSLCD_ACTION_PROTOCOL_BYNAME:  (void)nslcd_protocol_byname(fp, session); break;
422     case NSLCD_ACTION_PROTOCOL_BYNUMBER:(void)nslcd_protocol_bynumber(fp, session); break;
423     case NSLCD_ACTION_PROTOCOL_ALL:     (void)nslcd_protocol_all(fp, session); break;
424     case NSLCD_ACTION_RPC_BYNAME:       (void)nslcd_rpc_byname(fp, session); break;
425     case NSLCD_ACTION_RPC_BYNUMBER:     (void)nslcd_rpc_bynumber(fp, session); break;
426     case NSLCD_ACTION_RPC_ALL:          (void)nslcd_rpc_all(fp, session); break;
427     case NSLCD_ACTION_SERVICE_BYNAME:   (void)nslcd_service_byname(fp, session); break;
428     case NSLCD_ACTION_SERVICE_BYNUMBER: (void)nslcd_service_bynumber(fp, session); break;
429     case NSLCD_ACTION_SERVICE_ALL:      (void)nslcd_service_all(fp, session); break;
430     case NSLCD_ACTION_SHADOW_BYNAME:    (void)nslcd_shadow_byname(fp, session, uid); break;
431     case NSLCD_ACTION_SHADOW_ALL:
432       if (!nslcd_cfg->nss_disable_enumeration) (void)nslcd_shadow_all(fp, session, uid);
433       break;
434     case NSLCD_ACTION_PAM_AUTHC:        (void)nslcd_pam_authc(fp, session, uid); break;
435     case NSLCD_ACTION_PAM_AUTHZ:        (void)nslcd_pam_authz(fp, session); break;
436     case NSLCD_ACTION_PAM_SESS_O:       (void)nslcd_pam_sess_o(fp, session); break;
437     case NSLCD_ACTION_PAM_SESS_C:       (void)nslcd_pam_sess_c(fp, session); break;
438     case NSLCD_ACTION_PAM_PWMOD:        (void)nslcd_pam_pwmod(fp, session, uid); break;
439     case NSLCD_ACTION_USERMOD:          (void)nslcd_usermod(fp, session, uid); break;
440     default:
441       log_log(LOG_WARNING, "invalid request id: 0x%08x", (unsigned int)action);
442       break;
443   }
444   /* we're done with the request */
445   myldap_session_cleanup(session);
446   (void)tio_close(fp);
447   return;
448 }
449 
450 /* test to see if we can lock the specified file */
is_locked(const char * filename)451 static int is_locked(const char *filename)
452 {
453   int fd;
454   if (filename != NULL)
455   {
456     errno = 0;
457     if ((fd = open(filename, O_RDWR, 0644)) < 0)
458     {
459       if (errno == ENOENT)
460         return 0; /* if file doesn't exist it cannot be locked */
461       log_log(LOG_ERR, "cannot open lock file (%s): %s", filename, strerror(errno));
462       exit(EXIT_FAILURE);
463     }
464     if (lockf(fd, F_TEST, 0) < 0)
465     {
466       if (close(fd))
467         log_log(LOG_WARNING, "problem closing fd: %s", strerror(errno));
468       return -1;
469     }
470     if (close(fd))
471       log_log(LOG_WARNING, "problem closing fd: %s", strerror(errno));
472   }
473   return 0;
474 }
475 
476 /* write the current process id to the specified file */
create_pidfile(const char * filename)477 static void create_pidfile(const char *filename)
478 {
479   int fd;
480   char buffer[20];
481   if (filename != NULL)
482   {
483     mkdirname(filename);
484     if ((fd = open(filename, O_RDWR | O_CREAT, 0644)) < 0)
485     {
486       log_log(LOG_ERR, "cannot create pid file (%s): %s",
487               filename, strerror(errno));
488       exit(EXIT_FAILURE);
489     }
490     if (lockf(fd, F_TLOCK, 0) < 0)
491     {
492       log_log(LOG_ERR, "cannot lock pid file (%s): %s",
493               filename, strerror(errno));
494       exit(EXIT_FAILURE);
495     }
496     if (ftruncate(fd, 0) < 0)
497     {
498       log_log(LOG_ERR, "cannot truncate pid file (%s): %s",
499               filename, strerror(errno));
500       exit(EXIT_FAILURE);
501     }
502     mysnprintf(buffer, sizeof(buffer), "%lu\n", (unsigned long int)getpid());
503     if (write(fd, buffer, strlen(buffer)) != (int)strlen(buffer))
504     {
505       log_log(LOG_ERR, "error writing pid file (%s): %s",
506               filename, strerror(errno));
507       exit(EXIT_FAILURE);
508     }
509     /* we keep the pidfile open so the lock remains valid */
510   }
511 }
512 
513 /* try to install signal handler and check result */
install_sighandler(int signum,void (* handler)(int))514 static void install_sighandler(int signum, void (*handler) (int))
515 {
516   struct sigaction act;
517   memset(&act, 0, sizeof(struct sigaction));
518   act.sa_handler = handler;
519   sigemptyset(&act.sa_mask);
520   act.sa_flags = SA_RESTART | SA_NOCLDSTOP;
521   if (sigaction(signum, &act, NULL) != 0)
522   {
523     log_log(LOG_ERR, "error installing signal handler for '%s': %s",
524             signame(signum), strerror(errno));
525     exit(EXIT_FAILURE);
526   }
527 }
528 
worker_cleanup(void * arg)529 static void worker_cleanup(void *arg)
530 {
531   MYLDAP_SESSION *session = (MYLDAP_SESSION *)arg;
532   myldap_session_close(session);
533 }
534 
worker(void UNUSED (* arg))535 static void *worker(void UNUSED(*arg))
536 {
537   MYLDAP_SESSION *session;
538   int csock;
539   int j;
540   struct sockaddr_storage addr;
541   socklen_t alen;
542   fd_set fds;
543   struct timeval tv;
544   /* create a new LDAP session */
545   session = myldap_create_session();
546   /* clean up the session if we're done */
547   pthread_cleanup_push(worker_cleanup, session);
548   /* start waiting for incoming connections */
549   while (1)
550   {
551     /* time out connection to LDAP server if needed */
552     myldap_session_check(session);
553     /* set up the set of fds to wait on */
554     FD_ZERO(&fds);
555     FD_SET(nslcd_serversocket, &fds);
556     /* set up our timeout value */
557     tv.tv_sec = nslcd_cfg->idle_timelimit;
558     tv.tv_usec = 0;
559     /* wait for a new connection */
560     j = select(nslcd_serversocket + 1, &fds, NULL, NULL,
561                nslcd_cfg->idle_timelimit > 0 ? &tv : NULL);
562     /* check result of select() */
563     if (j < 0)
564     {
565       if (errno == EINTR)
566         log_log(LOG_DEBUG, "select() failed (ignored): %s", strerror(errno));
567       else
568         log_log(LOG_ERR, "select() failed: %s", strerror(errno));
569       continue;
570     }
571     /* see if our file descriptor is actually ready */
572     if (!FD_ISSET(nslcd_serversocket, &fds))
573       continue;
574     /* wait for a new connection */
575     alen = (socklen_t)sizeof(struct sockaddr_storage);
576     csock = accept(nslcd_serversocket, (struct sockaddr *)&addr, &alen);
577     if (csock < 0)
578     {
579       if ((errno == EINTR) || (errno == EAGAIN) || (errno == EWOULDBLOCK))
580         log_log(LOG_DEBUG, "accept() failed (ignored): %s", strerror(errno));
581       else
582         log_log(LOG_ERR, "accept() failed: %s", strerror(errno));
583       continue;
584     }
585     /* make sure O_NONBLOCK is not inherited */
586     if ((j = fcntl(csock, F_GETFL, 0)) < 0)
587     {
588       log_log(LOG_ERR, "fctnl(F_GETFL) failed: %s", strerror(errno));
589       if (close(csock))
590         log_log(LOG_WARNING, "problem closing socket: %s", strerror(errno));
591       continue;
592     }
593     if (fcntl(csock, F_SETFL, j & ~O_NONBLOCK) < 0)
594     {
595       log_log(LOG_ERR, "fctnl(F_SETFL,~O_NONBLOCK) failed: %s", strerror(errno));
596       if (close(csock))
597         log_log(LOG_WARNING, "problem closing socket: %s", strerror(errno));
598       continue;
599     }
600     /* indicate new connection to logging module (generates unique id) */
601     log_newsession();
602     /* handle the connection */
603     handleconnection(csock, session);
604     /* indicate end of session in log messages */
605     log_clearsession();
606   }
607   pthread_cleanup_pop(1);
608   return NULL;
609 }
610 
611 /* function to disable lookups through the nss_ldap module to avoid lookup
612    loops */
disable_nss_ldap(void)613 static void disable_nss_ldap(void)
614 {
615   void *handle;
616   char *error;
617   char **version_info;
618   int *enable_flag;
619   /* try to load the NSS module */
620 #ifdef RTLD_NODELETE
621   handle = dlopen(NSS_LDAP_SONAME, RTLD_LAZY | RTLD_NODELETE);
622 #else /* not RTLD_NODELETE */
623   handle = dlopen(NSS_LDAP_SONAME, RTLD_LAZY);
624 #endif /* RTLD_NODELETE */
625   if (handle == NULL)
626   {
627     log_log(LOG_WARNING, "Warning: NSS_LDAP module not loaded: %s", dlerror());
628     return;
629   }
630   /* clear any existing errors */
631   dlerror();
632   /* lookup the NSS version if possible */
633   version_info = (char **)dlsym(handle, "_nss_" MODULE_NAME "_version");
634   error = dlerror();
635   if ((version_info != NULL) && (error == NULL))
636     log_log(LOG_DEBUG, "NSS_LDAP %s %s", version_info[0], version_info[1]);
637   else
638     log_log(LOG_WARNING, "Warning: NSS_LDAP version missing: %s", error);
639   /* clear any existing errors */
640   dlerror();
641   /* try to look up the flag */
642   enable_flag = (int *)dlsym(handle, "_nss_" MODULE_NAME "_enablelookups");
643   error = dlerror();
644   if ((enable_flag == NULL) || (error != NULL))
645   {
646     log_log(LOG_WARNING, "Warning: %s (probably older NSS module loaded)",
647             error);
648     /* fall back to changing the way host lookup is done */
649 #ifdef HAVE___NSS_CONFIGURE_LOOKUP
650     if (__nss_configure_lookup("hosts", "files dns"))
651       log_log(LOG_ERR, "unable to override hosts lookup method: %s",
652               strerror(errno));
653 #endif /* HAVE___NSS_CONFIGURE_LOOKUP */
654     dlclose(handle);
655     return;
656   }
657   /* disable nss_ldap */
658   *enable_flag = 0;
659 #ifdef RTLD_NODELETE
660   /* only close the handle if RTLD_NODELETE was used */
661   dlclose(handle);
662 #endif /* RTLD_NODELETE */
663 }
664 
665 /* poke the OOM killer so nslcd will never get killed */
adjust_oom_score(void)666 static void adjust_oom_score(void)
667 {
668   int oom_adj_fd;
669   if ((oom_adj_fd = open(OOM_SCORE_ADJ_FILE, O_WRONLY)) >= 0)
670   {
671     if (write(oom_adj_fd, OOM_SCORE_ADJ, strlen(OOM_SCORE_ADJ)) < 0)
672       log_log(LOG_WARNING, "writing oom score adjustment of %s failed: %s",
673         OOM_SCORE_ADJ, strerror(errno));
674     close(oom_adj_fd);
675   }
676   else
677   {
678     log_log(LOG_DEBUG, "could not open %s to adjust the OOM score: %s",
679       OOM_SCORE_ADJ_FILE, strerror(errno));
680   }
681 }
682 
683 /* the main program... */
main(int argc,char * argv[])684 int main(int argc, char *argv[])
685 {
686   int i;
687   sigset_t signalmask, oldmask;
688 #ifdef HAVE_PTHREAD_TIMEDJOIN_NP
689   struct timespec ts;
690 #endif /* HAVE_PTHREAD_TIMEDJOIN_NP */
691   /* block all these signals so our worker threads won't handle them */
692   sigemptyset(&signalmask);
693   sigaddset(&signalmask, SIGHUP);
694   sigaddset(&signalmask, SIGINT);
695   sigaddset(&signalmask, SIGQUIT);
696   sigaddset(&signalmask, SIGABRT);
697   sigaddset(&signalmask, SIGPIPE);
698   sigaddset(&signalmask, SIGTERM);
699   sigaddset(&signalmask, SIGUSR1);
700   sigaddset(&signalmask, SIGUSR2);
701   pthread_sigmask(SIG_BLOCK, &signalmask, &oldmask);
702   /* close all file descriptors (except stdin/out/err) */
703   daemonize_closefds();
704   /* parse the command line */
705   parse_cmdline(argc, argv);
706   /* clean the environment */
707 #ifdef HAVE_CLEARENV
708   if (clearenv() || putenv("HOME=/") || putenv("TMPDIR=/tmp") ||
709       putenv("LDAPNOINIT=1"))
710   {
711     log_log(LOG_ERR, "clearing environment failed");
712     exit(EXIT_FAILURE);
713   }
714 #else /* not HAVE_CLEARENV */
715   /* this is a bit ugly */
716   environ = sane_environment;
717 #endif /* not HAVE_CLEARENV */
718   /* disable the nss_ldap module for this process */
719   disable_nss_ldap();
720   /* set LDAP log level */
721   if (myldap_set_debuglevel(nslcd_debugging) != LDAP_SUCCESS)
722     exit(EXIT_FAILURE);
723   /* read configuration file */
724   cfg_init(NSLCD_CONF_PATH);
725   /* set default mode for pidfile and socket */
726   (void)umask((mode_t)0022);
727   /* see if someone already locked the pidfile
728      if --check option was given exit TRUE if daemon runs
729      (pidfile locked), FALSE otherwise */
730   if (nslcd_checkonly)
731   {
732     if (is_locked(NSLCD_PIDFILE))
733     {
734       log_log(LOG_DEBUG, "pidfile (%s) is locked", NSLCD_PIDFILE);
735       exit(EXIT_SUCCESS);
736     }
737     else
738     {
739       log_log(LOG_DEBUG, "pidfile (%s) is not locked", NSLCD_PIDFILE);
740       exit(EXIT_FAILURE);
741     }
742   }
743   /* change directory */
744   if (chdir("/") != 0)
745   {
746     log_log(LOG_ERR, "chdir failed: %s", strerror(errno));
747     exit(EXIT_FAILURE);
748   }
749   /* normal check for pidfile locked */
750   if (is_locked(NSLCD_PIDFILE))
751   {
752     log_log(LOG_ERR, "nslcd may already be active, cannot acquire lock (%s): %s",
753             NSLCD_PIDFILE, strerror(errno));
754     exit(EXIT_FAILURE);
755   }
756   /* daemonize */
757   if ((!nslcd_debugging) && (!nslcd_nofork))
758   {
759     errno = 0;
760     if (daemonize_daemon() != 0)
761     {
762       log_log(LOG_ERR, "unable to daemonize: %s", strerror(errno));
763       exit(EXIT_FAILURE);
764     }
765   }
766   /* intilialize logging */
767   if (!nslcd_debugging)
768   {
769     daemonize_redirect_stdio();
770     log_startlogging();
771   }
772   /* write pidfile */
773   create_pidfile(NSLCD_PIDFILE);
774   /* log start */
775   log_log(LOG_INFO, "version %s starting", VERSION);
776   /* install handler to close stuff off on exit and log notice */
777   if (atexit(exithandler))
778   {
779     log_log(LOG_ERR, "atexit() failed: %s", strerror(errno));
780     daemonize_ready(EXIT_FAILURE, "atexit() failed\n");
781     exit(EXIT_FAILURE);
782   }
783   adjust_oom_score();
784   /* start subprocess to do invalidating if reconnect_invalidate is set */
785   for (i = 0; i < LM_NONE; i++)
786     if (nslcd_cfg->reconnect_invalidate[i])
787       break;
788   if (i < LM_NONE)
789     invalidator_start();
790   /* change nslcd group and supplemental groups */
791   if ((nslcd_cfg->gid != NOGID) && (nslcd_cfg->uidname != NULL))
792   {
793 #ifdef HAVE_INITGROUPS
794     /* load supplementary groups */
795     if (initgroups(nslcd_cfg->uidname, nslcd_cfg->gid) < 0)
796       log_log(LOG_WARNING, "cannot initgroups(\"%s\",%lu) (ignored): %s",
797               nslcd_cfg->uidname, (unsigned long int)nslcd_cfg->gid, strerror(errno));
798     else
799       log_log(LOG_DEBUG, "initgroups(\"%s\",%lu) done",
800               nslcd_cfg->uidname, (unsigned long int)nslcd_cfg->gid);
801 #else /* not HAVE_INITGROUPS */
802 #ifdef HAVE_SETGROUPS
803     /* just drop all supplemental groups */
804     if (setgroups(0, NULL) < 0)
805       log_log(LOG_WARNING, "cannot setgroups(0,NULL) (ignored): %s",
806               strerror(errno));
807     else
808       log_log(LOG_DEBUG, "setgroups(0,NULL) done");
809 #else /* not HAVE_SETGROUPS */
810     log_log(LOG_DEBUG, "neither initgroups() or setgroups() available");
811 #endif /* not HAVE_SETGROUPS */
812 #endif /* not HAVE_INITGROUPS */
813   }
814   /* change to nslcd gid */
815   if (nslcd_cfg->gid != NOGID)
816   {
817     if (setgid(nslcd_cfg->gid) != 0)
818     {
819       log_log(LOG_ERR, "cannot setgid(%lu): %s",
820               (unsigned long int)nslcd_cfg->gid, strerror(errno));
821       daemonize_ready(EXIT_FAILURE, "cannot setgid()\n");
822       exit(EXIT_FAILURE);
823     }
824     log_log(LOG_DEBUG, "setgid(%lu) done", (unsigned long int)nslcd_cfg->gid);
825   }
826   /* change to nslcd uid */
827   if (nslcd_cfg->uid != NOUID)
828   {
829     if (setuid(nslcd_cfg->uid) != 0)
830     {
831       log_log(LOG_ERR, "cannot setuid(%lu): %s",
832               (unsigned long int)nslcd_cfg->uid, strerror(errno));
833       daemonize_ready(EXIT_FAILURE, "cannot setuid()\n");
834       exit(EXIT_FAILURE);
835     }
836     log_log(LOG_DEBUG, "setuid(%lu) done", (unsigned long int)nslcd_cfg->uid);
837   }
838   /* create socket */
839   nslcd_serversocket = create_socket(NSLCD_SOCKET);
840   /* start worker threads */
841   log_log(LOG_INFO, "accepting connections");
842   nslcd_threads = (pthread_t *)malloc(nslcd_cfg->threads * sizeof(pthread_t));
843   if (nslcd_threads == NULL)
844   {
845     log_log(LOG_CRIT, "main(): malloc() failed to allocate memory");
846     daemonize_ready(EXIT_FAILURE, "malloc() failed to allocate memory\n");
847     exit(EXIT_FAILURE);
848   }
849   for (i = 0; i < nslcd_cfg->threads; i++)
850   {
851     if (pthread_create(&nslcd_threads[i], NULL, worker, NULL))
852     {
853       log_log(LOG_ERR, "unable to start worker thread %d: %s",
854               i, strerror(errno));
855       daemonize_ready(EXIT_FAILURE, "unable to start worker thread\n");
856       exit(EXIT_FAILURE);
857     }
858   }
859   /* install signal handlers for some signals */
860   install_sighandler(SIGHUP, sig_handler);
861   install_sighandler(SIGINT, sig_handler);
862   install_sighandler(SIGQUIT, sig_handler);
863   install_sighandler(SIGABRT, sig_handler);
864   install_sighandler(SIGPIPE, SIG_IGN);
865   install_sighandler(SIGTERM, sig_handler);
866   install_sighandler(SIGUSR1, sig_handler);
867   install_sighandler(SIGUSR2, SIG_IGN);
868   /* signal the starting process to exit because we can provide services now */
869   daemonize_ready(EXIT_SUCCESS, NULL);
870   /* enable receiving of signals */
871   pthread_sigmask(SIG_SETMASK, &oldmask, NULL);
872   /* wait until we received a signal */
873   while ((nslcd_receivedsignal == 0) || (nslcd_receivedsignal == SIGUSR1))
874   {
875     sleep(INT_MAX); /* sleep as long as we can or until we receive a signal */
876     if (nslcd_receivedsignal == SIGUSR1)
877     {
878       log_log(LOG_INFO, "caught signal %s (%d), refresh retries",
879               signame(nslcd_receivedsignal), nslcd_receivedsignal);
880       myldap_immediate_reconnect();
881       nslcd_receivedsignal = 0;
882     }
883   }
884   /* print something about received signal */
885   log_log(LOG_INFO, "caught signal %s (%d), shutting down",
886           signame(nslcd_receivedsignal), nslcd_receivedsignal);
887   /* cancel all running threads */
888   for (i = 0; i < nslcd_cfg->threads; i++)
889     if (pthread_cancel(nslcd_threads[i]))
890       log_log(LOG_WARNING, "failed to stop thread %d (ignored): %s",
891               i, strerror(errno));
892   /* close server socket to trigger failures in threads waiting on accept() */
893   close(nslcd_serversocket);
894   nslcd_serversocket = -1;
895   /* if we can, wait a few seconds for the threads to finish */
896 #ifdef HAVE_PTHREAD_TIMEDJOIN_NP
897   ts.tv_sec = time(NULL) + 3;
898   ts.tv_nsec = 0;
899 #endif /* HAVE_PTHREAD_TIMEDJOIN_NP */
900   for (i = 0; i < nslcd_cfg->threads; i++)
901   {
902 #ifdef HAVE_PTHREAD_TIMEDJOIN_NP
903     pthread_timedjoin_np(nslcd_threads[i], NULL, &ts);
904 #endif /* HAVE_PTHREAD_TIMEDJOIN_NP */
905     if (pthread_kill(nslcd_threads[i], 0) == 0)
906       log_log(LOG_ERR, "thread %d is still running, shutting down anyway", i);
907   }
908   /* we're done */
909   return EXIT_SUCCESS;
910 }
911