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