~ubuntu-branches/ubuntu/trusty/nss-pam-ldapd/trusty-proposed

« back to all changes in this revision

Viewing changes to .pc/implement-nofork.patch/nslcd/nslcd.c

  • Committer: Package Import Robot
  • Author(s): Arthur de Jong
  • Date: 2013-10-17 23:50:00 UTC
  • Revision ID: package-import@ubuntu.com-20131017235000-5o686322fj05e4l4
Tags: 0.8.13-3
* implement-nofork.patch: introduce a -n, --nofork option that skips the
  daemonising step on start-up (this is related to Ubuntu bug 806761 and
  may be required to get nslcd working with upstart)
* add init script dependency on $network to ensure that network is up
  before starting nslcd (closes: #726435)
* clean generated manual pages to allow the package to be built tice in
  a row

Show diffs side-by-side

added added

removed removed

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