]> git.sur5r.net Git - openldap/blob - servers/slurpd/config.c
9e88115c1dd4053f44de9582224fecfe1aef9a9f
[openldap] / servers / slurpd / config.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2004 The OpenLDAP Foundation.
5  * Portions Copyright 2003 Mark Benson.
6  * Portions Copyright 2002 John Morrissey.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* Portions Copyright (c) 1996 Regents of the University of Michigan.
18  * All rights reserved.
19  *
20  * Redistribution and use in source and binary forms are permitted
21  * provided that this notice is preserved and that due credit is given
22  * to the University of Michigan at Ann Arbor. The name of the University
23  * may not be used to endorse or promote products derived from this
24  * software without specific prior written permission. This software
25  * is provided ``as is'' without express or implied warranty.
26  */
27 /* ACKNOWLEDGEMENTS:
28  * This work was originally developed by the University of Michigan
29  * (as part of U-MICH LDAP).  Additional signficant contributors
30  * include:
31  *    John Morrissey
32  *    Mark Benson
33  */
34
35
36 /*
37  * config.c - configuration file handling routines
38  */
39
40 #include "portable.h"
41
42 #include <stdio.h>
43
44 #include <ac/stdlib.h>
45 #include <ac/string.h>
46 #include <ac/socket.h>
47 #include <ac/ctype.h>
48
49 #include <ldap.h>
50 #include <lutil.h>
51
52 #include "slurp.h"
53 #include "globals.h"
54
55 #define ARGS_STEP       512
56
57 /* Forward declarations */
58 static void     add_replica LDAP_P(( char **, int ));
59 static int      parse_replica_line LDAP_P(( char **, int, Ri *));
60 static void     parse_line LDAP_P(( char * ));
61 static char     *getline LDAP_P(( FILE * ));
62 static char     *strtok_quote LDAP_P(( char *, char * ));
63
64 int     cargc = 0, cargv_size = 0;
65 char    **cargv;
66 /* current config file line # */
67 static int      lineno;
68
69 char *slurpd_pid_file = NULL;
70 char *slurpd_args_file = NULL;
71
72 /*
73  * Read the slapd config file, looking only for config options we're
74  * interested in.  Since we haven't detached from the controlling
75  * terminal yet, we just perror() and fprintf here.
76  */
77 int
78 slurpd_read_config(
79     char        *fname
80 )
81 {
82     FILE        *fp;
83     char        *line;
84
85         if ( cargv == NULL ) {
86         cargv = ch_calloc( ARGS_STEP + 1, sizeof(*cargv) );
87         cargv_size = ARGS_STEP + 1;
88         }
89
90 #ifdef NEW_LOGGING
91     LDAP_LOG ( CONFIG, ARGS, 
92                 "slurpd_read_config: Config: opening config file \"%s\"\n", 
93                 fname, 0, 0 );
94 #else
95     Debug( LDAP_DEBUG_CONFIG, "Config: opening config file \"%s\"\n",
96             fname, 0, 0 );
97 #endif
98
99     if ( (fp = fopen( fname, "r" )) == NULL ) {
100         perror( fname );
101         exit( EXIT_FAILURE );
102     }
103
104     lineno = 0;
105     while ( (line = getline( fp )) != NULL ) {
106         /* skip comments and blank lines */
107         if ( line[0] == '#' || line[0] == '\0' ) {
108             continue;
109         }
110
111 #ifdef NEW_LOGGING
112     LDAP_LOG ( CONFIG, DETAIL1, 
113                 "slurpd_read_config: Config: (%s)\n", line, 0, 0 );
114 #else
115         Debug( LDAP_DEBUG_CONFIG, "Config: (%s)\n", line, 0, 0 );
116 #endif
117
118         parse_line( line );
119
120         if ( cargc < 1 ) {
121             fprintf( stderr, "line %d: bad config line (ignored)\n", lineno );
122             continue;
123         }
124
125         /* replication log file to which changes are appended */
126         if ( strcasecmp( cargv[0], "replogfile" ) == 0 ) {
127             /* 
128              * if slapd_replogfile has a value, the -r option was given,
129              * so use that value.  If slapd_replogfile has length == 0,
130              * then we should use the value in the config file we're reading.
131              */
132             if ( sglob->slapd_replogfile[ 0 ] == '\0' ) {
133                 if ( cargc < 2 ) {
134                     fprintf( stderr,
135                         "line %d: missing filename in \"replogfile ",
136                         lineno );
137                     fprintf( stderr, "<filename>\" line\n" );
138                     exit( EXIT_FAILURE );
139                 } else if ( cargc > 2 && *cargv[2] != '#' ) {
140                     fprintf( stderr,
141                         "line %d: extra cruft at the end of \"replogfile %s\"",
142                         lineno, cargv[1] );
143                     fprintf( stderr, "line (ignored)\n" );
144                 }
145                 LUTIL_SLASHPATH( cargv[1] );
146                 strcpy( sglob->slapd_replogfile, cargv[1] );
147             }
148         } else if ( strcasecmp( cargv[0], "replica" ) == 0 ) {
149             add_replica( cargv, cargc );
150             
151             /* include another config file */
152         } else if ( strcasecmp( cargv[0], "include" ) == 0 ) {
153             char *savefname;
154             int savelineno;
155
156             if ( cargc < 2 ) {
157 #ifdef NEW_LOGGING
158                 LDAP_LOG( CONFIG, CRIT,
159                         "%s: line %d: missing filename in \"include "
160                         "<filename>\" line.\n", fname, lineno , 0 );
161 #else
162                 Debug( LDAP_DEBUG_ANY,
163         "%s: line %d: missing filename in \"include <filename>\" line\n",
164                         fname, lineno, 0 );
165 #endif
166                 
167                 return( 1 );
168             }
169             LUTIL_SLASHPATH( cargv[1] );
170             savefname = strdup( cargv[1] );
171             savelineno = lineno;
172             
173             if ( slurpd_read_config( savefname ) != 0 ) {
174                 return( 1 );
175             }
176                 
177             free( savefname );
178             lineno = savelineno - 1;
179
180         } else if ( strcasecmp( cargv[0], "replica-pidfile" ) == 0 ) {
181                 if ( cargc < 2 ) {
182 #ifdef NEW_LOGGING
183                         LDAP_LOG( CONFIG, CRIT, 
184                                 "%s: line %d missing file name in \"replica-pidfile <file>\" "
185                                 "line.\n", fname, lineno, 0 );
186 #else
187                         Debug( LDAP_DEBUG_ANY,
188             "%s: line %d: missing file name in \"replica-pidfile <file>\" line\n",
189                                 fname, lineno, 0 );
190 #endif
191
192                         return( 1 );
193                 }
194
195                 LUTIL_SLASHPATH( cargv[1] );
196                 slurpd_pid_file = ch_strdup( cargv[1] );
197
198         } else if ( strcasecmp( cargv[0], "replica-argsfile" ) == 0 ) {
199                 if ( cargc < 2 ) {
200 #ifdef NEW_LOGGING
201                         LDAP_LOG( CONFIG, CRIT, 
202                                    "%s: %d: missing file name in "
203                                    "\"argsfile <file>\" line.\n",
204                                    fname, lineno, 0 );
205 #else
206                         Debug( LDAP_DEBUG_ANY,
207             "%s: line %d: missing file name in \"argsfile <file>\" line\n",
208                             fname, lineno, 0 );
209 #endif
210
211                         return( 1 );
212                 }
213
214                 LUTIL_SLASHPATH( cargv[1] );
215                 slurpd_args_file = ch_strdup( cargv[1] );
216
217                 } else if ( strcasecmp( cargv[0], "replicationinterval" ) == 0 ) {
218                         int c;
219                         if ( cargc < 2 ) {
220 #ifdef NEW_LOGGING
221                                 LDAP_LOG( CONFIG, CRIT, "%s: %d: missing interval in "
222                                         "\"replicationinterval <seconds>\" line.\n",
223                                         fname, lineno, 0 );
224 #else
225                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: missing interval in "
226                                         "\"replicationinterval <seconds>\" line\n",
227                                         fname, lineno, 0 );
228 #endif
229                                 return( 1 );
230                         }
231
232                         c = atoi( cargv[1] );
233                         if( c < 1 ) {
234 #ifdef NEW_LOGGING
235                                 LDAP_LOG( CONFIG, CRIT, "%s: line %d: invalid interval "
236                                         "(%d) in \"replicationinterval <seconds>\" line\n",
237                                         fname, lineno, c );
238 #else
239                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: invalid interval "
240                                         "(%d) in \"replicationinterval <seconds>\" line\n",
241                                         fname, lineno, c );
242 #endif
243
244                                 return( 1 );
245                         }
246
247                         sglob->no_work_interval = c;
248                 }
249     }
250     fclose( fp );
251 #ifdef NEW_LOGGING
252     LDAP_LOG ( CONFIG, RESULTS, 
253                 "slurpd_read_config: Config: "
254                 "** configuration file successfully read and parsed\n", 0, 0, 0 );
255 #else
256     Debug( LDAP_DEBUG_CONFIG,
257             "Config: ** configuration file successfully read and parsed\n",
258             0, 0, 0 );
259 #endif
260     return 0;
261 }
262
263
264
265
266 /*
267  * Parse one line of input.
268  */
269 static void
270 parse_line(
271     char        *line
272 )
273 {
274     char *      token;
275
276     cargc = 0;
277     for ( token = strtok_quote( line, " \t" ); token != NULL;
278         token = strtok_quote( NULL, " \t" ) )
279     {
280         if ( cargc == cargv_size - 1 ) {
281             char **tmp;
282             tmp = ch_realloc( cargv, (cargv_size + ARGS_STEP) *
283                                sizeof(*cargv) );
284             if (tmp == NULL) {
285                 cargc = 0;
286                 return;
287             }
288             cargv = tmp;
289             cargv_size += ARGS_STEP;
290         }
291
292         cargv[cargc++] = token;
293     }
294     cargv[cargc] = NULL;
295 }
296
297
298
299
300 static char *
301 strtok_quote(
302     char *line,
303     char *sep
304 )
305 {
306     int         inquote;
307     char        *tmp;
308     static char *next;
309
310     if ( line != NULL ) {
311         next = line;
312     }
313     while ( *next && strchr( sep, *next ) ) {
314         next++;
315     }
316
317     if ( *next == '\0' ) {
318         next = NULL;
319         return( NULL );
320     }
321     tmp = next;
322
323     for ( inquote = 0; *next; ) {
324         switch ( *next ) {
325         case '"':
326             if ( inquote ) {
327                 inquote = 0;
328             } else {
329                 inquote = 1;
330             }
331             AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
332             break;
333
334         case '\\':
335             if ( next[1] )
336                 AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
337             next++;             /* dont parse the escaped character */
338             break;
339
340         default:
341             if ( ! inquote ) {
342                 if ( strchr( sep, *next ) != NULL ) {
343                     *next++ = '\0';
344                     return( tmp );
345                 }
346             }
347             next++;
348             break;
349         }
350     }
351
352     return( tmp );
353 }
354
355 #define CATLINE( buf )  { \
356     int len; \
357     len = strlen( buf ); \
358     while ( lcur + len + 1 > lmax ) { \
359         lmax += BUFSIZ; \
360         line = (char *) ch_realloc( line, lmax ); \
361     } \
362     strcpy( line + lcur, buf ); \
363     lcur += len; \
364 }
365
366
367
368 /*
369  * Get a line of input.
370  */
371 static char *
372 getline(
373     FILE *fp
374 )
375 {
376     char        *p;
377     static char buf[BUFSIZ];
378     static char *line;
379     static int  lmax, lcur;
380
381     lcur = 0;
382     CATLINE( buf );
383     while ( fgets( buf, sizeof(buf), fp ) != NULL ) {
384         if ( (p = strchr( buf, '\n' )) != NULL ) {
385                 if( p > buf && p[-1] == '\r' ) --p;       
386                 *p = '\0';
387         }
388         lineno++;
389         if ( ! isspace( (unsigned char) buf[0] ) ) {
390             return( line );
391         }
392
393         /* change leading whitespace to space */
394         buf[0] = ' ';
395
396         CATLINE( buf );
397     }
398     buf[0] = '\0';
399
400     return( line[0] ? line : NULL );
401 }
402
403
404 /*
405  * Add a node to the array of replicas.
406  */
407 static void
408 add_replica(
409     char        **cargv,
410     int         cargc
411 )
412 {
413     int nr;
414
415     nr = ++sglob->num_replicas;
416     sglob->replicas = (Ri **) ch_realloc( sglob->replicas,
417             ( nr + 1 )  * sizeof( Re * ));
418     if ( sglob->replicas == NULL ) {
419         fprintf( stderr, "out of memory, add_replica\n" );
420         exit( EXIT_FAILURE );
421     }
422     sglob->replicas[ nr ] = NULL; 
423
424     if ( Ri_init( &(sglob->replicas[ nr - 1 ])) < 0 ) {
425         fprintf( stderr, "out of memory, Ri_init\n" );
426         exit( EXIT_FAILURE );
427     }
428     if ( parse_replica_line( cargv, cargc,
429             sglob->replicas[ nr - 1] ) < 0 ) {
430         /* Something bad happened - back out */
431         fprintf( stderr,
432             "Warning: failed to add replica \"%s:%d - ignoring replica\n",
433             sglob->replicas[ nr - 1 ]->ri_hostname == NULL ?
434             "(null)" : sglob->replicas[ nr - 1 ]->ri_hostname,
435             sglob->replicas[ nr - 1 ]->ri_port );
436         sglob->replicas[ nr - 1] = NULL;
437         sglob->num_replicas--;
438     } else {
439 #ifdef NEW_LOGGING
440     LDAP_LOG ( CONFIG, RESULTS, 
441                 "add_replica: Config: ** successfully added replica \"%s%d\"\n", 
442                 sglob->replicas[ nr - 1 ]->ri_hostname == NULL ?
443                 "(null)" : sglob->replicas[ nr - 1 ]->ri_hostname,
444                 sglob->replicas[ nr - 1 ]->ri_port, 0 );
445 #else
446         Debug( LDAP_DEBUG_CONFIG,
447                 "Config: ** successfully added replica \"%s:%d\"\n",
448                 sglob->replicas[ nr - 1 ]->ri_hostname == NULL ?
449                 "(null)" : sglob->replicas[ nr - 1 ]->ri_hostname,
450                 sglob->replicas[ nr - 1 ]->ri_port, 0 );
451 #endif
452         sglob->replicas[ nr - 1]->ri_stel =
453                 sglob->st->st_add( sglob->st,
454                 sglob->replicas[ nr - 1 ] );
455         if ( sglob->replicas[ nr - 1]->ri_stel == NULL ) {
456             fprintf( stderr, "Failed to add status element structure\n" );
457             exit( EXIT_FAILURE );
458         }
459     }
460 }
461
462
463
464 /* 
465  * Parse a "replica" line from the config file.  replica lines should be
466  * in the following format:
467  * replica    host=<hostname:portnumber> binddn=<binddn>
468  *            bindmethod="simple" credentials=<creds>
469  *
470  * where:
471  * <hostname:portnumber> describes the host name and port number where the
472  * replica is running,
473  *
474  * <binddn> is the DN to bind to the replica slapd as,
475  *
476  * bindmethod is "simple", and
477  *
478  * <creds> are the credentials (e.g. password) for binddn.  <creds> are
479  * only used for bindmethod=simple.  
480  *
481  * The "replica" config file line may be split across multiple lines.  If
482  * a line begins with whitespace, it is considered a continuation of the
483  * previous line.
484  */
485 #define GOT_HOST        1
486 #define GOT_DN          2
487 #define GOT_METHOD      4
488 #define GOT_ALL         ( GOT_HOST | GOT_DN | GOT_METHOD )
489 #define GOT_MECH        8
490
491 static int
492 parse_replica_line( 
493     char        **cargv,
494     int         cargc,
495     Ri          *ri
496 )
497 {
498     int         gots = 0;
499     int         i;
500     char        *hp, *val;
501     LDAPURLDesc *ludp;
502
503     for ( i = 1; i < cargc; i++ ) {
504         if ( !strncasecmp( cargv[ i ], HOSTSTR, sizeof( HOSTSTR ) - 1 ) ) {
505                 if ( gots & GOT_HOST ) {
506                         fprintf( stderr, "Error: Malformed \"replica\" line in slapd config " );
507                         fprintf( stderr, "file, too many host or uri names specified, line %d\n",
508                                 lineno );
509                         return -1;
510                 }       
511             val = cargv[ i ] + sizeof( HOSTSTR ); /* '\0' string terminator accounts for '=' */
512             if (( hp = strchr( val, ':' )) != NULL ) {
513                 *hp = '\0';
514                 hp++;
515                 ri->ri_port = atoi( hp );
516             }
517             if ( ri->ri_port <= 0 ) {
518                 ri->ri_port = 0;
519             }
520             ri->ri_hostname = strdup( val );
521             gots |= GOT_HOST;
522         } else if ( !strncasecmp( cargv[ i ], URISTR, sizeof( URISTR ) - 1 ) ) {
523                 if ( gots & GOT_HOST ) {
524                         fprintf( stderr, "Error: Malformed \"replica\" line in slapd config " );
525                         fprintf( stderr, "file, too many host or uri names specified, line %d\n",
526                                 lineno );
527                         return -1;
528                 }               
529                 if ( ldap_url_parse( cargv[ i ] + sizeof( URISTR ), &ludp ) != LDAP_SUCCESS ) {
530                         fprintf( stderr, "Error: Malformed \"replica\" line in slapd config " );
531                         fprintf( stderr, "file, bad uri format specified, line %d\n",
532                                 lineno );
533                         return -1;
534                 }
535                 if (ludp->lud_host == NULL) {
536                         fprintf( stderr, "Error: Malformed \"replica\" line in slapd config " );
537                         fprintf( stderr, "file, missing uri hostname, line %d\n",
538                                 lineno );
539                         return -1;
540                 }
541                 ri->ri_hostname = strdup ( ludp->lud_host );
542                 ri->ri_port = ludp->lud_port;
543                 ri->ri_uri = strdup ( cargv[ i ] + sizeof( URISTR ) );          
544                 ldap_free_urldesc( ludp );                              
545             gots |= GOT_HOST;
546         } else if ( !strncasecmp( cargv[ i ], 
547                         ATTRSTR, sizeof( ATTRSTR ) - 1 ) ) {
548             /* ignore it */ ;
549         } else if ( !strncasecmp( cargv[ i ], 
550                         SUFFIXSTR, sizeof( SUFFIXSTR ) - 1 ) ) {
551             /* ignore it */ ;
552         } else if ( !strncasecmp( cargv[i], STARTTLSSTR, sizeof(STARTTLSSTR)-1 )) {
553             val = cargv[ i ] + sizeof( STARTTLSSTR );
554                 if( !strcasecmp( val, CRITICALSTR ) ) {
555                         ri->ri_tls = TLS_CRITICAL;
556                 } else {
557                         ri->ri_tls = TLS_ON;
558                 }
559         } else if ( !strncasecmp( cargv[ i ], TLSSTR, sizeof( TLSSTR ) - 1 ) ) {
560             val = cargv[ i ] + sizeof( TLSSTR );
561                 if( !strcasecmp( val, CRITICALSTR ) ) {
562                         ri->ri_tls = TLS_CRITICAL;
563                 } else {
564                         ri->ri_tls = TLS_ON;
565                 }
566         } else if ( !strncasecmp( cargv[ i ],
567                         BINDDNSTR, sizeof( BINDDNSTR ) - 1 ) ) { 
568             val = cargv[ i ] + sizeof( BINDDNSTR );
569             ri->ri_bind_dn = strdup( val );
570             gots |= GOT_DN;
571         } else if ( !strncasecmp( cargv[ i ], BINDMETHSTR,
572                 sizeof( BINDMETHSTR ) - 1 ) ) {
573             val = cargv[ i ] + sizeof( BINDMETHSTR );
574             if ( !strcasecmp( val, KERBEROSSTR )) {
575             fprintf( stderr, "Error: a bind method of \"kerberos\" was\n" );
576             fprintf( stderr, "specified in the slapd configuration file.\n" );
577             fprintf( stderr, "slurpd no longer supports Kerberos.\n" );
578             exit( EXIT_FAILURE );
579             } else if ( !strcasecmp( val, SIMPLESTR )) {
580                 ri->ri_bind_method = LDAP_AUTH_SIMPLE;
581                 gots |= GOT_METHOD;
582             } else if ( !strcasecmp( val, SASLSTR )) {
583                 ri->ri_bind_method = LDAP_AUTH_SASL;
584                 gots |= GOT_METHOD;
585             } else {
586                 ri->ri_bind_method = -1;
587             }
588         } else if ( !strncasecmp( cargv[ i ], 
589                         SASLMECHSTR, sizeof( SASLMECHSTR ) - 1 ) ) {
590             val = cargv[ i ] + sizeof( SASLMECHSTR );
591             gots |= GOT_MECH;
592             ri->ri_saslmech = strdup( val );
593         } else if ( !strncasecmp( cargv[ i ], 
594                         CREDSTR, sizeof( CREDSTR ) - 1 ) ) {
595             val = cargv[ i ] + sizeof( CREDSTR );
596             ri->ri_password = strdup( val );
597         } else if ( !strncasecmp( cargv[ i ], 
598                         SECPROPSSTR, sizeof( SECPROPSSTR ) - 1 ) ) {
599             val = cargv[ i ] + sizeof( SECPROPSSTR );
600             ri->ri_secprops = strdup( val );
601         } else if ( !strncasecmp( cargv[ i ], 
602                         REALMSTR, sizeof( REALMSTR ) - 1 ) ) {
603             val = cargv[ i ] + sizeof( REALMSTR );
604             ri->ri_realm = strdup( val );
605         } else if ( !strncasecmp( cargv[ i ], 
606                         AUTHCSTR, sizeof( AUTHCSTR ) - 1 ) ) {
607             val = cargv[ i ] + sizeof( AUTHCSTR );
608             ri->ri_authcId = strdup( val );
609         } else if ( !strncasecmp( cargv[ i ], 
610                         OLDAUTHCSTR, sizeof( OLDAUTHCSTR ) - 1 ) ) {
611             /* Old authcID is provided for some backwards compatibility */
612             val = cargv[ i ] + sizeof( OLDAUTHCSTR );
613             ri->ri_authcId = strdup( val );
614         } else if ( !strncasecmp( cargv[ i ], 
615                         AUTHZSTR, sizeof( AUTHZSTR ) - 1 ) ) {
616             val = cargv[ i ] + sizeof( AUTHZSTR );
617             ri->ri_authzId = strdup( val );
618         } else if ( !strncasecmp( cargv[ i ], 
619                         SRVTABSTR, sizeof( SRVTABSTR ) - 1 ) ) {
620             val = cargv[ i ] + sizeof( SRVTABSTR );
621             if ( ri->ri_srvtab != NULL ) {
622                 free( ri->ri_srvtab );
623             }
624             ri->ri_srvtab = strdup( val );
625         } else {
626             fprintf( stderr, 
627                     "Error: parse_replica_line: unknown keyword \"%s\"\n",
628                     cargv[ i ] );
629         }
630     }
631     
632         if ( ri->ri_bind_method == LDAP_AUTH_SASL) {
633                 if ((gots & GOT_MECH) == 0) {
634                         fprintf( stderr, "Error: \"replica\" line needs SASLmech flag in " );
635                         fprintf( stderr, "slapd config file, line %d\n", lineno );
636                         return -1;
637                 }
638         } else if ( gots != GOT_ALL ) {
639                 fprintf( stderr, "Error: Malformed \"replica\" line in slapd " );
640                 fprintf( stderr, "config file, line %d\n", lineno );
641                 return -1;
642         }
643     return 0;
644 }
645