2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1998-2007 The OpenLDAP Foundation.
5 * Portions Copyright 2003 Mark Benson.
6 * Portions Copyright 2002 John Morrissey.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
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>.
17 /* Portions Copyright (c) 1996 Regents of the University of Michigan.
18 * All rights reserved.
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.
28 * This work was originally developed by the University of Michigan
29 * (as part of U-MICH LDAP). Additional signficant contributors
37 * config.c - configuration file handling routines
44 #include <ac/stdlib.h>
45 #include <ac/string.h>
46 #include <ac/socket.h>
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 *slurpd_getline LDAP_P(( FILE * ));
62 static char *strtok_quote LDAP_P(( char *, char * ));
64 int cargc = 0, cargv_size = 0;
66 /* current config file line # */
69 char *slurpd_pid_file = NULL;
70 char *slurpd_args_file = NULL;
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.
85 #define GOT_REPLOG_NO (0)
86 #define GOT_REPLOG_ONE (1)
87 #define GOT_REPLOG_YES (2)
88 #define GOT_REPLOG_DONE (3)
89 #define GOT_REPLOG_MASK (0xF)
90 #define GOT_REPLOG(i) ((i) & GOT_REPLOG_MASK)
91 #define GOT_REPLOG_SET(i,v) ((i) = ((i) & ~GOT_REPLOG_MASK) | ((v) & GOT_REPLOG_MASK))
93 #define GOT_REPLOG_PID (0x10)
94 #define GOT_REPLOG_ARGS (0x20)
95 #define GOT_REPLOG_INTERVAL (0x40)
96 int got_replog = GOT_REPLOG_NO;
99 * replica-pidfile and replica-argsfile can appear before any replog;
100 * in this case they're global (legacy behavior); otherwise, since
101 * each replog needs a slurpd, they can appear after a replogfile line;
102 * in that case, the replog specific values are used.
105 if ( cargv == NULL ) {
106 cargv = ch_calloc( ARGS_STEP + 1, sizeof(*cargv) );
107 cargv_size = ARGS_STEP + 1;
110 Debug( LDAP_DEBUG_CONFIG, "Config: opening config file \"%s\"\n",
113 if ( (fp = fopen( fname, "r" )) == NULL ) {
115 exit( EXIT_FAILURE );
119 while ( (line = slurpd_getline( fp )) != NULL ) {
120 /* skip comments and blank lines */
121 if ( line[0] == '#' || line[0] == '\0' ) {
125 Debug( LDAP_DEBUG_CONFIG, "Config: (%s)\n", line, 0, 0 );
130 fprintf( stderr, "line %d: bad config line (ignored)\n", lineno );
134 /* replication log file to which changes are appended */
135 if ( strcasecmp( cargv[0], "replogfile" ) == 0 ) {
137 * if slapd_replogfile has a value, the -r option was given,
138 * so use that value. If slapd_replogfile has length == 0,
139 * then we should use the value in the config file we're reading.
143 "line %d: missing filename in \"replogfile ",
145 fprintf( stderr, "<filename>\" line\n" );
146 exit( EXIT_FAILURE );
148 } else if ( cargc > 2 && *cargv[2] != '#' ) {
150 "line %d: extra cruft at the end of \"replogfile %s\"",
152 fprintf( stderr, "line (ignored)\n" );
155 LUTIL_SLASHPATH( cargv[1] );
156 if ( sglob->slapd_replogfile[0] == '\0' ) {
157 strcpy( sglob->slapd_replogfile, cargv[1] );
158 GOT_REPLOG_SET(got_replog, GOT_REPLOG_YES);
161 if ( strcmp( sglob->slapd_replogfile, cargv[1] ) == 0 ) {
162 GOT_REPLOG_SET(got_replog, GOT_REPLOG_YES);
164 } else if ( GOT_REPLOG(got_replog) == GOT_REPLOG_YES ) {
165 GOT_REPLOG_SET(got_replog, GOT_REPLOG_DONE);
168 GOT_REPLOG_SET(got_replog, GOT_REPLOG_ONE);
172 } else if ( strcasecmp( cargv[0], "replica" ) == 0 ) {
173 add_replica( cargv, cargc );
175 /* include another config file */
176 } else if ( strcasecmp( cargv[0], "include" ) == 0 ) {
181 Debug( LDAP_DEBUG_ANY,
182 "%s: line %d: missing filename in \"include <filename>\" line\n",
188 LUTIL_SLASHPATH( cargv[1] );
189 savefname = strdup( cargv[1] );
192 if ( slurpd_read_config( savefname ) != 0 ) {
198 lineno = savelineno - 1;
200 } else if ( strcasecmp( cargv[0], "replica-pidfile" ) == 0 ) {
202 Debug( LDAP_DEBUG_ANY,
203 "%s: line %d: missing file name in \"replica-pidfile <file>\" line\n",
210 switch ( GOT_REPLOG(got_replog) ) {
212 Debug( LDAP_DEBUG_CONFIG, "%s: line %d: "
213 "got replog specific replica-pidfile \"%s\".\n",
214 fname, lineno, cargv[1] );
216 LUTIL_SLASHPATH( cargv[1] );
217 if ( slurpd_pid_file != NULL ) {
218 ch_free( slurpd_pid_file );
220 slurpd_pid_file = ch_strdup( cargv[1] );
221 got_replog |= GOT_REPLOG_PID;
225 Debug( LDAP_DEBUG_CONFIG, "%s: line %d: "
226 "replica-pidfile \"%s\" not mine.\n",
227 fname, lineno, cargv[1] );
231 } else if ( strcasecmp( cargv[0], "replica-argsfile" ) == 0 ) {
233 Debug( LDAP_DEBUG_ANY,
234 "%s: line %d: missing file name in \"argsfile <file>\" line\n",
241 switch ( GOT_REPLOG(got_replog) ) {
243 Debug( LDAP_DEBUG_CONFIG, "%s: line %d: "
244 "got replog specific replica-argsfile \"%s\".\n",
245 fname, lineno, cargv[1] );
247 LUTIL_SLASHPATH( cargv[1] );
248 if ( slurpd_args_file != NULL ) {
249 ch_free( slurpd_args_file );
251 slurpd_args_file = ch_strdup( cargv[1] );
252 got_replog |= GOT_REPLOG_ARGS;
256 Debug( LDAP_DEBUG_CONFIG, "%s: line %d: "
257 "replica-argsfile \"%s\" not mine.\n",
258 fname, lineno, cargv[1] );
262 } else if ( strcasecmp( cargv[0], "replicationinterval" ) == 0 ) {
266 Debug( LDAP_DEBUG_ANY, "%s: line %d: missing interval in "
267 "\"replicationinterval <seconds>\" line\n",
273 if ( lutil_atoi( &c, cargv[1] ) != 0 || c < 1 ) {
274 Debug( LDAP_DEBUG_ANY, "%s: line %d: invalid interval "
275 "(%d) in \"replicationinterval <seconds>\" line\n",
282 switch ( GOT_REPLOG(got_replog) ) {
284 Debug( LDAP_DEBUG_CONFIG, "%s: line %d: "
285 "got replog specific replicationinterval \"%s\".\n",
286 fname, lineno, cargv[1] );
288 sglob->no_work_interval = c;
289 got_replog |= GOT_REPLOG_INTERVAL;
293 Debug( LDAP_DEBUG_CONFIG, "%s: line %d: "
294 "replicationinterval \"%s\" not mine.\n",
295 fname, lineno, cargv[1] );
302 Debug( LDAP_DEBUG_CONFIG,
303 "Config: ** configuration file successfully read and parsed\n",
312 * Parse one line of input.
322 for ( token = strtok_quote( line, " \t" ); token != NULL;
323 token = strtok_quote( NULL, " \t" ) )
325 if ( cargc == cargv_size - 1 ) {
327 tmp = ch_realloc( cargv, (cargv_size + ARGS_STEP) *
334 cargv_size += ARGS_STEP;
337 cargv[cargc++] = token;
355 if ( line != NULL ) {
358 while ( *next && strchr( sep, *next ) ) {
362 if ( *next == '\0' ) {
368 for ( inquote = 0; *next; ) {
376 AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
381 AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
382 next++; /* dont parse the escaped character */
387 if ( strchr( sep, *next ) != NULL ) {
400 #define CATLINE( buf ) { \
402 len = strlen( buf ); \
403 while ( lcur + len + 1 > lmax ) { \
405 line = (char *) ch_realloc( line, lmax ); \
407 strcpy( line + lcur, buf ); \
414 * Get a line of input.
422 static char buf[BUFSIZ];
424 static int lmax, lcur;
428 while ( fgets( buf, sizeof(buf), fp ) != NULL ) {
429 if ( (p = strchr( buf, '\n' )) != NULL ) {
430 if( p > buf && p[-1] == '\r' ) --p;
434 if ( ! isspace( (unsigned char) buf[0] ) ) {
438 /* change leading whitespace to space */
445 return( line[0] ? line : NULL );
450 * Add a node to the array of replicas.
460 nr = ++sglob->num_replicas;
461 sglob->replicas = (Ri **) ch_realloc( sglob->replicas,
462 ( nr + 1 ) * sizeof( Re * ));
463 if ( sglob->replicas == NULL ) {
464 fprintf( stderr, "out of memory, add_replica\n" );
465 exit( EXIT_FAILURE );
467 sglob->replicas[ nr ] = NULL;
469 if ( Ri_init( &(sglob->replicas[ nr - 1 ])) < 0 ) {
470 fprintf( stderr, "out of memory, Ri_init\n" );
471 exit( EXIT_FAILURE );
473 if ( parse_replica_line( cargv, cargc,
474 sglob->replicas[ nr - 1] ) < 0 ) {
475 /* Something bad happened - back out */
477 "Warning: failed to add replica \"%s:%d - ignoring replica\n",
478 sglob->replicas[ nr - 1 ]->ri_hostname == NULL ?
479 "(null)" : sglob->replicas[ nr - 1 ]->ri_hostname,
480 sglob->replicas[ nr - 1 ]->ri_port );
481 sglob->replicas[ nr - 1] = NULL;
482 sglob->num_replicas--;
484 Debug( LDAP_DEBUG_CONFIG,
485 "Config: ** successfully added replica \"%s:%d\"\n",
486 sglob->replicas[ nr - 1 ]->ri_hostname == NULL ?
487 "(null)" : sglob->replicas[ nr - 1 ]->ri_hostname,
488 sglob->replicas[ nr - 1 ]->ri_port, 0 );
489 sglob->replicas[ nr - 1]->ri_stel =
490 sglob->st->st_add( sglob->st,
491 sglob->replicas[ nr - 1 ] );
492 if ( sglob->replicas[ nr - 1]->ri_stel == NULL ) {
493 fprintf( stderr, "Failed to add status element structure\n" );
494 exit( EXIT_FAILURE );
502 * Parse a "replica" line from the config file. replica lines should be
503 * in the following format:
504 * replica host=<hostname:portnumber> binddn=<binddn>
505 * bindmethod="simple" credentials=<creds>
508 * <hostname:portnumber> describes the host name and port number where the
509 * replica is running,
511 * <binddn> is the DN to bind to the replica slapd as,
513 * bindmethod is "simple", and
515 * <creds> are the credentials (e.g. password) for binddn. <creds> are
516 * only used for bindmethod=simple.
518 * The "replica" config file line may be split across multiple lines. If
519 * a line begins with whitespace, it is considered a continuation of the
525 #define GOT_ALL ( GOT_HOST | GOT_DN | GOT_METHOD )
540 for ( i = 1; i < cargc; i++ ) {
541 if ( !strncasecmp( cargv[ i ], HOSTSTR, sizeof( HOSTSTR ) - 1 ) ) {
542 if ( gots & GOT_HOST ) {
543 fprintf( stderr, "Error: Malformed \"replica\" line in slapd config " );
544 fprintf( stderr, "file, too many host or uri names specified, line %d\n",
548 val = cargv[ i ] + sizeof( HOSTSTR ); /* '\0' string terminator accounts for '=' */
549 if (( hp = strchr( val, ':' )) != NULL ) {
552 if ( lutil_atoi( &ri->ri_port, hp ) != 0 ) {
553 fprintf( stderr, "unable to parse port \"%s\", line %d\n",
558 if ( ri->ri_port <= 0 ) {
559 ri->ri_port = LDAP_PORT;
561 ri->ri_hostname = strdup( val );
563 } else if ( !strncasecmp( cargv[ i ], URISTR, sizeof( URISTR ) - 1 ) ) {
564 if ( gots & GOT_HOST ) {
565 fprintf( stderr, "Error: Malformed \"replica\" line in slapd config " );
566 fprintf( stderr, "file, too many host or uri names specified, line %d\n",
570 if ( ldap_url_parse( cargv[ i ] + sizeof( URISTR ), &ludp ) != LDAP_SUCCESS ) {
571 fprintf( stderr, "Error: Malformed \"replica\" line in slapd config " );
572 fprintf( stderr, "file, bad uri format specified, line %d\n",
576 if (ludp->lud_host == NULL) {
577 fprintf( stderr, "Error: Malformed \"replica\" line in slapd config " );
578 fprintf( stderr, "file, missing uri hostname, line %d\n",
580 ldap_free_urldesc( ludp );
583 ri->ri_hostname = strdup ( ludp->lud_host );
584 ri->ri_port = ludp->lud_port;
585 ri->ri_uri = strdup ( cargv[ i ] + sizeof( URISTR ) );
586 ldap_free_urldesc( ludp );
588 } else if ( !strncasecmp( cargv[ i ],
589 ATTRSTR, sizeof( ATTRSTR ) - 1 ) ) {
591 } else if ( !strncasecmp( cargv[ i ],
592 SUFFIXSTR, sizeof( SUFFIXSTR ) - 1 ) ) {
594 } else if ( !strncasecmp( cargv[i], STARTTLSSTR, sizeof(STARTTLSSTR)-1 )) {
595 val = cargv[ i ] + sizeof( STARTTLSSTR );
596 if( !strcasecmp( val, CRITICALSTR ) ) {
597 ri->ri_tls = TLS_CRITICAL;
601 } else if ( !strncasecmp( cargv[ i ], TLSSTR, sizeof( TLSSTR ) - 1 ) ) {
602 val = cargv[ i ] + sizeof( TLSSTR );
603 if( !strcasecmp( val, CRITICALSTR ) ) {
604 ri->ri_tls = TLS_CRITICAL;
608 } else if ( !strncasecmp( cargv[ i ],
609 BINDDNSTR, sizeof( BINDDNSTR ) - 1 ) ) {
610 val = cargv[ i ] + sizeof( BINDDNSTR );
611 ri->ri_bind_dn = strdup( val );
613 } else if ( !strncasecmp( cargv[ i ], BINDMETHSTR,
614 sizeof( BINDMETHSTR ) - 1 ) ) {
615 val = cargv[ i ] + sizeof( BINDMETHSTR );
616 if ( !strcasecmp( val, SIMPLESTR )) {
617 ri->ri_bind_method = LDAP_AUTH_SIMPLE;
619 } else if ( !strcasecmp( val, SASLSTR )) {
620 ri->ri_bind_method = LDAP_AUTH_SASL;
623 ri->ri_bind_method = -1;
625 } else if ( !strncasecmp( cargv[ i ],
626 SASLMECHSTR, sizeof( SASLMECHSTR ) - 1 ) ) {
627 val = cargv[ i ] + sizeof( SASLMECHSTR );
629 ri->ri_saslmech = strdup( val );
630 } else if ( !strncasecmp( cargv[ i ],
631 CREDSTR, sizeof( CREDSTR ) - 1 ) ) {
632 val = cargv[ i ] + sizeof( CREDSTR );
633 ri->ri_password = strdup( val );
634 } else if ( !strncasecmp( cargv[ i ],
635 SECPROPSSTR, sizeof( SECPROPSSTR ) - 1 ) ) {
636 val = cargv[ i ] + sizeof( SECPROPSSTR );
637 ri->ri_secprops = strdup( val );
638 } else if ( !strncasecmp( cargv[ i ],
639 REALMSTR, sizeof( REALMSTR ) - 1 ) ) {
640 val = cargv[ i ] + sizeof( REALMSTR );
641 ri->ri_realm = strdup( val );
642 } else if ( !strncasecmp( cargv[ i ],
643 AUTHCSTR, sizeof( AUTHCSTR ) - 1 ) ) {
644 val = cargv[ i ] + sizeof( AUTHCSTR );
645 ri->ri_authcId = strdup( val );
646 } else if ( !strncasecmp( cargv[ i ],
647 OLDAUTHCSTR, sizeof( OLDAUTHCSTR ) - 1 ) ) {
648 /* Old authcID is provided for some backwards compatibility */
649 val = cargv[ i ] + sizeof( OLDAUTHCSTR );
650 ri->ri_authcId = strdup( val );
651 } else if ( !strncasecmp( cargv[ i ],
652 AUTHZSTR, sizeof( AUTHZSTR ) - 1 ) ) {
653 val = cargv[ i ] + sizeof( AUTHZSTR );
654 ri->ri_authzId = strdup( val );
657 "Error: parse_replica_line: unknown keyword \"%s\"\n",
662 if ( ri->ri_bind_method == LDAP_AUTH_SASL) {
663 if ((gots & GOT_MECH) == 0) {
664 fprintf( stderr, "Error: \"replica\" line needs SASLmech flag in " );
665 fprintf( stderr, "slapd config file, line %d\n", lineno );
668 } else if ( gots != GOT_ALL ) {
669 fprintf( stderr, "Error: Malformed \"replica\" line in slapd " );
670 fprintf( stderr, "config file, line %d\n", lineno );