From 3ccdb86edb093cdb63e277ce87fed65c30f4984d Mon Sep 17 00:00:00 2001 From: Pierangelo Masarati Date: Sat, 27 Sep 2008 13:30:43 +0000 Subject: [PATCH] allow definition of syntaxes via configuration, including X-SUBST (ITS#5663) --- doc/man/man5/slapd.conf.5 | 31 ++++++++ servers/slapd/bconfig.c | 125 ++++++++++++++++++++++++++++- servers/slapd/proto-slap.h | 12 +++ servers/slapd/schemaparse.c | 65 +++++++++++++++ servers/slapd/slap.h | 3 +- servers/slapd/syntax.c | 155 +++++++++++++++++++++++++++++++----- 6 files changed, 366 insertions(+), 25 deletions(-) diff --git a/doc/man/man5/slapd.conf.5 b/doc/man/man5/slapd.conf.5 index 9267801bbd..31412cbf70 100644 --- a/doc/man/man5/slapd.conf.5 +++ b/doc/man/man5/slapd.conf.5 @@ -508,6 +508,37 @@ changing these settings will generally require deleting any indices that depend on these parameters and recreating them with .BR slapindex (8). +.HP +.hy 0 +.B ldapsyntax "(\ \ + [DESC\ ]\ + [X-SUBST ]\ )" +.RS +Specify an LDAP syntax using the LDAPv3 syntax defined in RFC 4512. +The slapd parser extends the RFC 4512 definition by allowing string +forms as well as numeric OIDs to be used for the syntax OID. +(See the +.B objectidentifier +description.) +The slapd parser also honors the +.B X-SUBST +extension (an OpenLDAP-specific extension), which allows to use the +.B ldapsyntax +statement to define a non-implemented syntax along with another syntax, +the extension value +.IR substitute\-syntax , +as its temporary replacement. +The +.I substitute\-syntax +must be defined. +This allows to define attribute types that make use of non-implemented syntaxes +using the correct syntax OID. +Unless +.B X-SUBST +is used, this configuration statement would result in an error, +since no handlers would be associated to the resulting syntax structure. +.RE + .TP .B localSSF Specifies the Security Strength Factor (SSF) to be given local LDAP sessions, diff --git a/servers/slapd/bconfig.c b/servers/slapd/bconfig.c index 60dd39362d..d8cae7fb3b 100644 --- a/servers/slapd/bconfig.c +++ b/servers/slapd/bconfig.c @@ -63,6 +63,7 @@ typedef struct ConfigFile { ContentRule *c_cr_head, *c_cr_tail; ObjectClass *c_oc_head, *c_oc_tail; OidMacro *c_om_head, *c_om_tail; + Syntax *c_syn_head, *c_syn_tail; BerVarray c_dseFiles; } ConfigFile; @@ -87,7 +88,7 @@ static struct berval cfdir; /* Private state */ static AttributeDescription *cfAd_backend, *cfAd_database, *cfAd_overlay, - *cfAd_include, *cfAd_attr, *cfAd_oc, *cfAd_om; + *cfAd_include, *cfAd_attr, *cfAd_oc, *cfAd_om, *cfAd_syntax; static ConfigFile *cfn; @@ -97,9 +98,11 @@ static Avlnode *CfOcTree; extern AttributeType *at_sys_tail; /* at.c */ extern ObjectClass *oc_sys_tail; /* oc.c */ extern OidMacro *om_sys_tail; /* oidm.c */ +extern Syntax *syn_sys_tail; /* syntax.c */ static AttributeType *cf_at_tail; static ObjectClass *cf_oc_tail; static OidMacro *cf_om_tail; +static Syntax *cf_syn_tail; static int config_add_internal( CfBackInfo *cfb, Entry *e, ConfigArgs *ca, SlapReply *rs, int *renumber, Operation *op ); @@ -180,6 +183,7 @@ enum { CFG_SERVERID, CFG_SORTVALS, CFG_IX_INTLEN, + CFG_SYNTAX, CFG_LAST }; @@ -381,6 +385,13 @@ static ConfigTable config_back_cf_table[] = { { "lastmod", "on|off", 2, 2, 0, ARG_DB|ARG_ON_OFF|ARG_MAGIC|CFG_LASTMOD, &config_generic, "( OLcfgDbAt:0.4 NAME 'olcLastMod' " "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL }, + { "ldapsyntax", "syntax", 2, 0, 0, + ARG_PAREN|ARG_MAGIC|CFG_SYNTAX, + &config_generic, "( OLcfgGlAt:85 NAME 'olcLdapSyntaxes' " + "DESC 'OpenLDAP ldapSyntax' " + "EQUALITY caseIgnoreMatch " + "SYNTAX OMsDirectoryString X-ORDERED 'VALUES' )", + NULL, NULL }, { "limits", "limits", 2, 0, 0, ARG_DB|ARG_MAGIC|CFG_LIMITS, &config_generic, "( OLcfgDbAt:0.5 NAME 'olcLimits' " "EQUALITY caseIgnoreMatch " @@ -732,13 +743,13 @@ static ConfigOCs cf_ocs[] = { "olcTLSRandFile $ olcTLSVerifyClient $ olcTLSDHParamFile $ " "olcTLSCRLFile $ olcToolThreads $ " "olcObjectIdentifier $ olcAttributeTypes $ olcObjectClasses $ " - "olcDitContentRules ) )", Cft_Global }, + "olcDitContentRules $ olcLdapSyntaxes ) )", Cft_Global }, { "( OLcfgGlOc:2 " "NAME 'olcSchemaConfig' " "DESC 'OpenLDAP schema object' " "SUP olcConfig STRUCTURAL " "MAY ( cn $ olcObjectIdentifier $ olcAttributeTypes $ " - "olcObjectClasses $ olcDitContentRules ) )", + "olcObjectClasses $ olcDitContentRules $ olcLdapSyntaxes ) )", Cft_Schema, NULL, cfAddSchema }, { "( OLcfgGlOc:3 " "NAME 'olcBackendConfig' " @@ -927,6 +938,17 @@ config_generic(ConfigArgs *c) { rc = 1; } break; + case CFG_SYNTAX: { + ConfigFile *cf = c->ca_private; + if ( !cf ) + syn_unparse( &c->rvalue_vals, NULL, NULL, 1 ); + else if ( cf->c_syn_head ) + syn_unparse( &c->rvalue_vals, cf->c_syn_head, + cf->c_syn_tail, 0 ); + if ( !c->rvalue_vals ) + rc = 1; + } + break; case CFG_DIT: { ConfigFile *cf = c->ca_private; if ( !cf ) @@ -1277,6 +1299,44 @@ config_generic(ConfigArgs *c) { } } break; + + case CFG_SYNTAX: { + CfEntryInfo *ce; + /* Can be NULL when undoing a failed add */ + if ( c->ca_entry ) { + ce = c->ca_entry->e_private; + /* can't modify the hardcoded schema */ + if ( ce->ce_parent->ce_type == Cft_Global ) + return 1; + } + } + cfn = c->ca_private; + if ( c->valx < 0 ) { + Syntax *syn; + + for( syn = cfn->c_syn_head; syn; syn_next( &syn )) { + syn_delete( syn ); + if ( syn == cfn->c_syn_tail ) + break; + } + cfn->c_syn_head = cfn->c_syn_tail = NULL; + } else { + Syntax *syn, *prev = NULL; + + for ( i = 0, syn = cfn->c_syn_head; i < c->valx; i++) { + prev = syn; + syn_next( &syn ); + } + syn_delete( syn ); + if ( cfn->c_syn_tail == syn ) { + cfn->c_syn_tail = prev; + } + if ( cfn->c_syn_head == syn ) { + syn_next( &syn ); + cfn->c_syn_head = syn; + } + } + break; case CFG_SORTVALS: if ( c->valx < 0 ) { ADlist *sv; @@ -1503,6 +1563,38 @@ config_generic(ConfigArgs *c) { } break; + case CFG_SYNTAX: { + Syntax *syn, *prev; + + if ( c->op == LDAP_MOD_ADD && c->ca_private && cfn != c->ca_private ) + cfn = c->ca_private; + if ( c->valx < 0 ) { + prev = cfn->c_syn_tail; + } else { + prev = NULL; + /* If adding anything after the first, prev is easy */ + if ( c->valx ) { + int i; + for ( i = 0, syn = cfn->c_syn_head; i < c->valx; i++ ) { + prev = syn; + syn_next( &syn ); + } + } else + /* If adding the first, and head exists, find its prev */ + if (cfn->c_syn_head) { + for ( syn_start( &syn ); syn != cfn->c_syn_head; ) { + prev = syn; + syn_next( &syn ); + } + } + /* else prev is NULL, append to end of global list */ + } + if ( parse_syn( c, &syn, prev ) ) return(1); + if ( !cfn->c_syn_head ) cfn->c_syn_head = syn; + if ( cfn->c_syn_tail == prev ) cfn->c_syn_tail = syn; + } + break; + case CFG_DIT: { ContentRule *cr; @@ -4062,6 +4154,13 @@ schema_destroy_one( ConfigArgs *ca, ConfigOCs **colst, int nocs, ct = config_find_table( colst, nocs, ad, ca ); config_del_vals( ct, ca ); } + if ( cfn->c_syn_head ) { + struct berval bv = BER_BVC("olcLdapSyntaxes"); + ad = NULL; + slap_bv2ad( &bv, &ad, &text ); + ct = config_find_table( colst, nocs, ad, ca ); + config_del_vals( ct, ca ); + } if ( cfn->c_om_head ) { struct berval bv = BER_BVC("olcObjectIdentifier"); ad = NULL; @@ -5568,7 +5667,7 @@ config_build_schema_inc( ConfigArgs *c, CfEntryInfo *ceparent, for (; cf; cf=cf->c_sibs, c->depth++) { if ( !cf->c_at_head && !cf->c_cr_head && !cf->c_oc_head && - !cf->c_om_head ) continue; + !cf->c_om_head && !cf->c_syn_head ) continue; c->value_dn.bv_val = c->log; LUTIL_SLASHPATH( cf->c_file.bv_val ); bv.bv_val = strrchr(cf->c_file.bv_val, LDAP_DIRSEP[0]); @@ -5697,6 +5796,21 @@ config_check_schema(Operation *op, CfBackInfo *cfb) ber_bvarray_free( bv ); cf_oc_tail = oc_sys_tail; } + if ( cf_syn_tail != syn_sys_tail ) { + a = attr_find( e->e_attrs, cfAd_syntax ); + if ( a ) { + if ( a->a_nvals != a->a_vals ) + ber_bvarray_free( a->a_nvals ); + ber_bvarray_free( a->a_vals ); + a->a_vals = NULL; + a->a_nvals = NULL; + a->a_numvals = 0; + } + syn_unparse( &bv, NULL, NULL, 1 ); + attr_merge_normalize( e, cfAd_syntax, bv, NULL ); + ber_bvarray_free( bv ); + cf_syn_tail = syn_sys_tail; + } } else { SlapReply rs = {REP_RESULT}; c.ca_private = NULL; @@ -5710,6 +5824,7 @@ config_check_schema(Operation *op, CfBackInfo *cfb) cf_at_tail = at_sys_tail; cf_oc_tail = oc_sys_tail; cf_om_tail = om_sys_tail; + cf_syn_tail = syn_sys_tail; } return 0; } @@ -5801,6 +5916,7 @@ config_back_db_open( BackendDB *be, ConfigReply *cr ) cf_at_tail = at_sys_tail; cf_oc_tail = oc_sys_tail; cf_om_tail = om_sys_tail; + cf_syn_tail = syn_sys_tail; /* Create schema nodes for included schema... */ if ( cfb->cb_config->c_kids ) { @@ -6234,6 +6350,7 @@ static struct { { "backend", &cfAd_backend }, { "database", &cfAd_database }, { "include", &cfAd_include }, + { "ldapsyntax", &cfAd_syntax }, { "objectclass", &cfAd_oc }, { "objectidentifier", &cfAd_om }, { "overlay", &cfAd_overlay }, diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h index ea22921999..27aaee2951 100644 --- a/servers/slapd/proto-slap.h +++ b/servers/slapd/proto-slap.h @@ -1699,6 +1699,8 @@ LDAP_SLAPD_F (int) parse_at LDAP_P(( LDAP_SLAPD_F (char *) scherr2str LDAP_P((int code)) LDAP_GCCATTR((const)); LDAP_SLAPD_F (int) dscompare LDAP_P(( const char *s1, const char *s2del, char delim )); +LDAP_SLAPD_F (int) parse_syn LDAP_P(( + struct config_args_s *ca, Syntax **sat, Syntax *prev )); /* * sessionlog.c @@ -1758,7 +1760,10 @@ LDAP_SLAPD_F (Syntax *) syn_find_desc LDAP_P(( const char *syndesc, int *slen )); LDAP_SLAPD_F (int) syn_add LDAP_P(( LDAPSyntax *syn, + int user, slap_syntax_defs_rec *def, + Syntax **ssyn, + Syntax *prev, const char **err )); LDAP_SLAPD_F (void) syn_destroy LDAP_P(( void )); @@ -1767,6 +1772,13 @@ LDAP_SLAPD_F (int) register_syntax LDAP_P(( LDAP_SLAPD_F (int) syn_schema_info( Entry *e ); +LDAP_SLAPD_F (int) syn_start LDAP_P(( Syntax **at )); +LDAP_SLAPD_F (int) syn_next LDAP_P(( Syntax **at )); +LDAP_SLAPD_F (void) syn_delete LDAP_P(( Syntax *at )); + +LDAP_SLAPD_F (void) syn_unparse LDAP_P(( + BerVarray *bva, Syntax *start, Syntax *end, int system )); + /* * user.c */ diff --git a/servers/slapd/schemaparse.c b/servers/slapd/schemaparse.c index fa9cb2ee07..ce2d4ce7ea 100644 --- a/servers/slapd/schemaparse.c +++ b/servers/slapd/schemaparse.c @@ -333,3 +333,68 @@ done:; return code; } + +static void +syn_usage( void ) +{ + fprintf( stderr, "%s", + "SyntaxDescription = \"(\" whsp\n" + " numericoid whsp ; object identifier\n" + " [ whsp \"DESC\" whsp qdstring ] ; description\n" + " extensions whsp \")\" ; extensions\n" + " whsp \")\"\n"); +} + +int +parse_syn( + struct config_args_s *c, + Syntax **ssyn, + Syntax *prev ) +{ + LDAPSyntax *syn; + slap_syntax_defs_rec def = { 0 }; + int code; + const char *err; + char *line = strchr( c->line, '(' ); + + syn = ldap_str2syntax( line, &code, &err, LDAP_SCHEMA_ALLOW_ALL ); + if ( !syn ) { + snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: %s before %s", + c->argv[0], ldap_scherr2str(code), err ); + Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, + "%s %s\n", c->log, c->cr_msg, 0 ); + syn_usage(); + return 1; + } + + if ( syn->syn_oid == NULL ) { + snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: OID is missing", + c->argv[0] ); + Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, + "%s %s\n", c->log, c->cr_msg, 0 ); + syn_usage(); + code = 1; + goto done; + } + + code = syn_add( syn, 1, &def, ssyn, prev, &err ); + if ( code ) { + snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: %s: \"%s\"", + c->argv[0], scherr2str(code), err); + Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, + "%s %s\n", c->log, c->cr_msg, 0 ); + code = 1; + goto done; + } + +done:; + if ( code ) { + ldap_syntax_free( syn ); + + } else { + ldap_memfree( syn ); + } + + return code; +} + diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h index 24fc1c1719..06181e1769 100644 --- a/servers/slapd/slap.h +++ b/servers/slapd/slap.h @@ -417,6 +417,7 @@ struct Syntax { #else #define SLAP_SYNTAX_HIDE 0x8000U /* hide (do not publish) */ #endif +#define SLAP_SYNTAX_HARDCODE 0x10000U /* This is hardcoded schema */ Syntax **ssyn_sups; @@ -433,7 +434,7 @@ struct Syntax { struct ComponentDesc* ssync_comp_syntax; #endif - LDAP_SLIST_ENTRY(Syntax) ssyn_next; + LDAP_STAILQ_ENTRY(Syntax) ssyn_next; }; #define slap_syntax_is_flag(s,flag) ((int)((s)->ssyn_flags & (flag)) ? 1 : 0) diff --git a/servers/slapd/syntax.c b/servers/slapd/syntax.c index c7089acc45..12ff35f7dd 100644 --- a/servers/slapd/syntax.c +++ b/servers/slapd/syntax.c @@ -30,8 +30,11 @@ struct sindexrec { }; static Avlnode *syn_index = NULL; -static LDAP_SLIST_HEAD(SyntaxList, Syntax) syn_list - = LDAP_SLIST_HEAD_INITIALIZER(&syn_list); +static LDAP_STAILQ_HEAD(SyntaxList, Syntax) syn_list + = LDAP_STAILQ_HEAD_INITIALIZER(syn_list); + +/* Last hardcoded attribute registered */ +Syntax *syn_sys_tail; static int syn_index_cmp( @@ -68,7 +71,7 @@ syn_find_desc( const char *syndesc, int *len ) { Syntax *synp; - LDAP_SLIST_FOREACH(synp, &syn_list, ssyn_next) { + LDAP_STAILQ_FOREACH(synp, &syn_list, ssyn_next) { if ((*len = dscompare( synp->ssyn_syn.syn_desc, syndesc, '{' /*'}'*/ ))) { return synp; } @@ -111,9 +114,9 @@ syn_destroy( void ) Syntax *s; avl_free( syn_index, ldap_memfree ); - while( !LDAP_SLIST_EMPTY( &syn_list ) ) { - s = LDAP_SLIST_FIRST( &syn_list ); - LDAP_SLIST_REMOVE_HEAD( &syn_list, ssyn_next ); + while( !LDAP_STAILQ_EMPTY( &syn_list ) ) { + s = LDAP_STAILQ_FIRST( &syn_list ); + LDAP_STAILQ_REMOVE_HEAD( &syn_list, ssyn_next ); if ( s->ssyn_sups ) { SLAP_FREE( s->ssyn_sups ); } @@ -123,14 +126,13 @@ syn_destroy( void ) static int syn_insert( - Syntax *ssyn, - const char **err -) + Syntax *ssyn, + Syntax *prev, + const char **err ) { struct sindexrec *sir; - LDAP_SLIST_NEXT( ssyn, ssyn_next ) = NULL; - LDAP_SLIST_INSERT_HEAD( &syn_list, ssyn, ssyn_next ); + LDAP_STAILQ_NEXT( ssyn, ssyn_next ) = NULL; if ( ssyn->ssyn_oid ) { sir = (struct sindexrec *) @@ -150,19 +152,36 @@ syn_insert( /* FIX: temporal consistency check */ syn_find(sir->sir_name); } + + if ( ssyn->ssyn_flags & SLAP_AT_HARDCODE ) { + prev = syn_sys_tail; + syn_sys_tail = ssyn; + } + + if ( prev ) { + LDAP_STAILQ_INSERT_AFTER( &syn_list, prev, ssyn, ssyn_next ); + } else { + LDAP_STAILQ_INSERT_TAIL( &syn_list, ssyn, ssyn_next ); + } return 0; } int syn_add( - LDAPSyntax *syn, - slap_syntax_defs_rec *def, - const char **err -) + LDAPSyntax *syn, + int user, + slap_syntax_defs_rec *def, + Syntax **ssynp, + Syntax *prev, + const char **err ) { Syntax *ssyn; int code = 0; + if ( ssynp != NULL ) { + *ssynp = NULL; + } + ssyn = (Syntax *) SLAP_CALLOC( 1, sizeof(Syntax) ); if ( ssyn == NULL ) { Debug( LDAP_DEBUG_ANY, "SLAP_CALLOC Error\n", 0, 0, 0 ); @@ -171,7 +190,7 @@ syn_add( AC_MEMCPY( &ssyn->ssyn_syn, syn, sizeof(LDAPSyntax) ); - LDAP_SLIST_NEXT(ssyn,ssyn_next) = NULL; + LDAP_STAILQ_NEXT(ssyn,ssyn_next) = NULL; /* * note: ssyn_bvoid uses the same memory of ssyn_syn.syn_oid; @@ -254,9 +273,11 @@ syn_add( } } - if ( code == 0 ) { - code = syn_insert( ssyn, err ); + if ( !user ) + ssyn->ssyn_flags |= SLAP_SYNTAX_HARDCODE; + if ( code == 0 ) { + code = syn_insert( ssyn, prev, err ); } if ( code != 0 && ssyn != NULL ) { @@ -264,6 +285,11 @@ syn_add( SLAP_FREE( ssyn->ssyn_sups ); } SLAP_FREE( ssyn ); + ssyn = NULL; + } + + if (ssynp ) { + *ssynp = ssyn; } return code; @@ -285,7 +311,7 @@ register_syntax( return( -1 ); } - code = syn_add( syn, def, &err ); + code = syn_add( syn, 0, def, NULL, NULL, &err ); if ( code ) { Debug( LDAP_DEBUG_ANY, "Error in register_syntax: %s %s in %s\n", @@ -308,7 +334,7 @@ syn_schema_info( Entry *e ) struct berval val; struct berval nval; - LDAP_SLIST_FOREACH(syn, &syn_list, ssyn_next ) { + LDAP_STAILQ_FOREACH(syn, &syn_list, ssyn_next ) { if ( ! syn->ssyn_validate ) { /* skip syntaxes without validators */ continue; @@ -338,3 +364,92 @@ syn_schema_info( Entry *e ) return 0; } +void +syn_delete( Syntax *syn ) +{ + LDAP_STAILQ_REMOVE(&syn_list, syn, Syntax, ssyn_next); +} + +int +syn_start( Syntax **syn ) +{ + assert( syn != NULL ); + + *syn = LDAP_STAILQ_FIRST(&syn_list); + + return (*syn != NULL); +} + +int +syn_next( Syntax **syn ) +{ + assert( syn != NULL ); + +#if 0 /* pedantic check: don't use this */ + { + Syntax *tmp = NULL; + + LDAP_STAILQ_FOREACH(tmp,&syn_list,ssyn_next) { + if ( tmp == *syn ) { + break; + } + } + + assert( tmp != NULL ); + } +#endif + + *syn = LDAP_STAILQ_NEXT(*syn,ssyn_next); + + return (*syn != NULL); +} + +void +syn_unparse( BerVarray *res, Syntax *start, Syntax *end, int sys ) +{ + Syntax *syn; + int i, num; + struct berval bv, *bva = NULL, idx; + char ibuf[32]; + + if ( !start ) + start = LDAP_STAILQ_FIRST( &syn_list ); + + /* count the result size */ + i = 0; + for ( syn = start; syn; syn = LDAP_STAILQ_NEXT( syn, ssyn_next ) ) { + if ( sys && !( syn->ssyn_flags & SLAP_SYNTAX_HARDCODE ) ) break; + i++; + if ( syn == end ) break; + } + if ( !i ) return; + + num = i; + bva = ch_malloc( (num+1) * sizeof(struct berval) ); + BER_BVZERO( bva ); + idx.bv_val = ibuf; + if ( sys ) { + idx.bv_len = 0; + ibuf[0] = '\0'; + } + i = 0; + for ( syn = start; syn; syn = LDAP_STAILQ_NEXT( syn, ssyn_next ) ) { + if ( sys && !( syn->ssyn_flags & SLAP_SYNTAX_HARDCODE ) ) break; + if ( ldap_syntax2bv( &syn->ssyn_syn, &bv ) == NULL ) { + ber_bvarray_free( bva ); + } + if ( !sys ) { + idx.bv_len = sprintf(idx.bv_val, "{%d}", i); + } + bva[i].bv_len = idx.bv_len + bv.bv_len; + bva[i].bv_val = ch_malloc( bva[i].bv_len + 1 ); + strcpy( bva[i].bv_val, ibuf ); + strcpy( bva[i].bv_val + idx.bv_len, bv.bv_val ); + i++; + bva[i].bv_val = NULL; + ldap_memfree( bv.bv_val ); + if ( syn == end ) break; + } + *res = bva; +} + -- 2.39.5