xref: /netbsd-src/external/bsd/openldap/dist/contrib/slapd-modules/dsaschema/dsaschema.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1 /*	$NetBSD: dsaschema.c,v 1.3 2021/08/14 16:14:51 christos Exp $	*/
2 
3 /* dsaschema.c */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2004-2021 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 #include <slap.h>
46 #include <slap-config.h>
47 
48 #define ARGS_STEP 512
49 
50 static char *fp_getline(FILE *fp, int *lineno);
51 static void fp_getline_init(int *lineno);
52 static int fp_parse_line(int lineno, char *line);
53 static char *strtok_quote( char *line, char *sep );
54 
55 static char **cargv = NULL;
56 static int cargv_size = 0;
57 static int cargc = 0;
58 static char *strtok_quote_ptr;
59 
60 int init_module(int argc, char *argv[]);
61 
dsaschema_parse_cr(const char * fname,int lineno,char * line,char ** argv)62 static int dsaschema_parse_cr(const char *fname, int lineno, char *line, char **argv)
63 {
64 	struct config_args_s c = { .line = line };
65 
66 	if ( parse_cr( &c, NULL ) ) {
67 		Debug( LDAP_DEBUG_ANY, "dsaschema_parse_cr: "
68 				"ditcontentrule definition invalid at %s:%d\n",
69 				fname, lineno );
70 		return 1;
71 	}
72 
73 	return 0;
74 }
75 
dsaschema_read_config(const char * fname,int depth)76 static int dsaschema_read_config(const char *fname, int depth)
77 {
78 	FILE *fp;
79 	char *line, *savefname, *saveline = NULL;
80 	int savelineno, lineno;
81 	int rc;
82 
83 	if (depth == 0) {
84 		cargv = ch_calloc(ARGS_STEP + 1, sizeof(*cargv));
85 		cargv_size = ARGS_STEP + 1;
86 	}
87 
88 	fp = fopen(fname, "r");
89 	if (fp == NULL) {
90 		char ebuf[128];
91 		int saved_errno = errno;
92 		fprintf(stderr, "could not open config file \"%s\": %s (%d)\n",
93 			fname, AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)), saved_errno);
94 		return 1;
95 	}
96 	fp_getline_init(&lineno);
97 
98 	while ((line = fp_getline(fp, &lineno)) != NULL) {
99 		/* skip comments and blank lines */
100 		if (line[0] == '#' || line[0] == '\0') {
101 			continue;
102 		}
103 
104 		saveline = ch_strdup(line);
105 
106 		if (fp_parse_line(lineno, line) != 0) {
107 			rc = 1;
108 			break;
109 		}
110 
111 		if (cargc < 1) {
112 			continue;
113 		}
114 
115 		if (strcasecmp(cargv[0], "attributetype") == 0 ||
116 		    strcasecmp(cargv[0], "attribute") == 0) {
117 			if (cargc < 2) {
118 				fprintf(stderr, "%s: line %d: illegal attribute type format\n",
119 					fname, lineno);
120 				rc = 1;
121 				break;
122 			} else if (*cargv[1] == '(' /*')'*/) {
123 				char *p;
124 
125 				p = strchr(saveline, '(' /*')'*/);
126 				rc = register_at(p, NULL, 0);
127 				if (rc != 0) {
128 					Debug( LDAP_DEBUG_ANY, "dsaschema_read_config: "
129 							"attribute definition invalid at %s:%d\n",
130 							fname, lineno );
131 					break;
132 				}
133 			} else {
134 				fprintf(stderr, "%s: line %d: old attribute type format not supported\n",
135 					fname, lineno);
136 			}
137 		} else if (strcasecmp(cargv[0], "ditcontentrule") == 0) {
138 			char *p;
139 			p = strchr(saveline, '(' /*')'*/);
140 			rc = dsaschema_parse_cr(fname, lineno, p, cargv);
141 			if (rc != 0)
142 				break;
143 		} else if (strcasecmp(cargv[0], "objectclass") == 0) {
144 			if (cargc < 2) {
145 				fprintf(stderr, "%s: line %d: illegal objectclass format\n",
146 					fname, lineno);
147 				rc = 1;
148 				break;
149 			} else if (*cargv[1] == '(' /*')'*/) {
150 				char *p;
151 
152 				p = strchr(saveline, '(' /*')'*/);
153 				rc = register_oc(p, NULL, 0);
154 				if (rc != 0) {
155 					Debug( LDAP_DEBUG_ANY, "dsaschema_read_config: "
156 							"objectclass definition invalid at %s:%d\n",
157 							fname, lineno );
158 					break;
159 				}
160 			} else {
161 				fprintf(stderr, "%s: line %d: object class format not supported\n",
162 					fname, lineno);
163 			}
164 		} else if (strcasecmp(cargv[0], "include") == 0) {
165 			if (cargc < 2) {
166 				fprintf(stderr, "%s: line %d: missing file name in \"include <filename>\" line",
167 					fname, lineno);
168 				rc = 1;
169 				break;
170 			}
171 			savelineno = lineno;
172 			savefname = ch_strdup(cargv[1]);
173 
174 			rc = dsaschema_read_config(savefname, depth + 1);
175 			ch_free(savefname);
176 			lineno = savelineno - 1;
177 			if (rc != 0) {
178 				break;
179 			}
180 		} else {
181 			fprintf(stderr, "%s: line %d: unknown directive \"%s\" (ignored)\n",
182 				fname, lineno, cargv[0]);
183 		}
184 
185 		ch_free(saveline);
186 		saveline = NULL;
187 	}
188 
189 	fclose(fp);
190 
191 	if (depth == 0)
192 		ch_free(cargv);
193 
194 	if (saveline != NULL)
195 		ch_free(saveline);
196 
197 	return rc;
198 }
199 
init_module(int argc,char * argv[])200 int init_module(int argc, char *argv[])
201 {
202 	int i;
203 	int rc;
204 
205 	for (i = 0; i < argc; i++) {
206 		rc = dsaschema_read_config(argv[i], 0);
207 		if (rc != 0) {
208 			break;
209 		}
210 	}
211 
212 	return rc;
213 }
214 
215 
216 static int
fp_parse_line(int lineno,char * line)217 fp_parse_line(
218     int		lineno,
219     char	*line
220 )
221 {
222 	char *	token;
223 
224 	cargc = 0;
225 	token = strtok_quote( line, " \t" );
226 
227 	if ( strtok_quote_ptr ) {
228 		*strtok_quote_ptr = ' ';
229 	}
230 
231 	if ( strtok_quote_ptr ) {
232 		*strtok_quote_ptr = '\0';
233 	}
234 
235 	for ( ; token != NULL; token = strtok_quote( NULL, " \t" ) ) {
236 		if ( cargc == cargv_size - 1 ) {
237 			char **tmp;
238 			tmp = ch_realloc( cargv, (cargv_size + ARGS_STEP) *
239 					    sizeof(*cargv) );
240 			cargv = tmp;
241 			cargv_size += ARGS_STEP;
242 		}
243 		cargv[cargc++] = token;
244 	}
245 	cargv[cargc] = NULL;
246 	return 0;
247 }
248 
249 static char *
strtok_quote(char * line,char * sep)250 strtok_quote( char *line, char *sep )
251 {
252 	int		inquote;
253 	char		*tmp;
254 	static char	*next;
255 
256 	strtok_quote_ptr = NULL;
257 	if ( line != NULL ) {
258 		next = line;
259 	}
260 	while ( *next && strchr( sep, *next ) ) {
261 		next++;
262 	}
263 
264 	if ( *next == '\0' ) {
265 		next = NULL;
266 		return( NULL );
267 	}
268 	tmp = next;
269 
270 	for ( inquote = 0; *next; ) {
271 		switch ( *next ) {
272 		case '"':
273 			if ( inquote ) {
274 				inquote = 0;
275 			} else {
276 				inquote = 1;
277 			}
278 			AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
279 			break;
280 
281 		case '\\':
282 			if ( next[1] )
283 				AC_MEMCPY( next,
284 					    next + 1, strlen( next + 1 ) + 1 );
285 			next++;		/* dont parse the escaped character */
286 			break;
287 
288 		default:
289 			if ( ! inquote ) {
290 				if ( strchr( sep, *next ) != NULL ) {
291 					strtok_quote_ptr = next;
292 					*next++ = '\0';
293 					return( tmp );
294 				}
295 			}
296 			next++;
297 			break;
298 		}
299 	}
300 
301 	return( tmp );
302 }
303 
304 static char	buf[BUFSIZ];
305 static char	*line;
306 static size_t lmax, lcur;
307 
308 #define CATLINE( buf ) \
309 	do { \
310 		size_t len = strlen( buf ); \
311 		while ( lcur + len + 1 > lmax ) { \
312 			lmax += BUFSIZ; \
313 			line = (char *) ch_realloc( line, lmax ); \
314 		} \
315 		strcpy( line + lcur, buf ); \
316 		lcur += len; \
317 	} while( 0 )
318 
319 static char *
fp_getline(FILE * fp,int * lineno)320 fp_getline( FILE *fp, int *lineno )
321 {
322 	char		*p;
323 
324 	lcur = 0;
325 	CATLINE( buf );
326 	(*lineno)++;
327 
328 	/* hack attack - keeps us from having to keep a stack of bufs... */
329 	if ( strncasecmp( line, "include", 7 ) == 0 ) {
330 		buf[0] = '\0';
331 		return( line );
332 	}
333 
334 	while ( fgets( buf, sizeof(buf), fp ) != NULL ) {
335 		/* trim off \r\n or \n */
336 		if ( (p = strchr( buf, '\n' )) != NULL ) {
337 			if( p > buf && p[-1] == '\r' ) --p;
338 			*p = '\0';
339 		}
340 
341 		/* trim off trailing \ and append the next line */
342 		if ( line[ 0 ] != '\0'
343 				&& (p = line + strlen( line ) - 1)[ 0 ] == '\\'
344 				&& p[ -1 ] != '\\' ) {
345 			p[ 0 ] = '\0';
346 			lcur--;
347 
348 		} else {
349 			if ( ! isspace( (unsigned char) buf[0] ) ) {
350 				return( line );
351 			}
352 
353 			/* change leading whitespace to a space */
354 			buf[0] = ' ';
355 		}
356 
357 		CATLINE( buf );
358 		(*lineno)++;
359 	}
360 	buf[0] = '\0';
361 
362 	return( line[0] ? line : NULL );
363 }
364 
365 static void
fp_getline_init(int * lineno)366 fp_getline_init( int *lineno )
367 {
368 	*lineno = -1;
369 	buf[0] = '\0';
370 }
371 
372