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