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