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