]> git.sur5r.net Git - openldap/blob - servers/ldapd/main.c
9edd4b532ea503d9886f9f7e84ed3577fb0111e4
[openldap] / servers / ldapd / main.c
1 /*
2  * Copyright (c) 1990-1996 Regents of the University of Michigan.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that this notice is preserved and that due credit is given
7  * to the University of Michigan at Ann Arbor. The name of the University
8  * may not be used to endorse or promote products derived from this
9  * software without specific prior written permission. This software
10  * is provided ``as is'' without express or implied warranty.
11  */
12 /*
13  * Some code fragments to run from inetd stolen from the University
14  * of Minnesota gopher distribution, which had this copyright on it:
15  *
16  * Part of the Internet Gopher program, copyright (C) 1991
17  * University of Minnesota Microcomputer Workstation and Networks Center
18  */
19
20 #include <stdio.h>
21 #include <string.h>
22 #include <sys/types.h>
23 #include <sys/time.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <netdb.h>
28 #include <sys/wait.h>
29 #include <signal.h>
30 #ifdef _AIX
31 #include <sys/select.h>
32 #endif
33 #include <syslog.h>
34 #include <quipu/commonarg.h>
35 #include <quipu/ds_error.h>
36 #include "portable.h"
37 #include "lber.h"
38 #include "ldap.h"
39 #include "common.h"
40
41 #ifdef USE_SYSCONF
42 #include <unistd.h>
43 #endif /* USE_SYSCONF */
44
45 #ifdef TCP_WRAPPERS
46 #include <tcpd.h>
47
48 int allow_severity = LOG_INFO;
49 int deny_severity = LOG_NOTICE;
50 #endif /* TCP_WRAPPERS */
51
52 void log_and_exit();
53 static set_socket();
54 static do_queries();
55 static SIG_FN wait4child();
56 #ifdef CLDAP
57 static udp_init();
58 #endif
59
60 #ifdef LDAP_DEBUG
61 int     ldap_debug;
62 #endif
63 int     version;
64 #ifdef COMPAT
65 int     ldap_compat;
66 #endif
67 int     dosyslog;
68 int     do_tcp = 1;
69 #ifdef CLDAP
70 int     do_udp = 0;
71 #endif
72 int     idletime = DEFAULT_TIMEOUT;
73 int     referral_connection_timeout = DEFAULT_REFERRAL_TIMEOUT;
74 struct timeval  conn_start_tv;
75 #ifdef KERBEROS
76 char    *krb_ldap_service = "ldapserver";
77 char    *krb_x500_service = "x500dsa";
78 char    *krb_x500_instance;
79 char    *krb_x500_nonce;
80 char    *kerberos_keyfile;
81 #endif
82
83 int     dtblsize;
84 int     RunFromInetd = 0;
85
86 extern char Versionstr[];
87
88 static usage( name )
89 char    *name;
90 {
91         fprintf( stderr, "usage: %s [-d debuglvl] [-p port] [-l] [-c dsa] [-r referraltimeout]", name );
92 #ifdef CLDAP
93         fprintf( stderr, " [ -U | -t timeout ]" );
94 #else
95         fprintf( stderr, " [ -t timeout ]" );
96 #endif
97         fprintf( stderr, " [-I]" );
98 #ifdef KERBEROS
99         fprintf( stderr, " [-i dsainstance]" );
100 #endif
101         fprintf( stderr, "\n" );
102 }
103
104 main (argc, argv)
105 int     argc;
106 char    **argv;
107 {
108         int                     tcps, ns;
109 #ifdef CLDAP
110         int                     udps;
111 #endif
112         int                     myport = LDAP_PORT;
113         int                     i, pid, socktype;
114         char                    *myname;
115         fd_set                  readfds;
116         struct hostent          *hp;
117         struct sockaddr_in      from;
118         int                     len;
119         int                     dsapargc;
120         char                    **dsapargv;
121         SIG_FN                  wait4child();
122 #ifndef NOSETPROCTITLE
123         char                    title[80];
124         extern char             **Argv;
125         extern int              Argc;
126 #endif
127         extern char             *optarg;
128         extern int              optind;
129
130 #ifdef VMS
131         /* Pick up socket from inetd-type server on VMS */
132         if ( (ns = socket_from_server( NULL )) > 0 )
133                 RunFromInetd = 1;
134 #else
135         /* Socket from inetd is usually 0 */
136         ns = 0;
137 #endif
138
139         /* for dsap_init */
140         if ( (dsapargv = (char **) malloc( 4 * sizeof(char *) )) == NULL ) {
141                 perror( "malloc" );
142                 exit( 1 );
143         }
144         dsapargv[0] = argv[0];
145         dsapargv[1] = 0;
146         dsapargv[2] = 0;
147         dsapargv[3] = 0;
148         dsapargc = 1;
149 #ifdef KERBEROS
150         kerberos_keyfile = "";
151 #endif
152
153         /* process command line arguments */
154         while ( (i = getopt( argc, argv, "d:lp:f:i:c:r:t:IuU" )) != EOF ) {
155                 switch ( i ) {
156                 case 'c':       /* specify dsa to contact */
157                         dsapargv[1] = "-call";
158                         dsapargv[2] = strdup( optarg );
159                         dsapargc = 3;
160                         break;
161
162                 case 'd':       /* turn on debugging */
163 #ifdef LDAP_DEBUG
164                         ldap_debug = atoi( optarg );
165                         if ( ldap_debug & LDAP_DEBUG_PACKETS )
166                                 lber_debug = ldap_debug;
167 #else
168                         fprintf( stderr, "Not compiled with -DLDAP_DEBUG!\n" );
169 #endif
170                         break;
171
172                 case 'l':       /* do syslogging */
173                         dosyslog = 1;
174                         break;
175
176                 case 'p':       /* specify port number */
177                         myport = atoi( optarg );
178                         break;
179
180                 case 'r':       /* timeout for referral connections */
181                         referral_connection_timeout = atoi( optarg );
182                         break;
183
184                 case 't':       /* timeout for idle connections */
185                         idletime = atoi( optarg );
186                         break;
187
188 #ifdef KERBEROS
189                 case 'f':       /* kerberos key file */
190                         kerberos_keyfile = strdup( optarg );
191                         break;
192
193                 case 'i':       /* x500 dsa kerberos instance */
194                         if ( krb_x500_instance != NULL )
195                                 free( krb_x500_instance );
196                         krb_x500_instance = strdup( optarg );
197                         break;
198 #endif
199
200                 case 'I':       /* Run from inetd */
201                         RunFromInetd = 1;
202                         break;
203
204 #ifdef CLDAP
205                 case 'U':       /* UDP only (no TCP) */
206                         do_tcp = 0;
207                         do_udp = 1;
208                         break;
209
210 #ifdef NOTYET
211                 case 'u':       /* allow UDP requests (CLDAP) */
212                         do_udp = 1;
213                         break;
214 #endif /* NOTYET */
215
216 #endif /* CLDAP */
217
218                 default:
219                         usage( argv[0] );
220                         exit( 1 );
221                 }
222         }
223
224         if ( optind < argc ) {
225                 usage( argv[ 0 ] );
226                 exit( 1 );
227         }
228
229 #ifdef CLDAP
230         if ( do_udp && !do_tcp && idletime != DEFAULT_TIMEOUT ) {
231                 usage( argv[ 0 ] );
232                 exit( 1 );
233         }
234 #endif
235
236         Debug( LDAP_DEBUG_TRACE, "%s", Versionstr, 0, 0 );
237
238 #ifdef USE_SYSCONF
239         dtblsize = sysconf( _SC_OPEN_MAX );
240 #else /* USE_SYSCONF */
241         dtblsize = getdtablesize();
242 #endif /* USE_SYSCONF */
243
244 #ifndef NOSETPROCTITLE
245         /* for setproctitle */
246         Argv = argv;
247         Argc = argc;
248 #endif
249
250         if ( (myname = strrchr( argv[0], '/' )) == NULL )
251                 myname = strdup( argv[0] );
252         else
253                 myname = strdup( myname + 1 );
254
255         /* 
256          * detach from the terminal if stderr is redirected or no
257          * debugging is wanted, and then arrange to reap children
258          * that have exited
259          */
260         if (!RunFromInetd) {
261 #ifndef NOSETPROCTITLE
262                 setproctitle( "initializing" );
263 #endif
264 #ifndef VMS
265                 (void) detach();
266 #endif
267                 (void) SIGNAL( SIGCHLD, (void *) wait4child );
268                 (void) SIGNAL( SIGINT, (void *) log_and_exit );
269         }
270
271         /* 
272          * set up syslogging (if desired)
273          */
274         if ( dosyslog ) {
275 #ifdef LOG_LOCAL4
276                 openlog( myname, OPENLOG_OPTIONS, LOG_LOCAL4 );
277 #else
278                 openlog( myname, OPENLOG_OPTIONS );
279 #endif
280         }
281
282         /* 
283          * load the syntax handlers, oidtables, and initialize some stuff,
284          * then start listening
285          */
286
287         (void) quipu_syntaxes();
288 #ifdef LDAP_USE_PP
289         (void) pp_quipu_init( argv[0] );
290 #endif
291 #if ISODEPACKAGE == IC
292 #if ICRELEASE > 2
293         dsa_operation_syntaxes();
294 #endif
295 #endif
296         (void) dsap_init( &dsapargc, &dsapargv );
297         (void) get_syntaxes();
298         if (RunFromInetd) {
299                 len = sizeof( socktype );
300                 getsockopt( ns, SOL_SOCKET, SO_TYPE, &socktype, &len );
301                 if ( socktype == SOCK_DGRAM ) {
302 #ifdef CLDAP
303                         Debug( LDAP_DEBUG_ARGS,
304                             "CLDAP request from unknown (%s)\n",
305                             inet_ntoa( from.sin_addr ), 0, 0 );
306                         conn_start_tv.tv_sec = 0;
307                         udp_init( 0, 0 );
308                         do_queries( ns, 1 );
309 #else /* CLDAP */
310                         Debug( LDAP_DEBUG_ARGS,
311                             "Compile with -DCLDAP for UDP support\n",0,0,0 );
312 #endif /* CLDAP */
313                         exit( 0 );
314                 }
315
316                 len = sizeof(from);
317                 if ( getpeername( ns, (struct sockaddr *) &from, &len )
318                     == 0 ) {
319                         hp = gethostbyaddr( (char *) &(from.sin_addr.s_addr),
320                         sizeof(from.sin_addr.s_addr), AF_INET );
321                         Debug( LDAP_DEBUG_ARGS, "connection from %s (%s)\n",
322                             (hp == NULL) ? "unknown" : hp->h_name,
323                             inet_ntoa( from.sin_addr ), 0 );
324
325                         if ( dosyslog ) {
326                                 syslog( LOG_INFO, "connection from %s (%s)",
327                                     (hp == NULL) ? "unknown" : hp->h_name,
328                                     inet_ntoa( from.sin_addr ) );
329                         }
330
331 #ifndef NOSETPROCTITLE
332                         sprintf( title, "%s %d\n", hp == NULL ?
333                             inet_ntoa( from.sin_addr ) : hp->h_name, myport );
334                         setproctitle( title );
335 #endif
336                 }
337                 gettimeofday( &conn_start_tv, (struct timezone *) NULL );
338                 do_queries( ns, 0 );
339
340                 exit( 0 );
341         }
342
343         if ( do_tcp )
344             tcps = set_socket( myport, 0 );
345
346 #ifdef CLDAP
347         if ( do_udp )
348                 udps = udp_init( myport, 1 );
349 #endif
350
351         /*
352          * loop, wait for a connection, then fork off a child to handle it
353          * if we are doing CLDAP as well, handle those requests on the fly
354          */
355
356 #ifndef NOSETPROCTITLE
357 #ifdef CLDAP
358         sprintf( title, "listening %s/%s %d", do_tcp ? "tcp" : "",
359             do_udp ? "udp" : "", myport );
360 #else
361         sprintf( title, "listening %s %d", do_tcp ? "tcp" : "", myport );
362 #endif
363         setproctitle( title );
364 #endif
365
366         for ( ;; ) {
367                 FD_ZERO( &readfds );
368                 if ( do_tcp )
369                         FD_SET( tcps, &readfds );
370 #ifdef CLDAP
371                 if ( do_udp )
372                         FD_SET( udps, &readfds );
373 #endif
374
375                 if ( select( dtblsize, &readfds, 0, 0, 0 ) < 1 ) {
376 #ifdef LDAP_DEBUG
377                         if ( ldap_debug ) perror( "main select" );
378 #endif
379                         continue;
380                 }
381
382 #ifdef CLDAP
383                 if ( do_udp && FD_ISSET( udps, &readfds ) ) {
384                         do_queries( udps, 1 );
385                 }
386 #endif
387
388                 if ( !do_tcp || ! FD_ISSET( tcps, &readfds ) ) {
389                         continue;
390                 }
391
392                 len = sizeof(from);
393                 if ( (ns = accept( tcps, (struct sockaddr *) &from, &len ))
394                     == -1 ) {
395 #ifdef LDAP_DEBUG
396                         if ( ldap_debug ) perror( "accept" );
397 #endif
398                         continue;
399                 }
400
401                 hp = gethostbyaddr( (char *) &(from.sin_addr.s_addr),
402                     sizeof(from.sin_addr.s_addr), AF_INET );
403
404 #ifdef TCP_WRAPPERS
405                 if ( !hosts_ctl("ldapd", (hp == NULL) ? "unknown" : hp->h_name,
406                         inet_ntoa( from.sin_addr ), STRING_UNKNOWN ) {
407
408                         Debug( LDAP_DEBUG_ARGS, "connection from %s (%s) denied.\n",
409                                 (hp == NULL) ? "unknown" : hp->h_name,
410                                 inet_ntoa( from.sin_addr ), 0 );
411
412                         if ( dosyslog ) {
413                                 syslog( LOG_NOTICE, "connection from %s (%s) denied.",
414                                     (hp == NULL) ? "unknown" : hp->h_name,
415                                     inet_ntoa( from.sin_addr ) );
416                         }
417
418                         close(ns);
419                         continue;
420                 }
421 #endif /* TCP_WRAPPERS */
422
423                 Debug( LDAP_DEBUG_ARGS, "connection from %s (%s)\n",
424                     (hp == NULL) ? "unknown" : hp->h_name,
425                     inet_ntoa( from.sin_addr ), 0 );
426
427
428                 if ( dosyslog ) {
429                         syslog( LOG_INFO, "connection from %s (%s)",
430                             (hp == NULL) ? "unknown" : hp->h_name,
431                             inet_ntoa( from.sin_addr ) );
432                 }
433
434 #ifdef VMS
435                 /* This is for debug on terminal on VMS */
436                 close( tcps );
437 #ifndef NOSETPROCTITLE
438                 setproctitle( hp == NULL ? inet_ntoa( from.sin_addr ) :
439                     hp->h_name );
440 #endif
441                 gettimeofday( &conn_start_tv, (struct timezone *) NULL );
442                 (void) SIGNAL( SIGPIPE, (void *) log_and_exit );
443
444                 do_queries( ns, 0 );
445                 /* NOT REACHED */
446 #endif
447
448                 switch( pid = fork() ) {
449                 case 0:         /* child */
450                         close( tcps );
451 #ifndef NOSETPROCTITLE
452                         sprintf( title, "%s (%d)\n", hp == NULL ?
453                                 inet_ntoa( from.sin_addr ) : hp->h_name,
454                                 myport );
455                         setproctitle( title );
456 #endif
457                         gettimeofday( &conn_start_tv, (struct timezone *) NULL );
458                         (void) SIGNAL( SIGPIPE, (void *) log_and_exit );
459
460                         do_queries( ns, 0 );
461                         break;
462
463                 case -1:        /* failed */
464 #ifdef LDAP_DEBUG
465                         if ( ldap_debug ) perror( "fork" );
466 #endif
467                         close( ns );
468                         syslog( LOG_ERR, "fork failed %m" );
469                         /* let things cool off */
470                         sleep( 15 );
471                         break;
472
473                 default:        /* parent */
474                         close( ns );
475                         Debug( LDAP_DEBUG_TRACE, "forked child %d\n", pid, 0,
476                             0 );
477                         break;
478                 }
479         }
480         /* NOT REACHED */
481 }
482
483 static
484 do_queries(
485     int clientsock,
486     int udp             /* is this a UDP (CLDAP) request? */
487 )
488 {
489         fd_set          readfds;
490         int             rc, i;
491         struct timeval  timeout;
492         Sockbuf         sb;
493 #ifdef CLDAP
494         struct sockaddr saddr, faddr;
495         struct sockaddr *saddrlist[ 1 ];
496 #endif /* CLDAP */
497
498         Debug( LDAP_DEBUG_TRACE, "do_queries%s\n",
499             udp ? " udp" : "", 0, 0 );
500
501         /*
502          * Loop, wait for a request from the client or a response from
503          * a dsa, then handle it.  Dsap_ad is always a connection to the
504          * "default" dsa.  Other connections can be made as a result of
505          * a referral being chased down.  These association descriptors
506          * are kept track of with the message that caused the referral.
507          * The set_dsa_fds() routine traverses the list of outstanding
508          * messages, setting the appropriate bits in readfds.
509          */
510
511         if ( !udp ) {
512                 conn_init();
513         }
514
515         (void) memset( (void *) &sb, '\0', sizeof( sb ) );
516         sb.sb_sd = clientsock;
517         sb.sb_naddr = ( udp ) ? 1 : 0;
518 #ifdef CLDAP
519         sb.sb_addrs = (void **)saddrlist;
520         sb.sb_fromaddr = &faddr;
521         sb.sb_useaddr = saddrlist[ 0 ] = &saddr;
522 #endif
523         sb.sb_ber.ber_buf = NULL;
524         sb.sb_ber.ber_ptr = NULL;
525         sb.sb_ber.ber_end = NULL;
526
527         timeout.tv_sec = idletime;
528         timeout.tv_usec = 0;
529         for ( ;; ) {
530                 struct conn             *dsaconn;
531                 extern struct conn      *conns;
532
533                 FD_ZERO( &readfds );
534                 FD_SET( clientsock, &readfds );
535                 conn_setfds( &readfds );
536
537 #ifdef LDAP_DEBUG
538                 if ( ldap_debug & LDAP_DEBUG_CONNS ) {
539                         Debug( LDAP_DEBUG_CONNS, "FDLIST:", 0, 0, 0 );
540                         for ( i = 0; i < dtblsize; i++ ) {
541                                 if ( FD_ISSET( i, &readfds ) ) {
542                                         Debug( LDAP_DEBUG_CONNS, " %d", i, 0,
543                                             0);
544                                 }
545                         }
546                         Debug( LDAP_DEBUG_CONNS, "\n", 0, 0, 0 );
547                 }
548 #endif
549
550                 /* 
551                  * hack - because of lber buffering, there might be stuff
552                  * already waiting for us on the client sock.
553                  */
554
555                 if ( sb.sb_ber.ber_ptr >= sb.sb_ber.ber_end ) {
556                         if ( (rc = select( dtblsize, &readfds, 0, 0,
557                             udp ? 0 : &timeout )) < 1 ) {
558 #ifdef LDAP_DEBUG
559                                 if ( ldap_debug ) perror( "do_queries select" );
560 #endif
561                                 if ( rc == 0 )
562                                         log_and_exit( 0 ); /* idle timeout */
563
564                                 Debug( LDAP_DEBUG_ANY, "select returns %d!\n",
565                                     rc, 0, 0 );
566
567                                 /* client gone away - we can too */
568                                 if ( isclosed( clientsock ) )
569                                         log_and_exit( 0 );
570
571                                 /*
572                                  * check if a dsa conn has gone away -
573                                  * mark it bad if so
574                                  */
575                                 conn_badfds();
576
577                                 continue;
578                         }
579                 }
580
581                 if ( sb.sb_ber.ber_ptr < sb.sb_ber.ber_end ||
582                     FD_ISSET( clientsock, &readfds ) ) {
583                         client_request( &sb, conns, udp );
584                 } else {
585                         if ( (dsaconn = conn_getfd( &readfds )) == NULL ) {
586                                 Debug( LDAP_DEBUG_ANY, "No DSA activity!\n",
587                                     0, 0, 0 );
588                                 continue;
589                         }
590
591                         dsa_response( dsaconn, &sb );
592                 }
593         }
594         /* NOT REACHED */
595 }
596
597 static set_socket(
598     int port,
599     int udp     /* UDP port? */
600 )
601 {
602         int                     s, i;
603         struct sockaddr_in      addr;
604
605         if ( (s = socket( AF_INET, udp ? SOCK_DGRAM:SOCK_STREAM, 0 )) == -1 ) {
606                 perror( "socket" );
607                 exit( 1 );
608         }
609
610         /* set option so clients can't keep us from coming back up */
611         i = 1;
612         if ( setsockopt( s, SOL_SOCKET, SO_REUSEADDR, (void *) &i, sizeof(i) )
613             < 0 ) {
614                 perror( "setsockopt" );
615                 exit( 1 );
616         }
617
618         /* bind to a name */
619         (void)memset( (void *)&addr, '\0', sizeof( addr ));
620         addr.sin_family = AF_INET;
621         addr.sin_addr.s_addr = INADDR_ANY;
622         addr.sin_port = htons( port );
623         if ( bind( s, (struct sockaddr *) &addr, sizeof(addr) ) ) {
624                 perror( "bind" );
625                 exit( 1 );
626         }
627
628         if ( !udp ) {
629                 /* listen for connections */
630                 if ( listen( s, 5 ) == -1 ) {
631                         perror( "listen" );
632                         exit( 1 );
633                 }
634         }
635  
636         Debug( LDAP_DEBUG_TRACE, "listening on %s port %d\n",
637                 udp ? "udp" : "tcp", port, 0 );
638
639         return( s );
640 }
641
642 static SIG_FN wait4child()
643 {
644         WAITSTATUSTYPE     status;
645
646         Debug( LDAP_DEBUG_TRACE, "parent: catching child status\n", 0, 0, 0 );
647
648 #ifdef USE_WAITPID
649         while( waitpid( (pid_t) -1, 0, WAIT_FLAGS ) > 0 )
650                 ;       /* NULL */
651 #else
652         while ( wait3( &status, WAIT_FLAGS, 0 ) > 0 )
653                 ;       /* NULL */
654 #endif
655
656         (void) SIGNAL( SIGCHLD, (void *) wait4child );
657 }
658
659
660 void
661 log_and_exit( int exitcode )
662 {
663         struct timeval  tv;
664
665         if ( dosyslog ) {
666                 if ( conn_start_tv.tv_sec == 0 ) {
667                         syslog( LOG_INFO, "UDP exit(%d)", exitcode );
668                 } else {
669                         gettimeofday( &tv, (struct timezone *)NULL );
670                         syslog( LOG_INFO, "TCP closed %d seconds,  exit(%d)",
671                             tv.tv_sec - conn_start_tv.tv_sec, exitcode );
672                 }
673         }
674
675         exit( exitcode );
676 }
677
678
679 #ifdef CLDAP
680 static int
681 udp_init(
682     int port,
683     int createsocket
684 )
685 {
686         int     s, bound;
687         char    *matched;
688         extern char             *dsa_address;
689         extern struct PSAPaddr  *psap_cpy();
690         extern struct conn      *conns;
691
692         if ( createsocket )
693                 s = set_socket( port, 1 );
694
695         conn_init();
696         conns->c_dn = strdup("");
697         conns->c_cred = strdup("");
698         conns->c_credlen = 0;
699         conns->c_method = LDAP_AUTH_SIMPLE;
700
701        if ( dsa_address == NULL || (conns->c_paddr = str2paddr( dsa_address ))
702             == NULLPA ) {
703                 fprintf(stderr, "Bad DSA address (%s)\n", dsa_address ?
704                     dsa_address : "NULL" );
705                 exit( 1 );
706         } else {
707                 conns->c_paddr = psap_cpy(conns->c_paddr);
708         }
709
710         if ( do_bind_real(conns, &bound, &matched) != LDAP_SUCCESS) {
711                 fprintf(stderr, "Cannot bind to directory\n");
712                 exit( 1 );
713         }
714         if ( matched != NULL )
715                 free( matched );
716
717         return( createsocket ? s : 0 );
718 }
719 #endif