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