]> git.sur5r.net Git - openldap/blob - servers/slurpd/config.c
4c3a285347fcd652a4a507ae1504ccca581650d5
[openldap] / servers / slurpd / config.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6 /*
7  * Copyright (c) 1996 Regents of the University of Michigan.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms are permitted
11  * provided that this notice is preserved and that due credit is given
12  * to the University of Michigan at Ann Arbor. The name of the University
13  * may not be used to endorse or promote products derived from this
14  * software without specific prior written permission. This software
15  * is provided ``as is'' without express or implied warranty.
16  */
17
18
19 /*
20  * config.c - configuration file handling routines
21  */
22
23 #include "portable.h"
24
25 #include <stdio.h>
26
27 #include <ac/stdlib.h>
28 #include <ac/string.h>
29 #include <ac/socket.h>
30 #include <ac/ctype.h>
31
32 #include <ldap.h>
33
34 #include "slurp.h"
35 #include "globals.h"
36
37 #define ARGS_STEP       512
38
39 /* Forward declarations */
40 static void     add_replica LDAP_P(( char **, int ));
41 static int      parse_replica_line LDAP_P(( char **, int, Ri *));
42 static void     parse_line LDAP_P(( char * ));
43 static char     *getline LDAP_P(( FILE * ));
44 static char     *strtok_quote LDAP_P(( char *, char * ));
45
46 int     cargc = 0, cargv_size = 0;
47 char    **cargv;
48 /* current config file line # */
49 static int      lineno;
50
51 char *slurpd_pid_file = NULL;
52 char *slurpd_args_file = NULL;
53
54 /*
55  * Read the slapd config file, looking only for config options we're
56  * interested in.  Since we haven't detached from the controlling
57  * terminal yet, we just perror() and fprintf here.
58  */
59 int
60 slurpd_read_config(
61     char        *fname
62 )
63 {
64     FILE        *fp;
65     char        *line;
66
67         cargv = ch_calloc( ARGS_STEP + 1, sizeof(*cargv) );
68         cargv_size = ARGS_STEP + 1;
69
70 #ifdef NEW_LOGGING
71     LDAP_LOG ( CONFIG, ARGS, 
72                 "slurpd_read_config: Config: opening config file \"%s\"\n", 
73                 fname, 0, 0 );
74 #else
75     Debug( LDAP_DEBUG_CONFIG, "Config: opening config file \"%s\"\n",
76             fname, 0, 0 );
77 #endif
78
79     if ( (fp = fopen( fname, "r" )) == NULL ) {
80         perror( fname );
81         exit( EXIT_FAILURE );
82     }
83
84     lineno = 0;
85     while ( (line = getline( fp )) != NULL ) {
86         /* skip comments and blank lines */
87         if ( line[0] == '#' || line[0] == '\0' ) {
88             continue;
89         }
90
91 #ifdef NEW_LOGGING
92     LDAP_LOG ( CONFIG, DETAIL1, 
93                 "slurpd_read_config: Config: (%s)\n", line, 0, 0 );
94 #else
95         Debug( LDAP_DEBUG_CONFIG, "Config: (%s)\n", line, 0, 0 );
96 #endif
97
98         parse_line( line );
99
100         if ( cargc < 1 ) {
101             fprintf( stderr, "line %d: bad config line (ignored)\n", lineno );
102             continue;
103         }
104
105         /* replication log file to which changes are appended */
106         if ( strcasecmp( cargv[0], "replogfile" ) == 0 ) {
107             /* 
108              * if slapd_replogfile has a value, the -r option was given,
109              * so use that value.  If slapd_replogfile has length == 0,
110              * then we should use the value in the config file we're reading.
111              */
112             if ( sglob->slapd_replogfile[ 0 ] == '\0' ) {
113                 if ( cargc < 2 ) {
114                     fprintf( stderr,
115                         "line %d: missing filename in \"replogfile ",
116                         lineno );
117                     fprintf( stderr, "<filename>\" line\n" );
118                     exit( EXIT_FAILURE );
119                 } else if ( cargc > 2 && *cargv[2] != '#' ) {
120                     fprintf( stderr,
121                         "line %d: extra cruft at the end of \"replogfile %s\"",
122                         lineno, cargv[1] );
123                     fprintf( stderr, "line (ignored)\n" );
124                 }
125                 strcpy( sglob->slapd_replogfile, cargv[1] );
126             }
127         } else if ( strcasecmp( cargv[0], "replica" ) == 0 ) {
128             add_replica( cargv, cargc );
129             
130             /* include another config file */
131         } else if ( strcasecmp( cargv[0], "include" ) == 0 ) {
132             char *savefname;
133             int savelineno;
134
135             if ( cargc < 2 ) {
136 #ifdef NEW_LOGGING
137                 LDAP_LOG( CONFIG, CRIT,
138                         "%s: line %d: missing filename in \"include "
139                         "<filename>\" line.\n", fname, lineno , 0 );
140 #else
141                 Debug( LDAP_DEBUG_ANY,
142         "%s: line %d: missing filename in \"include <filename>\" line\n",
143                         fname, lineno, 0 );
144 #endif
145                 
146                 return( 1 );
147             }
148             savefname = strdup( cargv[1] );
149             savelineno = lineno;
150             
151             if ( slurpd_read_config( savefname ) != 0 ) {
152                 return( 1 );
153             }
154                 
155             free( savefname );
156             lineno = savelineno - 1;
157
158         } else if ( strcasecmp( cargv[0], "replica-pidfile" ) == 0 ) {
159                 if ( cargc < 2 ) {
160 #ifdef NEW_LOGGING
161                         LDAP_LOG( CONFIG, CRIT, 
162                                 "%s: line %d missing file name in \"replica-pidfile <file>\" "
163                                 "line.\n", fname, lineno, 0 );
164 #else
165                         Debug( LDAP_DEBUG_ANY,
166             "%s: line %d: missing file name in \"replica-pidfile <file>\" line\n",
167                                 fname, lineno, 0 );
168 #endif
169
170                         return( 1 );
171                 }
172
173                 slurpd_pid_file = ch_strdup( cargv[1] );
174
175         } else if ( strcasecmp( cargv[0], "replica-argsfile" ) == 0 ) {
176                 if ( cargc < 2 ) {
177 #ifdef NEW_LOGGING
178                         LDAP_LOG( CONFIG, CRIT, 
179                                    "%s: %d: missing file name in "
180                                    "\"argsfile <file>\" line.\n",
181                                    fname, lineno, 0 );
182 #else
183                         Debug( LDAP_DEBUG_ANY,
184             "%s: line %d: missing file name in \"argsfile <file>\" line\n",
185                             fname, lineno, 0 );
186 #endif
187
188                         return( 1 );
189                 }
190
191                 slurpd_args_file = ch_strdup( cargv[1] );
192         }
193     }
194     fclose( fp );
195 #ifdef NEW_LOGGING
196     LDAP_LOG ( CONFIG, RESULTS, 
197                 "slurpd_read_config: Config: "
198                 "** configuration file successfully read and parsed\n", 0, 0, 0 );
199 #else
200     Debug( LDAP_DEBUG_CONFIG,
201             "Config: ** configuration file successfully read and parsed\n",
202             0, 0, 0 );
203 #endif
204     return 0;
205 }
206
207
208
209
210 /*
211  * Parse one line of input.
212  */
213 static void
214 parse_line(
215     char        *line
216 )
217 {
218     char *      token;
219
220     cargc = 0;
221     for ( token = strtok_quote( line, " \t" ); token != NULL;
222         token = strtok_quote( NULL, " \t" ) )
223     {
224         if ( cargc == cargv_size - 1 ) {
225             char **tmp;
226             tmp = ch_realloc( cargv, (cargv_size + ARGS_STEP) *
227                                sizeof(*cargv) );
228             if (tmp == NULL) {
229                 cargc = 0;
230                 return;
231             }
232             cargv = tmp;
233             cargv_size += ARGS_STEP;
234         }
235
236         cargv[cargc++] = token;
237     }
238     cargv[cargc] = NULL;
239 }
240
241
242
243
244 static char *
245 strtok_quote(
246     char *line,
247     char *sep
248 )
249 {
250     int         inquote;
251     char        *tmp;
252     static char *next;
253
254     if ( line != NULL ) {
255         next = line;
256     }
257     while ( *next && strchr( sep, *next ) ) {
258         next++;
259     }
260
261     if ( *next == '\0' ) {
262         next = NULL;
263         return( NULL );
264     }
265     tmp = next;
266
267     for ( inquote = 0; *next; ) {
268         switch ( *next ) {
269         case '"':
270             if ( inquote ) {
271                 inquote = 0;
272             } else {
273                 inquote = 1;
274             }
275             AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
276             break;
277
278         case '\\':
279             if ( next[1] )
280                 AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
281             next++;             /* dont parse the escaped character */
282             break;
283
284         default:
285             if ( ! inquote ) {
286                 if ( strchr( sep, *next ) != NULL ) {
287                     *next++ = '\0';
288                     return( tmp );
289                 }
290             }
291             next++;
292             break;
293         }
294     }
295
296     return( tmp );
297 }
298
299 #define CATLINE( buf )  { \
300     int len; \
301     len = strlen( buf ); \
302     while ( lcur + len + 1 > lmax ) { \
303         lmax += BUFSIZ; \
304         line = (char *) ch_realloc( line, lmax ); \
305     } \
306     strcpy( line + lcur, buf ); \
307     lcur += len; \
308 }
309
310
311
312 /*
313  * Get a line of input.
314  */
315 static char *
316 getline(
317     FILE *fp
318 )
319 {
320     char        *p;
321     static char buf[BUFSIZ];
322     static char *line;
323     static int  lmax, lcur;
324
325     lcur = 0;
326     CATLINE( buf );
327     while ( fgets( buf, sizeof(buf), fp ) != NULL ) {
328         if ( (p = strchr( buf, '\n' )) != NULL ) {
329                 if( p > buf && p[-1] == '\r' ) --p;       
330                 *p = '\0';
331         }
332         lineno++;
333         if ( ! isspace( (unsigned char) buf[0] ) ) {
334             return( line );
335         }
336
337         /* change leading whitespace to space */
338         buf[0] = ' ';
339
340         CATLINE( buf );
341     }
342     buf[0] = '\0';
343
344     return( line[0] ? line : NULL );
345 }
346
347
348 /*
349  * Add a node to the array of replicas.
350  */
351 static void
352 add_replica(
353     char        **cargv,
354     int         cargc
355 )
356 {
357     int nr;
358
359     nr = ++sglob->num_replicas;
360     sglob->replicas = (Ri **) ch_realloc( sglob->replicas,
361             ( nr + 1 )  * sizeof( Re * ));
362     if ( sglob->replicas == NULL ) {
363         fprintf( stderr, "out of memory, add_replica\n" );
364         exit( EXIT_FAILURE );
365     }
366     sglob->replicas[ nr ] = NULL; 
367
368     if ( Ri_init( &(sglob->replicas[ nr - 1 ])) < 0 ) {
369         fprintf( stderr, "out of memory, Ri_init\n" );
370         exit( EXIT_FAILURE );
371     }
372     if ( parse_replica_line( cargv, cargc,
373             sglob->replicas[ nr - 1] ) < 0 ) {
374         /* Something bad happened - back out */
375         fprintf( stderr,
376             "Warning: failed to add replica \"%s:%d - ignoring replica\n",
377             sglob->replicas[ nr - 1 ]->ri_hostname == NULL ?
378             "(null)" : sglob->replicas[ nr - 1 ]->ri_hostname,
379             sglob->replicas[ nr - 1 ]->ri_port );
380         sglob->replicas[ nr - 1] = NULL;
381         sglob->num_replicas--;
382     } else {
383 #ifdef NEW_LOGGING
384     LDAP_LOG ( CONFIG, RESULTS, 
385                 "add_replica: Config: ** successfully added replica \"%s%d\"\n", 
386                 sglob->replicas[ nr - 1 ]->ri_hostname == NULL ?
387                 "(null)" : sglob->replicas[ nr - 1 ]->ri_hostname,
388                 sglob->replicas[ nr - 1 ]->ri_port, 0 );
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 #endif
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
446     for ( i = 1; i < cargc; i++ ) {
447         if ( !strncasecmp( cargv[ i ], HOSTSTR, sizeof( HOSTSTR ) - 1 ) ) {
448             val = cargv[ i ] + sizeof( HOSTSTR ); /* '\0' string terminator accounts for '=' */
449             if (( hp = strchr( val, ':' )) != NULL ) {
450                 *hp = '\0';
451                 hp++;
452                 ri->ri_port = atoi( hp );
453             }
454             if ( ri->ri_port <= 0 ) {
455                 ri->ri_port = 0;
456             }
457             ri->ri_hostname = strdup( val );
458             gots |= GOT_HOST;
459         } else if ( !strncasecmp( cargv[ i ], 
460                         ATTRSTR, sizeof( ATTRSTR ) - 1 ) ) {
461             /* ignore it */ ;
462         } else if ( !strncasecmp( cargv[ i ], 
463                         SUFFIXSTR, sizeof( SUFFIXSTR ) - 1 ) ) {
464             /* ignore it */ ;
465         } else if ( !strncasecmp( cargv[ i ], TLSSTR, sizeof( TLSSTR ) - 1 ) ) {
466             val = cargv[ i ] + sizeof( TLSSTR );
467                 if( !strcasecmp( val, TLSCRITICALSTR ) ) {
468                         ri->ri_tls = TLS_CRITICAL;
469                 } else {
470                         ri->ri_tls = TLS_ON;
471                 }
472         } else if ( !strncasecmp( cargv[ i ],
473                         BINDDNSTR, sizeof( BINDDNSTR ) - 1 ) ) { 
474             val = cargv[ i ] + sizeof( BINDDNSTR );
475             ri->ri_bind_dn = strdup( val );
476             gots |= GOT_DN;
477         } else if ( !strncasecmp( cargv[ i ], BINDMETHSTR,
478                 sizeof( BINDMETHSTR ) - 1 ) ) {
479             val = cargv[ i ] + sizeof( BINDMETHSTR );
480             if ( !strcasecmp( val, KERBEROSSTR )) {
481             fprintf( stderr, "Error: a bind method of \"kerberos\" was\n" );
482             fprintf( stderr, "specified in the slapd configuration file.\n" );
483             fprintf( stderr, "slurpd no longer supports Kerberos.\n" );
484             exit( EXIT_FAILURE );
485             } else if ( !strcasecmp( val, SIMPLESTR )) {
486                 ri->ri_bind_method = AUTH_SIMPLE;
487                 gots |= GOT_METHOD;
488             } else if ( !strcasecmp( val, SASLSTR )) {
489                 ri->ri_bind_method = AUTH_SASL;
490                 gots |= GOT_METHOD;
491             } else {
492                 ri->ri_bind_method = -1;
493             }
494         } else if ( !strncasecmp( cargv[ i ], 
495                         SASLMECHSTR, sizeof( SASLMECHSTR ) - 1 ) ) {
496             val = cargv[ i ] + sizeof( SASLMECHSTR );
497             gots |= GOT_MECH;
498             ri->ri_saslmech = strdup( val );
499         } else if ( !strncasecmp( cargv[ i ], 
500                         CREDSTR, sizeof( CREDSTR ) - 1 ) ) {
501             val = cargv[ i ] + sizeof( CREDSTR );
502             ri->ri_password = strdup( val );
503         } else if ( !strncasecmp( cargv[ i ], 
504                         SECPROPSSTR, sizeof( SECPROPSSTR ) - 1 ) ) {
505             val = cargv[ i ] + sizeof( SECPROPSSTR );
506             ri->ri_secprops = strdup( val );
507         } else if ( !strncasecmp( cargv[ i ], 
508                         REALMSTR, sizeof( REALMSTR ) - 1 ) ) {
509             val = cargv[ i ] + sizeof( REALMSTR );
510             ri->ri_realm = strdup( val );
511         } else if ( !strncasecmp( cargv[ i ], 
512                         AUTHCSTR, sizeof( AUTHCSTR ) - 1 ) ) {
513             val = cargv[ i ] + sizeof( AUTHCSTR );
514             ri->ri_authcId = strdup( val );
515         } else if ( !strncasecmp( cargv[ i ], 
516                         OLDAUTHCSTR, sizeof( OLDAUTHCSTR ) - 1 ) ) {
517             /* Old authcID is provided for some backwards compatibility */
518             val = cargv[ i ] + sizeof( OLDAUTHCSTR );
519             ri->ri_authcId = strdup( val );
520         } else if ( !strncasecmp( cargv[ i ], 
521                         AUTHZSTR, sizeof( AUTHZSTR ) - 1 ) ) {
522             val = cargv[ i ] + sizeof( AUTHZSTR );
523             ri->ri_authzId = strdup( val );
524         } else if ( !strncasecmp( cargv[ i ], 
525                         SRVTABSTR, sizeof( SRVTABSTR ) - 1 ) ) {
526             val = cargv[ i ] + sizeof( SRVTABSTR );
527             if ( ri->ri_srvtab != NULL ) {
528                 free( ri->ri_srvtab );
529             }
530             ri->ri_srvtab = strdup( val );
531         } else {
532             fprintf( stderr, 
533                     "Error: parse_replica_line: unknown keyword \"%s\"\n",
534                     cargv[ i ] );
535         }
536     }
537     
538         if ( ri->ri_bind_method == AUTH_SASL) {
539                 if ((gots & GOT_MECH) == 0) {
540                         fprintf( stderr, "Error: \"replica\" line needs SASLmech flag in " );
541                         fprintf( stderr, "slapd config file, line %d\n", lineno );
542                         return -1;
543                 }
544         }
545         else if ( gots != GOT_ALL ) {
546                 fprintf( stderr, "Error: Malformed \"replica\" line in slapd " );
547                 fprintf( stderr, "config file, line %d\n", lineno );
548                 return -1;
549         }
550     return 0;
551 }
552