]> git.sur5r.net Git - openldap/blob - servers/slurpd/config.c
bd47c26a61348ca92c9301008c6648510f4afd9e
[openldap] / servers / slurpd / config.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2002 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
52
53 /*
54  * Read the slapd config file, looking only for config options we're
55  * interested in.  Since we haven't detached from the controlling
56  * terminal yet, we just perror() and fprintf here.
57  */
58 int
59 slurpd_read_config(
60     char        *fname
61 )
62 {
63     FILE        *fp;
64     char        *line;
65
66         cargv = ch_calloc( ARGS_STEP + 1, sizeof(*cargv) );
67         cargv_size = ARGS_STEP + 1;
68
69 #ifdef NEW_LOGGING
70     LDAP_LOG (( "config", LDAP_LEVEL_ARGS, 
71         "slurpd_read_config: Config: opening config file \"%s\"\n", fname ));
72 #else
73     Debug( LDAP_DEBUG_CONFIG, "Config: opening config file \"%s\"\n",
74             fname, 0, 0 );
75 #endif
76
77     if ( (fp = fopen( fname, "r" )) == NULL ) {
78         perror( fname );
79         exit( EXIT_FAILURE );
80     }
81
82     lineno = 0;
83     while ( (line = getline( fp )) != NULL ) {
84         /* skip comments and blank lines */
85         if ( line[0] == '#' || line[0] == '\0' ) {
86             continue;
87         }
88
89 #ifdef NEW_LOGGING
90     LDAP_LOG (( "config", LDAP_LEVEL_DETAIL1, 
91                 "slurpd_read_config: Config: (%s)\n", line ));
92 #else
93         Debug( LDAP_DEBUG_CONFIG, "Config: (%s)\n", line, 0, 0 );
94 #endif
95
96         parse_line( line );
97
98         if ( cargc < 1 ) {
99             fprintf( stderr, "line %d: bad config line (ignored)\n", lineno );
100             continue;
101         }
102
103         /* replication log file to which changes are appended */
104         if ( strcasecmp( cargv[0], "replogfile" ) == 0 ) {
105             /* 
106              * if slapd_replogfile has a value, the -r option was given,
107              * so use that value.  If slapd_replogfile has length == 0,
108              * then we should use the value in the config file we're reading.
109              */
110             if ( sglob->slapd_replogfile[ 0 ] == '\0' ) {
111                 if ( cargc < 2 ) {
112                     fprintf( stderr,
113                         "line %d: missing filename in \"replogfile ",
114                         lineno );
115                     fprintf( stderr, "<filename>\" line\n" );
116                     exit( EXIT_FAILURE );
117                 } else if ( cargc > 2 && *cargv[2] != '#' ) {
118                     fprintf( stderr,
119                         "line %d: extra cruft at the end of \"replogfile %s\"",
120                         lineno, cargv[1] );
121                     fprintf( stderr, "line (ignored)\n" );
122                 }
123                 strcpy( sglob->slapd_replogfile, cargv[1] );
124             }
125         } else if ( strcasecmp( cargv[0], "replica" ) == 0 ) {
126             add_replica( cargv, cargc );
127             
128             /* include another config file */
129         } else if ( strcasecmp( cargv[0], "include" ) == 0 ) {
130             char *savefname;
131             int savelineno;
132
133             if ( cargc < 2 ) {
134 #ifdef NEW_LOGGING
135                 LDAP_LOG(( "config", LDAP_LEVEL_CRIT,
136                         "%s: line %d: missing filename in \"include "
137                         "<filename>\" line.\n", fname, lineno ));
138 #else
139                 Debug( LDAP_DEBUG_ANY,
140         "%s: line %d: missing filename in \"include <filename>\" line\n",
141                         fname, lineno, 0 );
142 #endif
143                 
144                 return( 1 );
145             }
146             savefname = strdup( cargv[1] );
147             savelineno = lineno;
148             
149             if ( slurpd_read_config( savefname ) != 0 ) {
150                 return( 1 );
151             }
152                 
153             free( savefname );
154             lineno = savelineno - 1;
155         }
156     }
157     fclose( fp );
158 #ifdef NEW_LOGGING
159     LDAP_LOG (( "config", LDAP_LEVEL_RESULTS, 
160                 "slurpd_read_config: Config: "
161                 "** configuration file successfully read and parsed\n" ));
162 #else
163     Debug( LDAP_DEBUG_CONFIG,
164             "Config: ** configuration file successfully read and parsed\n",
165             0, 0, 0 );
166 #endif
167     return 0;
168 }
169
170
171
172
173 /*
174  * Parse one line of input.
175  */
176 static void
177 parse_line(
178     char        *line
179 )
180 {
181     char *      token;
182
183     cargc = 0;
184     for ( token = strtok_quote( line, " \t" ); token != NULL;
185         token = strtok_quote( NULL, " \t" ) )
186     {
187         if ( cargc == cargv_size - 1 ) {
188             char **tmp;
189             tmp = ch_realloc( cargv, (cargv_size + ARGS_STEP) *
190                                sizeof(*cargv) );
191             if (tmp == NULL) {
192                 cargc = 0;
193                 return;
194             }
195             cargv = tmp;
196             cargv_size += ARGS_STEP;
197         }
198
199         cargv[cargc++] = token;
200     }
201     cargv[cargc] = NULL;
202 }
203
204
205
206
207 static char *
208 strtok_quote(
209     char *line,
210     char *sep
211 )
212 {
213     int         inquote;
214     char        *tmp;
215     static char *next;
216
217     if ( line != NULL ) {
218         next = line;
219     }
220     while ( *next && strchr( sep, *next ) ) {
221         next++;
222     }
223
224     if ( *next == '\0' ) {
225         next = NULL;
226         return( NULL );
227     }
228     tmp = next;
229
230     for ( inquote = 0; *next; ) {
231         switch ( *next ) {
232         case '"':
233             if ( inquote ) {
234                 inquote = 0;
235             } else {
236                 inquote = 1;
237             }
238             AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
239             break;
240
241         case '\\':
242             if ( next[1] )
243                 AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
244             next++;             /* dont parse the escaped character */
245             break;
246
247         default:
248             if ( ! inquote ) {
249                 if ( strchr( sep, *next ) != NULL ) {
250                     *next++ = '\0';
251                     return( tmp );
252                 }
253             }
254             next++;
255             break;
256         }
257     }
258
259     return( tmp );
260 }
261
262 #define CATLINE( buf )  { \
263     int len; \
264     len = strlen( buf ); \
265     while ( lcur + len + 1 > lmax ) { \
266         lmax += BUFSIZ; \
267         line = (char *) ch_realloc( line, lmax ); \
268     } \
269     strcpy( line + lcur, buf ); \
270     lcur += len; \
271 }
272
273
274
275 /*
276  * Get a line of input.
277  */
278 static char *
279 getline(
280     FILE *fp
281 )
282 {
283     char        *p;
284     static char buf[BUFSIZ];
285     static char *line;
286     static int  lmax, lcur;
287
288     lcur = 0;
289     CATLINE( buf );
290     while ( fgets( buf, sizeof(buf), fp ) != NULL ) {
291         if ( (p = strchr( buf, '\n' )) != NULL ) {
292                 if( p > buf && p[-1] == '\r' ) --p;       
293                 *p = '\0';
294         }
295         lineno++;
296         if ( ! isspace( (unsigned char) buf[0] ) ) {
297             return( line );
298         }
299
300         /* change leading whitespace to space */
301         buf[0] = ' ';
302
303         CATLINE( buf );
304     }
305     buf[0] = '\0';
306
307     return( line[0] ? line : NULL );
308 }
309
310
311 /*
312  * Add a node to the array of replicas.
313  */
314 static void
315 add_replica(
316     char        **cargv,
317     int         cargc
318 )
319 {
320     int nr;
321
322     nr = ++sglob->num_replicas;
323     sglob->replicas = (Ri **) ch_realloc( sglob->replicas,
324             ( nr + 1 )  * sizeof( Re * ));
325     if ( sglob->replicas == NULL ) {
326         fprintf( stderr, "out of memory, add_replica\n" );
327         exit( EXIT_FAILURE );
328     }
329     sglob->replicas[ nr ] = NULL; 
330
331     if ( Ri_init( &(sglob->replicas[ nr - 1 ])) < 0 ) {
332         fprintf( stderr, "out of memory, Ri_init\n" );
333         exit( EXIT_FAILURE );
334     }
335     if ( parse_replica_line( cargv, cargc,
336             sglob->replicas[ nr - 1] ) < 0 ) {
337         /* Something bad happened - back out */
338         fprintf( stderr,
339             "Warning: failed to add replica \"%s:%d - ignoring replica\n",
340             sglob->replicas[ nr - 1 ]->ri_hostname == NULL ?
341             "(null)" : sglob->replicas[ nr - 1 ]->ri_hostname,
342             sglob->replicas[ nr - 1 ]->ri_port );
343         sglob->replicas[ nr - 1] = NULL;
344         sglob->num_replicas--;
345     } else {
346 #ifdef NEW_LOGGING
347     LDAP_LOG (( "config", LDAP_LEVEL_RESULTS, 
348                 "add_replica: Config: ** successfully added replica \"%s%d\"\n", 
349                 sglob->replicas[ nr - 1 ]->ri_hostname == NULL ?
350                 "(null)" : sglob->replicas[ nr - 1 ]->ri_hostname,
351                 sglob->replicas[ nr - 1 ]->ri_port, 0 ));
352 #else
353         Debug( LDAP_DEBUG_CONFIG,
354                 "Config: ** successfully added replica \"%s:%d\"\n",
355                 sglob->replicas[ nr - 1 ]->ri_hostname == NULL ?
356                 "(null)" : sglob->replicas[ nr - 1 ]->ri_hostname,
357                 sglob->replicas[ nr - 1 ]->ri_port, 0 );
358 #endif
359         sglob->replicas[ nr - 1]->ri_stel =
360                 sglob->st->st_add( sglob->st,
361                 sglob->replicas[ nr - 1 ] );
362         if ( sglob->replicas[ nr - 1]->ri_stel == NULL ) {
363             fprintf( stderr, "Failed to add status element structure\n" );
364             exit( EXIT_FAILURE );
365         }
366     }
367 }
368
369
370
371 /* 
372  * Parse a "replica" line from the config file.  replica lines should be
373  * in the following format:
374  * replica    host=<hostname:portnumber> binddn=<binddn>
375  *            bindmethod="simple" credentials=<creds>
376  *
377  * where:
378  * <hostname:portnumber> describes the host name and port number where the
379  * replica is running,
380  *
381  * <binddn> is the DN to bind to the replica slapd as,
382  *
383  * bindmethod is "simple", and
384  *
385  * <creds> are the credentials (e.g. password) for binddn.  <creds> are
386  * only used for bindmethod=simple.  
387  *
388  * The "replica" config file line may be split across multiple lines.  If
389  * a line begins with whitespace, it is considered a continuation of the
390  * previous line.
391  */
392 #define GOT_HOST        1
393 #define GOT_DN          2
394 #define GOT_METHOD      4
395 #define GOT_ALL         ( GOT_HOST | GOT_DN | GOT_METHOD )
396 #define GOT_MECH        8
397
398 static int
399 parse_replica_line( 
400     char        **cargv,
401     int         cargc,
402     Ri          *ri
403 )
404 {
405     int         gots = 0;
406     int         i;
407     char        *hp, *val;
408
409     for ( i = 1; i < cargc; i++ ) {
410         if ( !strncasecmp( cargv[ i ], HOSTSTR, sizeof( HOSTSTR ) - 1 ) ) {
411             val = cargv[ i ] + sizeof( HOSTSTR ); /* '\0' string terminator accounts for '=' */
412             if (( hp = strchr( val, ':' )) != NULL ) {
413                 *hp = '\0';
414                 hp++;
415                 ri->ri_port = atoi( hp );
416             }
417             if ( ri->ri_port <= 0 ) {
418                 ri->ri_port = 0;
419             }
420             ri->ri_hostname = strdup( val );
421             gots |= GOT_HOST;
422         } else if ( !strncasecmp( cargv[ i ], 
423                         ATTRSTR, sizeof( ATTRSTR ) - 1 ) ) {
424             /* ignore it */ ;
425         } else if ( !strncasecmp( cargv[ i ], 
426                         SUFFIXSTR, sizeof( SUFFIXSTR ) - 1 ) ) {
427             /* ignore it */ ;
428         } else if ( !strncasecmp( cargv[ i ], TLSSTR, sizeof( TLSSTR ) - 1 ) ) {
429             val = cargv[ i ] + sizeof( TLSSTR );
430                 if( !strcasecmp( val, TLSCRITICALSTR ) ) {
431                         ri->ri_tls = TLS_CRITICAL;
432                 } else {
433                         ri->ri_tls = TLS_ON;
434                 }
435         } else if ( !strncasecmp( cargv[ i ],
436                         BINDDNSTR, sizeof( BINDDNSTR ) - 1 ) ) { 
437             val = cargv[ i ] + sizeof( BINDDNSTR );
438             ri->ri_bind_dn = strdup( val );
439             gots |= GOT_DN;
440         } else if ( !strncasecmp( cargv[ i ], BINDMETHSTR,
441                 sizeof( BINDMETHSTR ) - 1 ) ) {
442             val = cargv[ i ] + sizeof( BINDMETHSTR );
443             if ( !strcasecmp( val, KERBEROSSTR )) {
444             fprintf( stderr, "Error: a bind method of \"kerberos\" was\n" );
445             fprintf( stderr, "specified in the slapd configuration file.\n" );
446             fprintf( stderr, "slurpd no longer supports Kerberos.\n" );
447             exit( EXIT_FAILURE );
448             } else if ( !strcasecmp( val, SIMPLESTR )) {
449                 ri->ri_bind_method = AUTH_SIMPLE;
450                 gots |= GOT_METHOD;
451             } else if ( !strcasecmp( val, SASLSTR )) {
452                 ri->ri_bind_method = AUTH_SASL;
453                 gots |= GOT_METHOD;
454             } else {
455                 ri->ri_bind_method = -1;
456             }
457         } else if ( !strncasecmp( cargv[ i ], 
458                         SASLMECHSTR, sizeof( SASLMECHSTR ) - 1 ) ) {
459             val = cargv[ i ] + sizeof( SASLMECHSTR );
460             gots |= GOT_MECH;
461             ri->ri_saslmech = strdup( val );
462         } else if ( !strncasecmp( cargv[ i ], 
463                         CREDSTR, sizeof( CREDSTR ) - 1 ) ) {
464             val = cargv[ i ] + sizeof( CREDSTR );
465             ri->ri_password = strdup( val );
466         } else if ( !strncasecmp( cargv[ i ], 
467                         SECPROPSSTR, sizeof( SECPROPSSTR ) - 1 ) ) {
468             val = cargv[ i ] + sizeof( SECPROPSSTR );
469             ri->ri_secprops = strdup( val );
470         } else if ( !strncasecmp( cargv[ i ], 
471                         REALMSTR, sizeof( REALMSTR ) - 1 ) ) {
472             val = cargv[ i ] + sizeof( REALMSTR );
473             ri->ri_realm = strdup( val );
474         } else if ( !strncasecmp( cargv[ i ], 
475                         AUTHCSTR, sizeof( AUTHCSTR ) - 1 ) ) {
476             val = cargv[ i ] + sizeof( AUTHCSTR );
477             ri->ri_authcId = strdup( val );
478         } else if ( !strncasecmp( cargv[ i ], 
479                         OLDAUTHCSTR, sizeof( OLDAUTHCSTR ) - 1 ) ) {
480             /* Old authcID is provided for some backwards compatibility */
481             val = cargv[ i ] + sizeof( OLDAUTHCSTR );
482             ri->ri_authcId = strdup( val );
483         } else if ( !strncasecmp( cargv[ i ], 
484                         AUTHZSTR, sizeof( AUTHZSTR ) - 1 ) ) {
485             val = cargv[ i ] + sizeof( AUTHZSTR );
486             ri->ri_authzId = strdup( val );
487         } else if ( !strncasecmp( cargv[ i ], 
488                         SRVTABSTR, sizeof( SRVTABSTR ) - 1 ) ) {
489             val = cargv[ i ] + sizeof( SRVTABSTR );
490             if ( ri->ri_srvtab != NULL ) {
491                 free( ri->ri_srvtab );
492             }
493             ri->ri_srvtab = strdup( val );
494         } else {
495             fprintf( stderr, 
496                     "Error: parse_replica_line: unknown keyword \"%s\"\n",
497                     cargv[ i ] );
498         }
499     }
500     
501         if ( ri->ri_bind_method == AUTH_SASL) {
502                 if ((gots & GOT_MECH) == 0) {
503                         fprintf( stderr, "Error: \"replica\" line needs SASLmech flag in " );
504                         fprintf( stderr, "slapd config file, line %d\n", lineno );
505                         return -1;
506                 }
507         }
508         else if ( gots != GOT_ALL ) {
509                 fprintf( stderr, "Error: Malformed \"replica\" line in slapd " );
510                 fprintf( stderr, "config file, line %d\n", lineno );
511                 return -1;
512         }
513     return 0;
514 }
515