]> git.sur5r.net Git - openldap/blob - servers/slurpd/config.c
Update for release
[openldap] / servers / slurpd / config.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2008 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     *slurpd_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     Debug( LDAP_DEBUG_CONFIG, "Config: opening config file \"%s\"\n",
91             fname, 0, 0 );
92
93     if ( (fp = fopen( fname, "r" )) == NULL ) {
94         perror( fname );
95         exit( EXIT_FAILURE );
96     }
97
98     lineno = 0;
99     while ( (line = slurpd_getline( fp )) != NULL ) {
100         /* skip comments and blank lines */
101         if ( line[0] == '#' || line[0] == '\0' ) {
102             continue;
103         }
104
105         Debug( LDAP_DEBUG_CONFIG, "Config: (%s)\n", line, 0, 0 );
106
107         parse_line( line );
108
109         if ( cargc < 1 ) {
110             fprintf( stderr, "line %d: bad config line (ignored)\n", lineno );
111             continue;
112         }
113
114         /* replication log file to which changes are appended */
115         if ( strcasecmp( cargv[0], "replogfile" ) == 0 ) {
116             /* 
117              * if slapd_replogfile has a value, the -r option was given,
118              * so use that value.  If slapd_replogfile has length == 0,
119              * then we should use the value in the config file we're reading.
120              */
121             if ( sglob->slapd_replogfile[ 0 ] == '\0' ) {
122                 if ( cargc < 2 ) {
123                     fprintf( stderr,
124                         "line %d: missing filename in \"replogfile ",
125                         lineno );
126                     fprintf( stderr, "<filename>\" line\n" );
127                     exit( EXIT_FAILURE );
128                 } else if ( cargc > 2 && *cargv[2] != '#' ) {
129                     fprintf( stderr,
130                         "line %d: extra cruft at the end of \"replogfile %s\"",
131                         lineno, cargv[1] );
132                     fprintf( stderr, "line (ignored)\n" );
133                 }
134                 LUTIL_SLASHPATH( cargv[1] );
135                 strcpy( sglob->slapd_replogfile, cargv[1] );
136             }
137         } else if ( strcasecmp( cargv[0], "replica" ) == 0 ) {
138             add_replica( cargv, cargc );
139             
140             /* include another config file */
141         } else if ( strcasecmp( cargv[0], "include" ) == 0 ) {
142             char *savefname;
143             int savelineno;
144
145             if ( cargc < 2 ) {
146                 Debug( LDAP_DEBUG_ANY,
147         "%s: line %d: missing filename in \"include <filename>\" line\n",
148                         fname, lineno, 0 );
149                 
150                 return( 1 );
151             }
152             LUTIL_SLASHPATH( cargv[1] );
153             savefname = strdup( cargv[1] );
154             savelineno = lineno;
155             
156             if ( slurpd_read_config( savefname ) != 0 ) {
157                 return( 1 );
158             }
159                 
160             free( savefname );
161             lineno = savelineno - 1;
162
163         } else if ( strcasecmp( cargv[0], "replica-pidfile" ) == 0 ) {
164                 if ( cargc < 2 ) {
165                         Debug( LDAP_DEBUG_ANY,
166             "%s: line %d: missing file name in \"replica-pidfile <file>\" line\n",
167                                 fname, lineno, 0 );
168
169                         return( 1 );
170                 }
171
172                 LUTIL_SLASHPATH( cargv[1] );
173                 slurpd_pid_file = ch_strdup( cargv[1] );
174
175         } else if ( strcasecmp( cargv[0], "replica-argsfile" ) == 0 ) {
176                 if ( cargc < 2 ) {
177                         Debug( LDAP_DEBUG_ANY,
178             "%s: line %d: missing file name in \"argsfile <file>\" line\n",
179                             fname, lineno, 0 );
180
181                         return( 1 );
182                 }
183
184                 LUTIL_SLASHPATH( cargv[1] );
185                 slurpd_args_file = ch_strdup( cargv[1] );
186
187                 } else if ( strcasecmp( cargv[0], "replicationinterval" ) == 0 ) {
188                         int c;
189                         if ( cargc < 2 ) {
190                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: missing interval in "
191                                         "\"replicationinterval <seconds>\" line\n",
192                                         fname, lineno, 0 );
193                                 return( 1 );
194                         }
195
196                         if ( lutil_atoi( &c, cargv[1] ) != 0 || c < 1 ) {
197                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: invalid interval "
198                                         "(%d) in \"replicationinterval <seconds>\" line\n",
199                                         fname, lineno, c );
200
201                                 return( 1 );
202                         }
203
204                         sglob->no_work_interval = c;
205                 }
206     }
207     fclose( fp );
208     Debug( LDAP_DEBUG_CONFIG,
209             "Config: ** configuration file successfully read and parsed\n",
210             0, 0, 0 );
211     return 0;
212 }
213
214
215
216
217 /*
218  * Parse one line of input.
219  */
220 static void
221 parse_line(
222     char        *line
223 )
224 {
225     char *      token;
226
227     cargc = 0;
228     for ( token = strtok_quote( line, " \t" ); token != NULL;
229         token = strtok_quote( NULL, " \t" ) )
230     {
231         if ( cargc == cargv_size - 1 ) {
232             char **tmp;
233             tmp = ch_realloc( cargv, (cargv_size + ARGS_STEP) *
234                                sizeof(*cargv) );
235             if (tmp == NULL) {
236                 cargc = 0;
237                 return;
238             }
239             cargv = tmp;
240             cargv_size += ARGS_STEP;
241         }
242
243         cargv[cargc++] = token;
244     }
245     cargv[cargc] = NULL;
246 }
247
248
249
250
251 static char *
252 strtok_quote(
253     char *line,
254     char *sep
255 )
256 {
257     int         inquote;
258     char        *tmp;
259     static char *next;
260
261     if ( line != NULL ) {
262         next = line;
263     }
264     while ( *next && strchr( sep, *next ) ) {
265         next++;
266     }
267
268     if ( *next == '\0' ) {
269         next = NULL;
270         return( NULL );
271     }
272     tmp = next;
273
274     for ( inquote = 0; *next; ) {
275         switch ( *next ) {
276         case '"':
277             if ( inquote ) {
278                 inquote = 0;
279             } else {
280                 inquote = 1;
281             }
282             AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
283             break;
284
285         case '\\':
286             if ( next[1] )
287                 AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
288             next++;             /* dont parse the escaped character */
289             break;
290
291         default:
292             if ( ! inquote ) {
293                 if ( strchr( sep, *next ) != NULL ) {
294                     *next++ = '\0';
295                     return( tmp );
296                 }
297             }
298             next++;
299             break;
300         }
301     }
302
303     return( tmp );
304 }
305
306 #define CATLINE( buf )  { \
307     int len; \
308     len = strlen( buf ); \
309     while ( lcur + len + 1 > lmax ) { \
310         lmax += BUFSIZ; \
311         line = (char *) ch_realloc( line, lmax ); \
312     } \
313     strcpy( line + lcur, buf ); \
314     lcur += len; \
315 }
316
317
318
319 /*
320  * Get a line of input.
321  */
322 static char *
323 slurpd_getline(
324     FILE *fp
325 )
326 {
327     char        *p;
328     static char buf[BUFSIZ];
329     static char *line;
330     static int  lmax, lcur;
331
332     lcur = 0;
333     CATLINE( buf );
334     while ( fgets( buf, sizeof(buf), fp ) != NULL ) {
335         if ( (p = strchr( buf, '\n' )) != NULL ) {
336                 if( p > buf && p[-1] == '\r' ) --p;       
337                 *p = '\0';
338         }
339         lineno++;
340         if ( ! isspace( (unsigned char) buf[0] ) ) {
341             return( line );
342         }
343
344         /* change leading whitespace to space */
345         buf[0] = ' ';
346
347         CATLINE( buf );
348     }
349     buf[0] = '\0';
350
351     return( line[0] ? line : NULL );
352 }
353
354
355 /*
356  * Add a node to the array of replicas.
357  */
358 static void
359 add_replica(
360     char        **cargv,
361     int         cargc
362 )
363 {
364     int nr;
365
366     nr = ++sglob->num_replicas;
367     sglob->replicas = (Ri **) ch_realloc( sglob->replicas,
368             ( nr + 1 )  * sizeof( Re * ));
369     if ( sglob->replicas == NULL ) {
370         fprintf( stderr, "out of memory, add_replica\n" );
371         exit( EXIT_FAILURE );
372     }
373     sglob->replicas[ nr ] = NULL; 
374
375     if ( Ri_init( &(sglob->replicas[ nr - 1 ])) < 0 ) {
376         fprintf( stderr, "out of memory, Ri_init\n" );
377         exit( EXIT_FAILURE );
378     }
379     if ( parse_replica_line( cargv, cargc,
380             sglob->replicas[ nr - 1] ) < 0 ) {
381         /* Something bad happened - back out */
382         fprintf( stderr,
383             "Warning: failed to add replica \"%s:%d - ignoring replica\n",
384             sglob->replicas[ nr - 1 ]->ri_hostname == NULL ?
385             "(null)" : sglob->replicas[ nr - 1 ]->ri_hostname,
386             sglob->replicas[ nr - 1 ]->ri_port );
387         sglob->replicas[ nr - 1] = NULL;
388         sglob->num_replicas--;
389     } else {
390         Debug( LDAP_DEBUG_CONFIG,
391                 "Config: ** successfully added replica \"%s:%d\"\n",
392                 sglob->replicas[ nr - 1 ]->ri_hostname == NULL ?
393                 "(null)" : sglob->replicas[ nr - 1 ]->ri_hostname,
394                 sglob->replicas[ nr - 1 ]->ri_port, 0 );
395         sglob->replicas[ nr - 1]->ri_stel =
396                 sglob->st->st_add( sglob->st,
397                 sglob->replicas[ nr - 1 ] );
398         if ( sglob->replicas[ nr - 1]->ri_stel == NULL ) {
399             fprintf( stderr, "Failed to add status element structure\n" );
400             exit( EXIT_FAILURE );
401         }
402     }
403 }
404
405
406
407 /* 
408  * Parse a "replica" line from the config file.  replica lines should be
409  * in the following format:
410  * replica    host=<hostname:portnumber> binddn=<binddn>
411  *            bindmethod="simple" credentials=<creds>
412  *
413  * where:
414  * <hostname:portnumber> describes the host name and port number where the
415  * replica is running,
416  *
417  * <binddn> is the DN to bind to the replica slapd as,
418  *
419  * bindmethod is "simple", and
420  *
421  * <creds> are the credentials (e.g. password) for binddn.  <creds> are
422  * only used for bindmethod=simple.  
423  *
424  * The "replica" config file line may be split across multiple lines.  If
425  * a line begins with whitespace, it is considered a continuation of the
426  * previous line.
427  */
428 #define GOT_HOST        1
429 #define GOT_DN          2
430 #define GOT_METHOD      4
431 #define GOT_ALL         ( GOT_HOST | GOT_DN | GOT_METHOD )
432 #define GOT_MECH        8
433
434 static int
435 parse_replica_line( 
436     char        **cargv,
437     int         cargc,
438     Ri          *ri
439 )
440 {
441     int         gots = 0;
442     int         i;
443     char        *hp, *val;
444     LDAPURLDesc *ludp;
445
446     for ( i = 1; i < cargc; i++ ) {
447         if ( !strncasecmp( cargv[ i ], HOSTSTR, sizeof( HOSTSTR ) - 1 ) ) {
448                 if ( gots & GOT_HOST ) {
449                         fprintf( stderr, "Error: Malformed \"replica\" line in slapd config " );
450                         fprintf( stderr, "file, too many host or uri names specified, line %d\n",
451                                 lineno );
452                         return -1;
453                 }       
454             val = cargv[ i ] + sizeof( HOSTSTR ); /* '\0' string terminator accounts for '=' */
455             if (( hp = strchr( val, ':' )) != NULL ) {
456                 *hp = '\0';
457                 hp++;
458                 if ( lutil_atoi( &ri->ri_port, hp ) != 0 ) {
459                     fprintf( stderr, "unable to parse port \"%s\", line %d\n",
460                             hp, lineno );
461                     return -1;
462                 }
463             }
464             if ( ri->ri_port <= 0 ) {
465                 ri->ri_port = LDAP_PORT;
466             }
467             ri->ri_hostname = strdup( val );
468             gots |= GOT_HOST;
469         } else if ( !strncasecmp( cargv[ i ], URISTR, sizeof( URISTR ) - 1 ) ) {
470                 if ( gots & GOT_HOST ) {
471                         fprintf( stderr, "Error: Malformed \"replica\" line in slapd config " );
472                         fprintf( stderr, "file, too many host or uri names specified, line %d\n",
473                                 lineno );
474                         return -1;
475                 }               
476                 if ( ldap_url_parse( cargv[ i ] + sizeof( URISTR ), &ludp ) != LDAP_SUCCESS ) {
477                         fprintf( stderr, "Error: Malformed \"replica\" line in slapd config " );
478                         fprintf( stderr, "file, bad uri format specified, line %d\n",
479                                 lineno );
480                         return -1;
481                 }
482                 if (ludp->lud_host == NULL) {
483                         fprintf( stderr, "Error: Malformed \"replica\" line in slapd config " );
484                         fprintf( stderr, "file, missing uri hostname, line %d\n",
485                                 lineno );
486                         return -1;
487                 }
488                 ri->ri_hostname = strdup ( ludp->lud_host );
489                 ri->ri_port = ludp->lud_port;
490                 ri->ri_uri = strdup ( cargv[ i ] + sizeof( URISTR ) );          
491                 ldap_free_urldesc( ludp );                              
492             gots |= GOT_HOST;
493         } else if ( !strncasecmp( cargv[ i ], 
494                         ATTRSTR, sizeof( ATTRSTR ) - 1 ) ) {
495             /* ignore it */ ;
496         } else if ( !strncasecmp( cargv[ i ], 
497                         SUFFIXSTR, sizeof( SUFFIXSTR ) - 1 ) ) {
498             /* ignore it */ ;
499         } else if ( !strncasecmp( cargv[i], STARTTLSSTR, sizeof(STARTTLSSTR)-1 )) {
500             val = cargv[ i ] + sizeof( STARTTLSSTR );
501                 if( !strcasecmp( val, CRITICALSTR ) ) {
502                         ri->ri_tls = TLS_CRITICAL;
503                 } else {
504                         ri->ri_tls = TLS_ON;
505                 }
506         } else if ( !strncasecmp( cargv[ i ], TLSSTR, sizeof( TLSSTR ) - 1 ) ) {
507             val = cargv[ i ] + sizeof( TLSSTR );
508                 if( !strcasecmp( val, CRITICALSTR ) ) {
509                         ri->ri_tls = TLS_CRITICAL;
510                 } else {
511                         ri->ri_tls = TLS_ON;
512                 }
513         } else if ( !strncasecmp( cargv[ i ],
514                         BINDDNSTR, sizeof( BINDDNSTR ) - 1 ) ) { 
515             val = cargv[ i ] + sizeof( BINDDNSTR );
516             ri->ri_bind_dn = strdup( val );
517             gots |= GOT_DN;
518         } else if ( !strncasecmp( cargv[ i ], BINDMETHSTR,
519                 sizeof( BINDMETHSTR ) - 1 ) ) {
520             val = cargv[ i ] + sizeof( BINDMETHSTR );
521             if ( !strcasecmp( val, KERBEROSSTR )) {
522             fprintf( stderr, "Error: a bind method of \"kerberos\" was\n" );
523             fprintf( stderr, "specified in the slapd configuration file.\n" );
524             fprintf( stderr, "slurpd no longer supports Kerberos.\n" );
525             exit( EXIT_FAILURE );
526             } else if ( !strcasecmp( val, SIMPLESTR )) {
527                 ri->ri_bind_method = LDAP_AUTH_SIMPLE;
528                 gots |= GOT_METHOD;
529             } else if ( !strcasecmp( val, SASLSTR )) {
530                 ri->ri_bind_method = LDAP_AUTH_SASL;
531                 gots |= GOT_METHOD;
532             } else {
533                 ri->ri_bind_method = -1;
534             }
535         } else if ( !strncasecmp( cargv[ i ], 
536                         SASLMECHSTR, sizeof( SASLMECHSTR ) - 1 ) ) {
537             val = cargv[ i ] + sizeof( SASLMECHSTR );
538             gots |= GOT_MECH;
539             ri->ri_saslmech = strdup( val );
540         } else if ( !strncasecmp( cargv[ i ], 
541                         CREDSTR, sizeof( CREDSTR ) - 1 ) ) {
542             val = cargv[ i ] + sizeof( CREDSTR );
543             ri->ri_password = strdup( val );
544         } else if ( !strncasecmp( cargv[ i ], 
545                         SECPROPSSTR, sizeof( SECPROPSSTR ) - 1 ) ) {
546             val = cargv[ i ] + sizeof( SECPROPSSTR );
547             ri->ri_secprops = strdup( val );
548         } else if ( !strncasecmp( cargv[ i ], 
549                         REALMSTR, sizeof( REALMSTR ) - 1 ) ) {
550             val = cargv[ i ] + sizeof( REALMSTR );
551             ri->ri_realm = strdup( val );
552         } else if ( !strncasecmp( cargv[ i ], 
553                         AUTHCSTR, sizeof( AUTHCSTR ) - 1 ) ) {
554             val = cargv[ i ] + sizeof( AUTHCSTR );
555             ri->ri_authcId = strdup( val );
556         } else if ( !strncasecmp( cargv[ i ], 
557                         OLDAUTHCSTR, sizeof( OLDAUTHCSTR ) - 1 ) ) {
558             /* Old authcID is provided for some backwards compatibility */
559             val = cargv[ i ] + sizeof( OLDAUTHCSTR );
560             ri->ri_authcId = strdup( val );
561         } else if ( !strncasecmp( cargv[ i ], 
562                         AUTHZSTR, sizeof( AUTHZSTR ) - 1 ) ) {
563             val = cargv[ i ] + sizeof( AUTHZSTR );
564             ri->ri_authzId = strdup( val );
565         } else if ( !strncasecmp( cargv[ i ], 
566                         SRVTABSTR, sizeof( SRVTABSTR ) - 1 ) ) {
567             val = cargv[ i ] + sizeof( SRVTABSTR );
568             if ( ri->ri_srvtab != NULL ) {
569                 free( ri->ri_srvtab );
570             }
571             ri->ri_srvtab = strdup( val );
572         } else {
573             fprintf( stderr, 
574                     "Error: parse_replica_line: unknown keyword \"%s\"\n",
575                     cargv[ i ] );
576         }
577     }
578     
579         if ( ri->ri_bind_method == LDAP_AUTH_SASL) {
580                 if ((gots & GOT_MECH) == 0) {
581                         fprintf( stderr, "Error: \"replica\" line needs SASLmech flag in " );
582                         fprintf( stderr, "slapd config file, line %d\n", lineno );
583                         return -1;
584                 }
585         } else if ( gots != GOT_ALL ) {
586                 fprintf( stderr, "Error: Malformed \"replica\" line in slapd " );
587                 fprintf( stderr, "config file, line %d\n", lineno );
588                 return -1;
589         }
590     return 0;
591 }
592