1 /* dsaschema.c */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2004-2021 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 
17 #include <portable.h>
18 
19 #include <ac/string.h>
20 #include <ac/ctype.h>
21 #include <ac/signal.h>
22 #include <ac/errno.h>
23 #include <ac/stdlib.h>
24 #include <ac/ctype.h>
25 #include <ac/time.h>
26 #include <ac/unistd.h>
27 
28 #include <stdio.h>
29 
30 /*
31  * Schema reader that allows us to define DSA schema (including
32  * operational attributes and non-user object classes)
33  *
34  * A kludge, at best, and in order to avoid including slapd
35  * headers we use fprintf() rather than slapd's native logging,
36  * which may confuse users...
37  *
38  */
39 
40 #include <ldap.h>
41 #include <ldap_schema.h>
42 
43 #include <slap.h>
44 #include <slap-config.h>
45 
46 #define ARGS_STEP 512
47 
48 static char *fp_getline(FILE *fp, int *lineno);
49 static void fp_getline_init(int *lineno);
50 static int fp_parse_line(int lineno, char *line);
51 static char *strtok_quote( char *line, char *sep );
52 
53 static char **cargv = NULL;
54 static int cargv_size = 0;
55 static int cargc = 0;
56 static char *strtok_quote_ptr;
57 
58 int init_module(int argc, char *argv[]);
59 
dsaschema_parse_cr(const char * fname,int lineno,char * line,char ** argv)60 static int dsaschema_parse_cr(const char *fname, int lineno, char *line, char **argv)
61 {
62 	struct config_args_s c = { .line = line };
63 
64 	if ( parse_cr( &c, NULL ) ) {
65 		Debug( LDAP_DEBUG_ANY, "dsaschema_parse_cr: "
66 				"ditcontentrule definition invalid at %s:%d\n",
67 				fname, lineno );
68 		return 1;
69 	}
70 
71 	return 0;
72 }
73 
dsaschema_read_config(const char * fname,int depth)74 static int dsaschema_read_config(const char *fname, int depth)
75 {
76 	FILE *fp;
77 	char *line, *savefname, *saveline = NULL;
78 	int savelineno, lineno;
79 	int rc;
80 
81 	if (depth == 0) {
82 		cargv = ch_calloc(ARGS_STEP + 1, sizeof(*cargv));
83 		cargv_size = ARGS_STEP + 1;
84 	}
85 
86 	fp = fopen(fname, "r");
87 	if (fp == NULL) {
88 		char ebuf[128];
89 		int saved_errno = errno;
90 		fprintf(stderr, "could not open config file \"%s\": %s (%d)\n",
91 			fname, AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)), saved_errno);
92 		return 1;
93 	}
94 	fp_getline_init(&lineno);
95 
96 	while ((line = fp_getline(fp, &lineno)) != NULL) {
97 		/* skip comments and blank lines */
98 		if (line[0] == '#' || line[0] == '\0') {
99 			continue;
100 		}
101 
102 		saveline = ch_strdup(line);
103 
104 		if (fp_parse_line(lineno, line) != 0) {
105 			rc = 1;
106 			break;
107 		}
108 
109 		if (cargc < 1) {
110 			continue;
111 		}
112 
113 		if (strcasecmp(cargv[0], "attributetype") == 0 ||
114 		    strcasecmp(cargv[0], "attribute") == 0) {
115 			if (cargc < 2) {
116 				fprintf(stderr, "%s: line %d: illegal attribute type format\n",
117 					fname, lineno);
118 				rc = 1;
119 				break;
120 			} else if (*cargv[1] == '(' /*')'*/) {
121 				char *p;
122 
123 				p = strchr(saveline, '(' /*')'*/);
124 				rc = register_at(p, NULL, 0);
125 				if (rc != 0) {
126 					Debug( LDAP_DEBUG_ANY, "dsaschema_read_config: "
127 							"attribute definition invalid at %s:%d\n",
128 							fname, lineno );
129 					break;
130 				}
131 			} else {
132 				fprintf(stderr, "%s: line %d: old attribute type format not supported\n",
133 					fname, lineno);
134 			}
135 		} else if (strcasecmp(cargv[0], "ditcontentrule") == 0) {
136 			char *p;
137 			p = strchr(saveline, '(' /*')'*/);
138 			rc = dsaschema_parse_cr(fname, lineno, p, cargv);
139 			if (rc != 0)
140 				break;
141 		} else if (strcasecmp(cargv[0], "objectclass") == 0) {
142 			if (cargc < 2) {
143 				fprintf(stderr, "%s: line %d: illegal objectclass format\n",
144 					fname, lineno);
145 				rc = 1;
146 				break;
147 			} else if (*cargv[1] == '(' /*')'*/) {
148 				char *p;
149 
150 				p = strchr(saveline, '(' /*')'*/);
151 				rc = register_oc(p, NULL, 0);
152 				if (rc != 0) {
153 					Debug( LDAP_DEBUG_ANY, "dsaschema_read_config: "
154 							"objectclass definition invalid at %s:%d\n",
155 							fname, lineno );
156 					break;
157 				}
158 			} else {
159 				fprintf(stderr, "%s: line %d: object class format not supported\n",
160 					fname, lineno);
161 			}
162 		} else if (strcasecmp(cargv[0], "include") == 0) {
163 			if (cargc < 2) {
164 				fprintf(stderr, "%s: line %d: missing file name in \"include <filename>\" line",
165 					fname, lineno);
166 				rc = 1;
167 				break;
168 			}
169 			savelineno = lineno;
170 			savefname = ch_strdup(cargv[1]);
171 
172 			rc = dsaschema_read_config(savefname, depth + 1);
173 			ch_free(savefname);
174 			lineno = savelineno - 1;
175 			if (rc != 0) {
176 				break;
177 			}
178 		} else {
179 			fprintf(stderr, "%s: line %d: unknown directive \"%s\" (ignored)\n",
180 				fname, lineno, cargv[0]);
181 		}
182 
183 		ch_free(saveline);
184 		saveline = NULL;
185 	}
186 
187 	fclose(fp);
188 
189 	if (depth == 0)
190 		ch_free(cargv);
191 
192 	if (saveline != NULL)
193 		ch_free(saveline);
194 
195 	return rc;
196 }
197 
init_module(int argc,char * argv[])198 int init_module(int argc, char *argv[])
199 {
200 	int i;
201 	int rc;
202 
203 	for (i = 0; i < argc; i++) {
204 		rc = dsaschema_read_config(argv[i], 0);
205 		if (rc != 0) {
206 			break;
207 		}
208 	}
209 
210 	return rc;
211 }
212 
213 
214 static int
fp_parse_line(int lineno,char * line)215 fp_parse_line(
216     int		lineno,
217     char	*line
218 )
219 {
220 	char *	token;
221 
222 	cargc = 0;
223 	token = strtok_quote( line, " \t" );
224 
225 	if ( strtok_quote_ptr ) {
226 		*strtok_quote_ptr = ' ';
227 	}
228 
229 	if ( strtok_quote_ptr ) {
230 		*strtok_quote_ptr = '\0';
231 	}
232 
233 	for ( ; token != NULL; token = strtok_quote( NULL, " \t" ) ) {
234 		if ( cargc == cargv_size - 1 ) {
235 			char **tmp;
236 			tmp = ch_realloc( cargv, (cargv_size + ARGS_STEP) *
237 					    sizeof(*cargv) );
238 			cargv = tmp;
239 			cargv_size += ARGS_STEP;
240 		}
241 		cargv[cargc++] = token;
242 	}
243 	cargv[cargc] = NULL;
244 	return 0;
245 }
246 
247 static char *
strtok_quote(char * line,char * sep)248 strtok_quote( char *line, char *sep )
249 {
250 	int		inquote;
251 	char		*tmp;
252 	static char	*next;
253 
254 	strtok_quote_ptr = NULL;
255 	if ( line != NULL ) {
256 		next = line;
257 	}
258 	while ( *next && strchr( sep, *next ) ) {
259 		next++;
260 	}
261 
262 	if ( *next == '\0' ) {
263 		next = NULL;
264 		return( NULL );
265 	}
266 	tmp = next;
267 
268 	for ( inquote = 0; *next; ) {
269 		switch ( *next ) {
270 		case '"':
271 			if ( inquote ) {
272 				inquote = 0;
273 			} else {
274 				inquote = 1;
275 			}
276 			AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
277 			break;
278 
279 		case '\\':
280 			if ( next[1] )
281 				AC_MEMCPY( next,
282 					    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 					strtok_quote_ptr = next;
290 					*next++ = '\0';
291 					return( tmp );
292 				}
293 			}
294 			next++;
295 			break;
296 		}
297 	}
298 
299 	return( tmp );
300 }
301 
302 static char	buf[BUFSIZ];
303 static char	*line;
304 static size_t lmax, lcur;
305 
306 #define CATLINE( buf ) \
307 	do { \
308 		size_t len = strlen( buf ); \
309 		while ( lcur + len + 1 > lmax ) { \
310 			lmax += BUFSIZ; \
311 			line = (char *) ch_realloc( line, lmax ); \
312 		} \
313 		strcpy( line + lcur, buf ); \
314 		lcur += len; \
315 	} while( 0 )
316 
317 static char *
fp_getline(FILE * fp,int * lineno)318 fp_getline( FILE *fp, int *lineno )
319 {
320 	char		*p;
321 
322 	lcur = 0;
323 	CATLINE( buf );
324 	(*lineno)++;
325 
326 	/* hack attack - keeps us from having to keep a stack of bufs... */
327 	if ( strncasecmp( line, "include", 7 ) == 0 ) {
328 		buf[0] = '\0';
329 		return( line );
330 	}
331 
332 	while ( fgets( buf, sizeof(buf), fp ) != NULL ) {
333 		/* trim off \r\n or \n */
334 		if ( (p = strchr( buf, '\n' )) != NULL ) {
335 			if( p > buf && p[-1] == '\r' ) --p;
336 			*p = '\0';
337 		}
338 
339 		/* trim off trailing \ and append the next line */
340 		if ( line[ 0 ] != '\0'
341 				&& (p = line + strlen( line ) - 1)[ 0 ] == '\\'
342 				&& p[ -1 ] != '\\' ) {
343 			p[ 0 ] = '\0';
344 			lcur--;
345 
346 		} else {
347 			if ( ! isspace( (unsigned char) buf[0] ) ) {
348 				return( line );
349 			}
350 
351 			/* change leading whitespace to a space */
352 			buf[0] = ' ';
353 		}
354 
355 		CATLINE( buf );
356 		(*lineno)++;
357 	}
358 	buf[0] = '\0';
359 
360 	return( line[0] ? line : NULL );
361 }
362 
363 static void
fp_getline_init(int * lineno)364 fp_getline_init( int *lineno )
365 {
366 	*lineno = -1;
367 	buf[0] = '\0';
368 }
369 
370