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