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