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