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