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