diff options
Diffstat (limited to '')
-rw-r--r-- | servers/slapd/schema.c | 167 | ||||
-rw-r--r-- | servers/slapd/schema/README | 80 | ||||
-rw-r--r-- | servers/slapd/schema/collective.ldif | 48 | ||||
-rw-r--r-- | servers/slapd/schema/corba.ldif | 42 | ||||
-rw-r--r-- | servers/slapd/schema/cosine.ldif | 200 | ||||
-rw-r--r-- | servers/slapd/schema/duaconf.ldif | 83 | ||||
-rw-r--r-- | servers/slapd/schema/dyngroup.ldif | 71 | ||||
-rw-r--r-- | servers/slapd/schema/dyngroup.schema | 91 | ||||
-rw-r--r-- | servers/slapd/schema/inetorgperson.ldif | 69 | ||||
-rw-r--r-- | servers/slapd/schema/java.ldif | 59 | ||||
-rw-r--r-- | servers/slapd/schema/misc.ldif | 45 | ||||
-rw-r--r-- | servers/slapd/schema/misc.schema | 75 | ||||
-rw-r--r-- | servers/slapd/schema/nis.ldif | 120 | ||||
-rw-r--r-- | servers/slapd/schema/nis.schema | 237 | ||||
-rw-r--r-- | servers/slapd/schema/openldap.ldif | 88 | ||||
-rw-r--r-- | servers/slapd/schema/openldap.schema | 54 | ||||
-rw-r--r-- | servers/slapd/schema/pmi.ldif | 123 | ||||
-rw-r--r-- | servers/slapd/schema/ppolicy.ldif | 87 | ||||
-rw-r--r-- | servers/slapd/schema_check.c | 938 | ||||
-rw-r--r-- | servers/slapd/schema_init.c | 6890 | ||||
-rw-r--r-- | servers/slapd/schema_prep.c | 1614 | ||||
-rw-r--r-- | servers/slapd/schemaparse.c | 400 |
22 files changed, 11581 insertions, 0 deletions
diff --git a/servers/slapd/schema.c b/servers/slapd/schema.c new file mode 100644 index 0000000..b3d9929 --- /dev/null +++ b/servers/slapd/schema.c @@ -0,0 +1,167 @@ +/* schema.c - routines to manage schema definitions */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/ctype.h> +#include <ac/string.h> +#include <ac/socket.h> + +#include "slap.h" +#include "lutil.h" + + +int +schema_info( Entry **entry, const char **text ) +{ + AttributeDescription *ad_structuralObjectClass + = slap_schema.si_ad_structuralObjectClass; + AttributeDescription *ad_objectClass + = slap_schema.si_ad_objectClass; + AttributeDescription *ad_createTimestamp + = slap_schema.si_ad_createTimestamp; + AttributeDescription *ad_modifyTimestamp + = slap_schema.si_ad_modifyTimestamp; + + Entry *e; + struct berval vals[5]; + struct berval nvals[5]; + + e = entry_alloc(); + if( e == NULL ) { + /* Out of memory, do something about it */ + Debug( LDAP_DEBUG_ANY, + "schema_info: entry_alloc failed - out of memory.\n", 0, 0, 0 ); + *text = "out of memory"; + return LDAP_OTHER; + } + + e->e_attrs = NULL; + /* backend-specific schema info should be created by the + * backend itself + */ + ber_dupbv( &e->e_name, &frontendDB->be_schemadn ); + ber_dupbv( &e->e_nname, &frontendDB->be_schemandn ); + e->e_private = NULL; + + BER_BVSTR( &vals[0], "subentry" ); + if( attr_merge_one( e, ad_structuralObjectClass, vals, NULL ) ) { + /* Out of memory, do something about it */ + entry_free( e ); + *text = "out of memory"; + return LDAP_OTHER; + } + + BER_BVSTR( &vals[0], "top" ); + BER_BVSTR( &vals[1], "subentry" ); + BER_BVSTR( &vals[2], "subschema" ); + BER_BVSTR( &vals[3], "extensibleObject" ); + BER_BVZERO( &vals[4] ); + if ( attr_merge( e, ad_objectClass, vals, NULL ) ) { + /* Out of memory, do something about it */ + entry_free( e ); + *text = "out of memory"; + return LDAP_OTHER; + } + + { + int rc; + AttributeDescription *desc = NULL; + struct berval rdn = frontendDB->be_schemadn; + vals[0].bv_val = ber_bvchr( &rdn, '=' ); + + if( vals[0].bv_val == NULL ) { + *text = "improperly configured subschema subentry"; + return LDAP_OTHER; + } + + vals[0].bv_val++; + vals[0].bv_len = rdn.bv_len - (vals[0].bv_val - rdn.bv_val); + rdn.bv_len -= vals[0].bv_len + 1; + + rc = slap_bv2ad( &rdn, &desc, text ); + + if( rc != LDAP_SUCCESS ) { + entry_free( e ); + *text = "improperly configured subschema subentry"; + return LDAP_OTHER; + } + + nvals[0].bv_val = ber_bvchr( &frontendDB->be_schemandn, '=' ); + assert( nvals[0].bv_val != NULL ); + nvals[0].bv_val++; + nvals[0].bv_len = frontendDB->be_schemandn.bv_len - + (nvals[0].bv_val - frontendDB->be_schemandn.bv_val); + + if ( attr_merge_one( e, desc, vals, nvals ) ) { + /* Out of memory, do something about it */ + entry_free( e ); + *text = "out of memory"; + return LDAP_OTHER; + } + } + + { + char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ]; + + /* + * According to RFC 4512: + + Servers SHOULD maintain the 'creatorsName', 'createTimestamp', + 'modifiersName', and 'modifyTimestamp' attributes for all entries of + the DIT. + + * to be conservative, we declare schema created + * AND modified at server startup time ... + */ + + vals[0].bv_val = timebuf; + vals[0].bv_len = sizeof( timebuf ); + + slap_timestamp( &starttime, vals ); + + if( attr_merge_one( e, ad_createTimestamp, vals, NULL ) ) { + /* Out of memory, do something about it */ + entry_free( e ); + *text = "out of memory"; + return LDAP_OTHER; + } + if( attr_merge_one( e, ad_modifyTimestamp, vals, NULL ) ) { + /* Out of memory, do something about it */ + entry_free( e ); + *text = "out of memory"; + return LDAP_OTHER; + } + } + + if ( syn_schema_info( e ) + || mr_schema_info( e ) + || mru_schema_info( e ) + || at_schema_info( e ) + || oc_schema_info( e ) + || cr_schema_info( e ) ) + { + /* Out of memory, do something about it */ + entry_free( e ); + *text = "out of memory"; + return LDAP_OTHER; + } + + *entry = e; + return LDAP_SUCCESS; +} diff --git a/servers/slapd/schema/README b/servers/slapd/schema/README new file mode 100644 index 0000000..b45baac --- /dev/null +++ b/servers/slapd/schema/README @@ -0,0 +1,80 @@ +This directory contains user application schema definitions for use +with slapd(8). + +File Description +---- ----------- +collective.schema Collective attributes (experimental) +corba.schema Corba Object +core.schema OpenLDAP "core" +cosine.schema COSINE Pilot +duaconf.schema Client Configuration (work in progress) +dyngroup.schema Dynamic Group (experimental) +inetorgperson.schema InetOrgPerson +java.schema Java Object +misc.schema Miscellaneous Schema (experimental) +nadf.schema North American Directory Forum (obsolete) +nis.schema Network Information Service (experimental) +openldap.schema OpenLDAP Project (FYI) +ppolicy.schema Password Policy Schema (work in progress) + +Additional "generally useful" schema definitions can be submitted +using the OpenLDAP Issue Tracking System <http://www.openldap.org/its/>. +Submissions should include a stable reference to a mature, open +technical specification (e.g., an RFC) for the schema. + +The core.ldif and openldap.ldif files are equivalent to their +corresponding .schema files. They have been provided as examples +for use with the dynamic configuration backend. These example files +are not actually necessary since slapd will automatically convert any +included *.schema files into LDIF when converting a slapd.conf file +to a configuration database, but they serve as a model of how to +convert schema files in general. + +--- + +This notice applies to all files in this directory. + +Copyright 1998-2021 The OpenLDAP Foundation, Redwood City, California, USA +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted only as authorized by the OpenLDAP +Public License. A copy of this license is available at +http://www.OpenLDAP.org/license.html or in file LICENSE in the +top-level directory of the distribution. + +--- + +This notice applies to all schema in this directory which are derived +from RFCs and other IETF documents. + +Portions Copyright 1991-2004, The Internet Society. All Rights Reserved. + +This document and translations of it may be copied and furnished +to others, and derivative works that comment on or otherwise explain +it or assist in its implementation may be prepared, copied, published +and distributed, in whole or in part, without restriction of any +kind, provided that the above copyright notice and this paragraph +are included on all such copies and derivative works. However, +this document itself may not be modified in any way, such as by +removing the copyright notice or references to the Internet Society +or other Internet organizations, except as needed for the purpose +of developing Internet standards in which case the procedures for +copyrights defined in the Internet Standards process must be +followed, or as required to translate it into languages other than +English. + +The limited permissions granted above are perpetual and will not +be revoked by the Internet Society or its successors or assigns. + +This document and the information contained herein is provided on +an "AS IS" basis and THE AUTHORS, THE INTERNET SOCIETY, AND THE +INTERNET ENGINEERING TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE +OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY +IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR +PURPOSE. + + +--- +$OpenLDAP$ diff --git a/servers/slapd/schema/collective.ldif b/servers/slapd/schema/collective.ldif new file mode 100644 index 0000000..8402951 --- /dev/null +++ b/servers/slapd/schema/collective.ldif @@ -0,0 +1,48 @@ +# collective.ldif -- Collective attribute schema +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2021 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. +# +## Portions Copyright (C) The Internet Society (2003). +## Please see full copyright statement below. +# +# From RFC 3671 [portions trimmed]: +# Collective Attributes in LDAP +# +# This file was automatically generated from collective.schema; see that file +# for complete references. +# +dn: cn=collective,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: collective +olcAttributeTypes: {0}( 2.5.4.7.1 NAME 'c-l' SUP l COLLECTIVE ) +olcAttributeTypes: {1}( 2.5.4.8.1 NAME 'c-st' SUP st COLLECTIVE ) +olcAttributeTypes: {2}( 2.5.4.9.1 NAME 'c-street' SUP street COLLECTIVE ) +olcAttributeTypes: {3}( 2.5.4.10.1 NAME 'c-o' SUP o COLLECTIVE ) +olcAttributeTypes: {4}( 2.5.4.11.1 NAME 'c-ou' SUP ou COLLECTIVE ) +olcAttributeTypes: {5}( 2.5.4.16.1 NAME 'c-PostalAddress' SUP postalAddress CO + LLECTIVE ) +olcAttributeTypes: {6}( 2.5.4.17.1 NAME 'c-PostalCode' SUP postalCode COLLECTI + VE ) +olcAttributeTypes: {7}( 2.5.4.18.1 NAME 'c-PostOfficeBox' SUP postOfficeBox CO + LLECTIVE ) +olcAttributeTypes: {8}( 2.5.4.19.1 NAME 'c-PhysicalDeliveryOfficeName' SUP phy + sicalDeliveryOfficeName COLLECTIVE ) +olcAttributeTypes: {9}( 2.5.4.20.1 NAME 'c-TelephoneNumber' SUP telephoneNumbe + r COLLECTIVE ) +olcAttributeTypes: {10}( 2.5.4.21.1 NAME 'c-TelexNumber' SUP telexNumber COLLE + CTIVE ) +olcAttributeTypes: {11}( 2.5.4.23.1 NAME 'c-FacsimileTelephoneNumber' SUP facs + imileTelephoneNumber COLLECTIVE ) +olcAttributeTypes: {12}( 2.5.4.25.1 NAME 'c-InternationalISDNNumber' SUP inter + nationalISDNNumber COLLECTIVE ) diff --git a/servers/slapd/schema/corba.ldif b/servers/slapd/schema/corba.ldif new file mode 100644 index 0000000..0b81686 --- /dev/null +++ b/servers/slapd/schema/corba.ldif @@ -0,0 +1,42 @@ +# corba.ldif -- Corba Object Schema +# depends upon core.ldif +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2021 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. +# +## Portions Copyright (C) The Internet Society (1999). +## Please see full copyright statement below. +# +# From RFC 2714 [portions trimmed]: +# Schema for Representing CORBA Object References in an LDAP Directory +# +# This file was automatically generated from corba.schema; see that file +# for complete references. +# +dn: cn=corba,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: corba +olcAttributeTypes: {0}( 1.3.6.1.4.1.42.2.27.4.1.14 NAME 'corbaIor' DESC 'Strin + gified interoperable object reference of a CORBA object' EQUALITY caseIgnoreI + A5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) +olcAttributeTypes: {1}( 1.3.6.1.4.1.42.2.27.4.1.15 NAME 'corbaRepositoryId' DE + SC 'Repository ids of interfaces implemented by a CORBA object' EQUALITY case + ExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +olcObjectClasses: {0}( 1.3.6.1.4.1.42.2.27.4.2.10 NAME 'corbaContainer' DESC ' + Container for a CORBA object' SUP top STRUCTURAL MUST cn ) +olcObjectClasses: {1}( 1.3.6.1.4.1.42.2.27.4.2.9 NAME 'corbaObject' DESC 'CORB + A object representation' SUP top ABSTRACT MAY ( corbaRepositoryId $ descripti + on ) ) +olcObjectClasses: {2}( 1.3.6.1.4.1.42.2.27.4.2.11 NAME 'corbaObjectReference' + DESC 'CORBA interoperable object reference' SUP corbaObject AUXILIARY MUST co + rbaIor ) diff --git a/servers/slapd/schema/cosine.ldif b/servers/slapd/schema/cosine.ldif new file mode 100644 index 0000000..4357642 --- /dev/null +++ b/servers/slapd/schema/cosine.ldif @@ -0,0 +1,200 @@ +# RFC1274: Cosine and Internet X.500 schema +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2021 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. +# +# RFC1274: Cosine and Internet X.500 schema +# +# This file contains LDAPv3 schema derived from X.500 COSINE "pilot" +# schema. As this schema was defined for X.500(89), some +# oddities were introduced in the mapping to LDAPv3. The +# mappings were based upon: draft-ietf-asid-ldapv3-attributes-03.txt +# (a work in progress) +# +# Note: It seems that the pilot schema evolved beyond what was +# described in RFC1274. However, this document attempts to describes +# RFC1274 as published. +# +# Depends on core.ldif +# +# This file was automatically generated from cosine.schema; see that +# file for complete background. +# +dn: cn=cosine,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: cosine +olcAttributeTypes: ( 0.9.2342.19200300.100.1.2 NAME 'textEncodedORAddress' + EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1. + 1466.115.121.1.15{256} ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.4 NAME 'info' DESC 'RFC1274: g + eneral information' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{2048} ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.5 NAME ( 'drink' 'favouriteDri + nk' ) DESC 'RFC1274: favorite drink' EQUALITY caseIgnoreMatch SUBSTR caseIgno + reSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.6 NAME 'roomNumber' DESC 'RFC1 + 274: room number' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch S + YNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.7 NAME 'photo' DESC 'RFC1274: + photo (G3 fax)' SYNTAX 1.3.6.1.4.1.1466.115.121.1.23{25000} ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.8 NAME 'userClass' DESC 'RFC12 + 74: category of user' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMat + ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.9 NAME 'host' DESC 'RFC1274: h + ost computer' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTA + X 1.3.6.1.4.1.1466.115.121.1.15{256} ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.10 NAME 'manager' DESC 'RFC127 + 4: DN of manager' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115 + .121.1.12 ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.11 NAME 'documentIdentifier' D + ESC 'RFC1274: unique identifier of document' EQUALITY caseIgnoreMatch SUBSTR + caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.12 NAME 'documentTitle' DESC ' + RFC1274: title of document' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstri + ngsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.13 NAME 'documentVersion' DES + C 'RFC1274: version of document' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSu + bstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.14 NAME 'documentAuthor' DESC + 'RFC1274: DN of author of document' EQUALITY distinguishedNameMatch SYNTAX 1 + .3.6.1.4.1.1466.115.121.1.12 ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.15 NAME 'documentLocation' DE + SC 'RFC1274: location of document original' EQUALITY caseIgnoreMatch SUBSTR c + aseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.20 NAME ( 'homePhone' 'homeTe + lephoneNumber' ) DESC 'RFC1274: home telephone number' EQUALITY telephoneNumb + erMatch SUBSTR telephoneNumberSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121 + .1.50 ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.21 NAME 'secretary' DESC 'RFC + 1274: DN of secretary' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.146 + 6.115.121.1.12 ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.22 NAME 'otherMailbox' SYNTAX + 1.3.6.1.4.1.1466.115.121.1.39 ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.26 NAME 'aRecord' EQUALITY ca + seIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.27 NAME 'mDRecord' EQUALITY c + aseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.28 NAME 'mXRecord' EQUALITY c + aseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.29 NAME 'nSRecord' EQUALITY c + aseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.30 NAME 'sOARecord' EQUALITY + caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.31 NAME 'cNAMERecord' EQUALIT + Y caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.38 NAME 'associatedName' DESC + 'RFC1274: DN of entry associated with domain' EQUALITY distinguishedNameMatc + h SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.39 NAME 'homePostalAddress' D + ESC 'RFC1274: home postal address' EQUALITY caseIgnoreListMatch SUBSTR caseIg + noreListSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.40 NAME 'personalTitle' DESC + 'RFC1274: personal title' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstring + sMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.41 NAME ( 'mobile' 'mobileTel + ephoneNumber' ) DESC 'RFC1274: mobile telephone number' EQUALITY telephoneNum + berMatch SUBSTR telephoneNumberSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.12 + 1.1.50 ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.42 NAME ( 'pager' 'pagerTelep + honeNumber' ) DESC 'RFC1274: pager telephone number' EQUALITY telephoneNumber + Match SUBSTR telephoneNumberSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1 + .50 ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.43 NAME ( 'co' 'friendlyCount + ryName' ) DESC 'RFC1274: friendly country name' EQUALITY caseIgnoreMatch SUBS + TR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.44 NAME 'uniqueIdentifier' DE + SC 'RFC1274: unique identifer' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.14 + 66.115.121.1.15{256} ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.45 NAME 'organizationalStatus + ' DESC 'RFC1274: organizational status' EQUALITY caseIgnoreMatch SUBSTR caseI + gnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.46 NAME 'janetMailbox' DESC ' + RFC1274: Janet mailbox' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5Subst + ringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.47 NAME 'mailPreferenceOption + ' DESC 'RFC1274: mail preference option' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 + ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.48 NAME 'buildingName' DESC ' + RFC1274: name of building' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstrin + gsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.49 NAME 'dSAQuality' DESC 'RF + C1274: DSA Quality' SYNTAX 1.3.6.1.4.1.1466.115.121.1.19 SINGLE-VALUE ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.50 NAME 'singleLevelQuality' + DESC 'RFC1274: Single Level Quality' SYNTAX 1.3.6.1.4.1.1466.115.121.1.13 SIN + GLE-VALUE ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.51 NAME 'subtreeMinimumQualit + y' DESC 'RFC1274: Subtree Mininum Quality' SYNTAX 1.3.6.1.4.1.1466.115.121.1. + 13 SINGLE-VALUE ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.52 NAME 'subtreeMaximumQualit + y' DESC 'RFC1274: Subtree Maximun Quality' SYNTAX 1.3.6.1.4.1.1466.115.121.1. + 13 SINGLE-VALUE ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.53 NAME 'personalSignature' D + ESC 'RFC1274: Personal Signature (G3 fax)' SYNTAX 1.3.6.1.4.1.1466.115.121.1. + 23 ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.54 NAME 'dITRedirect' DESC 'R + FC1274: DIT Redirect' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466 + .115.121.1.12 ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.55 NAME 'audio' DESC 'RFC1274 + : audio (u-law)' SYNTAX 1.3.6.1.4.1.1466.115.121.1.4{25000} ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.56 NAME 'documentPublisher' D + ESC 'RFC1274: publisher of document' EQUALITY caseIgnoreMatch SUBSTR caseIgno + reSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +olcObjectClasses: ( 0.9.2342.19200300.100.4.4 NAME ( 'pilotPerson' 'newPilo + tPerson' ) SUP person STRUCTURAL MAY ( userid $ textEncodedORAddress $ rfc822 + Mailbox $ favouriteDrink $ roomNumber $ userClass $ homeTelephoneNumber $ hom + ePostalAddress $ secretary $ personalTitle $ preferredDeliveryMethod $ busine + ssCategory $ janetMailbox $ otherMailbox $ mobileTelephoneNumber $ pagerTelep + honeNumber $ organizationalStatus $ mailPreferenceOption $ personalSignature + ) ) +olcObjectClasses: ( 0.9.2342.19200300.100.4.5 NAME 'account' SUP top STRUCT + URAL MUST userid MAY ( description $ seeAlso $ localityName $ organizationNam + e $ organizationalUnitName $ host ) ) +olcObjectClasses: ( 0.9.2342.19200300.100.4.6 NAME 'document' SUP top STRUC + TURAL MUST documentIdentifier MAY ( commonName $ description $ seeAlso $ loca + lityName $ organizationName $ organizationalUnitName $ documentTitle $ docume + ntVersion $ documentAuthor $ documentLocation $ documentPublisher ) ) +olcObjectClasses: ( 0.9.2342.19200300.100.4.7 NAME 'room' SUP top STRUCTURA + L MUST commonName MAY ( roomNumber $ description $ seeAlso $ telephoneNumber + ) ) +olcObjectClasses: ( 0.9.2342.19200300.100.4.9 NAME 'documentSeries' SUP top + STRUCTURAL MUST commonName MAY ( description $ seeAlso $ telephonenumber $ l + ocalityName $ organizationName $ organizationalUnitName ) ) +olcObjectClasses: ( 0.9.2342.19200300.100.4.13 NAME 'domain' SUP top STRUCT + URAL MUST domainComponent MAY ( associatedName $ organizationName $ descripti + on $ businessCategory $ seeAlso $ searchGuide $ userPassword $ localityName $ + stateOrProvinceName $ streetAddress $ physicalDeliveryOfficeName $ postalAdd + ress $ postalCode $ postOfficeBox $ streetAddress $ facsimileTelephoneNumber + $ internationalISDNNumber $ telephoneNumber $ teletexTerminalIdentifier $ tel + exNumber $ preferredDeliveryMethod $ destinationIndicator $ registeredAddress + $ x121Address ) ) +olcObjectClasses: ( 0.9.2342.19200300.100.4.14 NAME 'RFC822localPart' SUP d + omain STRUCTURAL MAY ( commonName $ surname $ description $ seeAlso $ telepho + neNumber $ physicalDeliveryOfficeName $ postalAddress $ postalCode $ postOffi + ceBox $ streetAddress $ facsimileTelephoneNumber $ internationalISDNNumber $ + telephoneNumber $ teletexTerminalIdentifier $ telexNumber $ preferredDelivery + Method $ destinationIndicator $ registeredAddress $ x121Address ) ) +olcObjectClasses: ( 0.9.2342.19200300.100.4.15 NAME 'dNSDomain' SUP domain + STRUCTURAL MAY ( ARecord $ MDRecord $ MXRecord $ NSRecord $ SOARecord $ CNAME + Record ) ) +olcObjectClasses: ( 0.9.2342.19200300.100.4.17 NAME 'domainRelatedObject' D + ESC 'RFC1274: an object related to an domain' SUP top AUXILIARY MUST associat + edDomain ) +olcObjectClasses: ( 0.9.2342.19200300.100.4.18 NAME 'friendlyCountry' SUP c + ountry STRUCTURAL MUST friendlyCountryName ) +olcObjectClasses: ( 0.9.2342.19200300.100.4.20 NAME 'pilotOrganization' SU + P ( organization $ organizationalUnit ) STRUCTURAL MAY buildingName ) +olcObjectClasses: ( 0.9.2342.19200300.100.4.21 NAME 'pilotDSA' SUP dsa STR + UCTURAL MAY dSAQuality ) +olcObjectClasses: ( 0.9.2342.19200300.100.4.22 NAME 'qualityLabelledData' + SUP top AUXILIARY MUST dsaQuality MAY ( subtreeMinimumQuality $ subtreeMaximu + mQuality ) ) diff --git a/servers/slapd/schema/duaconf.ldif b/servers/slapd/schema/duaconf.ldif new file mode 100644 index 0000000..8b267b8 --- /dev/null +++ b/servers/slapd/schema/duaconf.ldif @@ -0,0 +1,83 @@ +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2021 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. +# +# DUA schema from draft-joslin-config-schema (a work in progress) +# +# This file was automatically generated from duaconf.schema; see that file +# for complete references. +# +dn: cn=duaconf,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: duaconf +olcObjectIdentifier: {0}DUAConfSchemaOID 1.3.6.1.4.1.11.1.3.1 +olcAttributeTypes: {0}( DUAConfSchemaOID:1.0 NAME 'defaultServerList' DESC 'De + fault LDAP server host address used by a DUA' EQUALITY caseIgnoreMatch SYNTAX + 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE ) +olcAttributeTypes: {1}( DUAConfSchemaOID:1.1 NAME 'defaultSearchBase' DESC 'De + fault LDAP base DN used by a DUA' EQUALITY distinguishedNameMatch SYNTAX 1.3. + 6.1.4.1.1466.115.121.1.12 SINGLE-VALUE ) +olcAttributeTypes: {2}( DUAConfSchemaOID:1.2 NAME 'preferredServerList' DESC ' + Preferred LDAP server host addresses to be used by a DUA' EQUALITY + caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE ) +olcAttributeTypes: {3}( DUAConfSchemaOID:1.3 NAME 'searchTimeLimit' DESC 'Maxi + mum time in seconds a DUA should allow for a search to complete' E + QUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) +olcAttributeTypes: {4}( DUAConfSchemaOID:1.4 NAME 'bindTimeLimit' DESC 'Maximu + m time in seconds a DUA should allow for the bind operation to com + plete' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALU + E ) +olcAttributeTypes: {5}( DUAConfSchemaOID:1.5 NAME 'followReferrals' DESC 'Tell + s DUA if it should follow referrals returned by a DSA search resul + t' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE ) +olcAttributeTypes: {6}( DUAConfSchemaOID:1.16 NAME 'dereferenceAliases' DESC ' + Tells DUA if it should dereference aliases' EQUALITY booleanMatch SYNTAX 1.3. + 6.1.4.1.1466.115.121.1.7 SINGLE-VALUE ) +olcAttributeTypes: {7}( DUAConfSchemaOID:1.6 NAME 'authenticationMethod' DESC + 'A keystring which identifies the type of authentication method us + ed to contact the DSA' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.1 + 21.1.15 SINGLE-VALUE ) +olcAttributeTypes: {8}( DUAConfSchemaOID:1.7 NAME 'profileTTL' DESC 'Time to l + ive, in seconds, before a client DUA should re-read this configura + tion profile' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SING + LE-VALUE ) +olcAttributeTypes: {9}( DUAConfSchemaOID:1.14 NAME 'serviceSearchDescriptor' D + ESC 'LDAP search descriptor list used by a DUA' EQUALITY caseExactMatch SYNTA + X 1.3.6.1.4.1.1466.115.121.1.15 ) +olcAttributeTypes: {10}( DUAConfSchemaOID:1.9 NAME 'attributeMap' DESC 'Attrib + ute mappings used by a DUA' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.14 + 66.115.121.1.26 ) +olcAttributeTypes: {11}( DUAConfSchemaOID:1.10 NAME 'credentialLevel' DESC 'Id + entifies type of credentials a DUA should use when binding to the + LDAP server' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 + SINGLE-VALUE ) +olcAttributeTypes: {12}( DUAConfSchemaOID:1.11 NAME 'objectclassMap' DESC 'Obj + ectclass mappings used by a DUA' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4 + .1.1466.115.121.1.26 ) +olcAttributeTypes: {13}( DUAConfSchemaOID:1.12 NAME 'defaultSearchScope' DESC + 'Default search scope used by a DUA' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6 + .1.4.1.1466.115.121.1.26 SINGLE-VALUE ) +olcAttributeTypes: {14}( DUAConfSchemaOID:1.13 NAME 'serviceCredentialLevel' D + ESC 'Identifies type of credentials a DUA should use when binding + to the LDAP server for a specific service' EQUALITY caseIgnoreIA5M + atch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) +olcAttributeTypes: {15}( DUAConfSchemaOID:1.15 NAME 'serviceAuthenticationMeth + od' DESC 'Authentication method used by a service of the DUA' EQUALITY caseIg + noreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +olcObjectClasses: {0}( DUAConfSchemaOID:2.5 NAME 'DUAConfigProfile' DESC 'Abst + raction of a base configuration for a DUA' SUP top STRUCTURAL MUST cn MAY ( d + efaultServerList $ preferredServerList $ defaultSearchBase $ defaultSearchSco + pe $ searchTimeLimit $ bindTimeLimit $ credentialLevel $ authenticationMethod + $ followReferrals $ dereferenceAliases $ serviceSearchDescriptor $ serviceCr + edentialLevel $ serviceAuthenticationMethod $ objectclassMap $ attributeMap $ + profileTTL ) ) diff --git a/servers/slapd/schema/dyngroup.ldif b/servers/slapd/schema/dyngroup.ldif new file mode 100644 index 0000000..0f828c5 --- /dev/null +++ b/servers/slapd/schema/dyngroup.ldif @@ -0,0 +1,71 @@ +# dyngroup.schema -- Dynamic Group schema +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2021 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. +# +# Dynamic Group schema (experimental), as defined by Netscape. See +# http://www.redhat.com/docs/manuals/ent-server/pdf/esadmin611.pdf +# page 70 for details on how these groups were used. +# +# A description of the objectclass definition is available here: +# http://www.redhat.com/docs/manuals/dir-server/schema/7.1/oc_dir.html#1303745 +# +# depends upon: +# core.schema +# +# These definitions are considered experimental due to the lack of +# a formal specification (e.g., RFC). +# +# NOT RECOMMENDED FOR PRODUCTION USE! USE WITH CAUTION! +# +# The Netscape documentation describes this as an auxiliary objectclass +# but their implementations have always defined it as a structural class. +# The sloppiness here is because Netscape-derived servers don't actually +# implement the X.500 data model, and they don't honor the distinction +# between structural and auxiliary classes. This fact is noted here: +# http://forum.java.sun.com/thread.jspa?threadID=5016864&messageID=9034636 +# +# In accordance with other existing implementations, we define it as a +# structural class. +# +# Our definition of memberURL also does not match theirs but again +# their published definition and what works in practice do not agree. +# In other words, the Netscape definitions are broken and interoperability +# is not guaranteed. +# +# Also see the new DynGroup proposed spec at +# http://tools.ietf.org/html/draft-haripriya-dynamicgroup-02 +dn: cn=dyngroup,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: dyngroup +olcObjectIdentifier: {0}NetscapeRoot 2.16.840.1.113730 +olcObjectIdentifier: {1}NetscapeLDAP NetscapeRoot:3 +olcObjectIdentifier: {2}NetscapeLDAPattributeType NetscapeLDAP:1 +olcObjectIdentifier: {3}NetscapeLDAPobjectClass NetscapeLDAP:2 +olcObjectIdentifier: {4}OpenLDAPExp11 1.3.6.1.4.1.4203.666.11 +olcObjectIdentifier: {5}DynGroupBase OpenLDAPExp11:8 +olcObjectIdentifier: {6}DynGroupAttr DynGroupBase:1 +olcObjectIdentifier: {7}DynGroupOC DynGroupBase:2 +olcAttributeTypes: {0}( NetscapeLDAPattributeType:198 NAME 'memberURL' DESC 'I + dentifies an URL associated with each member of a group. Any type of labeled + URL can be used.' SUP labeledURI ) +olcAttributeTypes: {1}( DynGroupAttr:1 NAME 'dgIdentity' DESC 'Identity to use + when processing the memberURL' SUP distinguishedName SINGLE-VALUE ) +olcAttributeTypes: {2}( DynGroupAttr:2 NAME 'dgAuthz' DESC 'Optional authoriza + tion rules that determine who is allowed to assume the dgIdentity' EQUALITY a + uthzMatch SYNTAX 1.3.6.1.4.1.4203.666.2.7 X-ORDERED 'VALUES' ) +olcObjectClasses: {0}( NetscapeLDAPobjectClass:33 NAME 'groupOfURLs' SUP top S + TRUCTURAL MUST cn MAY ( memberURL $ businessCategory $ description $ o $ ou $ + owner $ seeAlso ) ) +olcObjectClasses: {1}( DynGroupOC:1 NAME 'dgIdentityAux' SUP top AUXILIARY MAY + ( dgIdentity $ dgAuthz ) ) diff --git a/servers/slapd/schema/dyngroup.schema b/servers/slapd/schema/dyngroup.schema new file mode 100644 index 0000000..58c33d6 --- /dev/null +++ b/servers/slapd/schema/dyngroup.schema @@ -0,0 +1,91 @@ +# dyngroup.schema -- Dynamic Group schema +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2021 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. +# +# Dynamic Group schema (experimental), as defined by Netscape. See +# http://www.redhat.com/docs/manuals/ent-server/pdf/esadmin611.pdf +# page 70 for details on how these groups were used. +# +# A description of the objectclass definition is available here: +# http://www.redhat.com/docs/manuals/dir-server/schema/7.1/oc_dir.html#1303745 +# +# depends upon: +# core.schema +# +# These definitions are considered experimental due to the lack of +# a formal specification (e.g., RFC). +# +# NOT RECOMMENDED FOR PRODUCTION USE! USE WITH CAUTION! +# +# The Netscape documentation describes this as an auxiliary objectclass +# but their implementations have always defined it as a structural class. +# The sloppiness here is because Netscape-derived servers don't actually +# implement the X.500 data model, and they don't honor the distinction +# between structural and auxiliary classes. This fact is noted here: +# http://forum.java.sun.com/thread.jspa?threadID=5016864&messageID=9034636 +# +# In accordance with other existing implementations, we define it as a +# structural class. +# +# Our definition of memberURL also does not match theirs but again +# their published definition and what works in practice do not agree. +# In other words, the Netscape definitions are broken and interoperability +# is not guaranteed. +# +# Also see the new DynGroup proposed spec at +# http://tools.ietf.org/html/draft-haripriya-dynamicgroup-02 + +objectIdentifier NetscapeRoot 2.16.840.1.113730 + +objectIdentifier NetscapeLDAP NetscapeRoot:3 +objectIdentifier NetscapeLDAPattributeType NetscapeLDAP:1 +objectIdentifier NetscapeLDAPobjectClass NetscapeLDAP:2 + +objectIdentifier OpenLDAPExp11 1.3.6.1.4.1.4203.666.11 +objectIdentifier DynGroupBase OpenLDAPExp11:8 +objectIdentifier DynGroupAttr DynGroupBase:1 +objectIdentifier DynGroupOC DynGroupBase:2 + +attributetype ( NetscapeLDAPattributeType:198 + NAME 'memberURL' + DESC 'Identifies an URL associated with each member of a group. Any type of labeled URL can be used.' + SUP labeledURI ) + +attributetype ( DynGroupAttr:1 + NAME 'dgIdentity' + DESC 'Identity to use when processing the memberURL' + SUP distinguishedName SINGLE-VALUE ) + +attributeType ( DynGroupAttr:2 + NAME 'dgAuthz' + DESC 'Optional authorization rules that determine who is allowed to assume the dgIdentity' + EQUALITY authzMatch + SYNTAX 1.3.6.1.4.1.4203.666.2.7 + X-ORDERED 'VALUES' ) + +objectClass ( NetscapeLDAPobjectClass:33 + NAME 'groupOfURLs' + SUP top STRUCTURAL + MUST cn + MAY ( memberURL $ businessCategory $ description $ o $ ou $ + owner $ seeAlso ) ) + +# The Haripriya dyngroup schema still needs a lot of work. +# We're just adding support for the dgIdentity attribute for now... +objectClass ( DynGroupOC:1 + NAME 'dgIdentityAux' + SUP top AUXILIARY + MAY ( dgIdentity $ dgAuthz ) ) + + diff --git a/servers/slapd/schema/inetorgperson.ldif b/servers/slapd/schema/inetorgperson.ldif new file mode 100644 index 0000000..965bf6f --- /dev/null +++ b/servers/slapd/schema/inetorgperson.ldif @@ -0,0 +1,69 @@ +# InetOrgPerson (RFC2798) +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2021 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. +# +# InetOrgPerson (RFC2798) +# +# Depends upon +# Definition of an X.500 Attribute Type and an Object Class to Hold +# Uniform Resource Identifiers (URIs) [RFC2079] +# (core.ldif) +# +# A Summary of the X.500(96) User Schema for use with LDAPv3 [RFC2256] +# (core.ldif) +# +# The COSINE and Internet X.500 Schema [RFC1274] (cosine.ldif) +# +# This file was automatically generated from inetorgperson.schema; see +# that file for complete references. +# +dn: cn=inetorgperson,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: inetorgperson +olcAttributeTypes: ( 2.16.840.1.113730.3.1.1 NAME 'carLicense' DESC 'RFC279 + 8: vehicle license or registration plate' EQUALITY caseIgnoreMatch SUBSTR cas + eIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +olcAttributeTypes: ( 2.16.840.1.113730.3.1.2 NAME 'departmentNumber' DESC ' + RFC2798: identifies a department within an organization' EQUALITY caseIgnoreM + atch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +olcAttributeTypes: ( 2.16.840.1.113730.3.1.241 NAME 'displayName' DESC 'RFC + 2798: preferred name to be used when displaying entries' EQUALITY caseIgnoreM + atch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SI + NGLE-VALUE ) +olcAttributeTypes: ( 2.16.840.1.113730.3.1.3 NAME 'employeeNumber' DESC 'RF + C2798: numerically identifies an employee within an organization' EQUALITY ca + seIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.12 + 1.1.15 SINGLE-VALUE ) +olcAttributeTypes: ( 2.16.840.1.113730.3.1.4 NAME 'employeeType' DESC 'RFC2 + 798: type of employment for a person' EQUALITY caseIgnoreMatch SUBSTR caseIgn + oreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +olcAttributeTypes: ( 0.9.2342.19200300.100.1.60 NAME 'jpegPhoto' DESC 'RFC2 + 798: a JPEG image' SYNTAX 1.3.6.1.4.1.1466.115.121.1.28 ) +olcAttributeTypes: ( 2.16.840.1.113730.3.1.39 NAME 'preferredLanguage' DESC + 'RFC2798: preferred written or spoken language for a person' EQUALITY caseIg + noreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1. + 15 SINGLE-VALUE ) +olcAttributeTypes: ( 2.16.840.1.113730.3.1.40 NAME 'userSMIMECertificate' D + ESC 'RFC2798: PKCS#7 SignedData used to support S/MIME' SYNTAX 1.3.6.1.4.1.14 + 66.115.121.1.5 ) +olcAttributeTypes: ( 2.16.840.1.113730.3.1.216 NAME 'userPKCS12' DESC 'RFC2 + 798: personal identity information, a PKCS #12 PFX' SYNTAX 1.3.6.1.4.1.1466.1 + 15.121.1.5 ) +olcObjectClasses: ( 2.16.840.1.113730.3.2.2 NAME 'inetOrgPerson' DESC 'RFC2 + 798: Internet Organizational Person' SUP organizationalPerson STRUCTURAL MAY + ( audio $ businessCategory $ carLicense $ departmentNumber $ displayName $ em + ployeeNumber $ employeeType $ givenName $ homePhone $ homePostalAddress $ ini + tials $ jpegPhoto $ labeledURI $ mail $ manager $ mobile $ o $ pager $ photo + $ roomNumber $ secretary $ uid $ userCertificate $ x500uniqueIdentifier $ pre + ferredLanguage $ userSMIMECertificate $ userPKCS12 ) ) diff --git a/servers/slapd/schema/java.ldif b/servers/slapd/schema/java.ldif new file mode 100644 index 0000000..dc7ae10 --- /dev/null +++ b/servers/slapd/schema/java.ldif @@ -0,0 +1,59 @@ +# java.ldif -- Java Object Schema +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2021 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. +# +# Java Object Schema (defined in RFC 2713) +# depends upon core.ldif +# +# This file was automatically generated from java.schema; see that file +# for complete references. +# +dn: cn=java,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: java +olcAttributeTypes: {0}( 1.3.6.1.4.1.42.2.27.4.1.6 NAME 'javaClassName' DESC 'F + ully qualified name of distinguished Java class or interface' EQUALITY caseEx + actMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE ) +olcAttributeTypes: {1}( 1.3.6.1.4.1.42.2.27.4.1.7 NAME 'javaCodebase' DESC 'UR + L(s) specifying the location of class definition' EQUALITY caseExactIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) +olcAttributeTypes: {2}( 1.3.6.1.4.1.42.2.27.4.1.13 NAME 'javaClassNames' DESC + 'Fully qualified Java class or interface name' EQUALITY caseExactMatch SYNTAX + 1.3.6.1.4.1.1466.115.121.1.15 ) +olcAttributeTypes: {3}( 1.3.6.1.4.1.42.2.27.4.1.8 NAME 'javaSerializedData' DE + SC 'Serialized form of a Java object' SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 SI + NGLE-VALUE ) +olcAttributeTypes: {4}( 1.3.6.1.4.1.42.2.27.4.1.10 NAME 'javaFactory' DESC 'Fu + lly qualified Java class name of a JNDI object factory' EQUALITY caseExactMat + ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE ) +olcAttributeTypes: {5}( 1.3.6.1.4.1.42.2.27.4.1.11 NAME 'javaReferenceAddress' + DESC 'Addresses associated with a JNDI Reference' EQUALITY caseExactMatch SY + NTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +olcAttributeTypes: {6}( 1.3.6.1.4.1.42.2.27.4.1.12 NAME 'javaDoc' DESC 'The Ja + va documentation for the class' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1 + .1466.115.121.1.26 ) +olcObjectClasses: {0}( 1.3.6.1.4.1.42.2.27.4.2.1 NAME 'javaContainer' DESC 'Co + ntainer for a Java object' SUP top STRUCTURAL MUST cn ) +olcObjectClasses: {1}( 1.3.6.1.4.1.42.2.27.4.2.4 NAME 'javaObject' DESC 'Java + object representation' SUP top ABSTRACT MUST javaClassName MAY ( javaClassNam + es $ javaCodebase $ javaDoc $ description ) ) +olcObjectClasses: {2}( 1.3.6.1.4.1.42.2.27.4.2.5 NAME 'javaSerializedObject' D + ESC 'Java serialized object' SUP javaObject AUXILIARY MUST javaSerializedData + ) +olcObjectClasses: {3}( 1.3.6.1.4.1.42.2.27.4.2.8 NAME 'javaMarshalledObject' D + ESC 'Java marshalled object' SUP javaObject AUXILIARY MUST javaSerializedData + ) +olcObjectClasses: {4}( 1.3.6.1.4.1.42.2.27.4.2.7 NAME 'javaNamingReference' DE + SC 'JNDI reference' SUP javaObject AUXILIARY MAY ( javaReferenceAddress $ jav + aFactory ) ) diff --git a/servers/slapd/schema/misc.ldif b/servers/slapd/schema/misc.ldif new file mode 100644 index 0000000..83b571a --- /dev/null +++ b/servers/slapd/schema/misc.ldif @@ -0,0 +1,45 @@ +# misc.ldif -- assorted schema definitions +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2021 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. +# +# Assorted definitions from several sources, including +# ''works in progress''. Contents of this file are +# subject to change (including deletion) without notice. +# +# Not recommended for production use! +# Use with extreme caution! +# +# This file was automatically generated from misc.schema; see that file +# for complete references. +# +dn: cn=misc,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: misc +olcAttributeTypes: {0}( 2.16.840.1.113730.3.1.13 NAME 'mailLocalAddress' DESC + 'RFC822 email address of this recipient' EQUALITY caseIgnoreIA5Match SYNTAX 1 + .3.6.1.4.1.1466.115.121.1.26{256} ) +olcAttributeTypes: {1}( 2.16.840.1.113730.3.1.18 NAME 'mailHost' DESC 'FQDN of + the SMTP/MTA of this recipient' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4 + .1.1466.115.121.1.26{256} SINGLE-VALUE ) +olcAttributeTypes: {2}( 2.16.840.1.113730.3.1.47 NAME 'mailRoutingAddress' DES + C 'RFC822 routing address of this recipient' EQUALITY caseIgnoreIA5Match SYNT + AX 1.3.6.1.4.1.1466.115.121.1.26{256} SINGLE-VALUE ) +olcAttributeTypes: {3}( 1.3.6.1.4.1.42.2.27.2.1.15 NAME 'rfc822MailMember' DES + C 'rfc822 mail address of group member(s)' EQUALITY caseIgnoreIA5Match SYNTAX + 1.3.6.1.4.1.1466.115.121.1.26 ) +olcObjectClasses: {0}( 2.16.840.1.113730.3.2.147 NAME 'inetLocalMailRecipient' + DESC 'Internet local mail recipient' SUP top AUXILIARY MAY ( mailLocalAddres + s $ mailHost $ mailRoutingAddress ) ) +olcObjectClasses: {1}( 1.3.6.1.4.1.42.2.27.1.2.5 NAME 'nisMailAlias' DESC 'NIS + mail alias' SUP top STRUCTURAL MUST cn MAY rfc822MailMember ) diff --git a/servers/slapd/schema/misc.schema b/servers/slapd/schema/misc.schema new file mode 100644 index 0000000..c38a9d0 --- /dev/null +++ b/servers/slapd/schema/misc.schema @@ -0,0 +1,75 @@ +# misc.schema -- assorted schema definitions +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2021 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. +# +# Assorted definitions from several sources, including +# ''works in progress''. Contents of this file are +# subject to change (including deletion) without notice. +# +# Not recommended for production use! +# Use with extreme caution! + +#----------------------------------------------------------- +# draft-lachman-laser-ldap-mail-routing-02.txt !!!EXPIRED!!! +# (a work in progress) +# +attributetype ( 2.16.840.1.113730.3.1.13 + NAME 'mailLocalAddress' + DESC 'RFC822 email address of this recipient' + EQUALITY caseIgnoreIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} ) + +attributetype ( 2.16.840.1.113730.3.1.18 + NAME 'mailHost' + DESC 'FQDN of the SMTP/MTA of this recipient' + EQUALITY caseIgnoreIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} + SINGLE-VALUE ) + +attributetype ( 2.16.840.1.113730.3.1.47 + NAME 'mailRoutingAddress' + DESC 'RFC822 routing address of this recipient' + EQUALITY caseIgnoreIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} + SINGLE-VALUE ) + +# I-D leaves this OID TBD. +# iPlanet uses 2.16.840.1.113.730.3.2.147 but that is an +# improperly delegated OID. A typo is likely. +objectclass ( 2.16.840.1.113730.3.2.147 + NAME 'inetLocalMailRecipient' + DESC 'Internet local mail recipient' + SUP top AUXILIARY + MAY ( mailLocalAddress $ mailHost $ mailRoutingAddress ) ) + +#----------------------------------------------------------- +# draft-srivastava-ldap-mail-00.txt !!!EXPIRED!!! +# (a work in progress) +# +attributetype ( 1.3.6.1.4.1.42.2.27.2.1.15 + NAME 'rfc822MailMember' + DESC 'rfc822 mail address of group member(s)' + EQUALITY caseIgnoreIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) + +#----------------------------------------------------------- +# !!!no I-D!!! +# (a work in progress) +# +objectclass ( 1.3.6.1.4.1.42.2.27.1.2.5 + NAME 'nisMailAlias' + DESC 'NIS mail alias' + SUP top STRUCTURAL + MUST cn + MAY rfc822MailMember ) diff --git a/servers/slapd/schema/nis.ldif b/servers/slapd/schema/nis.ldif new file mode 100644 index 0000000..950dae3 --- /dev/null +++ b/servers/slapd/schema/nis.ldif @@ -0,0 +1,120 @@ +# NIS (RFC2307) +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2021 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. +# +# Definitions from RFC2307 (Experimental) +# An Approach for Using LDAP as a Network Information Service +# +# Depends upon core.ldif and cosine.ldif +# +# This file was automatically generated from nis.schema; see that file +# for complete references. +# +dn: cn=nis,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: nis +olcAttributeTypes: ( 1.3.6.1.1.1.1.2 NAME 'gecos' DESC 'The GECOS field; th + e common name' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatc + h SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) +olcAttributeTypes: ( 1.3.6.1.1.1.1.3 NAME 'homeDirectory' DESC 'The absolut + e path to the home directory' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1 + 466.115.121.1.26 SINGLE-VALUE ) +olcAttributeTypes: ( 1.3.6.1.1.1.1.4 NAME 'loginShell' DESC 'The path to th + e login shell' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.2 + 6 SINGLE-VALUE ) +olcAttributeTypes: ( 1.3.6.1.1.1.1.5 NAME 'shadowLastChange' EQUALITY integ + erMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) +olcAttributeTypes: ( 1.3.6.1.1.1.1.6 NAME 'shadowMin' EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) +olcAttributeTypes: ( 1.3.6.1.1.1.1.7 NAME 'shadowMax' EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) +olcAttributeTypes: ( 1.3.6.1.1.1.1.8 NAME 'shadowWarning' EQUALITY integerM + atch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) +olcAttributeTypes: ( 1.3.6.1.1.1.1.9 NAME 'shadowInactive' EQUALITY integer + Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) +olcAttributeTypes: ( 1.3.6.1.1.1.1.10 NAME 'shadowExpire' EQUALITY integerM + atch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) +olcAttributeTypes: ( 1.3.6.1.1.1.1.11 NAME 'shadowFlag' EQUALITY integerMat + ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) +olcAttributeTypes: ( 1.3.6.1.1.1.1.12 NAME 'memberUid' EQUALITY caseExactI + A5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1. + 26 ) +olcAttributeTypes: ( 1.3.6.1.1.1.1.13 NAME 'memberNisNetgroup' EQUALITY ca + seExactIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.11 + 5.121.1.26 ) +olcAttributeTypes: ( 1.3.6.1.1.1.1.14 NAME 'nisNetgroupTriple' DESC 'Netgr + oup triple' SYNTAX 1.3.6.1.1.1.0.0 ) +olcAttributeTypes: ( 1.3.6.1.1.1.1.15 NAME 'ipServicePort' EQUALITY intege + rMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) +olcAttributeTypes: ( 1.3.6.1.1.1.1.16 NAME 'ipServiceProtocol' SUP name ) +olcAttributeTypes: ( 1.3.6.1.1.1.1.17 NAME 'ipProtocolNumber' EQUALITY int + egerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) +olcAttributeTypes: ( 1.3.6.1.1.1.1.18 NAME 'oncRpcNumber' EQUALITY integer + Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) +olcAttributeTypes: ( 1.3.6.1.1.1.1.19 NAME 'ipHostNumber' DESC 'IP address + ' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} ) +olcAttributeTypes: ( 1.3.6.1.1.1.1.20 NAME 'ipNetworkNumber' DESC 'IP netw + ork' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} SI + NGLE-VALUE ) +olcAttributeTypes: ( 1.3.6.1.1.1.1.21 NAME 'ipNetmaskNumber' DESC 'IP netm + ask' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} SI + NGLE-VALUE ) +olcAttributeTypes: ( 1.3.6.1.1.1.1.22 NAME 'macAddress' DESC 'MAC address' + EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} ) +olcAttributeTypes: ( 1.3.6.1.1.1.1.23 NAME 'bootParameter' DESC 'rpc.bootp + aramd parameter' SYNTAX 1.3.6.1.1.1.0.1 ) +olcAttributeTypes: ( 1.3.6.1.1.1.1.24 NAME 'bootFile' DESC 'Boot image nam + e' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) +olcAttributeTypes: ( 1.3.6.1.1.1.1.26 NAME 'nisMapName' SUP name ) +olcAttributeTypes: ( 1.3.6.1.1.1.1.27 NAME 'nisMapEntry' EQUALITY caseExac + tIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121. + 1.26{1024} SINGLE-VALUE ) +olcObjectClasses: ( 1.3.6.1.1.1.2.0 NAME 'posixAccount' DESC 'Abstraction o + f an account with POSIX attributes' SUP top AUXILIARY MUST ( cn $ uid $ uidNu + mber $ gidNumber $ homeDirectory ) MAY ( userPassword $ loginShell $ gecos $ + description ) ) +olcObjectClasses: ( 1.3.6.1.1.1.2.1 NAME 'shadowAccount' DESC 'Additional a + ttributes for shadow passwords' SUP top AUXILIARY MUST uid MAY ( userPassword + $ shadowLastChange $ shadowMin $ shadowMax $ shadowWarning $ shadowInactive + $ shadowExpire $ shadowFlag $ description ) ) +olcObjectClasses: ( 1.3.6.1.1.1.2.2 NAME 'posixGroup' DESC 'Abstraction of + a group of accounts' SUP top STRUCTURAL MUST ( cn $ gidNumber ) MAY ( userPas + sword $ memberUid $ description ) ) +olcObjectClasses: ( 1.3.6.1.1.1.2.3 NAME 'ipService' DESC 'Abstraction an I + nternet Protocol service' SUP top STRUCTURAL MUST ( cn $ ipServicePort $ ipSe + rviceProtocol ) MAY description ) +olcObjectClasses: ( 1.3.6.1.1.1.2.4 NAME 'ipProtocol' DESC 'Abstraction of + an IP protocol' SUP top STRUCTURAL MUST ( cn $ ipProtocolNumber $ description + ) MAY description ) +olcObjectClasses: ( 1.3.6.1.1.1.2.5 NAME 'oncRpc' DESC 'Abstraction of an O + NC/RPC binding' SUP top STRUCTURAL MUST ( cn $ oncRpcNumber $ description ) M + AY description ) +olcObjectClasses: ( 1.3.6.1.1.1.2.6 NAME 'ipHost' DESC 'Abstraction of a ho + st, an IP device' SUP top AUXILIARY MUST ( cn $ ipHostNumber ) MAY ( l $ desc + ription $ manager ) ) +olcObjectClasses: ( 1.3.6.1.1.1.2.7 NAME 'ipNetwork' DESC 'Abstraction of a + n IP network' SUP top STRUCTURAL MUST ( cn $ ipNetworkNumber ) MAY ( ipNetmas + kNumber $ l $ description $ manager ) ) +olcObjectClasses: ( 1.3.6.1.1.1.2.8 NAME 'nisNetgroup' DESC 'Abstraction of + a netgroup' SUP top STRUCTURAL MUST cn MAY ( nisNetgroupTriple $ memberNisNe + tgroup $ description ) ) +olcObjectClasses: ( 1.3.6.1.1.1.2.9 NAME 'nisMap' DESC 'A generic abstracti + on of a NIS map' SUP top STRUCTURAL MUST nisMapName MAY description ) +olcObjectClasses: ( 1.3.6.1.1.1.2.10 NAME 'nisObject' DESC 'An entry in a + NIS map' SUP top STRUCTURAL MUST ( cn $ nisMapEntry $ nisMapName ) MAY descri + ption ) +olcObjectClasses: ( 1.3.6.1.1.1.2.11 NAME 'ieee802Device' DESC 'A device w + ith a MAC address' SUP top AUXILIARY MAY macAddress ) +olcObjectClasses: ( 1.3.6.1.1.1.2.12 NAME 'bootableDevice' DESC 'A device + with boot parameters' SUP top AUXILIARY MAY ( bootFile $ bootParameter ) ) diff --git a/servers/slapd/schema/nis.schema b/servers/slapd/schema/nis.schema new file mode 100644 index 0000000..a54d70c --- /dev/null +++ b/servers/slapd/schema/nis.schema @@ -0,0 +1,237 @@ +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2021 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. + +# Definitions from RFC2307 (Experimental) +# An Approach for Using LDAP as a Network Information Service + +# Depends upon core.schema and cosine.schema + +# Note: The definitions in RFC2307 are given in syntaxes closely related +# to those in RFC2252, however, some liberties are taken that are not +# supported by RFC2252. This file has been written following RFC2252 +# strictly. + +# OID Base is iso(1) org(3) dod(6) internet(1) directory(1) nisSchema(1). +# i.e. nisSchema in RFC2307 is 1.3.6.1.1.1 +# +# Syntaxes are under 1.3.6.1.1.1.0 (two new syntaxes are defined) +# validaters for these syntaxes are incomplete, they only +# implement printable string validation (which is good as the +# common use of these syntaxes violates the specification). +# Attribute types are under 1.3.6.1.1.1.1 +# Object classes are under 1.3.6.1.1.1.2 + +# Attribute Type Definitions + +# builtin +#attributetype ( 1.3.6.1.1.1.1.0 NAME 'uidNumber' +# DESC 'An integer uniquely identifying a user in an administrative domain' +# EQUALITY integerMatch +# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +# builtin +#attributetype ( 1.3.6.1.1.1.1.1 NAME 'gidNumber' +# DESC 'An integer uniquely identifying a group in an administrative domain' +# EQUALITY integerMatch +# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.1.1.1.2 NAME 'gecos' + DESC 'The GECOS field; the common name' + EQUALITY caseIgnoreIA5Match + SUBSTR caseIgnoreIA5SubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.1.1.1.3 NAME 'homeDirectory' + DESC 'The absolute path to the home directory' + EQUALITY caseExactIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.1.1.1.4 NAME 'loginShell' + DESC 'The path to the login shell' + EQUALITY caseExactIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.1.1.1.5 NAME 'shadowLastChange' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.1.1.1.6 NAME 'shadowMin' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.1.1.1.7 NAME 'shadowMax' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.1.1.1.8 NAME 'shadowWarning' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.1.1.1.9 NAME 'shadowInactive' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.1.1.1.10 NAME 'shadowExpire' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.1.1.1.11 NAME 'shadowFlag' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.1.1.1.12 NAME 'memberUid' + EQUALITY caseExactIA5Match + SUBSTR caseExactIA5SubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) + +attributetype ( 1.3.6.1.1.1.1.13 NAME 'memberNisNetgroup' + EQUALITY caseExactIA5Match + SUBSTR caseExactIA5SubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) + +attributetype ( 1.3.6.1.1.1.1.14 NAME 'nisNetgroupTriple' + DESC 'Netgroup triple' + SYNTAX 1.3.6.1.1.1.0.0 ) + +attributetype ( 1.3.6.1.1.1.1.15 NAME 'ipServicePort' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.1.1.1.16 NAME 'ipServiceProtocol' + SUP name ) + +attributetype ( 1.3.6.1.1.1.1.17 NAME 'ipProtocolNumber' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.1.1.1.18 NAME 'oncRpcNumber' + EQUALITY integerMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE ) + +attributetype ( 1.3.6.1.1.1.1.19 NAME 'ipHostNumber' + DESC 'IP address' + EQUALITY caseIgnoreIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} ) + +attributetype ( 1.3.6.1.1.1.1.20 NAME 'ipNetworkNumber' + DESC 'IP network' + EQUALITY caseIgnoreIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} SINGLE-VALUE ) + +attributetype ( 1.3.6.1.1.1.1.21 NAME 'ipNetmaskNumber' + DESC 'IP netmask' + EQUALITY caseIgnoreIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} SINGLE-VALUE ) + +attributetype ( 1.3.6.1.1.1.1.22 NAME 'macAddress' + DESC 'MAC address' + EQUALITY caseIgnoreIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} ) + +attributetype ( 1.3.6.1.1.1.1.23 NAME 'bootParameter' + DESC 'rpc.bootparamd parameter' + SYNTAX 1.3.6.1.1.1.0.1 ) + +attributetype ( 1.3.6.1.1.1.1.24 NAME 'bootFile' + DESC 'Boot image name' + EQUALITY caseExactIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) + +attributetype ( 1.3.6.1.1.1.1.26 NAME 'nisMapName' + SUP name ) + +attributetype ( 1.3.6.1.1.1.1.27 NAME 'nisMapEntry' + EQUALITY caseExactIA5Match + SUBSTR caseExactIA5SubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{1024} SINGLE-VALUE ) + +# Object Class Definitions + +objectclass ( 1.3.6.1.1.1.2.0 NAME 'posixAccount' + DESC 'Abstraction of an account with POSIX attributes' + SUP top AUXILIARY + MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory ) + MAY ( userPassword $ loginShell $ gecos $ description ) ) + +objectclass ( 1.3.6.1.1.1.2.1 NAME 'shadowAccount' + DESC 'Additional attributes for shadow passwords' + SUP top AUXILIARY + MUST uid + MAY ( userPassword $ shadowLastChange $ shadowMin $ + shadowMax $ shadowWarning $ shadowInactive $ + shadowExpire $ shadowFlag $ description ) ) + +objectclass ( 1.3.6.1.1.1.2.2 NAME 'posixGroup' + DESC 'Abstraction of a group of accounts' + SUP top STRUCTURAL + MUST ( cn $ gidNumber ) + MAY ( userPassword $ memberUid $ description ) ) + +objectclass ( 1.3.6.1.1.1.2.3 NAME 'ipService' + DESC 'Abstraction an Internet Protocol service' + SUP top STRUCTURAL + MUST ( cn $ ipServicePort $ ipServiceProtocol ) + MAY ( description ) ) + +objectclass ( 1.3.6.1.1.1.2.4 NAME 'ipProtocol' + DESC 'Abstraction of an IP protocol' + SUP top STRUCTURAL + MUST ( cn $ ipProtocolNumber $ description ) + MAY description ) + +objectclass ( 1.3.6.1.1.1.2.5 NAME 'oncRpc' + DESC 'Abstraction of an ONC/RPC binding' + SUP top STRUCTURAL + MUST ( cn $ oncRpcNumber $ description ) + MAY description ) + +objectclass ( 1.3.6.1.1.1.2.6 NAME 'ipHost' + DESC 'Abstraction of a host, an IP device' + SUP top AUXILIARY + MUST ( cn $ ipHostNumber ) + MAY ( l $ description $ manager ) ) + +objectclass ( 1.3.6.1.1.1.2.7 NAME 'ipNetwork' + DESC 'Abstraction of an IP network' + SUP top STRUCTURAL + MUST ( cn $ ipNetworkNumber ) + MAY ( ipNetmaskNumber $ l $ description $ manager ) ) + +objectclass ( 1.3.6.1.1.1.2.8 NAME 'nisNetgroup' + DESC 'Abstraction of a netgroup' + SUP top STRUCTURAL + MUST cn + MAY ( nisNetgroupTriple $ memberNisNetgroup $ description ) ) + +objectclass ( 1.3.6.1.1.1.2.9 NAME 'nisMap' + DESC 'A generic abstraction of a NIS map' + SUP top STRUCTURAL + MUST nisMapName + MAY description ) + +objectclass ( 1.3.6.1.1.1.2.10 NAME 'nisObject' + DESC 'An entry in a NIS map' + SUP top STRUCTURAL + MUST ( cn $ nisMapEntry $ nisMapName ) + MAY description ) + +objectclass ( 1.3.6.1.1.1.2.11 NAME 'ieee802Device' + DESC 'A device with a MAC address' + SUP top AUXILIARY + MAY macAddress ) + +objectclass ( 1.3.6.1.1.1.2.12 NAME 'bootableDevice' + DESC 'A device with boot parameters' + SUP top AUXILIARY + MAY ( bootFile $ bootParameter ) ) diff --git a/servers/slapd/schema/openldap.ldif b/servers/slapd/schema/openldap.ldif new file mode 100644 index 0000000..0c26a23 --- /dev/null +++ b/servers/slapd/schema/openldap.ldif @@ -0,0 +1,88 @@ +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2021 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. +# +# +# OpenLDAP Project's directory schema items +# +# depends upon: +# core.schema +# cosine.schema +# inetorgperson.schema +# +# These are provided for informational purposes only. +# +# This openldap.ldif file is provided as a demonstration of how to +# convert a *.schema file into *.ldif format. The key points: +# In LDIF, a blank line terminates an entry. Blank lines in a *.schema +# file should be replaced with a single '#' to turn them into +# comments, or they should just be removed. +# In addition to the actual schema directives, the file needs a small +# header to make it a valid LDAP entry. This header must provide the +# dn of the entry, the objectClass, and the cn, as shown here: +# +dn: cn=openldap,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: openldap +# +# The schema directives need to be changed to LDAP Attributes. +# First a basic string substitution can be done on each of the keywords: +# objectIdentifier -> olcObjectIdentifier: +# objectClass -> olcObjectClasses: +# attributeType -> olcAttributeTypes: +# Then leading whitespace must be fixed. The slapd.conf format allows +# tabs or spaces to denote line continuation, while LDIF only allows +# the space character. +# Also slapd.conf preserves the continuation character, while LDIF strips +# it out. So a single TAB/SPACE in slapd.conf must be replaced with +# two SPACEs in LDIF, otherwise the continued text may get joined as +# a single word. +# The directives must be listed in a proper sequence: +# All olcObjectIdentifiers must be first, so they may be referenced by +# any following definitions. +# All olcAttributeTypes must be next, so they may be referenced by any +# following objectClass definitions. +# All olcObjectClasses must be after the olcAttributeTypes. +# And of course, any superior must occur before anything that inherits +# from it. +# +olcObjectIdentifier: OpenLDAProot 1.3.6.1.4.1.4203 +# +olcObjectIdentifier: OpenLDAP OpenLDAProot:1 +olcObjectIdentifier: OpenLDAPattributeType OpenLDAP:3 +olcObjectIdentifier: OpenLDAPobjectClass OpenLDAP:4 +# +olcObjectClasses: ( OpenLDAPobjectClass:3 + NAME 'OpenLDAPorg' + DESC 'OpenLDAP Organizational Object' + SUP organization + MAY ( buildingName $ displayName $ labeledURI ) ) +# +olcObjectClasses: ( OpenLDAPobjectClass:4 + NAME 'OpenLDAPou' + DESC 'OpenLDAP Organizational Unit Object' + SUP organizationalUnit + MAY ( buildingName $ displayName $ labeledURI $ o ) ) +# +olcObjectClasses: ( OpenLDAPobjectClass:5 + NAME 'OpenLDAPperson' + DESC 'OpenLDAP Person' + SUP ( pilotPerson $ inetOrgPerson ) + MUST ( uid $ cn ) + MAY ( givenName $ labeledURI $ o ) ) +# +olcObjectClasses: ( OpenLDAPobjectClass:6 + NAME 'OpenLDAPdisplayableObject' + DESC 'OpenLDAP Displayable Object' + AUXILIARY + MAY displayName ) diff --git a/servers/slapd/schema/openldap.schema b/servers/slapd/schema/openldap.schema new file mode 100644 index 0000000..be7280f --- /dev/null +++ b/servers/slapd/schema/openldap.schema @@ -0,0 +1,54 @@ +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2021 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. + +# +# OpenLDAP Project's directory schema items +# +# depends upon: +# core.schema +# cosine.schema +# inetorgperson.schema +# +# These are provided for informational purposes only. + +objectIdentifier OpenLDAProot 1.3.6.1.4.1.4203 + +objectIdentifier OpenLDAP OpenLDAProot:1 +objectIdentifier OpenLDAPattributeType OpenLDAP:3 +objectIdentifier OpenLDAPobjectClass OpenLDAP:4 + +objectClass ( OpenLDAPobjectClass:3 + NAME 'OpenLDAPorg' + DESC 'OpenLDAP Organizational Object' + SUP organization + MAY ( buildingName $ displayName $ labeledURI ) ) + +objectClass ( OpenLDAPobjectClass:4 + NAME 'OpenLDAPou' + DESC 'OpenLDAP Organizational Unit Object' + SUP organizationalUnit + MAY ( buildingName $ displayName $ labeledURI $ o ) ) + +objectClass ( OpenLDAPobjectClass:5 + NAME 'OpenLDAPperson' + DESC 'OpenLDAP Person' + SUP ( pilotPerson $ inetOrgPerson ) + MUST ( uid $ cn ) + MAY ( givenName $ labeledURI $ o ) ) + +objectClass ( OpenLDAPobjectClass:6 + NAME 'OpenLDAPdisplayableObject' + DESC 'OpenLDAP Displayable Object' + AUXILIARY + MAY displayName ) diff --git a/servers/slapd/schema/pmi.ldif b/servers/slapd/schema/pmi.ldif new file mode 100644 index 0000000..ee709ef --- /dev/null +++ b/servers/slapd/schema/pmi.ldif @@ -0,0 +1,123 @@ +# OpenLDAP X.509 PMI schema +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2021 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. +# +## Portions Copyright (C) The Internet Society (1997-2006). +## All Rights Reserved. +# +# Includes LDAPv3 schema items from: +# ITU X.509 (08/2005) +# +# This file was automatically generated from pmi.schema; see that file +# for complete references. +# +dn: cn=pmi,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: pmi +olcObjectIdentifier: {0}id-oc-pmiUser 2.5.6.24 +olcObjectIdentifier: {1}id-oc-pmiAA 2.5.6.25 +olcObjectIdentifier: {2}id-oc-pmiSOA 2.5.6.26 +olcObjectIdentifier: {3}id-oc-attCertCRLDistributionPts 2.5.6.27 +olcObjectIdentifier: {4}id-oc-privilegePolicy 2.5.6.32 +olcObjectIdentifier: {5}id-oc-pmiDelegationPath 2.5.6.33 +olcObjectIdentifier: {6}id-oc-protectedPrivilegePolicy 2.5.6.34 +olcObjectIdentifier: {7}id-at-attributeCertificate 2.5.4.58 +olcObjectIdentifier: {8}id-at-attributeCertificateRevocationList 2.5.4.59 +olcObjectIdentifier: {9}id-at-aACertificate 2.5.4.61 +olcObjectIdentifier: {10}id-at-attributeDescriptorCertificate 2.5.4.62 +olcObjectIdentifier: {11}id-at-attributeAuthorityRevocationList 2.5.4.63 +olcObjectIdentifier: {12}id-at-privPolicy 2.5.4.71 +olcObjectIdentifier: {13}id-at-role 2.5.4.72 +olcObjectIdentifier: {14}id-at-delegationPath 2.5.4.73 +olcObjectIdentifier: {15}id-at-protPrivPolicy 2.5.4.74 +olcObjectIdentifier: {16}id-at-xMLPrivilegeInfo 2.5.4.75 +olcObjectIdentifier: {17}id-at-xMLPprotPrivPolicy 2.5.4.76 +olcObjectIdentifier: {18}id-mr 2.5.13 +olcObjectIdentifier: {19}id-mr-attributeCertificateMatch id-mr:42 +olcObjectIdentifier: {20}id-mr-attributeCertificateExactMatch id-mr:45 +olcObjectIdentifier: {21}id-mr-holderIssuerMatch id-mr:46 +olcObjectIdentifier: {22}id-mr-authAttIdMatch id-mr:53 +olcObjectIdentifier: {23}id-mr-roleSpecCertIdMatch id-mr:54 +olcObjectIdentifier: {24}id-mr-basicAttConstraintsMatch id-mr:55 +olcObjectIdentifier: {25}id-mr-delegatedNameConstraintsMatch id-mr:56 +olcObjectIdentifier: {26}id-mr-timeSpecMatch id-mr:57 +olcObjectIdentifier: {27}id-mr-attDescriptorMatch id-mr:58 +olcObjectIdentifier: {28}id-mr-acceptableCertPoliciesMatch id-mr:59 +olcObjectIdentifier: {29}id-mr-delegationPathMatch id-mr:61 +olcObjectIdentifier: {30}id-mr-sOAIdentifierMatch id-mr:66 +olcObjectIdentifier: {31}id-mr-indirectIssuerMatch id-mr:67 +olcObjectIdentifier: {32}AttributeCertificate 1.3.6.1.4.1.4203.666.11.10.2.1 +olcObjectIdentifier: {33}CertificateList 1.3.6.1.4.1.1466.115.121.1.9 +olcObjectIdentifier: {34}AttCertPath 1.3.6.1.4.1.4203.666.11.10.2.4 +olcObjectIdentifier: {35}PolicySyntax 1.3.6.1.4.1.4203.666.11.10.2.5 +olcObjectIdentifier: {36}RoleSyntax 1.3.6.1.4.1.4203.666.11.10.2.6 +olcLdapSyntaxes: {0}( 1.3.6.1.4.1.4203.666.11.10.2.4 DESC 'X.509 PMI attribute + cartificate path: SEQUENCE OF AttributeCertificate' X-SUBST '1.3.6.1.4.1.146 + 6.115.121.1.15' ) +olcLdapSyntaxes: {1}( 1.3.6.1.4.1.4203.666.11.10.2.5 DESC 'X.509 PMI policy sy + ntax' X-SUBST '1.3.6.1.4.1.1466.115.121.1.15' ) +olcLdapSyntaxes: {2}( 1.3.6.1.4.1.4203.666.11.10.2.6 DESC 'X.509 PMI role synt + ax' X-SUBST '1.3.6.1.4.1.1466.115.121.1.15' ) +olcAttributeTypes: {0}( id-at-role NAME 'role' DESC 'X.509 Role attribute, use + ;binary' SYNTAX RoleSyntax ) +olcAttributeTypes: {1}( id-at-xMLPrivilegeInfo NAME 'xmlPrivilegeInfo' DESC 'X + .509 XML privilege information attribute' SYNTAX 1.3.6.1.4.1.1466.115.121.1.1 + 5 ) +olcAttributeTypes: {2}( id-at-attributeCertificate NAME 'attributeCertificateA + ttribute' DESC 'X.509 Attribute certificate attribute, use ;binary' EQUALITY + attributeCertificateExactMatch SYNTAX AttributeCertificate ) +olcAttributeTypes: {3}( id-at-aACertificate NAME 'aACertificate' DESC 'X.509 A + A certificate attribute, use ;binary' EQUALITY attributeCertificateExactMatch + SYNTAX AttributeCertificate ) +olcAttributeTypes: {4}( id-at-attributeDescriptorCertificate NAME 'attributeDe + scriptorCertificate' DESC 'X.509 Attribute descriptor certificate attribute, + use ;binary' EQUALITY attributeCertificateExactMatch SYNTAX AttributeCertific + ate ) +olcAttributeTypes: {5}( id-at-attributeCertificateRevocationList NAME 'attribu + teCertificateRevocationList' DESC 'X.509 Attribute certificate revocation lis + t attribute, use ;binary' SYNTAX CertificateList X-EQUALITY 'certificateListE + xactMatch, not implemented yet' ) +olcAttributeTypes: {6}( id-at-attributeAuthorityRevocationList NAME 'attribute + AuthorityRevocationList' DESC 'X.509 AA certificate revocation list attribute + , use ;binary' SYNTAX CertificateList X-EQUALITY 'certificateListExactMatch, + not implemented yet' ) +olcAttributeTypes: {7}( id-at-delegationPath NAME 'delegationPath' DESC 'X.509 + Delegation path attribute, use ;binary' SYNTAX AttCertPath ) +olcAttributeTypes: {8}( id-at-privPolicy NAME 'privPolicy' DESC 'X.509 Privile + ge policy attribute, use ;binary' SYNTAX PolicySyntax ) +olcAttributeTypes: {9}( id-at-protPrivPolicy NAME 'protPrivPolicy' DESC 'X.509 + Protected privilege policy attribute, use ;binary' EQUALITY attributeCertifi + cateExactMatch SYNTAX AttributeCertificate ) +olcAttributeTypes: {10}( id-at-xMLPprotPrivPolicy NAME 'xmlPrivPolicy' DESC 'X + .509 XML Protected privilege policy attribute' SYNTAX 1.3.6.1.4.1.1466.115.12 + 1.1.15 ) +olcObjectClasses: {0}( id-oc-pmiUser NAME 'pmiUser' DESC 'X.509 PMI user objec + t class' SUP top AUXILIARY MAY attributeCertificateAttribute ) +olcObjectClasses: {1}( id-oc-pmiAA NAME 'pmiAA' DESC 'X.509 PMI AA object clas + s' SUP top AUXILIARY MAY ( aACertificate $ attributeCertificateRevocationList + $ attributeAuthorityRevocationList ) ) +olcObjectClasses: {2}( id-oc-pmiSOA NAME 'pmiSOA' DESC 'X.509 PMI SOA object c + lass' SUP top AUXILIARY MAY ( attributeCertificateRevocationList $ attributeA + uthorityRevocationList $ attributeDescriptorCertificate ) ) +olcObjectClasses: {3}( id-oc-attCertCRLDistributionPts NAME 'attCertCRLDistrib + utionPt' DESC 'X.509 Attribute certificate CRL distribution point object clas + s' SUP top AUXILIARY MAY ( attributeCertificateRevocationList $ attributeAuth + orityRevocationList ) ) +olcObjectClasses: {4}( id-oc-pmiDelegationPath NAME 'pmiDelegationPath' DESC ' + X.509 PMI delegation path' SUP top AUXILIARY MAY delegationPath ) +olcObjectClasses: {5}( id-oc-privilegePolicy NAME 'privilegePolicy' DESC 'X.50 + 9 Privilege policy object class' SUP top AUXILIARY MAY privPolicy ) +olcObjectClasses: {6}( id-oc-protectedPrivilegePolicy NAME 'protectedPrivilege + Policy' DESC 'X.509 Protected privilege policy object class' SUP top AUXILIAR + Y MAY protPrivPolicy ) diff --git a/servers/slapd/schema/ppolicy.ldif b/servers/slapd/schema/ppolicy.ldif new file mode 100644 index 0000000..18c96b1 --- /dev/null +++ b/servers/slapd/schema/ppolicy.ldif @@ -0,0 +1,87 @@ +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 2004-2021 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. +# +## Portions Copyright (C) The Internet Society (2004). +## Please see full copyright statement below. +# +# Definitions from Draft behera-ldap-password-policy-07 (a work in progress) +# Password Policy for LDAP Directories +# With extensions from Hewlett-Packard: +# pwdCheckModule etc. +# +# Contents of this file are subject to change (including deletion) +# without notice. +# +# Not recommended for production use! +# Use with extreme caution! +# +# This file was automatically generated from ppolicy.schema; see that file +# for complete references. +# +dn: cn=ppolicy,cn=schema,cn=config +objectClass: olcSchemaConfig +cn: ppolicy +olcAttributeTypes: {0}( 1.3.6.1.4.1.42.2.27.8.1.1 NAME 'pwdAttribute' EQUALITY + objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 ) +olcAttributeTypes: {1}( 1.3.6.1.4.1.42.2.27.8.1.2 NAME 'pwdMinAge' EQUALITY in + tegerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 + SINGLE-VALUE ) +olcAttributeTypes: {2}( 1.3.6.1.4.1.42.2.27.8.1.3 NAME 'pwdMaxAge' EQUALITY in + tegerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 + SINGLE-VALUE ) +olcAttributeTypes: {3}( 1.3.6.1.4.1.42.2.27.8.1.4 NAME 'pwdInHistory' EQUALITY + integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1 + .27 SINGLE-VALUE ) +olcAttributeTypes: {4}( 1.3.6.1.4.1.42.2.27.8.1.5 NAME 'pwdCheckQuality' EQUAL + ITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.12 + 1.1.27 SINGLE-VALUE ) +olcAttributeTypes: {5}( 1.3.6.1.4.1.42.2.27.8.1.6 NAME 'pwdMinLength' EQUALITY + integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121. + 1.27 SINGLE-VALUE ) +olcAttributeTypes: {6}( 1.3.6.1.4.1.42.2.27.8.1.7 NAME 'pwdExpireWarning' EQUA + LITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115. + 121.1.27 SINGLE-VALUE ) +olcAttributeTypes: {7}( 1.3.6.1.4.1.42.2.27.8.1.8 NAME 'pwdGraceAuthNLimit' EQ + UALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.11 + 5.121.1.27 SINGLE-VALUE ) +olcAttributeTypes: {8}( 1.3.6.1.4.1.42.2.27.8.1.9 NAME 'pwdLockout' EQUALITY b + ooleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE ) +olcAttributeTypes: {9}( 1.3.6.1.4.1.42.2.27.8.1.10 NAME 'pwdLockoutDuration' E + QUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.1 + 15.121.1.27 SINGLE-VALUE ) +olcAttributeTypes: {10}( 1.3.6.1.4.1.42.2.27.8.1.11 NAME 'pwdMaxFailure' EQUAL + ITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.1 + 21.1.27 SINGLE-VALUE ) +olcAttributeTypes: {11}( 1.3.6.1.4.1.42.2.27.8.1.12 NAME 'pwdFailureCountInter + val' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1. + 1466.115.121.1.27 SINGLE-VALUE ) +olcAttributeTypes: {12}( 1.3.6.1.4.1.42.2.27.8.1.13 NAME 'pwdMustChange' EQUAL + ITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE ) +olcAttributeTypes: {13}( 1.3.6.1.4.1.42.2.27.8.1.14 NAME 'pwdAllowUserChange' + EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE ) +olcAttributeTypes: {14}( 1.3.6.1.4.1.42.2.27.8.1.15 NAME 'pwdSafeModify' EQUAL + ITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE ) +olcAttributeTypes: {15}( 1.3.6.1.4.1.4754.1.99.1 NAME 'pwdCheckModule' DESC 'L + oadable module that instantiates "check_password() function' EQUALITY caseExa + ctIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) +olcAttributeTypes: {16}( 1.3.6.1.4.1.42.2.27.8.1.30 NAME 'pwdMaxRecordedFailur + e' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1. + 1466.115.121.1.27 SINGLE-VALUE ) +olcObjectClasses: {0}( 1.3.6.1.4.1.4754.2.99.1 NAME 'pwdPolicyChecker' SUP top + AUXILIARY MAY pwdCheckModule ) +olcObjectClasses: {1}( 1.3.6.1.4.1.42.2.27.8.2.1 NAME 'pwdPolicy' SUP top AUXI + LIARY MUST pwdAttribute MAY ( pwdMinAge $ pwdMaxAge $ pwdInHistory $ pwdCheck + Quality $ pwdMinLength $ pwdExpireWarning $ pwdGraceAuthNLimit $ pwdLockout $ + pwdLockoutDuration $ pwdMaxFailure $ pwdFailureCountInterval $ pwdMustChange + $ pwdAllowUserChange $ pwdSafeModify $ pwdMaxRecordedFailure ) ) diff --git a/servers/slapd/schema_check.c b/servers/slapd/schema_check.c new file mode 100644 index 0000000..6ee3c4e --- /dev/null +++ b/servers/slapd/schema_check.c @@ -0,0 +1,938 @@ +/* schema_check.c - routines to enforce schema definitions */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/ctype.h> +#include <ac/string.h> +#include <ac/socket.h> + +#include "slap.h" + +static char * oc_check_required( + Entry *e, + ObjectClass *oc, + struct berval *ocname ); + +static int entry_naming_check( + Entry *e, + int manage, + int add_naming, + const char** text, + char *textbuf, size_t textlen ); +/* + * entry_schema_check - check that entry e conforms to the schema required + * by its object class(es). + * + * returns 0 if so, non-zero otherwise. + */ + +int +entry_schema_check( + Operation *op, + Entry *e, + Attribute *oldattrs, + int manage, + int add, + Attribute **socp, + const char** text, + char *textbuf, size_t textlen ) +{ + Attribute *a, *asc = NULL, *aoc = NULL; + ObjectClass *sc, *oc, **socs = NULL; + AttributeType *at; + ContentRule *cr; + int rc, i; + AttributeDescription *ad_structuralObjectClass + = slap_schema.si_ad_structuralObjectClass; + AttributeDescription *ad_objectClass + = slap_schema.si_ad_objectClass; + int extensible = 0; + int subentry = is_entry_subentry( e ); + int collectiveSubentry = 0; + + if ( SLAP_NO_SCHEMA_CHECK( op->o_bd )) { + return LDAP_SUCCESS; + } + + if ( get_no_schema_check( op ) ) { + return LDAP_SUCCESS; + } + + if( subentry ) { + collectiveSubentry = is_entry_collectiveAttributeSubentry( e ); + } + + *text = textbuf; + + /* misc attribute checks */ + for ( a = e->e_attrs; a != NULL; a = a->a_next ) { + const char *type = a->a_desc->ad_cname.bv_val; + + /* there should be at least one value */ + assert( a->a_vals != NULL ); + assert( a->a_vals[0].bv_val != NULL ); + + if( a->a_desc->ad_type->sat_check ) { + rc = (a->a_desc->ad_type->sat_check)( + op->o_bd, e, a, text, textbuf, textlen ); + if( rc != LDAP_SUCCESS ) { + return rc; + } + } + + if( a->a_desc == ad_structuralObjectClass ) + asc = a; + else if ( a->a_desc == ad_objectClass ) + aoc = a; + + if( !collectiveSubentry && is_at_collective( a->a_desc->ad_type ) ) { + snprintf( textbuf, textlen, + "'%s' can only appear in collectiveAttributeSubentry", + type ); + return LDAP_OBJECT_CLASS_VIOLATION; + } + + /* if single value type, check for multiple values */ + if( is_at_single_value( a->a_desc->ad_type ) && + a->a_vals[1].bv_val != NULL ) + { + snprintf( textbuf, textlen, + "attribute '%s' cannot have multiple values", + type ); + + Debug( LDAP_DEBUG_ANY, + "Entry (%s), %s\n", + e->e_dn, textbuf, 0 ); + + return LDAP_CONSTRAINT_VIOLATION; + } + } + + /* check the object class attribute */ + if ( aoc == NULL ) { + Debug( LDAP_DEBUG_ANY, "No objectClass for entry (%s)\n", + e->e_dn, 0, 0 ); + + *text = "no objectClass attribute"; + return LDAP_OBJECT_CLASS_VIOLATION; + } + + assert( aoc->a_vals != NULL ); + assert( aoc->a_vals[0].bv_val != NULL ); + + /* check the structural object class attribute */ + if ( asc == NULL && !add ) { + Debug( LDAP_DEBUG_ANY, + "No structuralObjectClass for entry (%s)\n", + e->e_dn, 0, 0 ); + + *text = "no structuralObjectClass operational attribute"; + return LDAP_OTHER; + } + + rc = structural_class( aoc->a_vals, &oc, &socs, text, textbuf, textlen, + op->o_tmpmemctx ); + if( rc != LDAP_SUCCESS ) { + return rc; + } + + if ( asc == NULL && add ) { + attr_merge_one( e, ad_structuralObjectClass, &oc->soc_cname, NULL ); + asc = attr_find( e->e_attrs, ad_structuralObjectClass ); + sc = oc; + goto got_soc; + } + + assert( asc->a_vals != NULL ); + assert( asc->a_vals[0].bv_val != NULL ); + assert( asc->a_vals[1].bv_val == NULL ); + + sc = oc_bvfind( &asc->a_vals[0] ); + if( sc == NULL ) { + snprintf( textbuf, textlen, + "unrecognized structuralObjectClass '%s'", + asc->a_vals[0].bv_val ); + + Debug( LDAP_DEBUG_ANY, + "entry_check_schema(%s): %s\n", + e->e_dn, textbuf, 0 ); + + rc = LDAP_OBJECT_CLASS_VIOLATION; + goto done; + } + + if( sc->soc_kind != LDAP_SCHEMA_STRUCTURAL ) { + snprintf( textbuf, textlen, + "structuralObjectClass '%s' is not STRUCTURAL", + asc->a_vals[0].bv_val ); + + Debug( LDAP_DEBUG_ANY, + "entry_check_schema(%s): %s\n", + e->e_dn, textbuf, 0 ); + + rc = LDAP_OTHER; + goto done; + } + +got_soc: + if( !manage && sc->soc_obsolete ) { + snprintf( textbuf, textlen, + "structuralObjectClass '%s' is OBSOLETE", + asc->a_vals[0].bv_val ); + + Debug( LDAP_DEBUG_ANY, + "entry_check_schema(%s): %s\n", + e->e_dn, textbuf, 0 ); + + rc = LDAP_OBJECT_CLASS_VIOLATION; + goto done; + } + + *text = textbuf; + + if ( oc == NULL ) { + snprintf( textbuf, textlen, + "unrecognized objectClass '%s'", + aoc->a_vals[0].bv_val ); + rc = LDAP_OBJECT_CLASS_VIOLATION; + goto done; + + } else if ( sc != oc ) { + if ( !manage && sc != slap_schema.si_oc_glue ) { + snprintf( textbuf, textlen, + "structural object class modification " + "from '%s' to '%s' not allowed", + asc->a_vals[0].bv_val, oc->soc_cname.bv_val ); + rc = LDAP_NO_OBJECT_CLASS_MODS; + goto done; + } + + assert( asc->a_vals != NULL ); + assert( !BER_BVISNULL( &asc->a_vals[0] ) ); + assert( BER_BVISNULL( &asc->a_vals[1] ) ); + assert( asc->a_nvals == asc->a_vals ); + + /* draft-zeilenga-ldap-relax: automatically modify + * structuralObjectClass if changed with relax */ + sc = oc; + ber_bvreplace( &asc->a_vals[ 0 ], &sc->soc_cname ); + if ( socp ) { + *socp = asc; + } + } + + /* naming check */ + if ( !is_entry_glue ( e ) ) { + rc = entry_naming_check( e, manage, add, text, textbuf, textlen ); + if( rc != LDAP_SUCCESS ) { + goto done; + } + } else { + /* Glue Entry */ + } + + /* find the content rule for the structural class */ + cr = cr_find( sc->soc_oid ); + + /* the cr must be same as the structural class */ + assert( !cr || !strcmp( cr->scr_oid, sc->soc_oid ) ); + + /* check that the entry has required attrs of the content rule */ + if( cr ) { + if( !manage && cr->scr_obsolete ) { + snprintf( textbuf, textlen, + "content rule '%s' is obsolete", + ldap_contentrule2name( &cr->scr_crule )); + + Debug( LDAP_DEBUG_ANY, + "Entry (%s): %s\n", + e->e_dn, textbuf, 0 ); + + rc = LDAP_OBJECT_CLASS_VIOLATION; + goto done; + } + + if( cr->scr_required ) for( i=0; cr->scr_required[i]; i++ ) { + at = cr->scr_required[i]; + + for ( a = e->e_attrs; a != NULL; a = a->a_next ) { + if( a->a_desc->ad_type == at ) { + break; + } + } + + /* not there => schema violation */ + if ( a == NULL ) { + snprintf( textbuf, textlen, + "content rule '%s' requires attribute '%s'", + ldap_contentrule2name( &cr->scr_crule ), + at->sat_cname.bv_val ); + + Debug( LDAP_DEBUG_ANY, + "Entry (%s): %s\n", + e->e_dn, textbuf, 0 ); + + rc = LDAP_OBJECT_CLASS_VIOLATION; + goto done; + } + } + + if( cr->scr_precluded ) for( i=0; cr->scr_precluded[i]; i++ ) { + at = cr->scr_precluded[i]; + + for ( a = e->e_attrs; a != NULL; a = a->a_next ) { + if( a->a_desc->ad_type == at ) { + break; + } + } + + /* there => schema violation */ + if ( a != NULL ) { + snprintf( textbuf, textlen, + "content rule '%s' precluded attribute '%s'", + ldap_contentrule2name( &cr->scr_crule ), + at->sat_cname.bv_val ); + + Debug( LDAP_DEBUG_ANY, + "Entry (%s): %s\n", + e->e_dn, textbuf, 0 ); + + rc = LDAP_OBJECT_CLASS_VIOLATION; + goto done; + } + } + } + + /* check that the entry has required attrs for each oc */ + for ( i = 0; socs[i]; i++ ) { + oc = socs[i]; + if ( !manage && oc->soc_obsolete ) { + /* disallow obsolete classes */ + snprintf( textbuf, textlen, + "objectClass '%s' is OBSOLETE", + aoc->a_vals[i].bv_val ); + + Debug( LDAP_DEBUG_ANY, + "entry_check_schema(%s): %s\n", + e->e_dn, textbuf, 0 ); + + rc = LDAP_OBJECT_CLASS_VIOLATION; + goto done; + } + + if ( oc->soc_check ) { + rc = (oc->soc_check)( op->o_bd, e, oc, + text, textbuf, textlen ); + if( rc != LDAP_SUCCESS ) { + goto done; + } + } + + if ( oc->soc_kind == LDAP_SCHEMA_ABSTRACT ) { + /* object class is abstract */ + if ( oc != slap_schema.si_oc_top && + !is_object_subclass( oc, sc )) + { + int j; + ObjectClass *xc = NULL; + for( j=0; socs[j]; j++ ) { + if( i != j ) { + xc = socs[j]; + + /* since we previous check against the + * structural object of this entry, the + * abstract class must be a (direct or indirect) + * superclass of one of the auxiliary classes of + * the entry. + */ + if ( xc->soc_kind == LDAP_SCHEMA_AUXILIARY && + is_object_subclass( oc, xc ) ) + { + xc = NULL; + break; + } + } + } + + if( xc != NULL ) { + snprintf( textbuf, textlen, "instantiation of " + "abstract objectClass '%s' not allowed", + aoc->a_vals[i].bv_val ); + + Debug( LDAP_DEBUG_ANY, + "entry_check_schema(%s): %s\n", + e->e_dn, textbuf, 0 ); + + rc = LDAP_OBJECT_CLASS_VIOLATION; + goto done; + } + } + + } else if ( oc->soc_kind != LDAP_SCHEMA_STRUCTURAL || oc == sc ) { + char *s; + + if( oc->soc_kind == LDAP_SCHEMA_AUXILIARY ) { + int k; + + if( cr ) { + int j; + + k = -1; + if( cr->scr_auxiliaries ) { + for( j = 0; cr->scr_auxiliaries[j]; j++ ) { + if( cr->scr_auxiliaries[j] == oc ) { + k = 0; + break; + } + } + } + if ( k ) { + snprintf( textbuf, textlen, + "class '%s' not allowed by content rule '%s'", + oc->soc_cname.bv_val, + ldap_contentrule2name( &cr->scr_crule ) ); + } + } else if ( global_disallows & SLAP_DISALLOW_AUX_WO_CR ) { + k = -1; + snprintf( textbuf, textlen, + "class '%s' not allowed by any content rule", + oc->soc_cname.bv_val ); + } else { + k = 0; + } + + if( k == -1 ) { + Debug( LDAP_DEBUG_ANY, + "Entry (%s): %s\n", + e->e_dn, textbuf, 0 ); + + rc = LDAP_OBJECT_CLASS_VIOLATION; + goto done; + } + } + + s = oc_check_required( e, oc, &aoc->a_vals[i] ); + if (s != NULL) { + snprintf( textbuf, textlen, + "object class '%s' requires attribute '%s'", + aoc->a_vals[i].bv_val, s ); + + Debug( LDAP_DEBUG_ANY, + "Entry (%s): %s\n", + e->e_dn, textbuf, 0 ); + + rc = LDAP_OBJECT_CLASS_VIOLATION; + goto done; + } + + if( oc == slap_schema.si_oc_extensibleObject ) { + extensible=1; + } + } + } + + if( extensible ) { + *text = NULL; + rc = LDAP_SUCCESS; + goto done; + } + + /* check that each attr in the entry is allowed by some oc */ + for ( a = e->e_attrs; a != NULL; a = a->a_next ) { + rc = LDAP_OBJECT_CLASS_VIOLATION; + + if( cr && cr->scr_required ) { + for( i=0; cr->scr_required[i]; i++ ) { + if( cr->scr_required[i] == a->a_desc->ad_type ) { + rc = LDAP_SUCCESS; + break; + } + } + } + + if( rc != LDAP_SUCCESS && cr && cr->scr_allowed ) { + for( i=0; cr->scr_allowed[i]; i++ ) { + if( cr->scr_allowed[i] == a->a_desc->ad_type ) { + rc = LDAP_SUCCESS; + break; + } + } + } + + if( rc != LDAP_SUCCESS ) + { + rc = oc_check_allowed( a->a_desc->ad_type, socs, sc ); + } + + if ( rc != LDAP_SUCCESS ) { + char *type = a->a_desc->ad_cname.bv_val; + + snprintf( textbuf, textlen, + "attribute '%s' not allowed", + type ); + + Debug( LDAP_DEBUG_ANY, + "Entry (%s), %s\n", + e->e_dn, textbuf, 0 ); + + goto done; + } + } + + *text = NULL; +done: + slap_sl_free( socs, op->o_tmpmemctx ); + return rc; +} + +static char * +oc_check_required( + Entry *e, + ObjectClass *oc, + struct berval *ocname ) +{ + AttributeType *at; + int i; + Attribute *a; + + Debug( LDAP_DEBUG_TRACE, + "oc_check_required entry (%s), objectClass \"%s\"\n", + e->e_dn, ocname->bv_val, 0 ); + + + /* check for empty oc_required */ + if(oc->soc_required == NULL) { + return NULL; + } + + /* for each required attribute */ + for ( i = 0; oc->soc_required[i] != NULL; i++ ) { + at = oc->soc_required[i]; + /* see if it's in the entry */ + for ( a = e->e_attrs; a != NULL; a = a->a_next ) { + if( a->a_desc->ad_type == at ) { + break; + } + } + /* not there => schema violation */ + if ( a == NULL ) { + return at->sat_cname.bv_val; + } + } + + return( NULL ); +} + +int oc_check_allowed( + AttributeType *at, + ObjectClass **socs, + ObjectClass *sc ) +{ + int i, j; + + Debug( LDAP_DEBUG_TRACE, + "oc_check_allowed type \"%s\"\n", + at->sat_cname.bv_val, 0, 0 ); + + /* always allow objectClass attribute */ + if ( strcasecmp( at->sat_cname.bv_val, "objectClass" ) == 0 ) { + return LDAP_SUCCESS; + } + + /* + * All operational attributions are allowed by schema rules. + */ + if( is_at_operational(at) ) { + return LDAP_SUCCESS; + } + + /* check to see if its allowed by the structuralObjectClass */ + if( sc ) { + /* does it require the type? */ + for ( j = 0; sc->soc_required != NULL && + sc->soc_required[j] != NULL; j++ ) + { + if( at == sc->soc_required[j] ) { + return LDAP_SUCCESS; + } + } + + /* does it allow the type? */ + for ( j = 0; sc->soc_allowed != NULL && + sc->soc_allowed[j] != NULL; j++ ) + { + if( at == sc->soc_allowed[j] ) { + return LDAP_SUCCESS; + } + } + } + + /* check that the type appears as req or opt in at least one oc */ + for ( i = 0; socs[i]; i++ ) { + /* if we know about the oc */ + ObjectClass *oc = socs[i]; + /* extensibleObject allows all */ + if ( oc == slap_schema.si_oc_extensibleObject ) { + return LDAP_SUCCESS; + } + if ( oc != NULL && oc->soc_kind != LDAP_SCHEMA_ABSTRACT && + ( sc == NULL || oc->soc_kind == LDAP_SCHEMA_AUXILIARY )) + { + /* does it require the type? */ + for ( j = 0; oc->soc_required != NULL && + oc->soc_required[j] != NULL; j++ ) + { + if( at == oc->soc_required[j] ) { + return LDAP_SUCCESS; + } + } + /* does it allow the type? */ + for ( j = 0; oc->soc_allowed != NULL && + oc->soc_allowed[j] != NULL; j++ ) + { + if( at == oc->soc_allowed[j] ) { + return LDAP_SUCCESS; + } + } + } + } + + /* not allowed by any oc */ + return LDAP_OBJECT_CLASS_VIOLATION; +} + +/* + * Determine the structural object class from a set of OIDs + */ +int structural_class( + BerVarray ocs, + ObjectClass **scp, + ObjectClass ***socsp, + const char **text, + char *textbuf, size_t textlen, + void *ctx ) +{ + int i, nocs; + ObjectClass *oc, **socs; + ObjectClass *sc = NULL; + int scn = -1; + + *text = "structural_class: internal error"; + + /* count them */ + for( i=0; ocs[i].bv_val; i++ ) ; + nocs = i; + + socs = slap_sl_malloc( (nocs+1) * sizeof(ObjectClass *), ctx ); + + for( i=0; ocs[i].bv_val; i++ ) { + socs[i] = oc_bvfind( &ocs[i] ); + + if( socs[i] == NULL ) { + snprintf( textbuf, textlen, + "unrecognized objectClass '%s'", + ocs[i].bv_val ); + *text = textbuf; + goto fail; + } + } + socs[i] = NULL; + + for( i=0; ocs[i].bv_val; i++ ) { + oc = socs[i]; + if( oc->soc_kind == LDAP_SCHEMA_STRUCTURAL ) { + if( sc == NULL || is_object_subclass( sc, oc ) ) { + sc = oc; + scn = i; + + } else if ( !is_object_subclass( oc, sc ) ) { + int j; + ObjectClass *xc = NULL; + + /* find common superior */ + for( j=i+1; ocs[j].bv_val; j++ ) { + xc = socs[j]; + + if( xc == NULL ) { + snprintf( textbuf, textlen, + "unrecognized objectClass '%s'", + ocs[j].bv_val ); + *text = textbuf; + goto fail; + } + + if( xc->soc_kind != LDAP_SCHEMA_STRUCTURAL ) { + xc = NULL; + continue; + } + + if( is_object_subclass( sc, xc ) && + is_object_subclass( oc, xc ) ) + { + /* found common subclass */ + break; + } + + xc = NULL; + } + + if( xc == NULL ) { + /* no common subclass */ + snprintf( textbuf, textlen, + "invalid structural object class chain (%s/%s)", + ocs[scn].bv_val, ocs[i].bv_val ); + *text = textbuf; + goto fail; + } + } + } + } + + if( scp ) { + *scp = sc; + } + + if( sc == NULL ) { + *text = "no structural object class provided"; + goto fail; + } + + if( scn < 0 ) { + *text = "invalid structural object class"; + goto fail; + } + + if ( socsp ) { + *socsp = socs; + } else { + slap_sl_free( socs, ctx ); + } + *text = NULL; + + return LDAP_SUCCESS; + +fail: + slap_sl_free( socs, ctx ); + return LDAP_OBJECT_CLASS_VIOLATION; +} + +/* + * Return structural object class from list of modifications + */ +int mods_structural_class( + Modifications *mods, + struct berval *sc, + const char **text, + char *textbuf, size_t textlen, void *ctx ) +{ + Modifications *ocmod = NULL; + ObjectClass *ssc; + int rc; + + for( ; mods != NULL; mods = mods->sml_next ) { + if( mods->sml_desc == slap_schema.si_ad_objectClass ) { + if( ocmod != NULL ) { + *text = "entry has multiple objectClass attributes"; + return LDAP_OBJECT_CLASS_VIOLATION; + } + ocmod = mods; + } + } + + if( ocmod == NULL ) { + *text = "entry has no objectClass attribute"; + return LDAP_OBJECT_CLASS_VIOLATION; + } + + if( ocmod->sml_values == NULL || ocmod->sml_values[0].bv_val == NULL ) { + *text = "objectClass attribute has no values"; + return LDAP_OBJECT_CLASS_VIOLATION; + } + + rc = structural_class( ocmod->sml_values, &ssc, NULL, + text, textbuf, textlen, ctx ); + if ( rc == LDAP_SUCCESS ) + *sc = ssc->soc_cname; + return rc; +} + + +static int +entry_naming_check( + Entry *e, + int manage, + int add_naming, + const char** text, + char *textbuf, size_t textlen ) +{ + /* naming check */ + LDAPRDN rdn = NULL; + const char *p = NULL; + ber_len_t cnt; + int rc = LDAP_SUCCESS; + + if ( BER_BVISEMPTY( &e->e_name )) { + return LDAP_SUCCESS; + } + + /* + * Get attribute type(s) and attribute value(s) of our RDN + */ + if ( ldap_bv2rdn( &e->e_name, &rdn, (char **)&p, + LDAP_DN_FORMAT_LDAP ) ) + { + *text = "unrecognized attribute type(s) in RDN"; + return LDAP_INVALID_DN_SYNTAX; + } + + /* Check that each AVA of the RDN is present in the entry */ + /* FIXME: Should also check that each AVA lists a distinct type */ + for ( cnt = 0; rdn[cnt]; cnt++ ) { + LDAPAVA *ava = rdn[cnt]; + AttributeDescription *desc = NULL; + Attribute *attr; + const char *errtext; + int add = 0; + + if( ava->la_flags & LDAP_AVA_BINARY ) { + snprintf( textbuf, textlen, + "value of naming attribute '%s' in unsupported BER form", + ava->la_attr.bv_val ); + rc = LDAP_NAMING_VIOLATION; + break; + } + + rc = slap_bv2ad( &ava->la_attr, &desc, &errtext ); + if ( rc != LDAP_SUCCESS ) { + snprintf( textbuf, textlen, "%s (in RDN)", errtext ); + break; + } + + if( desc->ad_type->sat_usage ) { + snprintf( textbuf, textlen, + "naming attribute '%s' is operational", + ava->la_attr.bv_val ); + rc = LDAP_NAMING_VIOLATION; + break; + } + + if( desc->ad_type->sat_collective ) { + snprintf( textbuf, textlen, + "naming attribute '%s' is collective", + ava->la_attr.bv_val ); + rc = LDAP_NAMING_VIOLATION; + break; + } + + if( !manage && desc->ad_type->sat_obsolete ) { + snprintf( textbuf, textlen, + "naming attribute '%s' is obsolete", + ava->la_attr.bv_val ); + rc = LDAP_NAMING_VIOLATION; + break; + } + + if( !desc->ad_type->sat_equality ) { + snprintf( textbuf, textlen, + "naming attribute '%s' has no equality matching rule", + ava->la_attr.bv_val ); + rc = LDAP_NAMING_VIOLATION; + break; + } + + if( !desc->ad_type->sat_equality->smr_match ) { + snprintf( textbuf, textlen, + "naming attribute '%s' has unsupported equality matching rule", + ava->la_attr.bv_val ); + rc = LDAP_NAMING_VIOLATION; + break; + } + + /* find the naming attribute */ + attr = attr_find( e->e_attrs, desc ); + if ( attr == NULL ) { + snprintf( textbuf, textlen, + "naming attribute '%s' is not present in entry", + ava->la_attr.bv_val ); + if ( add_naming ) { + add = 1; + + } else { + rc = LDAP_NAMING_VIOLATION; + } + + } else { + rc = attr_valfind( attr, SLAP_MR_VALUE_OF_ASSERTION_SYNTAX| + SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH, + &ava->la_value, NULL, NULL ); + + if ( rc != 0 ) { + switch( rc ) { + case LDAP_INAPPROPRIATE_MATCHING: + snprintf( textbuf, textlen, + "inappropriate matching for naming attribute '%s'", + ava->la_attr.bv_val ); + break; + case LDAP_INVALID_SYNTAX: + snprintf( textbuf, textlen, + "value of naming attribute '%s' is invalid", + ava->la_attr.bv_val ); + break; + case LDAP_NO_SUCH_ATTRIBUTE: + if ( add_naming ) { + if ( is_at_single_value( desc->ad_type ) ) { + snprintf( textbuf, textlen, + "value of single-valued naming attribute '%s' conflicts with value present in entry", + ava->la_attr.bv_val ); + + } else { + add = 1; + rc = LDAP_SUCCESS; + } + + } else { + snprintf( textbuf, textlen, + "value of naming attribute '%s' is not present in entry", + ava->la_attr.bv_val ); + } + break; + default: + snprintf( textbuf, textlen, + "naming attribute '%s' is inappropriate", + ava->la_attr.bv_val ); + } + + if ( !add ) { + rc = LDAP_NAMING_VIOLATION; + } + } + } + + if ( add ) { + attr_merge_normalize_one( e, desc, &ava->la_value, NULL ); + + } else if ( rc != LDAP_SUCCESS ) { + break; + } + } + + ldap_rdnfree( rdn ); + return rc; +} + diff --git a/servers/slapd/schema_init.c b/servers/slapd/schema_init.c new file mode 100644 index 0000000..31be115 --- /dev/null +++ b/servers/slapd/schema_init.c @@ -0,0 +1,6890 @@ +/* schema_init.c - init builtin schema */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* + * Syntaxes - implementation notes: + * + * Validate function(syntax, value): + * Called before the other functions here to check if the value + * is valid according to the syntax. + * + * Pretty function(syntax, input value, output prettified...): + * If it exists, maps different notations of the same value to a + * unique representation which can be stored in the directory and + * possibly be passed to the Match/Indexer/Filter() functions. + * + * E.g. DN "2.5.4.3 = foo\,bar, o = BAZ" -> "cn=foo\2Cbar,o=BAZ", + * but unlike DN normalization, "BAZ" is not mapped to "baz". + */ + +/* + * Matching rules - implementation notes: + * + * Matching rules match an attribute value (often from the directory) + * against an asserted value (e.g. from a filter). + * + * Invoked with validated and commonly pretty/normalized arguments, thus + * a number of matching rules can simply use the octetString functions. + * + * Normalize function(...input value, output normalized...): + * If it exists, maps matching values to a unique representation + * which is passed to the Match/Indexer/Filter() functions. + * + * Different matching rules can normalize values of the same syntax + * differently. E.g. caseIgnore rules normalize to lowercase, + * caseExact rules do not. + * + * Match function(*output matchp, ...value, asserted value): + * On success, set *matchp. 0 means match. For ORDERING/most EQUALITY, + * less/greater than 0 means value less/greater than asserted. However: + * + * In extensible match filters, ORDERING rules match if value<asserted. + * + * EQUALITY rules may order values differently than ORDERING rules for + * speed, since EQUALITY ordering is only used for SLAP_AT_SORTED_VAL. + * Some EQUALITY rules do not order values (ITS#6722). + * + * Indexer function(...attribute values, *output keysp,...): + * Generates index keys for the attribute values. Backends can store + * them in an index, a {key->entry ID set} mapping, for the attribute. + * + * A search can look up the DN/scope and asserted values in the + * indexes, if any, to narrow down the number of entires to check + * against the search criteria. + * + * Filter function(...asserted value, *output keysp,...): + * Generates index key(s) for the asserted value, to be looked up in + * the index from the Indexer function. *keysp is an array because + * substring matching rules can generate multiple lookup keys. + * + * Index keys: + * A key is usually a hash of match type, attribute value and schema + * info, because one index can contain keys for many filtering types. + * + * Some indexes instead have EQUALITY keys ordered so that if + * key(val1) < key(val2), then val1 < val2 by the ORDERING rule. + * That way the ORDERING rule can use the EQUALITY index. + * + * Substring indexing: + * This chops the attribute values up in small chunks and indexes all + * possible chunks of certain sizes. Substring filtering looks up + * SOME of the asserted value's chunks, and the caller uses the + * intersection of the resulting entry ID sets. + * See the index_substr_* keywords in slapd.conf(5). + */ + +#include "portable.h" + +#include <stdio.h> +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif + +#include <ac/ctype.h> +#include <ac/errno.h> +#include <ac/string.h> +#include <ac/socket.h> + +#include "slap.h" +#include "../../libraries/liblber/lber-int.h" /* get ber_ptrlen() */ + +#include "ldap_utf8.h" + +#include "lutil.h" +#include "lutil_hash.h" +#define HASH_BYTES LUTIL_HASH_BYTES +#define HASH_CONTEXT lutil_HASH_CTX +#define HASH_Init(c) lutil_HASHInit(c) +#define HASH_Update(c,buf,len) lutil_HASHUpdate(c,buf,len) +#define HASH_Final(d,c) lutil_HASHFinal(d,c) + +/* approx matching rules */ +#define directoryStringApproxMatchOID "1.3.6.1.4.1.4203.666.4.4" +#define directoryStringApproxMatch approxMatch +#define directoryStringApproxIndexer approxIndexer +#define directoryStringApproxFilter approxFilter +#define IA5StringApproxMatchOID "1.3.6.1.4.1.4203.666.4.5" +#define IA5StringApproxMatch approxMatch +#define IA5StringApproxIndexer approxIndexer +#define IA5StringApproxFilter approxFilter + +/* Change Sequence Number (CSN) - much of this will change */ +#define csnMatch octetStringMatch +#define csnOrderingMatch octetStringOrderingMatch +#define csnIndexer generalizedTimeIndexer +#define csnFilter generalizedTimeFilter + +#define authzMatch octetStringMatch + +/* X.509 PMI ldapSyntaxes */ +/* FIXME: need to create temporary OIDs under OpenLDAP's arc; + * these are currently hijacked + * + * 1.3.6.1.4.1.4203.666 OpenLDAP + * 1.3.6.1.4.1.4203.666.11 self-contained works + * 1.3.6.1.4.1.4203.666.11.10 X.509 PMI + * 1.3.6.1.4.1.4203.666.11.10.2 X.509 PMI ldapSyntaxes + * 1.3.6.1.4.1.4203.666.11.10.2.1 AttributeCertificate (supported) + * 1.3.6.1.4.1.4203.666.11.10.2.2 AttributeCertificateExactAssertion (supported) + * 1.3.6.1.4.1.4203.666.11.10.2.3 AttributeCertificateAssertion (not supported) + * 1.3.6.1.4.1.4203.666.11.10.2.4 AttCertPath (X-SUBST'ed right now in pmi.schema) + * 1.3.6.1.4.1.4203.666.11.10.2.5 PolicySyntax (X-SUBST'ed right now in pmi.schema) + * 1.3.6.1.4.1.4203.666.11.10.2.6 RoleSyntax (X-SUBST'ed right now in pmi.schema) + */ +#if 0 /* from <draft-ietf-pkix-ldap-schema-02.txt> (expired) */ +#define attributeCertificateSyntaxOID "1.2.826.0.1.3344810.7.5" +#define attributeCertificateExactAssertionSyntaxOID "1.2.826.0.1.3344810.7.6" +#define attributeCertificateAssertionSyntaxOID "1.2.826.0.1.3344810.7.7" +#else /* from OpenLDAP's experimental oid arc */ +#define X509_PMI_SyntaxOID "1.3.6.1.4.1.4203.666.11.10.2" +#define attributeCertificateSyntaxOID X509_PMI_SyntaxOID ".1" +#define attributeCertificateExactAssertionSyntaxOID X509_PMI_SyntaxOID ".2" +#define attributeCertificateAssertionSyntaxOID X509_PMI_SyntaxOID ".3" +#endif + +unsigned int index_substr_if_minlen = SLAP_INDEX_SUBSTR_IF_MINLEN_DEFAULT; +unsigned int index_substr_if_maxlen = SLAP_INDEX_SUBSTR_IF_MAXLEN_DEFAULT; +unsigned int index_substr_any_len = SLAP_INDEX_SUBSTR_ANY_LEN_DEFAULT; +unsigned int index_substr_any_step = SLAP_INDEX_SUBSTR_ANY_STEP_DEFAULT; + +unsigned int index_intlen = SLAP_INDEX_INTLEN_DEFAULT; +unsigned int index_intlen_strlen = SLAP_INDEX_INTLEN_STRLEN( + SLAP_INDEX_INTLEN_DEFAULT ); + +ldap_pvt_thread_mutex_t ad_index_mutex; +ldap_pvt_thread_mutex_t ad_undef_mutex; +ldap_pvt_thread_mutex_t oc_undef_mutex; + +static int +generalizedTimeValidate( + Syntax *syntax, + struct berval *in ); + +#ifdef SUPPORT_OBSOLETE_UTC_SYNTAX +static int +utcTimeValidate( + Syntax *syntax, + struct berval *in ); +#endif /* SUPPORT_OBSOLETE_UTC_SYNTAX */ + +static int +inValidate( + Syntax *syntax, + struct berval *in ) +{ + /* no value allowed */ + return LDAP_INVALID_SYNTAX; +} + +static int +blobValidate( + Syntax *syntax, + struct berval *in ) +{ + /* any value allowed */ + return LDAP_SUCCESS; +} + +#define berValidate blobValidate + +static int +sequenceValidate( + Syntax *syntax, + struct berval *in ) +{ + if ( in->bv_len < 2 ) return LDAP_INVALID_SYNTAX; + if ( in->bv_val[0] != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + + return LDAP_SUCCESS; +} + +/* X.509 related stuff */ + +enum { + SLAP_X509_V1 = 0, + SLAP_X509_V2 = 1, + SLAP_X509_V3 = 2 +}; + +enum { + SLAP_TAG_UTCTIME = 0x17U, + SLAP_TAG_GENERALIZEDTIME = 0x18U +}; + + +#define SLAP_X509_OPTION (LBER_CLASS_CONTEXT|LBER_CONSTRUCTED) + +enum { + SLAP_X509_OPT_C_VERSION = SLAP_X509_OPTION + 0, + SLAP_X509_OPT_C_ISSUERUNIQUEID = LBER_CLASS_CONTEXT + 1, + SLAP_X509_OPT_C_SUBJECTUNIQUEID = LBER_CLASS_CONTEXT + 2, + SLAP_X509_OPT_C_EXTENSIONS = SLAP_X509_OPTION + 3 +}; + +enum { + SLAP_X509_OPT_CL_CRLEXTENSIONS = SLAP_X509_OPTION + 0 +}; + +/* +GeneralName ::= CHOICE { + otherName [0] INSTANCE OF OTHER-NAME, + rfc822Name [1] IA5String, + dNSName [2] IA5String, + x400Address [3] ORAddress, + directoryName [4] Name, + ediPartyName [5] EDIPartyName, + uniformResourceIdentifier [6] IA5String, + iPAddress [7] OCTET STRING, + registeredID [8] OBJECT IDENTIFIER } +*/ +enum { + SLAP_X509_GN_OTHERNAME = SLAP_X509_OPTION + 0, + SLAP_X509_GN_RFC822NAME = SLAP_X509_OPTION + 1, + SLAP_X509_GN_DNSNAME = SLAP_X509_OPTION + 2, + SLAP_X509_GN_X400ADDRESS = SLAP_X509_OPTION + 3, + SLAP_X509_GN_DIRECTORYNAME = SLAP_X509_OPTION + 4, + SLAP_X509_GN_EDIPARTYNAME = SLAP_X509_OPTION + 5, + SLAP_X509_GN_URI = SLAP_X509_OPTION + 6, + SLAP_X509_GN_IPADDRESS = SLAP_X509_OPTION + 7, + SLAP_X509_GN_REGISTEREDID = SLAP_X509_OPTION + 8 +}; + +/* X.509 PMI related stuff */ +enum { + SLAP_X509AC_V1 = 0, + SLAP_X509AC_V2 = 1 +}; + +enum { + SLAP_X509AC_ISSUER = SLAP_X509_OPTION + 0 +}; + +/* X.509 certificate validation */ +static int +certificateValidate( Syntax *syntax, struct berval *in ) +{ + BerElementBuffer berbuf; + BerElement *ber = (BerElement *)&berbuf; + ber_tag_t tag; + ber_len_t len; + ber_int_t version = SLAP_X509_V1; + + if ( BER_BVISNULL( in ) || BER_BVISEMPTY( in )) + return LDAP_INVALID_SYNTAX; + + ber_init2( ber, in, LBER_USE_DER ); + tag = ber_skip_tag( ber, &len ); /* Signed wrapper */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + tag = ber_skip_tag( ber, &len ); /* Sequence */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + tag = ber_peek_tag( ber, &len ); + /* Optional version */ + if ( tag == SLAP_X509_OPT_C_VERSION ) { + tag = ber_skip_tag( ber, &len ); + tag = ber_get_int( ber, &version ); + if ( tag != LBER_INTEGER ) return LDAP_INVALID_SYNTAX; + } + /* NOTE: don't try to parse Serial, because it might be longer + * than sizeof(ber_int_t); deferred to certificateExactNormalize() */ + tag = ber_skip_tag( ber, &len ); /* Serial */ + if ( tag != LBER_INTEGER ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); /* Signature Algorithm */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); /* Issuer DN */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); /* Validity */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); /* Subject DN */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); /* Subject PublicKeyInfo */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); + if ( tag == SLAP_X509_OPT_C_ISSUERUNIQUEID ) { /* issuerUniqueID */ + if ( version < SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); + } + if ( tag == SLAP_X509_OPT_C_SUBJECTUNIQUEID ) { /* subjectUniqueID */ + if ( version < SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); + } + if ( tag == SLAP_X509_OPT_C_EXTENSIONS ) { /* Extensions */ + if ( version < SLAP_X509_V3 ) return LDAP_INVALID_SYNTAX; + tag = ber_skip_tag( ber, &len ); + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); + } + /* signatureAlgorithm */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); + /* Signature */ + if ( tag != LBER_BITSTRING ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); + /* Must be at end now */ + if ( len || tag != LBER_DEFAULT ) return LDAP_INVALID_SYNTAX; + return LDAP_SUCCESS; +} + +/* X.509 certificate list validation */ +static int +checkTime( struct berval *in, struct berval *out ); + +static int +certificateListValidate( Syntax *syntax, struct berval *in ) +{ + BerElementBuffer berbuf; + BerElement *ber = (BerElement *)&berbuf; + ber_tag_t tag; + ber_len_t len, wrapper_len; + char *wrapper_start; + int wrapper_ok = 0; + ber_int_t version = SLAP_X509_V1; + struct berval bvdn, bvtu; + + ber_init2( ber, in, LBER_USE_DER ); + tag = ber_skip_tag( ber, &wrapper_len ); /* Signed wrapper */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + wrapper_start = ber->ber_ptr; + tag = ber_skip_tag( ber, &len ); /* Sequence */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + tag = ber_peek_tag( ber, &len ); + /* Optional version */ + if ( tag == LBER_INTEGER ) { + tag = ber_get_int( ber, &version ); + if ( tag != LBER_INTEGER || version != SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX; + } + tag = ber_skip_tag( ber, &len ); /* Signature Algorithm */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_peek_tag( ber, &len ); /* Issuer DN */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + len = ber_ptrlen( ber ); + bvdn.bv_val = in->bv_val + len; + bvdn.bv_len = in->bv_len - len; + tag = ber_skip_tag( ber, &len ); + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); /* thisUpdate */ + /* Time is a CHOICE { UTCTime, GeneralizedTime } */ + if ( tag != SLAP_TAG_UTCTIME && tag != SLAP_TAG_GENERALIZEDTIME ) return LDAP_INVALID_SYNTAX; + bvtu.bv_val = (char *)ber->ber_ptr; + bvtu.bv_len = len; + ber_skip_data( ber, len ); + /* Optional nextUpdate */ + tag = ber_skip_tag( ber, &len ); + if ( tag == SLAP_TAG_UTCTIME || tag == SLAP_TAG_GENERALIZEDTIME ) { + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); + } + /* revokedCertificates - Sequence of Sequence, Optional */ + if ( tag == LBER_SEQUENCE ) { + ber_len_t seqlen; + ber_tag_t stag; + stag = ber_peek_tag( ber, &seqlen ); + if ( stag == LBER_SEQUENCE || !len ) { + /* RFC5280 requires non-empty, but X.509(2005) allows empty. */ + if ( len ) + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); + } + } + /* Optional Extensions - Sequence of Sequence */ + if ( tag == SLAP_X509_OPT_CL_CRLEXTENSIONS ) { /* ? */ + ber_len_t seqlen; + if ( version != SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX; + tag = ber_peek_tag( ber, &seqlen ); + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); + } + /* signatureAlgorithm */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); + /* Signature */ + if ( tag != LBER_BITSTRING ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + if ( ber->ber_ptr == wrapper_start + wrapper_len ) wrapper_ok = 1; + tag = ber_skip_tag( ber, &len ); + /* Must be at end now */ + /* NOTE: OpenSSL tolerates CL with garbage past the end */ + if ( len || tag != LBER_DEFAULT ) { + struct berval issuer_dn = BER_BVNULL, thisUpdate; + char tubuf[STRLENOF("YYYYmmddHHMMSSZ") + 1]; + int rc; + + if ( ! wrapper_ok ) { + return LDAP_INVALID_SYNTAX; + } + + rc = dnX509normalize( &bvdn, &issuer_dn ); + if ( rc != LDAP_SUCCESS ) { + rc = LDAP_INVALID_SYNTAX; + goto done; + } + + thisUpdate.bv_val = tubuf; + thisUpdate.bv_len = sizeof(tubuf); + if ( checkTime( &bvtu, &thisUpdate ) ) { + rc = LDAP_INVALID_SYNTAX; + goto done; + } + + Debug( LDAP_DEBUG_ANY, + "certificateListValidate issuer=\"%s\", thisUpdate=%s: extra cruft past end of certificateList\n", + issuer_dn.bv_val, thisUpdate.bv_val, 0 ); + +done:; + if ( ! BER_BVISNULL( &issuer_dn ) ) { + ber_memfree( issuer_dn.bv_val ); + } + + return rc; + } + + return LDAP_SUCCESS; +} + +/* X.509 PMI Attribute Certificate Validate */ +static int +attributeCertificateValidate( Syntax *syntax, struct berval *in ) +{ + BerElementBuffer berbuf; + BerElement *ber = (BerElement *)&berbuf; + ber_tag_t tag; + ber_len_t len; + ber_int_t version; + int cont = 0; + + ber_init2( ber, in, LBER_USE_DER ); + + tag = ber_skip_tag( ber, &len ); /* Signed wrapper */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + + tag = ber_skip_tag( ber, &len ); /* Sequence */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + + tag = ber_peek_tag( ber, &len ); /* Version */ + if ( tag != LBER_INTEGER ) return LDAP_INVALID_SYNTAX; + tag = ber_get_int( ber, &version ); /* X.509 only allows v2 */ + if ( version != SLAP_X509AC_V2 ) return LDAP_INVALID_SYNTAX; + + tag = ber_skip_tag( ber, &len ); /* Holder */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + + tag = ber_skip_tag( ber, &len ); /* Issuer */ + if ( tag != SLAP_X509AC_ISSUER ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + + tag = ber_skip_tag( ber, &len ); /* Signature */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + + tag = ber_skip_tag( ber, &len ); /* Serial number */ + if ( tag != LBER_INTEGER ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + + tag = ber_skip_tag( ber, &len ); /* AttCertValidityPeriod */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + + tag = ber_skip_tag( ber, &len ); /* Attributes */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + + tag = ber_peek_tag( ber, &len ); + + if ( tag == LBER_BITSTRING ) { /* issuerUniqueID */ + tag = ber_skip_tag( ber, &len ); + ber_skip_data( ber, len ); + tag = ber_peek_tag( ber, &len ); + } + + if ( tag == LBER_SEQUENCE ) { /* extensions or signatureAlgorithm */ + tag = ber_skip_tag( ber, &len ); + ber_skip_data( ber, len ); + cont++; + tag = ber_peek_tag( ber, &len ); + } + + if ( tag == LBER_SEQUENCE ) { /* signatureAlgorithm */ + tag = ber_skip_tag( ber, &len ); + ber_skip_data( ber, len ); + cont++; + tag = ber_peek_tag( ber, &len ); + } + + if ( tag == LBER_BITSTRING ) { /* Signature */ + tag = ber_skip_tag( ber, &len ); + ber_skip_data( ber, len ); + cont++; + tag = ber_peek_tag( ber, &len ); + } + + /* Must be at end now */ + if ( len != 0 || tag != LBER_DEFAULT || cont < 2 ) return LDAP_INVALID_SYNTAX; + + return LDAP_SUCCESS; +} + +int +octetStringMatch( + int *matchp, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *value, + void *assertedValue ) +{ + struct berval *asserted = (struct berval *) assertedValue; + ber_slen_t d = (ber_slen_t) value->bv_len - (ber_slen_t) asserted->bv_len; + + /* For speed, order first by length, then by contents */ + *matchp = d ? (sizeof(d) == sizeof(int) ? d : d < 0 ? -1 : 1) + : memcmp( value->bv_val, asserted->bv_val, value->bv_len ); + + return LDAP_SUCCESS; +} + +int +octetStringOrderingMatch( + int *matchp, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *value, + void *assertedValue ) +{ + struct berval *asserted = (struct berval *) assertedValue; + ber_len_t v_len = value->bv_len; + ber_len_t av_len = asserted->bv_len; + + int match = memcmp( value->bv_val, asserted->bv_val, + (v_len < av_len ? v_len : av_len) ); + + if( match == 0 ) + match = sizeof(v_len) == sizeof(int) + ? (int) v_len - (int) av_len + : v_len < av_len ? -1 : v_len > av_len; + + /* If used in extensible match filter, match if value < asserted */ + if ( flags & SLAP_MR_EXT ) + match = (match >= 0); + + *matchp = match; + return LDAP_SUCCESS; +} + +/* Initialize HASHcontext from match type and schema info */ +static void +hashPreset( + HASH_CONTEXT *HASHcontext, + struct berval *prefix, + char pre, + Syntax *syntax, + MatchingRule *mr) +{ + HASH_Init(HASHcontext); + if(prefix && prefix->bv_len > 0) { + HASH_Update(HASHcontext, + (unsigned char *)prefix->bv_val, prefix->bv_len); + } + if(pre) HASH_Update(HASHcontext, (unsigned char*)&pre, sizeof(pre)); + HASH_Update(HASHcontext, (unsigned char*)syntax->ssyn_oid, syntax->ssyn_oidlen); + HASH_Update(HASHcontext, (unsigned char*)mr->smr_oid, mr->smr_oidlen); + return; +} + +/* Set HASHdigest from HASHcontext and value:len */ +static void +hashIter( + HASH_CONTEXT *HASHcontext, + unsigned char *HASHdigest, + unsigned char *value, + int len) +{ + HASH_CONTEXT ctx = *HASHcontext; + HASH_Update( &ctx, value, len ); + HASH_Final( HASHdigest, &ctx ); +} + +/* Index generation function: Attribute values -> index hash keys */ +int octetStringIndexer( + slap_mask_t use, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *prefix, + BerVarray values, + BerVarray *keysp, + void *ctx ) +{ + int i; + size_t slen, mlen; + BerVarray keys; + HASH_CONTEXT HASHcontext; + unsigned char HASHdigest[HASH_BYTES]; + struct berval digest; + digest.bv_val = (char *)HASHdigest; + digest.bv_len = sizeof(HASHdigest); + + for( i=0; !BER_BVISNULL( &values[i] ); i++ ) { + /* just count them */ + } + + /* we should have at least one value at this point */ + assert( i > 0 ); + + keys = slap_sl_malloc( sizeof( struct berval ) * (i+1), ctx ); + + slen = syntax->ssyn_oidlen; + mlen = mr->smr_oidlen; + + hashPreset( &HASHcontext, prefix, 0, syntax, mr); + for( i=0; !BER_BVISNULL( &values[i] ); i++ ) { + hashIter( &HASHcontext, HASHdigest, + (unsigned char *)values[i].bv_val, values[i].bv_len ); + ber_dupbv_x( &keys[i], &digest, ctx ); + } + + BER_BVZERO( &keys[i] ); + + *keysp = keys; + + return LDAP_SUCCESS; +} + +/* Index generation function: Asserted value -> index hash key */ +int octetStringFilter( + slap_mask_t use, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *prefix, + void * assertedValue, + BerVarray *keysp, + void *ctx ) +{ + size_t slen, mlen; + BerVarray keys; + HASH_CONTEXT HASHcontext; + unsigned char HASHdigest[HASH_BYTES]; + struct berval *value = (struct berval *) assertedValue; + struct berval digest; + digest.bv_val = (char *)HASHdigest; + digest.bv_len = sizeof(HASHdigest); + + slen = syntax->ssyn_oidlen; + mlen = mr->smr_oidlen; + + keys = slap_sl_malloc( sizeof( struct berval ) * 2, ctx ); + + hashPreset( &HASHcontext, prefix, 0, syntax, mr ); + hashIter( &HASHcontext, HASHdigest, + (unsigned char *)value->bv_val, value->bv_len ); + + ber_dupbv_x( keys, &digest, ctx ); + BER_BVZERO( &keys[1] ); + + *keysp = keys; + + return LDAP_SUCCESS; +} + +static int +octetStringSubstringsMatch( + int *matchp, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *value, + void *assertedValue ) +{ + int match = 0; + SubstringsAssertion *sub = assertedValue; + struct berval left = *value; + int i; + ber_len_t inlen = 0; + + /* Add up asserted input length */ + if ( !BER_BVISNULL( &sub->sa_initial ) ) { + inlen += sub->sa_initial.bv_len; + } + if ( sub->sa_any ) { + for ( i = 0; !BER_BVISNULL( &sub->sa_any[i] ); i++ ) { + inlen += sub->sa_any[i].bv_len; + } + } + if ( !BER_BVISNULL( &sub->sa_final ) ) { + inlen += sub->sa_final.bv_len; + } + + if ( !BER_BVISNULL( &sub->sa_initial ) ) { + if ( inlen > left.bv_len ) { + match = 1; + goto done; + } + + match = memcmp( sub->sa_initial.bv_val, left.bv_val, + sub->sa_initial.bv_len ); + + if ( match != 0 ) { + goto done; + } + + left.bv_val += sub->sa_initial.bv_len; + left.bv_len -= sub->sa_initial.bv_len; + inlen -= sub->sa_initial.bv_len; + } + + if ( !BER_BVISNULL( &sub->sa_final ) ) { + if ( inlen > left.bv_len ) { + match = 1; + goto done; + } + + match = memcmp( sub->sa_final.bv_val, + &left.bv_val[left.bv_len - sub->sa_final.bv_len], + sub->sa_final.bv_len ); + + if ( match != 0 ) { + goto done; + } + + left.bv_len -= sub->sa_final.bv_len; + inlen -= sub->sa_final.bv_len; + } + + if ( sub->sa_any ) { + for ( i = 0; !BER_BVISNULL( &sub->sa_any[i] ); i++ ) { + ber_len_t idx; + char *p; + +retry: + if ( inlen > left.bv_len ) { + /* not enough length */ + match = 1; + goto done; + } + + if ( BER_BVISEMPTY( &sub->sa_any[i] ) ) { + continue; + } + + p = memchr( left.bv_val, *sub->sa_any[i].bv_val, left.bv_len ); + + if( p == NULL ) { + match = 1; + goto done; + } + + idx = p - left.bv_val; + + if ( idx >= left.bv_len ) { + /* this shouldn't happen */ + return LDAP_OTHER; + } + + left.bv_val = p; + left.bv_len -= idx; + + if ( sub->sa_any[i].bv_len > left.bv_len ) { + /* not enough left */ + match = 1; + goto done; + } + + match = memcmp( left.bv_val, + sub->sa_any[i].bv_val, + sub->sa_any[i].bv_len ); + + if ( match != 0 ) { + left.bv_val++; + left.bv_len--; + goto retry; + } + + left.bv_val += sub->sa_any[i].bv_len; + left.bv_len -= sub->sa_any[i].bv_len; + inlen -= sub->sa_any[i].bv_len; + } + } + +done: + *matchp = match; + return LDAP_SUCCESS; +} + +/* Substring index generation function: Attribute values -> index hash keys */ +static int +octetStringSubstringsIndexer( + slap_mask_t use, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *prefix, + BerVarray values, + BerVarray *keysp, + void *ctx ) +{ + ber_len_t i, nkeys; + size_t slen, mlen; + BerVarray keys; + + HASH_CONTEXT HCany, HCini, HCfin; + unsigned char HASHdigest[HASH_BYTES]; + struct berval digest; + digest.bv_val = (char *)HASHdigest; + digest.bv_len = sizeof(HASHdigest); + + nkeys = 0; + + for ( i = 0; !BER_BVISNULL( &values[i] ); i++ ) { + /* count number of indices to generate */ + if( flags & SLAP_INDEX_SUBSTR_INITIAL ) { + if( values[i].bv_len >= index_substr_if_maxlen ) { + nkeys += index_substr_if_maxlen - + (index_substr_if_minlen - 1); + } else if( values[i].bv_len >= index_substr_if_minlen ) { + nkeys += values[i].bv_len - (index_substr_if_minlen - 1); + } + } + + if( flags & SLAP_INDEX_SUBSTR_ANY ) { + if( values[i].bv_len >= index_substr_any_len ) { + nkeys += values[i].bv_len - (index_substr_any_len - 1); + } + } + + if( flags & SLAP_INDEX_SUBSTR_FINAL ) { + if( values[i].bv_len >= index_substr_if_maxlen ) { + nkeys += index_substr_if_maxlen - + (index_substr_if_minlen - 1); + } else if( values[i].bv_len >= index_substr_if_minlen ) { + nkeys += values[i].bv_len - (index_substr_if_minlen - 1); + } + } + } + + if( nkeys == 0 ) { + /* no keys to generate */ + *keysp = NULL; + return LDAP_SUCCESS; + } + + keys = slap_sl_malloc( sizeof( struct berval ) * (nkeys+1), ctx ); + + slen = syntax->ssyn_oidlen; + mlen = mr->smr_oidlen; + + if ( flags & SLAP_INDEX_SUBSTR_ANY ) + hashPreset( &HCany, prefix, SLAP_INDEX_SUBSTR_PREFIX, syntax, mr ); + if( flags & SLAP_INDEX_SUBSTR_INITIAL ) + hashPreset( &HCini, prefix, SLAP_INDEX_SUBSTR_INITIAL_PREFIX, syntax, mr ); + if( flags & SLAP_INDEX_SUBSTR_FINAL ) + hashPreset( &HCfin, prefix, SLAP_INDEX_SUBSTR_FINAL_PREFIX, syntax, mr ); + + nkeys = 0; + for ( i = 0; !BER_BVISNULL( &values[i] ); i++ ) { + ber_len_t j,max; + + if( ( flags & SLAP_INDEX_SUBSTR_ANY ) && + ( values[i].bv_len >= index_substr_any_len ) ) + { + max = values[i].bv_len - (index_substr_any_len - 1); + + for( j=0; j<max; j++ ) { + hashIter( &HCany, HASHdigest, + (unsigned char *)&values[i].bv_val[j], + index_substr_any_len ); + ber_dupbv_x( &keys[nkeys++], &digest, ctx ); + } + } + + /* skip if too short */ + if( values[i].bv_len < index_substr_if_minlen ) continue; + + max = index_substr_if_maxlen < values[i].bv_len + ? index_substr_if_maxlen : values[i].bv_len; + + for( j=index_substr_if_minlen; j<=max; j++ ) { + + if( flags & SLAP_INDEX_SUBSTR_INITIAL ) { + hashIter( &HCini, HASHdigest, + (unsigned char *)values[i].bv_val, j ); + ber_dupbv_x( &keys[nkeys++], &digest, ctx ); + } + + if( flags & SLAP_INDEX_SUBSTR_FINAL ) { + hashIter( &HCfin, HASHdigest, + (unsigned char *)&values[i].bv_val[values[i].bv_len-j], j ); + ber_dupbv_x( &keys[nkeys++], &digest, ctx ); + } + + } + } + + if( nkeys > 0 ) { + BER_BVZERO( &keys[nkeys] ); + *keysp = keys; + } else { + ch_free( keys ); + *keysp = NULL; + } + + return LDAP_SUCCESS; +} + +/* Substring index generation function: Assertion value -> index hash keys */ +static int +octetStringSubstringsFilter ( + slap_mask_t use, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *prefix, + void * assertedValue, + BerVarray *keysp, + void *ctx) +{ + SubstringsAssertion *sa; + char pre; + ber_len_t nkeys = 0; + size_t slen, mlen, klen; + BerVarray keys; + HASH_CONTEXT HASHcontext; + unsigned char HASHdigest[HASH_BYTES]; + struct berval *value; + struct berval digest; + + sa = (SubstringsAssertion *) assertedValue; + + if( flags & SLAP_INDEX_SUBSTR_INITIAL && + !BER_BVISNULL( &sa->sa_initial ) && + sa->sa_initial.bv_len >= index_substr_if_minlen ) + { + nkeys++; + if ( sa->sa_initial.bv_len > index_substr_if_maxlen && + ( flags & SLAP_INDEX_SUBSTR_ANY )) + { + nkeys += 1 + (sa->sa_initial.bv_len - index_substr_if_maxlen) / index_substr_any_step; + } + } + + if ( flags & SLAP_INDEX_SUBSTR_ANY && sa->sa_any != NULL ) { + ber_len_t i; + for( i=0; !BER_BVISNULL( &sa->sa_any[i] ); i++ ) { + if( sa->sa_any[i].bv_len >= index_substr_any_len ) { + /* don't bother accounting with stepping */ + nkeys += sa->sa_any[i].bv_len - + ( index_substr_any_len - 1 ); + } + } + } + + if( flags & SLAP_INDEX_SUBSTR_FINAL && + !BER_BVISNULL( &sa->sa_final ) && + sa->sa_final.bv_len >= index_substr_if_minlen ) + { + nkeys++; + if ( sa->sa_final.bv_len > index_substr_if_maxlen && + ( flags & SLAP_INDEX_SUBSTR_ANY )) + { + nkeys += 1 + (sa->sa_final.bv_len - index_substr_if_maxlen) / index_substr_any_step; + } + } + + if( nkeys == 0 ) { + *keysp = NULL; + return LDAP_SUCCESS; + } + + digest.bv_val = (char *)HASHdigest; + digest.bv_len = sizeof(HASHdigest); + + slen = syntax->ssyn_oidlen; + mlen = mr->smr_oidlen; + + keys = slap_sl_malloc( sizeof( struct berval ) * (nkeys+1), ctx ); + nkeys = 0; + + if( flags & SLAP_INDEX_SUBSTR_INITIAL && + !BER_BVISNULL( &sa->sa_initial ) && + sa->sa_initial.bv_len >= index_substr_if_minlen ) + { + pre = SLAP_INDEX_SUBSTR_INITIAL_PREFIX; + value = &sa->sa_initial; + + klen = index_substr_if_maxlen < value->bv_len + ? index_substr_if_maxlen : value->bv_len; + + hashPreset( &HASHcontext, prefix, pre, syntax, mr ); + hashIter( &HASHcontext, HASHdigest, + (unsigned char *)value->bv_val, klen ); + ber_dupbv_x( &keys[nkeys++], &digest, ctx ); + + /* If initial is too long and we have subany indexed, use it + * to match the excess... + */ + if (value->bv_len > index_substr_if_maxlen && (flags & SLAP_INDEX_SUBSTR_ANY)) + { + ber_len_t j; + pre = SLAP_INDEX_SUBSTR_PREFIX; + hashPreset( &HASHcontext, prefix, pre, syntax, mr); + for ( j=index_substr_if_maxlen-1; j <= value->bv_len - index_substr_any_len; j+=index_substr_any_step ) + { + hashIter( &HASHcontext, HASHdigest, + (unsigned char *)&value->bv_val[j], index_substr_any_len ); + ber_dupbv_x( &keys[nkeys++], &digest, ctx ); + } + } + } + + if( flags & SLAP_INDEX_SUBSTR_ANY && sa->sa_any != NULL ) { + ber_len_t i, j; + pre = SLAP_INDEX_SUBSTR_PREFIX; + klen = index_substr_any_len; + + for( i=0; !BER_BVISNULL( &sa->sa_any[i] ); i++ ) { + if( sa->sa_any[i].bv_len < index_substr_any_len ) { + continue; + } + + value = &sa->sa_any[i]; + + hashPreset( &HASHcontext, prefix, pre, syntax, mr); + for(j=0; + j <= value->bv_len - index_substr_any_len; + j += index_substr_any_step ) + { + hashIter( &HASHcontext, HASHdigest, + (unsigned char *)&value->bv_val[j], klen ); + ber_dupbv_x( &keys[nkeys++], &digest, ctx ); + } + } + } + + if( flags & SLAP_INDEX_SUBSTR_FINAL && + !BER_BVISNULL( &sa->sa_final ) && + sa->sa_final.bv_len >= index_substr_if_minlen ) + { + pre = SLAP_INDEX_SUBSTR_FINAL_PREFIX; + value = &sa->sa_final; + + klen = index_substr_if_maxlen < value->bv_len + ? index_substr_if_maxlen : value->bv_len; + + hashPreset( &HASHcontext, prefix, pre, syntax, mr ); + hashIter( &HASHcontext, HASHdigest, + (unsigned char *)&value->bv_val[value->bv_len-klen], klen ); + ber_dupbv_x( &keys[nkeys++], &digest, ctx ); + + /* If final is too long and we have subany indexed, use it + * to match the excess... + */ + if (value->bv_len > index_substr_if_maxlen && (flags & SLAP_INDEX_SUBSTR_ANY)) + { + ber_len_t j; + pre = SLAP_INDEX_SUBSTR_PREFIX; + hashPreset( &HASHcontext, prefix, pre, syntax, mr); + for ( j=0; j <= value->bv_len - index_substr_if_maxlen; j+=index_substr_any_step ) + { + hashIter( &HASHcontext, HASHdigest, + (unsigned char *)&value->bv_val[j], index_substr_any_len ); + ber_dupbv_x( &keys[nkeys++], &digest, ctx ); + } + } + } + + if( nkeys > 0 ) { + BER_BVZERO( &keys[nkeys] ); + *keysp = keys; + } else { + ch_free( keys ); + *keysp = NULL; + } + + return LDAP_SUCCESS; +} + +static int +bitStringValidate( + Syntax *syntax, + struct berval *in ) +{ + ber_len_t i; + + /* very unforgiving validation, requires no normalization + * before simplistic matching + */ + if( in->bv_len < 3 ) { + return LDAP_INVALID_SYNTAX; + } + + /* RFC 4517 Section 3.3.2 Bit String: + * BitString = SQUOTE *binary-digit SQUOTE "B" + * binary-digit = "0" / "1" + * + * where SQUOTE [RFC4512] is + * SQUOTE = %x27 ; single quote ("'") + * + * Example: '0101111101'B + */ + + if( in->bv_val[0] != '\'' || + in->bv_val[in->bv_len - 2] != '\'' || + in->bv_val[in->bv_len - 1] != 'B' ) + { + return LDAP_INVALID_SYNTAX; + } + + for( i = in->bv_len - 3; i > 0; i-- ) { + if( in->bv_val[i] != '0' && in->bv_val[i] != '1' ) { + return LDAP_INVALID_SYNTAX; + } + } + + return LDAP_SUCCESS; +} + +/* + * Syntaxes from RFC 4517 + * + +3.3.2. Bit String + + A value of the Bit String syntax is a sequence of binary digits. The + LDAP-specific encoding of a value of this syntax is defined by the + following ABNF: + + BitString = SQUOTE *binary-digit SQUOTE "B" + + binary-digit = "0" / "1" + + The <SQUOTE> rule is defined in [MODELS]. + + Example: + '0101111101'B + + The LDAP definition for the Bit String syntax is: + + ( 1.3.6.1.4.1.1466.115.121.1.6 DESC 'Bit String' ) + + This syntax corresponds to the BIT STRING ASN.1 type from [ASN.1]. + + ... + +3.3.21. Name and Optional UID + + A value of the Name and Optional UID syntax is the distinguished name + [MODELS] of an entity optionally accompanied by a unique identifier + that serves to differentiate the entity from others with an identical + distinguished name. + + The LDAP-specific encoding of a value of this syntax is defined by + the following ABNF: + + NameAndOptionalUID = distinguishedName [ SHARP BitString ] + + The <BitString> rule is defined in Section 3.3.2. The + <distinguishedName> rule is defined in [LDAPDN]. The <SHARP> rule is + defined in [MODELS]. + + Note that although the '#' character may occur in the string + representation of a distinguished name, no additional escaping of + this character is performed when a <distinguishedName> is encoded in + a <NameAndOptionalUID>. + + Example: + 1.3.6.1.4.1.1466.0=#04024869,O=Test,C=GB#'0101'B + + The LDAP definition for the Name and Optional UID syntax is: + + ( 1.3.6.1.4.1.1466.115.121.1.34 DESC 'Name And Optional UID' ) + + This syntax corresponds to the NameAndOptionalUID ASN.1 type from + [X.520]. + + * + * RFC 4512 says: + * + +1.4. Common ABNF Productions + + ... + SHARP = %x23 ; octothorpe (or sharp sign) ("#") + ... + SQUOTE = %x27 ; single quote ("'") + ... + + * + * Note: + * RFC 4514 clarifies that SHARP, i.e. "#", doesn't have to + * be escaped except when at the beginning of a value, the + * definition of Name and Optional UID appears to be flawed, + * because there is no clear means to determine whether the + * UID part is present or not. + * + * Example: + * + * cn=Someone,dc=example,dc=com#'1'B + * + * could be either a NameAndOptionalUID with trailing UID, i.e. + * + * DN = "cn=Someone,dc=example,dc=com" + * UID = "'1'B" + * + * or a NameAndOptionalUID with no trailing UID, and the AVA + * in the last RDN made of + * + * attributeType = dc + * attributeValue = com#'1'B + * + * in fact "com#'1'B" is a valid IA5 string. + * + * As a consequence, current slapd code takes the presence of + * #<valid BitString> at the end of the string representation + * of a NameAndOptionalUID to mean this is indeed a BitString. + * This is quite arbitrary - it has changed the past and might + * change in the future. + */ + + +static int +nameUIDValidate( + Syntax *syntax, + struct berval *in ) +{ + int rc; + struct berval dn, uid; + + if( BER_BVISEMPTY( in ) ) return LDAP_SUCCESS; + + ber_dupbv( &dn, in ); + if( !dn.bv_val ) return LDAP_OTHER; + + /* if there's a "#", try bitStringValidate()... */ + uid.bv_val = strrchr( dn.bv_val, '#' ); + if ( !BER_BVISNULL( &uid ) ) { + uid.bv_val++; + uid.bv_len = dn.bv_len - ( uid.bv_val - dn.bv_val ); + + rc = bitStringValidate( NULL, &uid ); + if ( rc == LDAP_SUCCESS ) { + /* in case of success, trim the UID, + * otherwise treat it as part of the DN */ + dn.bv_len -= uid.bv_len + 1; + uid.bv_val[-1] = '\0'; + } + } + + rc = dnValidate( NULL, &dn ); + + ber_memfree( dn.bv_val ); + return rc; +} + +int +nameUIDPretty( + Syntax *syntax, + struct berval *val, + struct berval *out, + void *ctx ) +{ + assert( val != NULL ); + assert( out != NULL ); + + + Debug( LDAP_DEBUG_TRACE, ">>> nameUIDPretty: <%s>\n", val->bv_val, 0, 0 ); + + if( BER_BVISEMPTY( val ) ) { + ber_dupbv_x( out, val, ctx ); + + } else if ( val->bv_len > SLAP_LDAPDN_MAXLEN ) { + return LDAP_INVALID_SYNTAX; + + } else { + int rc; + struct berval dnval = *val; + struct berval uidval = BER_BVNULL; + + uidval.bv_val = strrchr( val->bv_val, '#' ); + if ( !BER_BVISNULL( &uidval ) ) { + uidval.bv_val++; + uidval.bv_len = val->bv_len - ( uidval.bv_val - val->bv_val ); + + rc = bitStringValidate( NULL, &uidval ); + + if ( rc == LDAP_SUCCESS ) { + ber_dupbv_x( &dnval, val, ctx ); + uidval.bv_val--; + dnval.bv_len -= ++uidval.bv_len; + dnval.bv_val[dnval.bv_len] = '\0'; + + } else { + BER_BVZERO( &uidval ); + } + } + + rc = dnPretty( syntax, &dnval, out, ctx ); + if ( dnval.bv_val != val->bv_val ) { + slap_sl_free( dnval.bv_val, ctx ); + } + if( rc != LDAP_SUCCESS ) { + return rc; + } + + if( !BER_BVISNULL( &uidval ) ) { + char *tmp; + + tmp = slap_sl_realloc( out->bv_val, out->bv_len + + uidval.bv_len + 1, + ctx ); + if( tmp == NULL ) { + ber_memfree_x( out->bv_val, ctx ); + return LDAP_OTHER; + } + out->bv_val = tmp; + memcpy( out->bv_val + out->bv_len, uidval.bv_val, uidval.bv_len ); + out->bv_len += uidval.bv_len; + out->bv_val[out->bv_len] = '\0'; + } + } + + Debug( LDAP_DEBUG_TRACE, "<<< nameUIDPretty: <%s>\n", out->bv_val, 0, 0 ); + + return LDAP_SUCCESS; +} + +static int +uniqueMemberNormalize( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *normalized, + void *ctx ) +{ + struct berval out; + int rc; + + assert( SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) != 0 ); + + ber_dupbv_x( &out, val, ctx ); + if ( BER_BVISEMPTY( &out ) ) { + *normalized = out; + + } else { + struct berval uid = BER_BVNULL; + + uid.bv_val = strrchr( out.bv_val, '#' ); + if ( !BER_BVISNULL( &uid ) ) { + uid.bv_val++; + uid.bv_len = out.bv_len - ( uid.bv_val - out.bv_val ); + + rc = bitStringValidate( NULL, &uid ); + if ( rc == LDAP_SUCCESS ) { + uid.bv_val[-1] = '\0'; + out.bv_len -= uid.bv_len + 1; + } else { + BER_BVZERO( &uid ); + } + } + + rc = dnNormalize( 0, NULL, NULL, &out, normalized, ctx ); + + if( rc != LDAP_SUCCESS ) { + slap_sl_free( out.bv_val, ctx ); + return LDAP_INVALID_SYNTAX; + } + + if( !BER_BVISNULL( &uid ) ) { + char *tmp; + + tmp = ch_realloc( normalized->bv_val, + normalized->bv_len + uid.bv_len + + STRLENOF("#") + 1 ); + if ( tmp == NULL ) { + ber_memfree_x( normalized->bv_val, ctx ); + return LDAP_OTHER; + } + + normalized->bv_val = tmp; + + /* insert the separator */ + normalized->bv_val[normalized->bv_len++] = '#'; + + /* append the UID */ + AC_MEMCPY( &normalized->bv_val[normalized->bv_len], + uid.bv_val, uid.bv_len ); + normalized->bv_len += uid.bv_len; + + /* terminate */ + normalized->bv_val[normalized->bv_len] = '\0'; + } + + slap_sl_free( out.bv_val, ctx ); + } + + return LDAP_SUCCESS; +} + +static int +uniqueMemberMatch( + int *matchp, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *value, + void *assertedValue ) +{ + int match; + struct berval *asserted = (struct berval *) assertedValue; + struct berval assertedDN = *asserted; + struct berval assertedUID = BER_BVNULL; + struct berval valueDN = *value; + struct berval valueUID = BER_BVNULL; + int approx = ((flags & SLAP_MR_EQUALITY_APPROX) == SLAP_MR_EQUALITY_APPROX); + + if ( !BER_BVISEMPTY( asserted ) ) { + assertedUID.bv_val = strrchr( assertedDN.bv_val, '#' ); + if ( !BER_BVISNULL( &assertedUID ) ) { + assertedUID.bv_val++; + assertedUID.bv_len = assertedDN.bv_len + - ( assertedUID.bv_val - assertedDN.bv_val ); + + if ( bitStringValidate( NULL, &assertedUID ) == LDAP_SUCCESS ) { + assertedDN.bv_len -= assertedUID.bv_len + 1; + + } else { + BER_BVZERO( &assertedUID ); + } + } + } + + if ( !BER_BVISEMPTY( value ) ) { + + valueUID.bv_val = strrchr( valueDN.bv_val, '#' ); + if ( !BER_BVISNULL( &valueUID ) ) { + valueUID.bv_val++; + valueUID.bv_len = valueDN.bv_len + - ( valueUID.bv_val - valueDN.bv_val ); + + if ( bitStringValidate( NULL, &valueUID ) == LDAP_SUCCESS ) { + valueDN.bv_len -= valueUID.bv_len + 1; + + } else { + BER_BVZERO( &valueUID ); + } + } + } + + if( valueUID.bv_len && assertedUID.bv_len ) { + ber_slen_t d; + d = (ber_slen_t) valueUID.bv_len - (ber_slen_t) assertedUID.bv_len; + if ( d ) { + *matchp = sizeof(d) == sizeof(int) ? d : d < 0 ? -1 : 1; + return LDAP_SUCCESS; + } + + match = memcmp( valueUID.bv_val, assertedUID.bv_val, valueUID.bv_len ); + if( match ) { + *matchp = match; + return LDAP_SUCCESS; + } + + } else if ( !approx && valueUID.bv_len ) { + match = -1; + *matchp = match; + return LDAP_SUCCESS; + + } else if ( !approx && assertedUID.bv_len ) { + match = 1; + *matchp = match; + return LDAP_SUCCESS; + } + + return dnMatch( matchp, flags, syntax, mr, &valueDN, &assertedDN ); +} + +static int +uniqueMemberIndexer( + slap_mask_t use, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *prefix, + BerVarray values, + BerVarray *keysp, + void *ctx ) +{ + BerVarray dnvalues; + int rc; + int i; + for( i=0; !BER_BVISNULL( &values[i] ); i++ ) { + /* just count them */ + } + assert( i > 0 ); + + dnvalues = slap_sl_malloc( sizeof( struct berval ) * (i+1), ctx ); + + for( i=0; !BER_BVISNULL( &values[i] ); i++ ) { + struct berval assertedDN = values[i]; + struct berval assertedUID = BER_BVNULL; + + if ( !BER_BVISEMPTY( &assertedDN ) ) { + assertedUID.bv_val = strrchr( assertedDN.bv_val, '#' ); + if ( !BER_BVISNULL( &assertedUID ) ) { + assertedUID.bv_val++; + assertedUID.bv_len = assertedDN.bv_len + - ( assertedUID.bv_val - assertedDN.bv_val ); + + if ( bitStringValidate( NULL, &assertedUID ) == LDAP_SUCCESS ) { + assertedDN.bv_len -= assertedUID.bv_len + 1; + + } else { + BER_BVZERO( &assertedUID ); + } + } + } + + dnvalues[i] = assertedDN; + } + BER_BVZERO( &dnvalues[i] ); + + rc = octetStringIndexer( use, flags, syntax, mr, prefix, + dnvalues, keysp, ctx ); + + slap_sl_free( dnvalues, ctx ); + return rc; +} + +static int +uniqueMemberFilter( + slap_mask_t use, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *prefix, + void * assertedValue, + BerVarray *keysp, + void *ctx ) +{ + struct berval *asserted = (struct berval *) assertedValue; + struct berval assertedDN = *asserted; + struct berval assertedUID = BER_BVNULL; + + if ( !BER_BVISEMPTY( asserted ) ) { + assertedUID.bv_val = strrchr( assertedDN.bv_val, '#' ); + if ( !BER_BVISNULL( &assertedUID ) ) { + assertedUID.bv_val++; + assertedUID.bv_len = assertedDN.bv_len + - ( assertedUID.bv_val - assertedDN.bv_val ); + + if ( bitStringValidate( NULL, &assertedUID ) == LDAP_SUCCESS ) { + assertedDN.bv_len -= assertedUID.bv_len + 1; + + } else { + BER_BVZERO( &assertedUID ); + } + } + } + + return octetStringFilter( use, flags, syntax, mr, prefix, + &assertedDN, keysp, ctx ); +} + + +/* + * Handling boolean syntax and matching is quite rigid. + * A more flexible approach would be to allow a variety + * of strings to be normalized and prettied into TRUE + * and FALSE. + */ +static int +booleanValidate( + Syntax *syntax, + struct berval *in ) +{ + /* very unforgiving validation, requires no normalization + * before simplistic matching + */ + + if( in->bv_len == 4 ) { + if( bvmatch( in, &slap_true_bv ) ) { + return LDAP_SUCCESS; + } + } else if( in->bv_len == 5 ) { + if( bvmatch( in, &slap_false_bv ) ) { + return LDAP_SUCCESS; + } + } + + return LDAP_INVALID_SYNTAX; +} + +static int +booleanMatch( + int *matchp, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *value, + void *assertedValue ) +{ + /* simplistic matching allowed by rigid validation */ + struct berval *asserted = (struct berval *) assertedValue; + *matchp = (int) asserted->bv_len - (int) value->bv_len; + return LDAP_SUCCESS; +} + +/*------------------------------------------------------------------- +LDAP/X.500 string syntax / matching rules have a few oddities. This +comment attempts to detail how slapd(8) treats them. + +Summary: + StringSyntax X.500 LDAP Matching/Comments + DirectoryString CHOICE UTF8 i/e + ignore insignificant spaces + PrintableString subset subset i/e + ignore insignificant spaces + PrintableString subset subset i/e + ignore insignificant spaces + NumericString subset subset ignore all spaces + IA5String ASCII ASCII i/e + ignore insignificant spaces + TeletexString T.61 T.61 i/e + ignore insignificant spaces + + TelephoneNumber subset subset i + ignore all spaces and "-" + + See RFC 4518 for details. + + +Directory String - + In X.500(93), a directory string can be either a PrintableString, + a bmpString, or a UniversalString (e.g., UCS (a subset of Unicode)). + In later versions, more CHOICEs were added. In all cases the string + must be non-empty. + + In LDAPv3, a directory string is a UTF-8 encoded UCS string. + A directory string cannot be zero length. + + For matching, there are both case ignore and exact rules. Both + also require that "insignificant" spaces be ignored. + spaces before the first non-space are ignored; + spaces after the last non-space are ignored; + spaces after a space are ignored. + Note: by these rules (and as clarified in X.520), a string of only + spaces is to be treated as if held one space, not empty (which + would be a syntax error). + +NumericString + In ASN.1, numeric string is just a string of digits and spaces + and could be empty. However, in X.500, all attribute values of + numeric string carry a non-empty constraint. For example: + + internationalISDNNumber ATTRIBUTE ::= { + WITH SYNTAX InternationalISDNNumber + EQUALITY MATCHING RULE numericStringMatch + SUBSTRINGS MATCHING RULE numericStringSubstringsMatch + ID id-at-internationalISDNNumber } + InternationalISDNNumber ::= + NumericString (SIZE(1..ub-international-isdn-number)) + + Unforunately, some assertion values are don't carry the same + constraint (but its unclear how such an assertion could ever + be true). In LDAP, there is one syntax (numericString) not two + (numericString with constraint, numericString without constraint). + This should be treated as numericString with non-empty constraint. + Note that while someone may have no ISDN number, there are no ISDN + numbers which are zero length. + + In matching, spaces are ignored. + +PrintableString + In ASN.1, Printable string is just a string of printable characters + and can be empty. In X.500, semantics much like NumericString (see + serialNumber for a like example) excepting uses insignificant space + handling instead of ignore all spaces. They must be non-empty. + +IA5String + Basically same as PrintableString. There are no examples in X.500, + but same logic applies. Empty strings are allowed. + +-------------------------------------------------------------------*/ + +static int +UTF8StringValidate( + Syntax *syntax, + struct berval *in ) +{ + int len; + unsigned char *u = (unsigned char *)in->bv_val, *end = in->bv_val + in->bv_len; + + if( BER_BVISEMPTY( in ) && syntax == slap_schema.si_syn_directoryString ) { + /* directory strings cannot be empty */ + return LDAP_INVALID_SYNTAX; + } + + for( ; u < end; u += len ) { + /* get the length indicated by the first byte */ + len = LDAP_UTF8_CHARLEN2( u, len ); + + /* very basic checks */ + switch( len ) { + case 6: + if( (u[5] & 0xC0) != 0x80 ) { + return LDAP_INVALID_SYNTAX; + } + case 5: + if( (u[4] & 0xC0) != 0x80 ) { + return LDAP_INVALID_SYNTAX; + } + case 4: + if( (u[3] & 0xC0) != 0x80 ) { + return LDAP_INVALID_SYNTAX; + } + case 3: + if( (u[2] & 0xC0 )!= 0x80 ) { + return LDAP_INVALID_SYNTAX; + } + case 2: + if( (u[1] & 0xC0) != 0x80 ) { + return LDAP_INVALID_SYNTAX; + } + case 1: + /* CHARLEN already validated it */ + break; + default: + return LDAP_INVALID_SYNTAX; + } + + /* make sure len corresponds with the offset + to the next character */ + if( LDAP_UTF8_OFFSET( (char *)u ) != len ) return LDAP_INVALID_SYNTAX; + } + + if( u > end ) { + return LDAP_INVALID_SYNTAX; + } + + return LDAP_SUCCESS; +} + +static int +UTF8StringNormalize( + slap_mask_t use, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *normalized, + void *ctx ) +{ + struct berval tmp, nvalue; + int flags, wasspace; + ber_len_t i; + + assert( SLAP_MR_IS_VALUE_OF_SYNTAX( use ) != 0 ); + + if( BER_BVISNULL( val ) ) { + /* assume we're dealing with a syntax (e.g., UTF8String) + * which allows empty strings + */ + BER_BVZERO( normalized ); + return LDAP_SUCCESS; + } + + flags = SLAP_MR_ASSOCIATED( mr, slap_schema.si_mr_caseExactMatch ) + ? LDAP_UTF8_NOCASEFOLD : LDAP_UTF8_CASEFOLD; + flags |= ( ( use & SLAP_MR_EQUALITY_APPROX ) == SLAP_MR_EQUALITY_APPROX ) + ? LDAP_UTF8_APPROX : 0; + + val = UTF8bvnormalize( val, &tmp, flags, ctx ); + /* out of memory or syntax error, the former is unlikely */ + if( val == NULL ) { + return LDAP_INVALID_SYNTAX; + } + + /* collapse spaces (in place) */ + nvalue.bv_len = 0; + nvalue.bv_val = tmp.bv_val; + + /* trim leading spaces? */ + wasspace = !((( use & SLAP_MR_SUBSTR_ANY ) == SLAP_MR_SUBSTR_ANY ) || + (( use & SLAP_MR_SUBSTR_FINAL ) == SLAP_MR_SUBSTR_FINAL )); + + for( i = 0; i < tmp.bv_len; i++) { + if ( ASCII_SPACE( tmp.bv_val[i] )) { + if( wasspace++ == 0 ) { + /* trim repeated spaces */ + nvalue.bv_val[nvalue.bv_len++] = tmp.bv_val[i]; + } + } else { + wasspace = 0; + nvalue.bv_val[nvalue.bv_len++] = tmp.bv_val[i]; + } + } + + if( !BER_BVISEMPTY( &nvalue ) ) { + /* trim trailing space? */ + if( wasspace && ( + (( use & SLAP_MR_SUBSTR_INITIAL ) != SLAP_MR_SUBSTR_INITIAL ) && + ( use & SLAP_MR_SUBSTR_ANY ) != SLAP_MR_SUBSTR_ANY )) + { + --nvalue.bv_len; + } + nvalue.bv_val[nvalue.bv_len] = '\0'; + + } else if ( tmp.bv_len ) { + /* string of all spaces is treated as one space */ + nvalue.bv_val[0] = ' '; + nvalue.bv_val[1] = '\0'; + nvalue.bv_len = 1; + } /* should never be entered with 0-length val */ + + *normalized = nvalue; + return LDAP_SUCCESS; +} + +static int +directoryStringSubstringsMatch( + int *matchp, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *value, + void *assertedValue ) +{ + int match = 0; + SubstringsAssertion *sub = assertedValue; + struct berval left = *value; + ber_len_t i; + int priorspace=0; + + if ( !BER_BVISNULL( &sub->sa_initial ) ) { + if ( sub->sa_initial.bv_len > left.bv_len ) { + /* not enough left */ + match = 1; + goto done; + } + + match = memcmp( sub->sa_initial.bv_val, left.bv_val, + sub->sa_initial.bv_len ); + + if ( match != 0 ) { + goto done; + } + + left.bv_val += sub->sa_initial.bv_len; + left.bv_len -= sub->sa_initial.bv_len; + + priorspace = ASCII_SPACE( + sub->sa_initial.bv_val[sub->sa_initial.bv_len] ); + } + + if ( sub->sa_any ) { + for ( i = 0; !BER_BVISNULL( &sub->sa_any[i] ); i++ ) { + ber_len_t idx; + char *p; + + if( priorspace && !BER_BVISEMPTY( &sub->sa_any[i] ) + && ASCII_SPACE( sub->sa_any[i].bv_val[0] )) + { + /* allow next space to match */ + left.bv_val--; + left.bv_len++; + } + priorspace=0; + +retry: + if ( BER_BVISEMPTY( &sub->sa_any[i] ) ) { + continue; + } + + if ( sub->sa_any[i].bv_len > left.bv_len ) { + /* not enough left */ + match = 1; + goto done; + } + + p = memchr( left.bv_val, *sub->sa_any[i].bv_val, left.bv_len ); + + if( p == NULL ) { + match = 1; + goto done; + } + + idx = p - left.bv_val; + + if ( idx >= left.bv_len ) { + /* this shouldn't happen */ + return LDAP_OTHER; + } + + left.bv_val = p; + left.bv_len -= idx; + + if ( sub->sa_any[i].bv_len > left.bv_len ) { + /* not enough left */ + match = 1; + goto done; + } + + match = memcmp( left.bv_val, + sub->sa_any[i].bv_val, + sub->sa_any[i].bv_len ); + + if ( match != 0 ) { + left.bv_val++; + left.bv_len--; + goto retry; + } + + left.bv_val += sub->sa_any[i].bv_len; + left.bv_len -= sub->sa_any[i].bv_len; + + priorspace = ASCII_SPACE( + sub->sa_any[i].bv_val[sub->sa_any[i].bv_len] ); + } + } + + if ( !BER_BVISNULL( &sub->sa_final ) ) { + if( priorspace && !BER_BVISEMPTY( &sub->sa_final ) + && ASCII_SPACE( sub->sa_final.bv_val[0] )) + { + /* allow next space to match */ + left.bv_val--; + left.bv_len++; + } + + if ( sub->sa_final.bv_len > left.bv_len ) { + /* not enough left */ + match = 1; + goto done; + } + + match = memcmp( sub->sa_final.bv_val, + &left.bv_val[left.bv_len - sub->sa_final.bv_len], + sub->sa_final.bv_len ); + + if ( match != 0 ) { + goto done; + } + } + +done: + *matchp = match; + return LDAP_SUCCESS; +} + +#if defined(SLAPD_APPROX_INITIALS) +# define SLAPD_APPROX_DELIMITER "._ " +# define SLAPD_APPROX_WORDLEN 2 +#else +# define SLAPD_APPROX_DELIMITER " " +# define SLAPD_APPROX_WORDLEN 1 +#endif + +static int +approxMatch( + int *matchp, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *value, + void *assertedValue ) +{ + struct berval *nval, *assertv; + char *val, **values, **words, *c; + int i, count, len, nextchunk=0, nextavail=0; + + /* Yes, this is necessary */ + nval = UTF8bvnormalize( value, NULL, LDAP_UTF8_APPROX, NULL ); + if( nval == NULL ) { + *matchp = 1; + return LDAP_SUCCESS; + } + + /* Yes, this is necessary */ + assertv = UTF8bvnormalize( ((struct berval *)assertedValue), + NULL, LDAP_UTF8_APPROX, NULL ); + if( assertv == NULL ) { + ber_bvfree( nval ); + *matchp = 1; + return LDAP_SUCCESS; + } + + /* Isolate how many words there are */ + for ( c = nval->bv_val, count = 1; *c; c++ ) { + c = strpbrk( c, SLAPD_APPROX_DELIMITER ); + if ( c == NULL ) break; + *c = '\0'; + count++; + } + + /* Get a phonetic copy of each word */ + words = (char **)ch_malloc( count * sizeof(char *) ); + values = (char **)ch_malloc( count * sizeof(char *) ); + for ( c = nval->bv_val, i = 0; i < count; i++, c += strlen(c) + 1 ) { + words[i] = c; + values[i] = phonetic(c); + } + + /* Work through the asserted value's words, to see if at least some + * of the words are there, in the same order. */ + len = 0; + while ( (ber_len_t) nextchunk < assertv->bv_len ) { + len = strcspn( assertv->bv_val + nextchunk, SLAPD_APPROX_DELIMITER); + if( len == 0 ) { + nextchunk++; + continue; + } +#if defined(SLAPD_APPROX_INITIALS) + else if( len == 1 ) { + /* Single letter words need to at least match one word's initial */ + for( i=nextavail; i<count; i++ ) + if( !strncasecmp( assertv->bv_val + nextchunk, words[i], 1 )) { + nextavail=i+1; + break; + } + } +#endif + else { + /* Isolate the next word in the asserted value and phonetic it */ + assertv->bv_val[nextchunk+len] = '\0'; + val = phonetic( assertv->bv_val + nextchunk ); + + /* See if this phonetic chunk is in the remaining words of *value */ + for( i=nextavail; i<count; i++ ){ + if( !strcmp( val, values[i] ) ){ + nextavail = i+1; + break; + } + } + ch_free( val ); + } + + /* This chunk in the asserted value was NOT within the *value. */ + if( i >= count ) { + nextavail=-1; + break; + } + + /* Go on to the next word in the asserted value */ + nextchunk += len+1; + } + + /* If some of the words were seen, call it a match */ + if( nextavail > 0 ) { + *matchp = 0; + } + else { + *matchp = 1; + } + + /* Cleanup allocs */ + ber_bvfree( assertv ); + for( i=0; i<count; i++ ) { + ch_free( values[i] ); + } + ch_free( values ); + ch_free( words ); + ber_bvfree( nval ); + + return LDAP_SUCCESS; +} + +static int +approxIndexer( + slap_mask_t use, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *prefix, + BerVarray values, + BerVarray *keysp, + void *ctx ) +{ + char *c; + int i,j, len, wordcount, keycount=0; + struct berval *newkeys; + BerVarray keys=NULL; + + for( j = 0; !BER_BVISNULL( &values[j] ); j++ ) { + struct berval val = BER_BVNULL; + /* Yes, this is necessary */ + UTF8bvnormalize( &values[j], &val, LDAP_UTF8_APPROX, NULL ); + assert( !BER_BVISNULL( &val ) ); + + /* Isolate how many words there are. There will be a key for each */ + for( wordcount = 0, c = val.bv_val; *c; c++) { + len = strcspn(c, SLAPD_APPROX_DELIMITER); + if( len >= SLAPD_APPROX_WORDLEN ) wordcount++; + c+= len; + if (*c == '\0') break; + *c = '\0'; + } + + /* Allocate/increase storage to account for new keys */ + newkeys = (struct berval *)ch_malloc( (keycount + wordcount + 1) + * sizeof(struct berval) ); + AC_MEMCPY( newkeys, keys, keycount * sizeof(struct berval) ); + if( keys ) ch_free( keys ); + keys = newkeys; + + /* Get a phonetic copy of each word */ + for( c = val.bv_val, i = 0; i < wordcount; c += len + 1 ) { + len = strlen( c ); + if( len < SLAPD_APPROX_WORDLEN ) continue; + ber_str2bv( phonetic( c ), 0, 0, &keys[keycount] ); + if( keys[keycount].bv_len ) { + keycount++; + } else { + ch_free( keys[keycount].bv_val ); + } + i++; + } + + ber_memfree( val.bv_val ); + } + BER_BVZERO( &keys[keycount] ); + *keysp = keys; + + return LDAP_SUCCESS; +} + +static int +approxFilter( + slap_mask_t use, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *prefix, + void * assertedValue, + BerVarray *keysp, + void *ctx ) +{ + char *c; + int i, count, len; + struct berval *val; + BerVarray keys; + + /* Yes, this is necessary */ + val = UTF8bvnormalize( ((struct berval *)assertedValue), + NULL, LDAP_UTF8_APPROX, NULL ); + if( val == NULL || BER_BVISNULL( val ) ) { + keys = (struct berval *)ch_malloc( sizeof(struct berval) ); + BER_BVZERO( &keys[0] ); + *keysp = keys; + ber_bvfree( val ); + return LDAP_SUCCESS; + } + + /* Isolate how many words there are. There will be a key for each */ + for( count = 0,c = val->bv_val; *c; c++) { + len = strcspn(c, SLAPD_APPROX_DELIMITER); + if( len >= SLAPD_APPROX_WORDLEN ) count++; + c+= len; + if (*c == '\0') break; + *c = '\0'; + } + + /* Allocate storage for new keys */ + keys = (struct berval *)ch_malloc( (count + 1) * sizeof(struct berval) ); + + /* Get a phonetic copy of each word */ + for( c = val->bv_val, i = 0; i < count; c += len + 1 ) { + len = strlen(c); + if( len < SLAPD_APPROX_WORDLEN ) continue; + ber_str2bv( phonetic( c ), 0, 0, &keys[i] ); + i++; + } + + ber_bvfree( val ); + + BER_BVZERO( &keys[count] ); + *keysp = keys; + + return LDAP_SUCCESS; +} + +/* Remove all spaces and '-' characters, unless the result would be empty */ +static int +telephoneNumberNormalize( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *normalized, + void *ctx ) +{ + char *q; + ber_len_t c; + + assert( SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) != 0 ); + + /* Ensure q is big enough, though validator should have caught this */ + if ( BER_BVISEMPTY( val )) { + BER_BVZERO( normalized ); + return LDAP_INVALID_SYNTAX; + } + + q = normalized->bv_val = slap_sl_malloc( val->bv_len + 1, ctx ); + + for( c = 0; c < val->bv_len; c++ ) { + if ( ! ( ASCII_SPACE( val->bv_val[c] ) || val->bv_val[c] == '-' )) { + *q++ = val->bv_val[c]; + } + } + if ( q == normalized->bv_val ) { + *q++ = ' '; + } + *q = '\0'; + + normalized->bv_len = q - normalized->bv_val; + + return LDAP_SUCCESS; +} + +static int +postalAddressValidate( + Syntax *syntax, + struct berval *in ) +{ + struct berval bv = *in; + ber_len_t c; + + for ( c = 0; c < in->bv_len; c++ ) { + if ( in->bv_val[c] == '\\' ) { + c++; + if ( strncasecmp( &in->bv_val[c], "24", STRLENOF( "24" ) ) != 0 + && strncasecmp( &in->bv_val[c], "5C", STRLENOF( "5C" ) ) != 0 ) + { + return LDAP_INVALID_SYNTAX; + } + continue; + } + + if ( in->bv_val[c] == '$' ) { + bv.bv_len = &in->bv_val[c] - bv.bv_val; + if ( UTF8StringValidate( NULL, &bv ) != LDAP_SUCCESS ) { + return LDAP_INVALID_SYNTAX; + } + bv.bv_val = &in->bv_val[c] + 1; + } + } + + bv.bv_len = &in->bv_val[c] - bv.bv_val; + return UTF8StringValidate( NULL, &bv ); +} + +static int +postalAddressNormalize( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *normalized, + void *ctx ) +{ + BerVarray lines = NULL, nlines = NULL; + ber_len_t l, c; + int rc = LDAP_SUCCESS; + MatchingRule *xmr = NULL; + char *p; + + if ( SLAP_MR_ASSOCIATED( mr, slap_schema.si_mr_caseIgnoreListMatch ) ) { + xmr = slap_schema.si_mr_caseIgnoreMatch; + + } else { + xmr = slap_schema.si_mr_caseExactMatch; + } + + for ( l = 0, c = 0; c < val->bv_len; c++ ) { + if ( val->bv_val[c] == '$' ) { + l++; + } + } + + lines = slap_sl_calloc( sizeof( struct berval ), 2 * ( l + 2 ), ctx ); + nlines = &lines[l + 2]; + + lines[0].bv_val = val->bv_val; + for ( l = 0, c = 0; c < val->bv_len; c++ ) { + if ( val->bv_val[c] == '$' ) { + lines[l].bv_len = &val->bv_val[c] - lines[l].bv_val; + l++; + lines[l].bv_val = &val->bv_val[c + 1]; + } + } + lines[l].bv_len = &val->bv_val[c] - lines[l].bv_val; + + normalized->bv_len = c = l; + + for ( l = 0; l <= c; l++ ) { + /* NOTE: we directly normalize each line, + * without unescaping the values, since the special + * values '\24' ('$') and '\5C' ('\') are not affected + * by normalization */ + if ( !lines[l].bv_len ) { + nlines[l].bv_len = 0; + nlines[l].bv_val = NULL; + continue; + } + rc = UTF8StringNormalize( usage, NULL, xmr, &lines[l], &nlines[l], ctx ); + if ( rc != LDAP_SUCCESS ) { + rc = LDAP_INVALID_SYNTAX; + goto done; + } + + normalized->bv_len += nlines[l].bv_len; + } + + normalized->bv_val = slap_sl_malloc( normalized->bv_len + 1, ctx ); + + p = normalized->bv_val; + for ( l = 0; l <= c ; l++ ) { + p = lutil_strbvcopy( p, &nlines[l] ); + *p++ = '$'; + } + *--p = '\0'; + + assert( p == &normalized->bv_val[normalized->bv_len] ); + +done:; + if ( nlines != NULL ) { + for ( l = 0; !BER_BVISNULL( &nlines[ l ] ); l++ ) { + slap_sl_free( nlines[l].bv_val, ctx ); + } + + slap_sl_free( lines, ctx ); + } + + return rc; +} + +int +numericoidValidate( + Syntax *syntax, + struct berval *in ) +{ + struct berval val = *in; + + if( BER_BVISEMPTY( &val ) ) { + /* disallow empty strings */ + return LDAP_INVALID_SYNTAX; + } + + while( OID_LEADCHAR( val.bv_val[0] ) ) { + if ( val.bv_len == 1 ) { + return LDAP_SUCCESS; + } + + if ( val.bv_val[0] == '0' && !OID_SEPARATOR( val.bv_val[1] )) { + break; + } + + val.bv_val++; + val.bv_len--; + + while ( OID_LEADCHAR( val.bv_val[0] )) { + val.bv_val++; + val.bv_len--; + + if ( val.bv_len == 0 ) { + return LDAP_SUCCESS; + } + } + + if( !OID_SEPARATOR( val.bv_val[0] )) { + break; + } + + val.bv_val++; + val.bv_len--; + } + + return LDAP_INVALID_SYNTAX; +} + +static int +integerValidate( + Syntax *syntax, + struct berval *in ) +{ + ber_len_t i; + struct berval val = *in; + + if ( BER_BVISEMPTY( &val ) ) return LDAP_INVALID_SYNTAX; + + if ( val.bv_val[0] == '-' ) { + val.bv_len--; + val.bv_val++; + + if( BER_BVISEMPTY( &val ) ) { /* bare "-" */ + return LDAP_INVALID_SYNTAX; + } + + if( val.bv_val[0] == '0' ) { /* "-0" */ + return LDAP_INVALID_SYNTAX; + } + + } else if ( val.bv_val[0] == '0' ) { + if( val.bv_len > 1 ) { /* "0<more>" */ + return LDAP_INVALID_SYNTAX; + } + + return LDAP_SUCCESS; + } + + for( i=0; i < val.bv_len; i++ ) { + if( !ASCII_DIGIT(val.bv_val[i]) ) { + return LDAP_INVALID_SYNTAX; + } + } + + return LDAP_SUCCESS; +} + +static int +integerMatch( + int *matchp, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *value, + void *assertedValue ) +{ + struct berval *asserted = (struct berval *) assertedValue; + int vsign = 1, asign = 1; /* default sign = '+' */ + struct berval v, a; + int match; + + v = *value; + if( v.bv_val[0] == '-' ) { + vsign = -1; + v.bv_val++; + v.bv_len--; + } + + if( BER_BVISEMPTY( &v ) ) vsign = 0; + + a = *asserted; + if( a.bv_val[0] == '-' ) { + asign = -1; + a.bv_val++; + a.bv_len--; + } + + if( BER_BVISEMPTY( &a ) ) vsign = 0; + + match = vsign - asign; + if( match == 0 ) { + match = ( v.bv_len != a.bv_len + ? ( v.bv_len < a.bv_len ? -1 : 1 ) + : memcmp( v.bv_val, a.bv_val, v.bv_len )); + if( vsign < 0 ) match = -match; + } + + /* Ordering rule used in extensible match filter? */ + if ( (flags & SLAP_MR_EXT) && (mr->smr_usage & SLAP_MR_ORDERING) ) + match = (match >= 0); + + *matchp = match; + return LDAP_SUCCESS; +} + +/* 10**Chop < 256**Chopbytes and Chop > Chopbytes<<1 (for sign bit and itmp) */ +#define INDEX_INTLEN_CHOP 7 +#define INDEX_INTLEN_CHOPBYTES 3 + +static int +integerVal2Key( + struct berval *in, + struct berval *key, + struct berval *tmp, + void *ctx ) +{ + /* Integer index key format, designed for memcmp to collate correctly: + * if too large: one's complement sign*<approx exponent=chopped bytes>, + * two's complement value (sign-extended or chopped as needed), + * however in first byte above, the top <number of exponent-bytes + 1> + * bits are the inverse sign and next bit is the sign as delimiter. + */ + ber_slen_t k = index_intlen_strlen; + ber_len_t chop = 0; + unsigned signmask = ~0x7fU; + unsigned char lenbuf[sizeof(k) + 2], *lenp, neg = 0xff; + struct berval val = *in, itmp = *tmp; + + if ( val.bv_val[0] != '-' ) { + neg = 0; + --k; + } + + /* Chop least significant digits, increase length instead */ + if ( val.bv_len > (ber_len_t) k ) { + chop = (val.bv_len-k+2)/INDEX_INTLEN_CHOP; /* 2 fewer digits */ + val.bv_len -= chop * INDEX_INTLEN_CHOP; /* #digits chopped */ + chop *= INDEX_INTLEN_CHOPBYTES; /* #bytes added */ + } + + if ( lutil_str2bin( &val, &itmp, ctx )) { + return LDAP_INVALID_SYNTAX; + } + + /* Omit leading sign byte */ + if ( itmp.bv_val[0] == neg ) { + itmp.bv_val++; + itmp.bv_len--; + } + + k = (ber_slen_t) index_intlen - (ber_slen_t) (itmp.bv_len + chop); + if ( k > 0 ) { + assert( chop == 0 ); + memset( key->bv_val, neg, k ); /* sign-extend */ + } else if ( k != 0 || ((itmp.bv_val[0] ^ neg) & 0xc0) ) { + /* Got exponent -k, or no room for 2 sign bits */ + lenp = lenbuf + sizeof(lenbuf); + chop = - (ber_len_t) k; + do { + *--lenp = ((unsigned char) chop & 0xff) ^ neg; + signmask >>= 1; + } while ( (chop >>= 8) != 0 || (signmask >> 1) & (*lenp ^ neg) ); + /* With n bytes in lenbuf, the top n+1 bits of (signmask&0xff) + * are 1, and the top n+2 bits of lenp[0] are the sign bit. */ + k = (lenbuf + sizeof(lenbuf)) - lenp; + if ( k > (ber_slen_t) index_intlen ) + k = index_intlen; + memcpy( key->bv_val, lenp, k ); + itmp.bv_len = index_intlen - k; + } + memcpy( key->bv_val + k, itmp.bv_val, itmp.bv_len ); + key->bv_val[0] ^= (unsigned char) signmask & 0xff; /* invert sign */ + return 0; +} + +/* Index generation function: Ordered index */ +static int +integerIndexer( + slap_mask_t use, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *prefix, + BerVarray values, + BerVarray *keysp, + void *ctx ) +{ + char ibuf[64]; + struct berval itmp; + BerVarray keys; + ber_len_t vlen; + int i, rc; + unsigned maxstrlen = index_intlen_strlen + INDEX_INTLEN_CHOP-1; + + /* count the values and find max needed length */ + vlen = 0; + for( i = 0; !BER_BVISNULL( &values[i] ); i++ ) { + if ( vlen < values[i].bv_len ) + vlen = values[i].bv_len; + } + if ( vlen > maxstrlen ) + vlen = maxstrlen; + + /* we should have at least one value at this point */ + assert( i > 0 ); + + keys = slap_sl_malloc( sizeof( struct berval ) * (i+1), ctx ); + for ( i = 0; !BER_BVISNULL( &values[i] ); i++ ) { + keys[i].bv_len = index_intlen; + keys[i].bv_val = slap_sl_malloc( index_intlen, ctx ); + } + keys[i].bv_len = 0; + keys[i].bv_val = NULL; + + if ( vlen > sizeof(ibuf) ) { + itmp.bv_val = slap_sl_malloc( vlen, ctx ); + } else { + itmp.bv_val = ibuf; + } + itmp.bv_len = sizeof(ibuf); + + for ( i=0; !BER_BVISNULL( &values[i] ); i++ ) { + if ( itmp.bv_val != ibuf ) { + itmp.bv_len = values[i].bv_len; + if ( itmp.bv_len <= sizeof(ibuf) ) + itmp.bv_len = sizeof(ibuf); + else if ( itmp.bv_len > maxstrlen ) + itmp.bv_len = maxstrlen; + } + rc = integerVal2Key( &values[i], &keys[i], &itmp, ctx ); + if ( rc ) { + slap_sl_free( keys, ctx ); + goto func_leave; + } + } + *keysp = keys; +func_leave: + if ( itmp.bv_val != ibuf ) { + slap_sl_free( itmp.bv_val, ctx ); + } + return rc; +} + +/* Index generation function: Ordered index */ +static int +integerFilter( + slap_mask_t use, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *prefix, + void * assertedValue, + BerVarray *keysp, + void *ctx ) +{ + char ibuf[64]; + struct berval iv; + BerVarray keys; + struct berval *value; + int rc; + + value = (struct berval *) assertedValue; + + keys = slap_sl_malloc( sizeof( struct berval ) * 2, ctx ); + + keys[0].bv_len = index_intlen; + keys[0].bv_val = slap_sl_malloc( index_intlen, ctx ); + keys[1].bv_len = 0; + keys[1].bv_val = NULL; + + iv.bv_len = value->bv_len < index_intlen_strlen + INDEX_INTLEN_CHOP-1 + ? value->bv_len : index_intlen_strlen + INDEX_INTLEN_CHOP-1; + if ( iv.bv_len > (int) sizeof(ibuf) ) { + iv.bv_val = slap_sl_malloc( iv.bv_len, ctx ); + } else { + iv.bv_val = ibuf; + iv.bv_len = sizeof(ibuf); + } + + rc = integerVal2Key( value, keys, &iv, ctx ); + + if ( iv.bv_val != ibuf ) { + slap_sl_free( iv.bv_val, ctx ); + } + + if ( rc == 0 ) + *keysp = keys; + else + slap_sl_free( keys, ctx ); + + return rc; +} + +static int +countryStringValidate( + Syntax *syntax, + struct berval *val ) +{ + if( val->bv_len != 2 ) return LDAP_INVALID_SYNTAX; + + if( !SLAP_PRINTABLE(val->bv_val[0]) ) { + return LDAP_INVALID_SYNTAX; + } + if( !SLAP_PRINTABLE(val->bv_val[1]) ) { + return LDAP_INVALID_SYNTAX; + } + + return LDAP_SUCCESS; +} + +static int +printableStringValidate( + Syntax *syntax, + struct berval *val ) +{ + ber_len_t i; + + if( BER_BVISEMPTY( val ) ) return LDAP_INVALID_SYNTAX; + + for(i=0; i < val->bv_len; i++) { + if( !SLAP_PRINTABLE(val->bv_val[i]) ) { + return LDAP_INVALID_SYNTAX; + } + } + + return LDAP_SUCCESS; +} + +static int +printablesStringValidate( + Syntax *syntax, + struct berval *val ) +{ + ber_len_t i, len; + + if( BER_BVISEMPTY( val ) ) return LDAP_INVALID_SYNTAX; + + for(i=0,len=0; i < val->bv_len; i++) { + int c = val->bv_val[i]; + + if( c == '$' ) { + if( len == 0 ) { + return LDAP_INVALID_SYNTAX; + } + len = 0; + + } else if ( SLAP_PRINTABLE(c) ) { + len++; + } else { + return LDAP_INVALID_SYNTAX; + } + } + + if( len == 0 ) { + return LDAP_INVALID_SYNTAX; + } + + return LDAP_SUCCESS; +} + +static int +IA5StringValidate( + Syntax *syntax, + struct berval *val ) +{ + ber_len_t i; + + for(i=0; i < val->bv_len; i++) { + if( !LDAP_ASCII(val->bv_val[i]) ) { + return LDAP_INVALID_SYNTAX; + } + } + + return LDAP_SUCCESS; +} + +static int +IA5StringNormalize( + slap_mask_t use, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *normalized, + void *ctx ) +{ + char *p, *q, *end; + int casefold = !SLAP_MR_ASSOCIATED( mr, + slap_schema.si_mr_caseExactIA5Match ); + + assert( SLAP_MR_IS_VALUE_OF_SYNTAX( use ) != 0 ); + + p = val->bv_val; + end = val->bv_val + val->bv_len; + + /* Ignore initial whitespace */ + while ( p < end && ASCII_SPACE( *p ) ) p++; + + normalized->bv_len = p < end ? (val->bv_len - ( p - val->bv_val )) : 0; + normalized->bv_val = slap_sl_malloc( normalized->bv_len + 1, ctx ); + AC_MEMCPY( normalized->bv_val, p, normalized->bv_len ); + normalized->bv_val[normalized->bv_len] = '\0'; + + p = q = normalized->bv_val; + + while ( *p ) { + if ( ASCII_SPACE( *p ) ) { + *q++ = *p++; + + /* Ignore the extra whitespace */ + while ( ASCII_SPACE( *p ) ) { + p++; + } + + } else if ( casefold ) { + /* Most IA5 rules require casefolding */ + *q++ = TOLOWER(*p); p++; + + } else { + *q++ = *p++; + } + } + + assert( normalized->bv_val <= p ); + assert( q <= p ); + + /* + * If the string ended in space, backup the pointer one + * position. One is enough because the above loop collapsed + * all whitespace to a single space. + */ + if ( q > normalized->bv_val && ASCII_SPACE( q[-1] ) ) --q; + + /* null terminate */ + *q = '\0'; + + normalized->bv_len = q - normalized->bv_val; + + return LDAP_SUCCESS; +} + +static int +UUIDValidate( + Syntax *syntax, + struct berval *in ) +{ + int i; + if( in->bv_len != 36 ) { + return LDAP_INVALID_SYNTAX; + } + + for( i=0; i<36; i++ ) { + switch(i) { + case 8: + case 13: + case 18: + case 23: + if( in->bv_val[i] != '-' ) { + return LDAP_INVALID_SYNTAX; + } + break; + default: + if( !ASCII_HEX( in->bv_val[i]) ) { + return LDAP_INVALID_SYNTAX; + } + } + } + + return LDAP_SUCCESS; +} + +static int +UUIDPretty( + Syntax *syntax, + struct berval *in, + struct berval *out, + void *ctx ) +{ + int i; + int rc=LDAP_INVALID_SYNTAX; + + assert( in != NULL ); + assert( out != NULL ); + + if( in->bv_len != 36 ) return LDAP_INVALID_SYNTAX; + + out->bv_len = 36; + out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx ); + + for( i=0; i<36; i++ ) { + switch(i) { + case 8: + case 13: + case 18: + case 23: + if( in->bv_val[i] != '-' ) { + goto handle_error; + } + out->bv_val[i] = '-'; + break; + + default: + if( !ASCII_HEX( in->bv_val[i]) ) { + goto handle_error; + } + out->bv_val[i] = TOLOWER( in->bv_val[i] ); + } + } + + rc = LDAP_SUCCESS; + out->bv_val[ out->bv_len ] = '\0'; + + if( 0 ) { +handle_error: + slap_sl_free( out->bv_val, ctx ); + out->bv_val = NULL; + } + + return rc; +} + +int +UUIDNormalize( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *normalized, + void *ctx ) +{ + unsigned char octet = '\0'; + int i; + int j; + + if ( SLAP_MR_IS_DENORMALIZE( usage ) ) { + /* NOTE: must be a normalized UUID */ + if( val->bv_len != 16 ) + return LDAP_INVALID_SYNTAX; + + normalized->bv_val = slap_sl_malloc( LDAP_LUTIL_UUIDSTR_BUFSIZE, ctx ); + normalized->bv_len = lutil_uuidstr_from_normalized( val->bv_val, + val->bv_len, normalized->bv_val, LDAP_LUTIL_UUIDSTR_BUFSIZE ); + if( normalized->bv_len != STRLENOF( "BADBADBA-DBAD-0123-4567-BADBADBADBAD" ) ) + return LDAP_INVALID_SYNTAX; + + return LDAP_SUCCESS; + } + + normalized->bv_len = 16; + normalized->bv_val = slap_sl_malloc( normalized->bv_len + 1, ctx ); + + for( i=0, j=0; i<36; i++ ) { + unsigned char nibble; + if( val->bv_val[i] == '-' ) { + continue; + + } else if( ASCII_DIGIT( val->bv_val[i] ) ) { + nibble = val->bv_val[i] - '0'; + + } else if( ASCII_HEXLOWER( val->bv_val[i] ) ) { + nibble = val->bv_val[i] - ('a'-10); + + } else if( ASCII_HEXUPPER( val->bv_val[i] ) ) { + nibble = val->bv_val[i] - ('A'-10); + + } else { + slap_sl_free( normalized->bv_val, ctx ); + BER_BVZERO( normalized ); + return LDAP_INVALID_SYNTAX; + } + + if( j & 1 ) { + octet |= nibble; + normalized->bv_val[j>>1] = octet; + } else { + octet = nibble << 4; + } + j++; + } + + normalized->bv_val[normalized->bv_len] = 0; + return LDAP_SUCCESS; +} + + + +int +numericStringValidate( + Syntax *syntax, + struct berval *in ) +{ + ber_len_t i; + + if( BER_BVISEMPTY( in ) ) return LDAP_INVALID_SYNTAX; + + for(i=0; i < in->bv_len; i++) { + if( !SLAP_NUMERIC(in->bv_val[i]) ) { + return LDAP_INVALID_SYNTAX; + } + } + + return LDAP_SUCCESS; +} + +static int +numericStringNormalize( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *normalized, + void *ctx ) +{ + /* removal all spaces */ + char *p, *q; + + assert( !BER_BVISEMPTY( val ) ); + + normalized->bv_val = slap_sl_malloc( val->bv_len + 1, ctx ); + + p = val->bv_val; + q = normalized->bv_val; + + while ( *p ) { + if ( ASCII_SPACE( *p ) ) { + /* Ignore whitespace */ + p++; + } else { + *q++ = *p++; + } + } + + /* we should have copied no more than is in val */ + assert( (q - normalized->bv_val) <= (p - val->bv_val) ); + + /* null terminate */ + *q = '\0'; + + normalized->bv_len = q - normalized->bv_val; + + if( BER_BVISEMPTY( normalized ) ) { + normalized->bv_val = slap_sl_realloc( normalized->bv_val, 2, ctx ); + normalized->bv_val[0] = ' '; + normalized->bv_val[1] = '\0'; + normalized->bv_len = 1; + } + + return LDAP_SUCCESS; +} + +/* + * Integer conversion macros that will use the largest available + * type. + */ +#if defined(HAVE_STRTOLL) && defined(HAVE_LONG_LONG) +# define SLAP_STRTOL(n,e,b) strtoll(n,e,b) +# define SLAP_LONG long long +#else +# define SLAP_STRTOL(n,e,b) strtol(n,e,b) +# define SLAP_LONG long +#endif /* HAVE_STRTOLL ... */ + +static int +integerBitAndMatch( + int *matchp, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *value, + void *assertedValue ) +{ + SLAP_LONG lValue, lAssertedValue; + + errno = 0; + /* safe to assume integers are NUL terminated? */ + lValue = SLAP_STRTOL(value->bv_val, NULL, 10); + if( errno == ERANGE ) + { + return LDAP_CONSTRAINT_VIOLATION; + } + + lAssertedValue = SLAP_STRTOL(((struct berval *)assertedValue)->bv_val, + NULL, 10); + if( errno == ERANGE ) + { + return LDAP_CONSTRAINT_VIOLATION; + } + + *matchp = ((lValue & lAssertedValue) == lAssertedValue) ? 0 : 1; + return LDAP_SUCCESS; +} + +static int +integerBitOrMatch( + int *matchp, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *value, + void *assertedValue ) +{ + SLAP_LONG lValue, lAssertedValue; + + errno = 0; + /* safe to assume integers are NUL terminated? */ + lValue = SLAP_STRTOL(value->bv_val, NULL, 10); + if( errno == ERANGE ) + { + return LDAP_CONSTRAINT_VIOLATION; + } + + lAssertedValue = SLAP_STRTOL( ((struct berval *)assertedValue)->bv_val, + NULL, 10); + if( errno == ERANGE ) + { + return LDAP_CONSTRAINT_VIOLATION; + } + + *matchp = ((lValue & lAssertedValue) != 0) ? 0 : -1; + return LDAP_SUCCESS; +} + +static int +checkNum( struct berval *in, struct berval *out ) +{ + /* parse serialNumber */ + ber_len_t neg = 0, extra = 0; + char first = '\0'; + + out->bv_val = in->bv_val; + out->bv_len = 0; + + if ( out->bv_val[0] == '-' ) { + neg++; + out->bv_len++; + } + + if ( strncasecmp( out->bv_val, "0x", STRLENOF("0x") ) == 0 ) { + first = out->bv_val[2]; + extra = 2; + + out->bv_len += STRLENOF("0x"); + for ( ; out->bv_len < in->bv_len; out->bv_len++ ) { + if ( !ASCII_HEX( out->bv_val[out->bv_len] ) ) break; + } + + } else if ( out->bv_val[0] == '\'' ) { + first = out->bv_val[1]; + extra = 3; + + out->bv_len += STRLENOF("'"); + + for ( ; out->bv_len < in->bv_len; out->bv_len++ ) { + if ( !ASCII_HEX( out->bv_val[out->bv_len] ) ) break; + } + if ( strncmp( &out->bv_val[out->bv_len], "'H", STRLENOF("'H") ) != 0 ) { + return -1; + } + out->bv_len += STRLENOF("'H"); + + } else { + first = out->bv_val[0]; + for ( ; out->bv_len < in->bv_len; out->bv_len++ ) { + if ( !ASCII_DIGIT( out->bv_val[out->bv_len] ) ) break; + } + } + + if ( !( out->bv_len > neg ) ) { + return -1; + } + + if ( ( out->bv_len > extra + 1 + neg ) && ( first == '0' ) ) { + return -1; + } + + return 0; +} + +static int +serialNumberAndIssuerCheck( + struct berval *in, + struct berval *sn, + struct berval *is, + void *ctx ) +{ + ber_len_t n; + + if( in->bv_len < 3 ) return LDAP_INVALID_SYNTAX; + + if( in->bv_val[0] != '{' || in->bv_val[in->bv_len-1] != '}' ) { + /* Parse old format */ + is->bv_val = ber_bvchr( in, '$' ); + if( BER_BVISNULL( is ) ) return LDAP_INVALID_SYNTAX; + + sn->bv_val = in->bv_val; + sn->bv_len = is->bv_val - in->bv_val; + + is->bv_val++; + is->bv_len = in->bv_len - (sn->bv_len + 1); + + /* eat leading zeros */ + for( n=0; n < (sn->bv_len-1); n++ ) { + if( sn->bv_val[n] != '0' ) break; + } + sn->bv_val += n; + sn->bv_len -= n; + + for( n=0; n < sn->bv_len; n++ ) { + if( !ASCII_DIGIT(sn->bv_val[n]) ) return LDAP_INVALID_SYNTAX; + } + + } else { + /* Parse GSER format */ + enum { + HAVE_NONE = 0x0, + HAVE_ISSUER = 0x1, + HAVE_SN = 0x2, + HAVE_ALL = ( HAVE_ISSUER | HAVE_SN ) + } have = HAVE_NONE; + + int numdquotes = 0, gotquote; + struct berval x = *in; + struct berval ni; + x.bv_val++; + x.bv_len -= 2; + + do { + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + /* should be at issuer or serialNumber NamedValue */ + if ( strncasecmp( x.bv_val, "issuer", STRLENOF("issuer") ) == 0 ) { + if ( have & HAVE_ISSUER ) return LDAP_INVALID_SYNTAX; + + /* parse issuer */ + x.bv_val += STRLENOF("issuer"); + x.bv_len -= STRLENOF("issuer"); + + if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + /* For backward compatibility, this part is optional */ + if ( strncasecmp( x.bv_val, "rdnSequence:", STRLENOF("rdnSequence:") ) == 0 ) { + x.bv_val += STRLENOF("rdnSequence:"); + x.bv_len -= STRLENOF("rdnSequence:"); + } + + if ( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + is->bv_val = x.bv_val; + is->bv_len = 0; + + for ( gotquote=0; is->bv_len < x.bv_len; ) { + if ( is->bv_val[is->bv_len] != '"' ) { + is->bv_len++; + continue; + } + gotquote = 1; + if ( is->bv_val[is->bv_len+1] == '"' ) { + /* double dquote */ + numdquotes++; + is->bv_len += 2; + continue; + } + break; + } + if ( !gotquote ) return LDAP_INVALID_SYNTAX; + + x.bv_val += is->bv_len + 1; + x.bv_len -= is->bv_len + 1; + + have |= HAVE_ISSUER; + + } else if ( strncasecmp( x.bv_val, "serialNumber", STRLENOF("serialNumber") ) == 0 ) + { + if ( have & HAVE_SN ) return LDAP_INVALID_SYNTAX; + + /* parse serialNumber */ + x.bv_val += STRLENOF("serialNumber"); + x.bv_len -= STRLENOF("serialNumber"); + + if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( checkNum( &x, sn ) ) { + return LDAP_INVALID_SYNTAX; + } + + x.bv_val += sn->bv_len; + x.bv_len -= sn->bv_len; + + have |= HAVE_SN; + + } else { + return LDAP_INVALID_SYNTAX; + } + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( have == HAVE_ALL ) { + break; + } + + if ( x.bv_val[0] != ',' ) { + return LDAP_INVALID_SYNTAX; + } + + x.bv_val++; + x.bv_len--; + } while ( 1 ); + + /* should have no characters left... */ + if ( x.bv_len ) return LDAP_INVALID_SYNTAX; + + if ( numdquotes == 0 ) { + ber_dupbv_x( &ni, is, ctx ); + + } else { + ber_len_t src, dst; + + ni.bv_len = is->bv_len - numdquotes; + ni.bv_val = ber_memalloc_x( ni.bv_len + 1, ctx ); + for ( src = 0, dst = 0; src < is->bv_len; src++, dst++ ) { + if ( is->bv_val[src] == '"' ) { + src++; + } + ni.bv_val[dst] = is->bv_val[src]; + } + ni.bv_val[dst] = '\0'; + } + + *is = ni; + } + + return 0; +} + +static int +serialNumberAndIssuerValidate( + Syntax *syntax, + struct berval *in ) +{ + int rc; + struct berval sn, i; + + Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerValidate: <%s>\n", + in->bv_val, 0, 0 ); + + rc = serialNumberAndIssuerCheck( in, &sn, &i, NULL ); + if ( rc ) { + goto done; + } + + /* validate DN -- doesn't handle double dquote */ + rc = dnValidate( NULL, &i ); + if ( rc ) { + rc = LDAP_INVALID_SYNTAX; + } + + if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) { + slap_sl_free( i.bv_val, NULL ); + } + + Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerValidate: <%s> err=%d\n", + in->bv_val, rc, 0 ); + +done:; + return rc; +} + +static int +serialNumberAndIssuerPretty( + Syntax *syntax, + struct berval *in, + struct berval *out, + void *ctx ) +{ + int rc; + struct berval sn, i, ni = BER_BVNULL; + char *p; + + assert( in != NULL ); + assert( out != NULL ); + + BER_BVZERO( out ); + + Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerPretty: <%s>\n", + in->bv_val, 0, 0 ); + + rc = serialNumberAndIssuerCheck( in, &sn, &i, ctx ); + if ( rc ) { + goto done; + } + + rc = dnPretty( syntax, &i, &ni, ctx ); + + if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) { + slap_sl_free( i.bv_val, ctx ); + } + + if ( rc ) { + rc = LDAP_INVALID_SYNTAX; + goto done; + } + + /* make room from sn + "$" */ + out->bv_len = STRLENOF("{ serialNumber , issuer rdnSequence:\"\" }") + + sn.bv_len + ni.bv_len; + out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx ); + + if ( out->bv_val == NULL ) { + out->bv_len = 0; + rc = LDAP_OTHER; + goto done; + } + + p = out->bv_val; + p = lutil_strcopy( p, "{ serialNumber " /*}*/ ); + p = lutil_strbvcopy( p, &sn ); + p = lutil_strcopy( p, ", issuer rdnSequence:\"" ); + p = lutil_strbvcopy( p, &ni ); + p = lutil_strcopy( p, /*{*/ "\" }" ); + + assert( p == &out->bv_val[out->bv_len] ); + +done:; + Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerPretty: <%s> => <%s>\n", + in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)", 0 ); + + slap_sl_free( ni.bv_val, ctx ); + + return LDAP_SUCCESS; +} + +static int +slap_bin2hex( + struct berval *in, + struct berval *out, + void *ctx ) + +{ + /* Use hex format. '123456789abcdef'H */ + unsigned char *ptr, zero = '\0'; + char *sptr; + int first; + ber_len_t i, len, nlen; + + assert( in != NULL ); + assert( !BER_BVISNULL( in ) ); + assert( out != NULL ); + assert( !BER_BVISNULL( out ) ); + + ptr = (unsigned char *)in->bv_val; + len = in->bv_len; + + /* Check for minimal encodings */ + if ( len > 1 ) { + if ( ptr[0] & 0x80 ) { + if ( ( ptr[0] == 0xff ) && ( ptr[1] & 0x80 ) ) { + return -1; + } + + } else if ( ptr[0] == 0 ) { + if ( !( ptr[1] & 0x80 ) ) { + return -1; + } + len--; + ptr++; + } + + } else if ( len == 0 ) { + /* FIXME: this should not be possible, + * since a value of zero would have length 1 */ + len = 1; + ptr = &zero; + } + + first = !( ptr[0] & 0xf0U ); + nlen = len * 2 - first + STRLENOF("''H"); /* quotes, H */ + if ( nlen >= out->bv_len ) { + out->bv_val = slap_sl_malloc( nlen + 1, ctx ); + } + sptr = out->bv_val; + *sptr++ = '\''; + i = 0; + if ( first ) { + sprintf( sptr, "%01X", ( ptr[0] & 0x0fU ) ); + sptr++; + i = 1; + } + for ( ; i < len; i++ ) { + sprintf( sptr, "%02X", ptr[i] ); + sptr += 2; + } + *sptr++ = '\''; + *sptr++ = 'H'; + *sptr = '\0'; + + assert( sptr == &out->bv_val[nlen] ); + + out->bv_len = nlen; + + return 0; +} + +#define SLAP_SN_BUFLEN (64) + +/* + * This routine is called by certificateExactNormalize when + * certificateExactNormalize receives a search string instead of + * a certificate. This routine checks if the search value is valid + * and then returns the normalized value + */ +static int +serialNumberAndIssuerNormalize( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *in, + struct berval *out, + void *ctx ) +{ + struct berval sn, sn2, sn3, i, ni; + char sbuf2[SLAP_SN_BUFLEN]; + char sbuf3[SLAP_SN_BUFLEN]; + char *p; + int rc; + + assert( in != NULL ); + assert( out != NULL ); + + Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerNormalize: <%s>\n", + in->bv_val, 0, 0 ); + + rc = serialNumberAndIssuerCheck( in, &sn, &i, ctx ); + if ( rc ) { + return rc; + } + + rc = dnNormalize( usage, syntax, mr, &i, &ni, ctx ); + + if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) { + slap_sl_free( i.bv_val, ctx ); + } + + if ( rc ) { + return LDAP_INVALID_SYNTAX; + } + + /* Convert sn to canonical hex */ + sn2.bv_val = sbuf2; + if ( sn.bv_len > sizeof( sbuf2 ) ) { + sn2.bv_val = slap_sl_malloc( sn.bv_len, ctx ); + } + sn2.bv_len = sn.bv_len; + sn3.bv_val = sbuf3; + sn3.bv_len = sizeof(sbuf3); + if ( lutil_str2bin( &sn, &sn2, ctx ) || slap_bin2hex( &sn2, &sn3, ctx ) ) { + rc = LDAP_INVALID_SYNTAX; + goto func_leave; + } + + out->bv_len = STRLENOF( "{ serialNumber , issuer rdnSequence:\"\" }" ) + + sn3.bv_len + ni.bv_len; + out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx ); + if ( out->bv_val == NULL ) { + out->bv_len = 0; + rc = LDAP_OTHER; + goto func_leave; + } + + p = out->bv_val; + + p = lutil_strcopy( p, "{ serialNumber " /*}*/ ); + p = lutil_strbvcopy( p, &sn3 ); + p = lutil_strcopy( p, ", issuer rdnSequence:\"" ); + p = lutil_strbvcopy( p, &ni ); + p = lutil_strcopy( p, /*{*/ "\" }" ); + + assert( p == &out->bv_val[out->bv_len] ); + +func_leave: + Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerNormalize: <%s> => <%s>\n", + in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)", 0 ); + + if ( sn2.bv_val != sbuf2 ) { + slap_sl_free( sn2.bv_val, ctx ); + } + + if ( sn3.bv_val != sbuf3 ) { + slap_sl_free( sn3.bv_val, ctx ); + } + + slap_sl_free( ni.bv_val, ctx ); + + return rc; +} + +static int +certificateExactNormalize( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *normalized, + void *ctx ) +{ + BerElementBuffer berbuf; + BerElement *ber = (BerElement *)&berbuf; + ber_tag_t tag; + ber_len_t len; + ber_int_t i; + char serialbuf2[SLAP_SN_BUFLEN]; + struct berval sn, sn2 = BER_BVNULL; + struct berval issuer_dn = BER_BVNULL, bvdn; + char *p; + int rc = LDAP_INVALID_SYNTAX; + + assert( val != NULL ); + + Debug( LDAP_DEBUG_TRACE, ">>> certificateExactNormalize: <%p, %lu>\n", + val->bv_val, val->bv_len, 0 ); + + if ( BER_BVISEMPTY( val ) ) goto done; + + if ( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX(usage) ) { + return serialNumberAndIssuerNormalize( 0, NULL, NULL, val, normalized, ctx ); + } + + assert( SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX(usage) != 0 ); + + ber_init2( ber, val, LBER_USE_DER ); + tag = ber_skip_tag( ber, &len ); /* Signed Sequence */ + tag = ber_skip_tag( ber, &len ); /* Sequence */ + tag = ber_peek_tag( ber, &len ); /* Optional version? */ + if ( tag == SLAP_X509_OPT_C_VERSION ) { + tag = ber_skip_tag( ber, &len ); + tag = ber_get_int( ber, &i ); /* version */ + } + + /* NOTE: move the test here from certificateValidate, + * so that we can validate certs with serial longer + * than sizeof(ber_int_t) */ + tag = ber_skip_tag( ber, &len ); /* serial */ + sn.bv_len = len; + sn.bv_val = (char *)ber->ber_ptr; + sn2.bv_val = serialbuf2; + sn2.bv_len = sizeof(serialbuf2); + if ( slap_bin2hex( &sn, &sn2, ctx ) ) { + rc = LDAP_INVALID_SYNTAX; + goto done; + } + ber_skip_data( ber, len ); + + tag = ber_skip_tag( ber, &len ); /* SignatureAlg */ + ber_skip_data( ber, len ); + tag = ber_peek_tag( ber, &len ); /* IssuerDN */ + if ( len ) { + len = ber_ptrlen( ber ); + bvdn.bv_val = val->bv_val + len; + bvdn.bv_len = val->bv_len - len; + + rc = dnX509normalize( &bvdn, &issuer_dn ); + if ( rc != LDAP_SUCCESS ) { + rc = LDAP_INVALID_SYNTAX; + goto done; + } + } + + normalized->bv_len = STRLENOF( "{ serialNumber , issuer rdnSequence:\"\" }" ) + + sn2.bv_len + issuer_dn.bv_len; + normalized->bv_val = ch_malloc( normalized->bv_len + 1 ); + + p = normalized->bv_val; + + p = lutil_strcopy( p, "{ serialNumber " /*}*/ ); + p = lutil_strbvcopy( p, &sn2 ); + p = lutil_strcopy( p, ", issuer rdnSequence:\"" ); + p = lutil_strbvcopy( p, &issuer_dn ); + p = lutil_strcopy( p, /*{*/ "\" }" ); + + rc = LDAP_SUCCESS; + +done: + Debug( LDAP_DEBUG_TRACE, "<<< certificateExactNormalize: <%p, %lu> => <%s>\n", + val->bv_val, val->bv_len, rc == LDAP_SUCCESS ? normalized->bv_val : "(err)" ); + + if ( issuer_dn.bv_val ) ber_memfree( issuer_dn.bv_val ); + if ( sn2.bv_val != serialbuf2 ) ber_memfree_x( sn2.bv_val, ctx ); + + return rc; +} + +/* X.509 PKI certificateList stuff */ +static int +checkTime( struct berval *in, struct berval *out ) +{ + int rc; + ber_len_t i; + char buf[STRLENOF("YYYYmmddHHMMSSZ") + 1]; + struct berval bv; + + assert( in != NULL ); + assert( !BER_BVISNULL( in ) ); + assert( !BER_BVISEMPTY( in ) ); + + if ( in->bv_len < STRLENOF( "YYmmddHHMMSSZ" ) ) { + return -1; + } + + if ( out != NULL ) { + assert( !BER_BVISNULL( out ) ); + assert( out->bv_len >= sizeof( buf ) ); + bv.bv_val = out->bv_val; + + } else { + bv.bv_val = buf; + } + + for ( i = 0; i < STRLENOF( "YYYYmmddHHMMSS" ); i++ ) { + if ( !ASCII_DIGIT( in->bv_val[i] ) ) break; + } + + if ( in->bv_val[i] != 'Z' ) { + return -1; + } + i++; + + if ( i != in->bv_len ) { + return -1; + } + + if ( i == STRLENOF( "YYYYmmddHHMMSSZ" ) ) { + lutil_strncopy( bv.bv_val, in->bv_val, i ); + bv.bv_len = i; + + } else if ( i == STRLENOF( "YYmmddHHMMSSZ" ) ) { + char *p = bv.bv_val; + if ( in->bv_val[0] < '7' ) { + p = lutil_strcopy( p, "20" ); + + } else { + p = lutil_strcopy( p, "19" ); + } + lutil_strncopy( p, in->bv_val, i ); + bv.bv_len = 2 + i; + + } else { + return -1; + } + + rc = generalizedTimeValidate( NULL, &bv ); + if ( rc == LDAP_SUCCESS && out != NULL ) { + if ( out->bv_len > bv.bv_len ) { + out->bv_val[ bv.bv_len ] = '\0'; + } + out->bv_len = bv.bv_len; + } + + return rc != LDAP_SUCCESS; +} + +static int +issuerAndThisUpdateCheck( + struct berval *in, + struct berval *is, + struct berval *tu, + void *ctx ) +{ + int numdquotes = 0; + struct berval x = *in; + struct berval ni = BER_BVNULL; + /* Parse GSER format */ + enum { + HAVE_NONE = 0x0, + HAVE_ISSUER = 0x1, + HAVE_THISUPDATE = 0x2, + HAVE_ALL = ( HAVE_ISSUER | HAVE_THISUPDATE ) + } have = HAVE_NONE; + + + if ( in->bv_len < STRLENOF( "{issuer \"\",thisUpdate \"YYMMDDhhmmssZ\"}" ) ) return LDAP_INVALID_SYNTAX; + + if ( in->bv_val[0] != '{' || in->bv_val[in->bv_len-1] != '}' ) { + return LDAP_INVALID_SYNTAX; + } + + x.bv_val++; + x.bv_len -= STRLENOF("{}"); + + do { + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + /* should be at issuer or thisUpdate */ + if ( strncasecmp( x.bv_val, "issuer", STRLENOF("issuer") ) == 0 ) { + if ( have & HAVE_ISSUER ) return LDAP_INVALID_SYNTAX; + + /* parse issuer */ + x.bv_val += STRLENOF("issuer"); + x.bv_len -= STRLENOF("issuer"); + + if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + /* For backward compatibility, this part is optional */ + if ( strncasecmp( x.bv_val, "rdnSequence:", STRLENOF("rdnSequence:") ) != 0 ) { + return LDAP_INVALID_SYNTAX; + } + x.bv_val += STRLENOF("rdnSequence:"); + x.bv_len -= STRLENOF("rdnSequence:"); + + if ( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + is->bv_val = x.bv_val; + is->bv_len = 0; + + for ( ; is->bv_len < x.bv_len; ) { + if ( is->bv_val[is->bv_len] != '"' ) { + is->bv_len++; + continue; + } + if ( is->bv_val[is->bv_len+1] == '"' ) { + /* double dquote */ + numdquotes++; + is->bv_len += 2; + continue; + } + break; + } + x.bv_val += is->bv_len + 1; + x.bv_len -= is->bv_len + 1; + + have |= HAVE_ISSUER; + + } else if ( strncasecmp( x.bv_val, "thisUpdate", STRLENOF("thisUpdate") ) == 0 ) + { + if ( have & HAVE_THISUPDATE ) return LDAP_INVALID_SYNTAX; + + /* parse thisUpdate */ + x.bv_val += STRLENOF("thisUpdate"); + x.bv_len -= STRLENOF("thisUpdate"); + + if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( !x.bv_len || x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + tu->bv_val = x.bv_val; + tu->bv_len = 0; + + for ( ; tu->bv_len < x.bv_len; tu->bv_len++ ) { + if ( tu->bv_val[tu->bv_len] == '"' ) { + break; + } + } + x.bv_val += tu->bv_len + 1; + x.bv_len -= tu->bv_len + 1; + + have |= HAVE_THISUPDATE; + + } else { + return LDAP_INVALID_SYNTAX; + } + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( have == HAVE_ALL ) { + break; + } + + if ( x.bv_val[0] != ',' ) { + return LDAP_INVALID_SYNTAX; + } + + x.bv_val++; + x.bv_len--; + } while ( 1 ); + + /* should have no characters left... */ + if ( x.bv_len ) return LDAP_INVALID_SYNTAX; + + if ( numdquotes == 0 ) { + ber_dupbv_x( &ni, is, ctx ); + + } else { + ber_len_t src, dst; + + ni.bv_len = is->bv_len - numdquotes; + ni.bv_val = ber_memalloc_x( ni.bv_len + 1, ctx ); + for ( src = 0, dst = 0; src < is->bv_len; src++, dst++ ) { + if ( is->bv_val[src] == '"' ) { + src++; + } + ni.bv_val[dst] = is->bv_val[src]; + } + ni.bv_val[dst] = '\0'; + } + + *is = ni; + + return 0; +} + +static int +issuerAndThisUpdateValidate( + Syntax *syntax, + struct berval *in ) +{ + int rc; + struct berval i, tu; + + Debug( LDAP_DEBUG_TRACE, ">>> issuerAndThisUpdateValidate: <%s>\n", + in->bv_val, 0, 0 ); + + rc = issuerAndThisUpdateCheck( in, &i, &tu, NULL ); + if ( rc ) { + goto done; + } + + /* validate DN -- doesn't handle double dquote */ + rc = dnValidate( NULL, &i ); + if ( rc ) { + rc = LDAP_INVALID_SYNTAX; + + } else if ( checkTime( &tu, NULL ) ) { + rc = LDAP_INVALID_SYNTAX; + } + + if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) { + slap_sl_free( i.bv_val, NULL ); + } + + Debug( LDAP_DEBUG_TRACE, "<<< issuerAndThisUpdateValidate: <%s> err=%d\n", + in->bv_val, rc, 0 ); + +done:; + return rc; +} + +static int +issuerAndThisUpdatePretty( + Syntax *syntax, + struct berval *in, + struct berval *out, + void *ctx ) +{ + int rc; + struct berval i, tu, ni = BER_BVNULL; + char *p; + + assert( in != NULL ); + assert( out != NULL ); + + BER_BVZERO( out ); + + Debug( LDAP_DEBUG_TRACE, ">>> issuerAndThisUpdatePretty: <%s>\n", + in->bv_val, 0, 0 ); + + rc = issuerAndThisUpdateCheck( in, &i, &tu, ctx ); + if ( rc ) { + goto done; + } + + rc = dnPretty( syntax, &i, &ni, ctx ); + + if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) { + slap_sl_free( i.bv_val, ctx ); + } + + if ( rc || checkTime( &tu, NULL ) ) { + rc = LDAP_INVALID_SYNTAX; + goto done; + } + + /* make room */ + out->bv_len = STRLENOF("{ issuer rdnSequence:\"\", thisUpdate \"\" }") + + ni.bv_len + tu.bv_len; + out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx ); + + if ( out->bv_val == NULL ) { + out->bv_len = 0; + rc = LDAP_OTHER; + goto done; + } + + p = out->bv_val; + p = lutil_strcopy( p, "{ issuer rdnSequence:\"" /*}*/ ); + p = lutil_strbvcopy( p, &ni ); + p = lutil_strcopy( p, "\", thisUpdate \"" ); + p = lutil_strbvcopy( p, &tu ); + p = lutil_strcopy( p, /*{*/ "\" }" ); + + assert( p == &out->bv_val[out->bv_len] ); + +done:; + Debug( LDAP_DEBUG_TRACE, "<<< issuerAndThisUpdatePretty: <%s> => <%s>\n", + in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)", 0 ); + + slap_sl_free( ni.bv_val, ctx ); + + return rc; +} + +static int +issuerAndThisUpdateNormalize( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *in, + struct berval *out, + void *ctx ) +{ + struct berval i, ni, tu, tu2; + char sbuf[STRLENOF("YYYYmmddHHMMSSZ") + 1]; + char *p; + int rc; + + assert( in != NULL ); + assert( out != NULL ); + + Debug( LDAP_DEBUG_TRACE, ">>> issuerAndThisUpdateNormalize: <%s>\n", + in->bv_val, 0, 0 ); + + rc = issuerAndThisUpdateCheck( in, &i, &tu, ctx ); + if ( rc ) { + return rc; + } + + rc = dnNormalize( usage, syntax, mr, &i, &ni, ctx ); + + if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) { + slap_sl_free( i.bv_val, ctx ); + } + + tu2.bv_val = sbuf; + tu2.bv_len = sizeof( sbuf ); + if ( rc || checkTime( &tu, &tu2 ) ) { + return LDAP_INVALID_SYNTAX; + } + + out->bv_len = STRLENOF( "{ issuer rdnSequence:\"\", thisUpdate \"\" }" ) + + ni.bv_len + tu2.bv_len; + out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx ); + + if ( out->bv_val == NULL ) { + out->bv_len = 0; + rc = LDAP_OTHER; + goto func_leave; + } + + p = out->bv_val; + + p = lutil_strcopy( p, "{ issuer rdnSequence:\"" /*}*/ ); + p = lutil_strbvcopy( p, &ni ); + p = lutil_strcopy( p, "\", thisUpdate \"" ); + p = lutil_strbvcopy( p, &tu2 ); + p = lutil_strcopy( p, /*{*/ "\" }" ); + + assert( p == &out->bv_val[out->bv_len] ); + +func_leave: + Debug( LDAP_DEBUG_TRACE, "<<< issuerAndThisUpdateNormalize: <%s> => <%s>\n", + in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)", 0 ); + + slap_sl_free( ni.bv_val, ctx ); + + return rc; +} + +static int +certificateListExactNormalize( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *normalized, + void *ctx ) +{ + BerElementBuffer berbuf; + BerElement *ber = (BerElement *)&berbuf; + ber_tag_t tag; + ber_len_t len; + ber_int_t version; + struct berval issuer_dn = BER_BVNULL, bvdn, + thisUpdate, bvtu; + char *p, tubuf[STRLENOF("YYYYmmddHHMMSSZ") + 1]; + int rc = LDAP_INVALID_SYNTAX; + + assert( val != NULL ); + + Debug( LDAP_DEBUG_TRACE, ">>> certificateListExactNormalize: <%p, %lu>\n", + val->bv_val, val->bv_len, 0 ); + + if ( BER_BVISEMPTY( val ) ) goto done; + + if ( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX(usage) ) { + return issuerAndThisUpdateNormalize( 0, NULL, NULL, val, normalized, ctx ); + } + + assert( SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX(usage) != 0 ); + + ber_init2( ber, val, LBER_USE_DER ); + tag = ber_skip_tag( ber, &len ); /* Signed wrapper */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + tag = ber_skip_tag( ber, &len ); /* Sequence */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + tag = ber_peek_tag( ber, &len ); + /* Optional version */ + if ( tag == LBER_INTEGER ) { + tag = ber_get_int( ber, &version ); + assert( tag == LBER_INTEGER ); + if ( version != SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX; + } + tag = ber_skip_tag( ber, &len ); /* Signature Algorithm */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + ber_skip_data( ber, len ); + + tag = ber_peek_tag( ber, &len ); /* IssuerDN */ + if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX; + len = ber_ptrlen( ber ); + bvdn.bv_val = val->bv_val + len; + bvdn.bv_len = val->bv_len - len; + tag = ber_skip_tag( ber, &len ); + ber_skip_data( ber, len ); + + tag = ber_skip_tag( ber, &len ); /* thisUpdate */ + /* Time is a CHOICE { UTCTime, GeneralizedTime } */ + if ( tag != SLAP_TAG_UTCTIME && tag != SLAP_TAG_GENERALIZEDTIME ) return LDAP_INVALID_SYNTAX; + bvtu.bv_val = (char *)ber->ber_ptr; + bvtu.bv_len = len; + + rc = dnX509normalize( &bvdn, &issuer_dn ); + if ( rc != LDAP_SUCCESS ) { + rc = LDAP_INVALID_SYNTAX; + goto done; + } + + thisUpdate.bv_val = tubuf; + thisUpdate.bv_len = sizeof(tubuf); + if ( checkTime( &bvtu, &thisUpdate ) ) { + rc = LDAP_INVALID_SYNTAX; + goto done; + } + + normalized->bv_len = STRLENOF( "{ issuer rdnSequence:\"\", thisUpdate \"\" }" ) + + issuer_dn.bv_len + thisUpdate.bv_len; + normalized->bv_val = ch_malloc( normalized->bv_len + 1 ); + + p = normalized->bv_val; + + p = lutil_strcopy( p, "{ issuer rdnSequence:\"" ); + p = lutil_strbvcopy( p, &issuer_dn ); + p = lutil_strcopy( p, "\", thisUpdate \"" ); + p = lutil_strbvcopy( p, &thisUpdate ); + p = lutil_strcopy( p, /*{*/ "\" }" ); + + rc = LDAP_SUCCESS; + +done: + Debug( LDAP_DEBUG_TRACE, "<<< certificateListExactNormalize: <%p, %lu> => <%s>\n", + val->bv_val, val->bv_len, rc == LDAP_SUCCESS ? normalized->bv_val : "(err)" ); + + if ( issuer_dn.bv_val ) ber_memfree( issuer_dn.bv_val ); + + return rc; +} + +/* X.509 PMI serialNumberAndIssuerSerialCheck + +AttributeCertificateExactAssertion ::= SEQUENCE { + serialNumber CertificateSerialNumber, + issuer AttCertIssuer } + +CertificateSerialNumber ::= INTEGER + +AttCertIssuer ::= [0] SEQUENCE { +issuerName GeneralNames OPTIONAL, +baseCertificateID [0] IssuerSerial OPTIONAL, +objectDigestInfo [1] ObjectDigestInfo OPTIONAL } +-- At least one component shall be present + +GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + +GeneralName ::= CHOICE { + otherName [0] INSTANCE OF OTHER-NAME, + rfc822Name [1] IA5String, + dNSName [2] IA5String, + x400Address [3] ORAddress, + directoryName [4] Name, + ediPartyName [5] EDIPartyName, + uniformResourceIdentifier [6] IA5String, + iPAddress [7] OCTET STRING, + registeredID [8] OBJECT IDENTIFIER } + +IssuerSerial ::= SEQUENCE { + issuer GeneralNames, + serial CertificateSerialNumber, + issuerUID UniqueIdentifier OPTIONAL } + +ObjectDigestInfo ::= SEQUENCE { + digestedObjectType ENUMERATED { + publicKey (0), + publicKeyCert (1), + otherObjectTypes (2) }, + otherObjectTypeID OBJECT IDENTIFIER OPTIONAL, + digestAlgorithm AlgorithmIdentifier, + objectDigest BIT STRING } + + * The way I interpret it, an assertion should look like + + { serialNumber 'dd'H, + issuer { issuerName { directoryName:rdnSequence:"cn=yyy" }, -- optional + baseCertificateID { serial '1d'H, + issuer { directoryName:rdnSequence:"cn=zzz" }, + issuerUID <value> -- optional + }, -- optional + objectDigestInfo { ... } -- optional + } + } + + * with issuerName, baseCertificateID and objectDigestInfo optional, + * at least one present; the way it's currently implemented, it is + + { serialNumber 'dd'H, + issuer { baseCertificateID { serial '1d'H, + issuer { directoryName:rdnSequence:"cn=zzz" } + } + } + } + + * with all the above parts mandatory. + */ +static int +serialNumberAndIssuerSerialCheck( + struct berval *in, + struct berval *sn, + struct berval *is, + struct berval *i_sn, /* contain serial of baseCertificateID */ + void *ctx ) +{ + /* Parse GSER format */ + enum { + HAVE_NONE = 0x0, + HAVE_SN = 0x1, + HAVE_ISSUER = 0x2, + HAVE_ALL = ( HAVE_SN | HAVE_ISSUER ) + } have = HAVE_NONE, have2 = HAVE_NONE; + int numdquotes = 0; + struct berval x = *in; + struct berval ni; + + if ( in->bv_len < 3 ) return LDAP_INVALID_SYNTAX; + + /* no old format */ + if ( in->bv_val[0] != '{' || in->bv_val[in->bv_len-1] != '}' ) return LDAP_INVALID_SYNTAX; + + x.bv_val++; + x.bv_len -= 2; + + do { + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + /* should be at issuer or serialNumber NamedValue */ + if ( strncasecmp( x.bv_val, "issuer", STRLENOF("issuer") ) == 0 ) { + if ( have & HAVE_ISSUER ) { + return LDAP_INVALID_SYNTAX; + } + + /* parse IssuerSerial */ + x.bv_val += STRLENOF("issuer"); + x.bv_len -= STRLENOF("issuer"); + + if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( x.bv_val[0] != '{' /*}*/ ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( strncasecmp( x.bv_val, "baseCertificateID ", STRLENOF("baseCertificateID ") ) != 0 ) { + return LDAP_INVALID_SYNTAX; + } + x.bv_val += STRLENOF("baseCertificateID "); + x.bv_len -= STRLENOF("baseCertificateID "); + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( x.bv_val[0] != '{' /*}*/ ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + do { + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + /* parse issuer of baseCertificateID */ + if ( strncasecmp( x.bv_val, "issuer ", STRLENOF("issuer ") ) == 0 ) { + if ( have2 & HAVE_ISSUER ) { + return LDAP_INVALID_SYNTAX; + } + + x.bv_val += STRLENOF("issuer "); + x.bv_len -= STRLENOF("issuer "); + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( x.bv_val[0] != '{' /*}*/ ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( strncasecmp( x.bv_val, "directoryName:rdnSequence:", STRLENOF("directoryName:rdnSequence:") ) != 0 ) { + return LDAP_INVALID_SYNTAX; + } + x.bv_val += STRLENOF("directoryName:rdnSequence:"); + x.bv_len -= STRLENOF("directoryName:rdnSequence:"); + + if ( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + is->bv_val = x.bv_val; + is->bv_len = 0; + + for ( ; is->bv_len < x.bv_len; ) { + if ( is->bv_val[is->bv_len] != '"' ) { + is->bv_len++; + continue; + } + if ( is->bv_val[is->bv_len + 1] == '"' ) { + /* double dquote */ + numdquotes++; + is->bv_len += 2; + continue; + } + break; + } + x.bv_val += is->bv_len + 1; + x.bv_len -= is->bv_len + 1; + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( x.bv_val[0] != /*{*/ '}' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + have2 |= HAVE_ISSUER; + + } else if ( strncasecmp( x.bv_val, "serial ", STRLENOF("serial ") ) == 0 ) { + if ( have2 & HAVE_SN ) { + return LDAP_INVALID_SYNTAX; + } + + x.bv_val += STRLENOF("serial "); + x.bv_len -= STRLENOF("serial "); + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) { + /* empty */; + } + + if ( checkNum( &x, i_sn ) ) { + return LDAP_INVALID_SYNTAX; + } + + x.bv_val += i_sn->bv_len; + x.bv_len -= i_sn->bv_len; + + have2 |= HAVE_SN; + + } else { + return LDAP_INVALID_SYNTAX; + } + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( have2 == HAVE_ALL ) { + break; + } + + if ( x.bv_val[0] != ',' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + } while ( 1 ); + + if ( x.bv_val[0] != /*{*/ '}' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( x.bv_val[0] != /*{*/ '}' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + have |= HAVE_ISSUER; + + } else if ( strncasecmp( x.bv_val, "serialNumber", STRLENOF("serialNumber") ) == 0 ) { + if ( have & HAVE_SN ) { + return LDAP_INVALID_SYNTAX; + } + + /* parse serialNumber */ + x.bv_val += STRLENOF("serialNumber"); + x.bv_len -= STRLENOF("serialNumber"); + + if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX; + x.bv_val++; + x.bv_len--; + + /* eat leading spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( checkNum( &x, sn ) ) { + return LDAP_INVALID_SYNTAX; + } + + x.bv_val += sn->bv_len; + x.bv_len -= sn->bv_len; + + have |= HAVE_SN; + + } else { + return LDAP_INVALID_SYNTAX; + } + + /* eat spaces */ + for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) { + /* empty */; + } + + if ( have == HAVE_ALL ) { + break; + } + + if ( x.bv_val[0] != ',' ) { + return LDAP_INVALID_SYNTAX; + } + x.bv_val++ ; + x.bv_len--; + } while ( 1 ); + + /* should have no characters left... */ + if( x.bv_len ) return LDAP_INVALID_SYNTAX; + + if ( numdquotes == 0 ) { + ber_dupbv_x( &ni, is, ctx ); + + } else { + ber_len_t src, dst; + + ni.bv_len = is->bv_len - numdquotes; + ni.bv_val = ber_memalloc_x( ni.bv_len + 1, ctx ); + for ( src = 0, dst = 0; src < is->bv_len; src++, dst++ ) { + if ( is->bv_val[src] == '"' ) { + src++; + } + ni.bv_val[dst] = is->bv_val[src]; + } + ni.bv_val[dst] = '\0'; + } + + *is = ni; + + /* need to handle double dquotes here */ + return 0; +} + +/* X.509 PMI serialNumberAndIssuerSerialValidate */ +static int +serialNumberAndIssuerSerialValidate( + Syntax *syntax, + struct berval *in ) +{ + int rc; + struct berval sn, i, i_sn; + + Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerSerialValidate: <%s>\n", + in->bv_val, 0, 0 ); + + rc = serialNumberAndIssuerSerialCheck( in, &sn, &i, &i_sn, NULL ); + if ( rc ) { + goto done; + } + + /* validate DN -- doesn't handle double dquote */ + rc = dnValidate( NULL, &i ); + if ( rc ) { + rc = LDAP_INVALID_SYNTAX; + } + + if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) { + slap_sl_free( i.bv_val, NULL ); + } + +done:; + Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerSerialValidate: <%s> err=%d\n", + in->bv_val, rc, 0 ); + + return rc; +} + +/* X.509 PMI serialNumberAndIssuerSerialPretty */ +static int +serialNumberAndIssuerSerialPretty( + Syntax *syntax, + struct berval *in, + struct berval *out, + void *ctx ) +{ + struct berval sn, i, i_sn, ni = BER_BVNULL; + char *p; + int rc; + + assert( in != NULL ); + assert( out != NULL ); + + Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerSerialPretty: <%s>\n", + in->bv_val, 0, 0 ); + + rc = serialNumberAndIssuerSerialCheck( in, &sn, &i, &i_sn, ctx ); + if ( rc ) { + goto done; + } + + rc = dnPretty( syntax, &i, &ni, ctx ); + + if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) { + slap_sl_free( i.bv_val, ctx ); + } + + if ( rc ) { + rc = LDAP_INVALID_SYNTAX; + goto done; + } + + /* make room from sn + "$" */ + out->bv_len = STRLENOF("{ serialNumber , issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"\" }, serial } } }") + + sn.bv_len + ni.bv_len + i_sn.bv_len; + out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx ); + + if ( out->bv_val == NULL ) { + out->bv_len = 0; + rc = LDAP_OTHER; + goto done; + } + + p = out->bv_val; + p = lutil_strcopy( p, "{ serialNumber " ); + p = lutil_strbvcopy( p, &sn ); + p = lutil_strcopy( p, ", issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"" ); + p = lutil_strbvcopy( p, &ni ); + p = lutil_strcopy( p, "\" }, serial " ); + p = lutil_strbvcopy( p, &i_sn ); + p = lutil_strcopy( p, " } } }" ); + + assert( p == &out->bv_val[out->bv_len] ); + +done:; + Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerSerialPretty: <%s> => <%s>\n", + in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)", 0 ); + + slap_sl_free( ni.bv_val, ctx ); + + return rc; +} + +/* X.509 PMI serialNumberAndIssuerSerialNormalize */ +/* + * This routine is called by attributeCertificateExactNormalize + * when attributeCertificateExactNormalize receives a search + * string instead of a attribute certificate. This routine + * checks if the search value is valid and then returns the + * normalized value + */ +static int +serialNumberAndIssuerSerialNormalize( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *in, + struct berval *out, + void *ctx ) +{ + struct berval i, ni = BER_BVNULL, + sn, sn2 = BER_BVNULL, sn3 = BER_BVNULL, + i_sn, i_sn2 = BER_BVNULL, i_sn3 = BER_BVNULL; + char sbuf2[SLAP_SN_BUFLEN], i_sbuf2[SLAP_SN_BUFLEN], + sbuf3[SLAP_SN_BUFLEN], i_sbuf3[SLAP_SN_BUFLEN]; + char *p; + int rc; + + assert( in != NULL ); + assert( out != NULL ); + + Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerSerialNormalize: <%s>\n", + in->bv_val, 0, 0 ); + + rc = serialNumberAndIssuerSerialCheck( in, &sn, &i, &i_sn, ctx ); + if ( rc ) { + goto func_leave; + } + + rc = dnNormalize( usage, syntax, mr, &i, &ni, ctx ); + + if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) { + slap_sl_free( i.bv_val, ctx ); + } + + if ( rc ) { + rc = LDAP_INVALID_SYNTAX; + goto func_leave; + } + + /* Convert sn to canonical hex */ + sn2.bv_val = sbuf2; + sn2.bv_len = sn.bv_len; + if ( sn.bv_len > sizeof( sbuf2 ) ) { + sn2.bv_val = slap_sl_malloc( sn.bv_len, ctx ); + } + if ( lutil_str2bin( &sn, &sn2, ctx ) ) { + rc = LDAP_INVALID_SYNTAX; + goto func_leave; + } + + /* Convert i_sn to canonical hex */ + i_sn2.bv_val = i_sbuf2; + i_sn2.bv_len = i_sn.bv_len; + if ( i_sn.bv_len > sizeof( i_sbuf2 ) ) { + i_sn2.bv_val = slap_sl_malloc( i_sn.bv_len, ctx ); + } + if ( lutil_str2bin( &i_sn, &i_sn2, ctx ) ) { + rc = LDAP_INVALID_SYNTAX; + goto func_leave; + } + + sn3.bv_val = sbuf3; + sn3.bv_len = sizeof(sbuf3); + if ( slap_bin2hex( &sn2, &sn3, ctx ) ) { + rc = LDAP_INVALID_SYNTAX; + goto func_leave; + } + + i_sn3.bv_val = i_sbuf3; + i_sn3.bv_len = sizeof(i_sbuf3); + if ( slap_bin2hex( &i_sn2, &i_sn3, ctx ) ) { + rc = LDAP_INVALID_SYNTAX; + goto func_leave; + } + + out->bv_len = STRLENOF("{ serialNumber , issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"\" }, serial } } }") + + sn3.bv_len + ni.bv_len + i_sn3.bv_len; + out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx ); + + if ( out->bv_val == NULL ) { + out->bv_len = 0; + rc = LDAP_OTHER; + goto func_leave; + } + + p = out->bv_val; + + p = lutil_strcopy( p, "{ serialNumber " ); + p = lutil_strbvcopy( p, &sn3 ); + p = lutil_strcopy( p, ", issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"" ); + p = lutil_strbvcopy( p, &ni ); + p = lutil_strcopy( p, "\" }, serial " ); + p = lutil_strbvcopy( p, &i_sn3 ); + p = lutil_strcopy( p, " } } }" ); + + assert( p == &out->bv_val[out->bv_len] ); + +func_leave: + Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerSerialNormalize: <%s> => <%s>\n", + in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)", 0 ); + + if ( sn2.bv_val != sbuf2 ) { + slap_sl_free( sn2.bv_val, ctx ); + } + + if ( i_sn2.bv_val != i_sbuf2 ) { + slap_sl_free( i_sn2.bv_val, ctx ); + } + + if ( sn3.bv_val != sbuf3 ) { + slap_sl_free( sn3.bv_val, ctx ); + } + + if ( i_sn3.bv_val != i_sbuf3 ) { + slap_sl_free( i_sn3.bv_val, ctx ); + } + + slap_sl_free( ni.bv_val, ctx ); + + return rc; +} + +/* X.509 PMI attributeCertificateExactNormalize */ +static int +attributeCertificateExactNormalize( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *normalized, + void *ctx ) +{ + BerElementBuffer berbuf; + BerElement *ber = (BerElement *)&berbuf; + ber_tag_t tag; + ber_len_t len; + char issuer_serialbuf[SLAP_SN_BUFLEN], serialbuf[SLAP_SN_BUFLEN]; + struct berval sn, i_sn, sn2 = BER_BVNULL, i_sn2 = BER_BVNULL; + struct berval issuer_dn = BER_BVNULL, bvdn; + char *p; + int rc = LDAP_INVALID_SYNTAX; + + if ( BER_BVISEMPTY( val ) ) { + return rc; + } + + if ( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX(usage) ) { + return serialNumberAndIssuerSerialNormalize( 0, NULL, NULL, val, normalized, ctx ); + } + + assert( SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX(usage) != 0 ); + + ber_init2( ber, val, LBER_USE_DER ); + tag = ber_skip_tag( ber, &len ); /* Signed Sequence */ + tag = ber_skip_tag( ber, &len ); /* Sequence */ + tag = ber_skip_tag( ber, &len ); /* (Mandatory) version; must be v2(1) */ + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); /* Holder Sequence */ + ber_skip_data( ber, len ); + + /* Issuer */ + tag = ber_skip_tag( ber, &len ); /* Sequence */ + /* issuerName (GeneralNames sequence; optional)? */ + tag = ber_skip_tag( ber, &len ); /* baseCertificateID (sequence; optional)? */ + tag = ber_skip_tag( ber, &len ); /* GeneralNames (sequence) */ + tag = ber_skip_tag( ber, &len ); /* directoryName (we only accept this form of GeneralName) */ + if ( tag != SLAP_X509_GN_DIRECTORYNAME ) { + return LDAP_INVALID_SYNTAX; + } + tag = ber_peek_tag( ber, &len ); /* sequence of RDN */ + len = ber_ptrlen( ber ); + bvdn.bv_val = val->bv_val + len; + bvdn.bv_len = val->bv_len - len; + rc = dnX509normalize( &bvdn, &issuer_dn ); + if ( rc != LDAP_SUCCESS ) { + rc = LDAP_INVALID_SYNTAX; + goto done; + } + + tag = ber_skip_tag( ber, &len ); /* sequence of RDN */ + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); /* serial number */ + if ( tag != LBER_INTEGER ) { + rc = LDAP_INVALID_SYNTAX; + goto done; + } + i_sn.bv_val = (char *)ber->ber_ptr; + i_sn.bv_len = len; + i_sn2.bv_val = issuer_serialbuf; + i_sn2.bv_len = sizeof(issuer_serialbuf); + if ( slap_bin2hex( &i_sn, &i_sn2, ctx ) ) { + rc = LDAP_INVALID_SYNTAX; + goto done; + } + ber_skip_data( ber, len ); + + /* issuerUID (bitstring; optional)? */ + /* objectDigestInfo (sequence; optional)? */ + + tag = ber_skip_tag( ber, &len ); /* Signature (sequence) */ + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); /* serial number */ + if ( tag != LBER_INTEGER ) { + rc = LDAP_INVALID_SYNTAX; + goto done; + } + sn.bv_val = (char *)ber->ber_ptr; + sn.bv_len = len; + sn2.bv_val = serialbuf; + sn2.bv_len = sizeof(serialbuf); + if ( slap_bin2hex( &sn, &sn2, ctx ) ) { + rc = LDAP_INVALID_SYNTAX; + goto done; + } + ber_skip_data( ber, len ); + + normalized->bv_len = STRLENOF( "{ serialNumber , issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"\" }, serial } } }" ) + + sn2.bv_len + issuer_dn.bv_len + i_sn2.bv_len; + normalized->bv_val = ch_malloc( normalized->bv_len + 1 ); + + p = normalized->bv_val; + + p = lutil_strcopy( p, "{ serialNumber " ); + p = lutil_strbvcopy( p, &sn2 ); + p = lutil_strcopy( p, ", issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"" ); + p = lutil_strbvcopy( p, &issuer_dn ); + p = lutil_strcopy( p, "\" }, serial " ); + p = lutil_strbvcopy( p, &i_sn2 ); + p = lutil_strcopy( p, " } } }" ); + + Debug( LDAP_DEBUG_TRACE, "attributeCertificateExactNormalize: %s\n", + normalized->bv_val, NULL, NULL ); + + rc = LDAP_SUCCESS; + +done: + if ( issuer_dn.bv_val ) ber_memfree( issuer_dn.bv_val ); + if ( i_sn2.bv_val != issuer_serialbuf ) ber_memfree_x( i_sn2.bv_val, ctx ); + if ( sn2.bv_val != serialbuf ) ber_memfree_x( sn2.bv_val, ctx ); + + return rc; +} + + +static int +hexValidate( + Syntax *syntax, + struct berval *in ) +{ + ber_len_t i; + + assert( in != NULL ); + assert( !BER_BVISNULL( in ) ); + + for ( i = 0; i < in->bv_len; i++ ) { + if ( !ASCII_HEX( in->bv_val[ i ] ) ) { + return LDAP_INVALID_SYNTAX; + } + } + + return LDAP_SUCCESS; +} + +/* Normalize a SID as used inside a CSN: + * three-digit numeric string */ +static int +hexNormalize( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *normalized, + void *ctx ) +{ + ber_len_t i; + + assert( val != NULL ); + assert( normalized != NULL ); + + ber_dupbv_x( normalized, val, ctx ); + + for ( i = 0; i < normalized->bv_len; i++ ) { + if ( !ASCII_HEX( normalized->bv_val[ i ] ) ) { + ber_memfree_x( normalized->bv_val, ctx ); + BER_BVZERO( normalized ); + return LDAP_INVALID_SYNTAX; + } + + normalized->bv_val[ i ] = TOLOWER( normalized->bv_val[ i ] ); + } + + return LDAP_SUCCESS; +} + +static int +sidValidate ( + Syntax *syntax, + struct berval *in ) +{ + assert( in != NULL ); + assert( !BER_BVISNULL( in ) ); + + if ( in->bv_len != 3 ) { + return LDAP_INVALID_SYNTAX; + } + + return hexValidate( NULL, in ); +} + +/* Normalize a SID as used inside a CSN: + * three-digit numeric string */ +static int +sidNormalize( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *normalized, + void *ctx ) +{ + if ( val->bv_len != 3 ) { + return LDAP_INVALID_SYNTAX; + } + + return hexNormalize( 0, NULL, NULL, val, normalized, ctx ); +} + +static int +sidPretty( + Syntax *syntax, + struct berval *val, + struct berval *out, + void *ctx ) +{ + return sidNormalize( SLAP_MR_VALUE_OF_SYNTAX, NULL, NULL, val, out, ctx ); +} + +/* Normalize a SID as used inside a CSN, either as-is + * (assertion value) or extracted from the CSN + * (attribute value) */ +static int +csnSidNormalize( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *normalized, + void *ctx ) +{ + struct berval bv; + char *ptr, + buf[ 4 ]; + + + if ( BER_BVISEMPTY( val ) ) { + return LDAP_INVALID_SYNTAX; + } + + if ( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX(usage) ) { + return sidNormalize( 0, NULL, NULL, val, normalized, ctx ); + } + + assert( SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX(usage) != 0 ); + + ptr = ber_bvchr( val, '#' ); + if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) { + return LDAP_INVALID_SYNTAX; + } + + bv.bv_val = ptr + 1; + bv.bv_len = val->bv_len - ( ptr + 1 - val->bv_val ); + + ptr = ber_bvchr( &bv, '#' ); + if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) { + return LDAP_INVALID_SYNTAX; + } + + bv.bv_val = ptr + 1; + bv.bv_len = val->bv_len - ( ptr + 1 - val->bv_val ); + + ptr = ber_bvchr( &bv, '#' ); + if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) { + return LDAP_INVALID_SYNTAX; + } + + bv.bv_len = ptr - bv.bv_val; + + if ( bv.bv_len == 2 ) { + /* OpenLDAP 2.3 SID */ + buf[ 0 ] = '0'; + buf[ 1 ] = bv.bv_val[ 0 ]; + buf[ 2 ] = bv.bv_val[ 1 ]; + buf[ 3 ] = '\0'; + + bv.bv_val = buf; + bv.bv_len = 3; + } + + return sidNormalize( 0, NULL, NULL, &bv, normalized, ctx ); +} + +static int +csnValidate( + Syntax *syntax, + struct berval *in ) +{ + struct berval bv; + char *ptr; + int rc; + + assert( in != NULL ); + + if ( BER_BVISNULL( in ) || BER_BVISEMPTY( in ) ) { + return LDAP_INVALID_SYNTAX; + } + + bv = *in; + + ptr = ber_bvchr( &bv, '#' ); + if ( ptr == NULL || ptr == &bv.bv_val[bv.bv_len] ) { + return LDAP_INVALID_SYNTAX; + } + + bv.bv_len = ptr - bv.bv_val; + if ( bv.bv_len != STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ" ) && + bv.bv_len != STRLENOF( "YYYYmmddHHMMSSZ" ) ) + { + return LDAP_INVALID_SYNTAX; + } + + rc = generalizedTimeValidate( NULL, &bv ); + if ( rc != LDAP_SUCCESS ) { + return rc; + } + + bv.bv_val = ptr + 1; + bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val ); + + ptr = ber_bvchr( &bv, '#' ); + if ( ptr == NULL || ptr == &in->bv_val[in->bv_len] ) { + return LDAP_INVALID_SYNTAX; + } + + bv.bv_len = ptr - bv.bv_val; + if ( bv.bv_len != 6 ) { + return LDAP_INVALID_SYNTAX; + } + + rc = hexValidate( NULL, &bv ); + if ( rc != LDAP_SUCCESS ) { + return rc; + } + + bv.bv_val = ptr + 1; + bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val ); + + ptr = ber_bvchr( &bv, '#' ); + if ( ptr == NULL || ptr == &in->bv_val[in->bv_len] ) { + return LDAP_INVALID_SYNTAX; + } + + bv.bv_len = ptr - bv.bv_val; + if ( bv.bv_len == 2 ) { + /* tolerate old 2-digit replica-id */ + rc = hexValidate( NULL, &bv ); + + } else { + rc = sidValidate( NULL, &bv ); + } + if ( rc != LDAP_SUCCESS ) { + return rc; + } + + bv.bv_val = ptr + 1; + bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val ); + + if ( bv.bv_len != 6 ) { + return LDAP_INVALID_SYNTAX; + } + + return hexValidate( NULL, &bv ); +} + +/* Normalize a CSN in OpenLDAP 2.1 format */ +static int +csnNormalize21( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *normalized, + void *ctx ) +{ + struct berval gt, cnt, sid, mod; + struct berval bv; + char buf[ STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" ) + 1 ]; + char *ptr; + ber_len_t i; + + assert( SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) != 0 ); + assert( !BER_BVISEMPTY( val ) ); + + gt = *val; + + ptr = ber_bvchr( >, '#' ); + if ( ptr == NULL || ptr == >.bv_val[gt.bv_len] ) { + return LDAP_INVALID_SYNTAX; + } + + gt.bv_len = ptr - gt.bv_val; + if ( gt.bv_len != STRLENOF( "YYYYmmddHH:MM:SSZ" ) ) { + return LDAP_INVALID_SYNTAX; + } + + if ( gt.bv_val[ 10 ] != ':' || gt.bv_val[ 13 ] != ':' ) { + return LDAP_INVALID_SYNTAX; + } + + cnt.bv_val = ptr + 1; + cnt.bv_len = val->bv_len - ( cnt.bv_val - val->bv_val ); + + ptr = ber_bvchr( &cnt, '#' ); + if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) { + return LDAP_INVALID_SYNTAX; + } + + cnt.bv_len = ptr - cnt.bv_val; + if ( cnt.bv_len != STRLENOF( "0x0000" ) ) { + return LDAP_INVALID_SYNTAX; + } + + if ( strncmp( cnt.bv_val, "0x", STRLENOF( "0x" ) ) != 0 ) { + return LDAP_INVALID_SYNTAX; + } + + cnt.bv_val += STRLENOF( "0x" ); + cnt.bv_len -= STRLENOF( "0x" ); + + sid.bv_val = ptr + 1; + sid.bv_len = val->bv_len - ( sid.bv_val - val->bv_val ); + + ptr = ber_bvchr( &sid, '#' ); + if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) { + return LDAP_INVALID_SYNTAX; + } + + sid.bv_len = ptr - sid.bv_val; + if ( sid.bv_len != STRLENOF( "0" ) ) { + return LDAP_INVALID_SYNTAX; + } + + mod.bv_val = ptr + 1; + mod.bv_len = val->bv_len - ( mod.bv_val - val->bv_val ); + if ( mod.bv_len != STRLENOF( "0000" ) ) { + return LDAP_INVALID_SYNTAX; + } + + bv.bv_len = STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" ); + bv.bv_val = buf; + + ptr = bv.bv_val; + ptr = lutil_strncopy( ptr, gt.bv_val, STRLENOF( "YYYYmmddHH" ) ); + ptr = lutil_strncopy( ptr, >.bv_val[ STRLENOF( "YYYYmmddHH:" ) ], + STRLENOF( "MM" ) ); + ptr = lutil_strncopy( ptr, >.bv_val[ STRLENOF( "YYYYmmddHH:MM:" ) ], + STRLENOF( "SS" ) ); + ptr = lutil_strcopy( ptr, ".000000Z#00" ); + ptr = lutil_strbvcopy( ptr, &cnt ); + *ptr++ = '#'; + *ptr++ = '0'; + *ptr++ = '0'; + *ptr++ = sid.bv_val[ 0 ]; + *ptr++ = '#'; + *ptr++ = '0'; + *ptr++ = '0'; + for ( i = 0; i < mod.bv_len; i++ ) { + *ptr++ = TOLOWER( mod.bv_val[ i ] ); + } + *ptr = '\0'; + + assert( ptr == &bv.bv_val[bv.bv_len] ); + + if ( csnValidate( syntax, &bv ) != LDAP_SUCCESS ) { + return LDAP_INVALID_SYNTAX; + } + + ber_dupbv_x( normalized, &bv, ctx ); + + return LDAP_SUCCESS; +} + +/* Normalize a CSN in OpenLDAP 2.3 format */ +static int +csnNormalize23( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *normalized, + void *ctx ) +{ + struct berval gt, cnt, sid, mod; + struct berval bv; + char buf[ STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" ) + 1 ]; + char *ptr; + ber_len_t i; + + assert( SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) != 0 ); + assert( !BER_BVISEMPTY( val ) ); + + gt = *val; + + ptr = ber_bvchr( >, '#' ); + if ( ptr == NULL || ptr == >.bv_val[gt.bv_len] ) { + return LDAP_INVALID_SYNTAX; + } + + gt.bv_len = ptr - gt.bv_val; + if ( gt.bv_len != STRLENOF( "YYYYmmddHHMMSSZ" ) ) { + return LDAP_INVALID_SYNTAX; + } + + cnt.bv_val = ptr + 1; + cnt.bv_len = val->bv_len - ( cnt.bv_val - val->bv_val ); + + ptr = ber_bvchr( &cnt, '#' ); + if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) { + return LDAP_INVALID_SYNTAX; + } + + cnt.bv_len = ptr - cnt.bv_val; + if ( cnt.bv_len != STRLENOF( "000000" ) ) { + return LDAP_INVALID_SYNTAX; + } + + sid.bv_val = ptr + 1; + sid.bv_len = val->bv_len - ( sid.bv_val - val->bv_val ); + + ptr = ber_bvchr( &sid, '#' ); + if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) { + return LDAP_INVALID_SYNTAX; + } + + sid.bv_len = ptr - sid.bv_val; + if ( sid.bv_len != STRLENOF( "00" ) ) { + return LDAP_INVALID_SYNTAX; + } + + mod.bv_val = ptr + 1; + mod.bv_len = val->bv_len - ( mod.bv_val - val->bv_val ); + if ( mod.bv_len != STRLENOF( "000000" ) ) { + return LDAP_INVALID_SYNTAX; + } + + bv.bv_len = STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" ); + bv.bv_val = buf; + + ptr = bv.bv_val; + ptr = lutil_strncopy( ptr, gt.bv_val, gt.bv_len - 1 ); + ptr = lutil_strcopy( ptr, ".000000Z#" ); + ptr = lutil_strbvcopy( ptr, &cnt ); + *ptr++ = '#'; + *ptr++ = '0'; + for ( i = 0; i < sid.bv_len; i++ ) { + *ptr++ = TOLOWER( sid.bv_val[ i ] ); + } + *ptr++ = '#'; + for ( i = 0; i < mod.bv_len; i++ ) { + *ptr++ = TOLOWER( mod.bv_val[ i ] ); + } + *ptr = '\0'; + + if ( ptr != &bv.bv_val[bv.bv_len] || + csnValidate( syntax, &bv ) != LDAP_SUCCESS ) { + return LDAP_INVALID_SYNTAX; + } + + ber_dupbv_x( normalized, &bv, ctx ); + + return LDAP_SUCCESS; +} + +/* Normalize a CSN */ +static int +csnNormalize( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *normalized, + void *ctx ) +{ + struct berval cnt, sid, mod; + char *ptr; + ber_len_t i; + + assert( val != NULL ); + assert( normalized != NULL ); + + assert( SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) != 0 ); + + if ( BER_BVISEMPTY( val ) ) { + return LDAP_INVALID_SYNTAX; + } + + if ( val->bv_len == STRLENOF( "YYYYmmddHHMMSSZ#SSSSSS#ID#ssssss" ) ) { + /* Openldap <= 2.3 */ + + return csnNormalize23( usage, syntax, mr, val, normalized, ctx ); + } + + if ( val->bv_len == STRLENOF( "YYYYmmddHH:MM:SSZ#0xSSSS#I#ssss" ) ) { + /* Openldap 2.1 */ + + return csnNormalize21( usage, syntax, mr, val, normalized, ctx ); + } + + if ( val->bv_len != STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" ) ) { + return LDAP_INVALID_SYNTAX; + } + + ptr = ber_bvchr( val, '#' ); + if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) { + return LDAP_INVALID_SYNTAX; + } + + if ( ptr - val->bv_val != STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ" ) ) { + return LDAP_INVALID_SYNTAX; + } + + cnt.bv_val = ptr + 1; + cnt.bv_len = val->bv_len - ( cnt.bv_val - val->bv_val ); + + ptr = ber_bvchr( &cnt, '#' ); + if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) { + return LDAP_INVALID_SYNTAX; + } + + if ( ptr - cnt.bv_val != STRLENOF( "000000" ) ) { + return LDAP_INVALID_SYNTAX; + } + + sid.bv_val = ptr + 1; + sid.bv_len = val->bv_len - ( sid.bv_val - val->bv_val ); + + ptr = ber_bvchr( &sid, '#' ); + if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) { + return LDAP_INVALID_SYNTAX; + } + + sid.bv_len = ptr - sid.bv_val; + if ( sid.bv_len != STRLENOF( "000" ) ) { + return LDAP_INVALID_SYNTAX; + } + + mod.bv_val = ptr + 1; + mod.bv_len = val->bv_len - ( mod.bv_val - val->bv_val ); + + if ( mod.bv_len != STRLENOF( "000000" ) ) { + return LDAP_INVALID_SYNTAX; + } + + ber_dupbv_x( normalized, val, ctx ); + + for ( i = STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#" ); + i < normalized->bv_len; i++ ) + { + /* assume it's already validated that's all hex digits */ + normalized->bv_val[ i ] = TOLOWER( normalized->bv_val[ i ] ); + } + + return LDAP_SUCCESS; +} + +static int +csnPretty( + Syntax *syntax, + struct berval *val, + struct berval *out, + void *ctx ) +{ + return csnNormalize( SLAP_MR_VALUE_OF_SYNTAX, NULL, NULL, val, out, ctx ); +} + +#ifndef SUPPORT_OBSOLETE_UTC_SYNTAX +/* slight optimization - does not need the start parameter */ +#define check_time_syntax(v, start, p, f) (check_time_syntax)(v, p, f) +enum { start = 0 }; +#endif + +static int +check_time_syntax (struct berval *val, + int start, + int *parts, + struct berval *fraction) +{ + /* + * start=0 GeneralizedTime YYYYmmddHH[MM[SS]][(./,)d...](Z|(+/-)HH[MM]) + * start=1 UTCTime YYmmddHHMM[SS][Z|(+/-)HHMM] + * GeneralizedTime supports leap seconds, UTCTime does not. + */ + static const int ceiling[9] = { 100, 100, 12, 31, 24, 60, 60, 24, 60 }; + static const int mdays[2][12] = { + /* non-leap years */ + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + /* leap years */ + { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } + }; + char *p, *e; + int part, c, c1, c2, tzoffset, leapyear = 0; + + p = val->bv_val; + e = p + val->bv_len; + +#ifdef SUPPORT_OBSOLETE_UTC_SYNTAX + parts[0] = 20; /* century - any multiple of 4 from 04 to 96 */ +#endif + for (part = start; part < 7 && p < e; part++) { + c1 = *p; + if (!ASCII_DIGIT(c1)) { + break; + } + p++; + if (p == e) { + return LDAP_INVALID_SYNTAX; + } + c = *p++; + if (!ASCII_DIGIT(c)) { + return LDAP_INVALID_SYNTAX; + } + c += c1 * 10 - '0' * 11; + if ((part | 1) == 3) { + --c; + if (c < 0) { + return LDAP_INVALID_SYNTAX; + } + } + if (c >= ceiling[part]) { + if (! (c == 60 && part == 6 && start == 0)) + return LDAP_INVALID_SYNTAX; + } + parts[part] = c; + } + if (part < 5 + start) { + return LDAP_INVALID_SYNTAX; + } + for (; part < 9; part++) { + parts[part] = 0; + } + + /* leapyear check for the Gregorian calendar (year>1581) */ + if (parts[parts[1] == 0 ? 0 : 1] % 4 == 0) { + leapyear = 1; + } + + if (parts[3] >= mdays[leapyear][parts[2]]) { + return LDAP_INVALID_SYNTAX; + } + + if (start == 0) { + fraction->bv_val = p; + fraction->bv_len = 0; + if (p < e && (*p == '.' || *p == ',')) { + char *end_num; + while (++p < e && ASCII_DIGIT(*p)) { + /* EMTPY */; + } + if (p - fraction->bv_val == 1) { + return LDAP_INVALID_SYNTAX; + } + for (end_num = p; end_num[-1] == '0'; --end_num) { + /* EMPTY */; + } + c = end_num - fraction->bv_val; + if (c != 1) fraction->bv_len = c; + } + } + + if (p == e) { + /* no time zone */ + return start == 0 ? LDAP_INVALID_SYNTAX : LDAP_SUCCESS; + } + + tzoffset = *p++; + switch (tzoffset) { + default: + return LDAP_INVALID_SYNTAX; + case 'Z': + /* UTC */ + break; + case '+': + case '-': + for (part = 7; part < 9 && p < e; part++) { + c1 = *p; + if (!ASCII_DIGIT(c1)) { + break; + } + p++; + if (p == e) { + return LDAP_INVALID_SYNTAX; + } + c2 = *p++; + if (!ASCII_DIGIT(c2)) { + return LDAP_INVALID_SYNTAX; + } + parts[part] = c1 * 10 + c2 - '0' * 11; + if (parts[part] >= ceiling[part]) { + return LDAP_INVALID_SYNTAX; + } + } + if (part < 8 + start) { + return LDAP_INVALID_SYNTAX; + } + + if (tzoffset == '-') { + /* negative offset to UTC, ie west of Greenwich */ + parts[4] += parts[7]; + parts[5] += parts[8]; + /* offset is just hhmm, no seconds */ + for (part = 6; --part >= 0; ) { + if (part != 3) { + c = ceiling[part]; + } else { + c = mdays[leapyear][parts[2]]; + } + if (parts[part] >= c) { + if (part == 0) { + return LDAP_INVALID_SYNTAX; + } + parts[part] -= c; + parts[part - 1]++; + continue; + } else if (part != 5) { + break; + } + } + } else { + /* positive offset to UTC, ie east of Greenwich */ + parts[4] -= parts[7]; + parts[5] -= parts[8]; + for (part = 6; --part >= 0; ) { + if (parts[part] < 0) { + if (part == 0) { + return LDAP_INVALID_SYNTAX; + } + if (part != 3) { + c = ceiling[part]; + } else { + /* make first arg to % non-negative */ + c = mdays[leapyear][(parts[2] - 1 + 12) % 12]; + } + parts[part] += c; + parts[part - 1]--; + continue; + } else if (part != 5) { + break; + } + } + } + } + + return p != e ? LDAP_INVALID_SYNTAX : LDAP_SUCCESS; +} + +#ifdef SUPPORT_OBSOLETE_UTC_SYNTAX + +#if 0 +static int +xutcTimeNormalize( + Syntax *syntax, + struct berval *val, + struct berval *normalized ) +{ + int parts[9], rc; + + rc = check_time_syntax(val, 1, parts, NULL); + if (rc != LDAP_SUCCESS) { + return rc; + } + + normalized->bv_val = ch_malloc( 14 ); + if ( normalized->bv_val == NULL ) { + return LBER_ERROR_MEMORY; + } + + sprintf( normalized->bv_val, "%02d%02d%02d%02d%02d%02dZ", + parts[1], parts[2] + 1, parts[3] + 1, + parts[4], parts[5], parts[6] ); + normalized->bv_len = 13; + + return LDAP_SUCCESS; +} +#endif /* 0 */ + +static int +utcTimeValidate( + Syntax *syntax, + struct berval *in ) +{ + int parts[9]; + return check_time_syntax(in, 1, parts, NULL); +} + +#endif /* SUPPORT_OBSOLETE_UTC_SYNTAX */ + +static int +generalizedTimeValidate( + Syntax *syntax, + struct berval *in ) +{ + int parts[9]; + struct berval fraction; + return check_time_syntax(in, 0, parts, &fraction); +} + +static int +generalizedTimeNormalize( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *normalized, + void *ctx ) +{ + int parts[9], rc; + unsigned int len; + struct berval fraction; + + rc = check_time_syntax(val, 0, parts, &fraction); + if (rc != LDAP_SUCCESS) { + return rc; + } + + len = STRLENOF("YYYYmmddHHMMSSZ") + fraction.bv_len; + normalized->bv_val = slap_sl_malloc( len + 1, ctx ); + if ( BER_BVISNULL( normalized ) ) { + return LBER_ERROR_MEMORY; + } + + sprintf( normalized->bv_val, "%02d%02d%02d%02d%02d%02d%02d", + parts[0], parts[1], parts[2] + 1, parts[3] + 1, + parts[4], parts[5], parts[6] ); + if ( !BER_BVISEMPTY( &fraction ) ) { + memcpy( normalized->bv_val + STRLENOF("YYYYmmddHHMMSSZ")-1, + fraction.bv_val, fraction.bv_len ); + normalized->bv_val[STRLENOF("YYYYmmddHHMMSSZ")-1] = '.'; + } + strcpy( normalized->bv_val + len-1, "Z" ); + normalized->bv_len = len; + + return LDAP_SUCCESS; +} + +static int +generalizedTimeOrderingMatch( + int *matchp, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *value, + void *assertedValue ) +{ + struct berval *asserted = (struct berval *) assertedValue; + ber_len_t v_len = value->bv_len; + ber_len_t av_len = asserted->bv_len; + + /* ignore trailing 'Z' when comparing */ + int match = memcmp( value->bv_val, asserted->bv_val, + (v_len < av_len ? v_len : av_len) - 1 ); + if ( match == 0 ) match = v_len - av_len; + + /* If used in extensible match filter, match if value < asserted */ + if ( flags & SLAP_MR_EXT ) + match = (match >= 0); + + *matchp = match; + return LDAP_SUCCESS; +} + +/* Index generation function: Ordered index */ +int generalizedTimeIndexer( + slap_mask_t use, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *prefix, + BerVarray values, + BerVarray *keysp, + void *ctx ) +{ + int i, j; + BerVarray keys; + char tmp[5]; + BerValue bvtmp; /* 40 bit index */ + struct lutil_tm tm; + struct lutil_timet tt; + + bvtmp.bv_len = sizeof(tmp); + bvtmp.bv_val = tmp; + for( i=0; values[i].bv_val != NULL; i++ ) { + /* just count them */ + } + + /* we should have at least one value at this point */ + assert( i > 0 ); + + keys = slap_sl_malloc( sizeof( struct berval ) * (i+1), ctx ); + + /* GeneralizedTime YYYYmmddHH[MM[SS]][(./,)d...](Z|(+/-)HH[MM]) */ + for( i=0, j=0; values[i].bv_val != NULL; i++ ) { + assert(values[i].bv_val != NULL && values[i].bv_len >= 10); + /* Use 40 bits of time for key */ + if ( lutil_parsetime( values[i].bv_val, &tm ) == 0 ) { + lutil_tm2time( &tm, &tt ); + tmp[0] = tt.tt_gsec & 0xff; + tmp[4] = tt.tt_sec & 0xff; + tt.tt_sec >>= 8; + tmp[3] = tt.tt_sec & 0xff; + tt.tt_sec >>= 8; + tmp[2] = tt.tt_sec & 0xff; + tt.tt_sec >>= 8; + tmp[1] = tt.tt_sec & 0xff; + + ber_dupbv_x(&keys[j++], &bvtmp, ctx ); + } + } + + keys[j].bv_val = NULL; + keys[j].bv_len = 0; + + *keysp = keys; + + return LDAP_SUCCESS; +} + +/* Index generation function: Ordered index */ +int generalizedTimeFilter( + slap_mask_t use, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *prefix, + void * assertedValue, + BerVarray *keysp, + void *ctx ) +{ + BerVarray keys; + char tmp[5]; + BerValue bvtmp; /* 40 bit index */ + BerValue *value = (BerValue *) assertedValue; + struct lutil_tm tm; + struct lutil_timet tt; + + bvtmp.bv_len = sizeof(tmp); + bvtmp.bv_val = tmp; + /* GeneralizedTime YYYYmmddHH[MM[SS]][(./,)d...](Z|(+/-)HH[MM]) */ + /* Use 40 bits of time for key */ + if ( value->bv_val && value->bv_len >= 10 && + lutil_parsetime( value->bv_val, &tm ) == 0 ) { + + lutil_tm2time( &tm, &tt ); + tmp[0] = tt.tt_gsec & 0xff; + tmp[4] = tt.tt_sec & 0xff; + tt.tt_sec >>= 8; + tmp[3] = tt.tt_sec & 0xff; + tt.tt_sec >>= 8; + tmp[2] = tt.tt_sec & 0xff; + tt.tt_sec >>= 8; + tmp[1] = tt.tt_sec & 0xff; + + keys = slap_sl_malloc( sizeof( struct berval ) * 2, ctx ); + ber_dupbv_x(keys, &bvtmp, ctx ); + keys[1].bv_val = NULL; + keys[1].bv_len = 0; + } else { + keys = NULL; + } + + *keysp = keys; + + return LDAP_SUCCESS; +} + +static int +deliveryMethodValidate( + Syntax *syntax, + struct berval *val ) +{ +#undef LENOF +#define LENOF(s) (sizeof(s)-1) + struct berval tmp = *val; + /* + * DeliveryMethod = pdm *( WSP DOLLAR WSP DeliveryMethod ) + * pdm = "any" / "mhs" / "physical" / "telex" / "teletex" / + * "g3fax" / "g4fax" / "ia5" / "videotex" / "telephone" + */ +again: + if( tmp.bv_len < 3 ) return LDAP_INVALID_SYNTAX; + + switch( tmp.bv_val[0] ) { + case 'a': + case 'A': + if(( tmp.bv_len >= LENOF("any") ) && + ( strncasecmp(tmp.bv_val, "any", LENOF("any")) == 0 )) + { + tmp.bv_len -= LENOF("any"); + tmp.bv_val += LENOF("any"); + break; + } + return LDAP_INVALID_SYNTAX; + + case 'm': + case 'M': + if(( tmp.bv_len >= LENOF("mhs") ) && + ( strncasecmp(tmp.bv_val, "mhs", LENOF("mhs")) == 0 )) + { + tmp.bv_len -= LENOF("mhs"); + tmp.bv_val += LENOF("mhs"); + break; + } + return LDAP_INVALID_SYNTAX; + + case 'p': + case 'P': + if(( tmp.bv_len >= LENOF("physical") ) && + ( strncasecmp(tmp.bv_val, "physical", LENOF("physical")) == 0 )) + { + tmp.bv_len -= LENOF("physical"); + tmp.bv_val += LENOF("physical"); + break; + } + return LDAP_INVALID_SYNTAX; + + case 't': + case 'T': /* telex or teletex or telephone */ + if(( tmp.bv_len >= LENOF("telex") ) && + ( strncasecmp(tmp.bv_val, "telex", LENOF("telex")) == 0 )) + { + tmp.bv_len -= LENOF("telex"); + tmp.bv_val += LENOF("telex"); + break; + } + if(( tmp.bv_len >= LENOF("teletex") ) && + ( strncasecmp(tmp.bv_val, "teletex", LENOF("teletex")) == 0 )) + { + tmp.bv_len -= LENOF("teletex"); + tmp.bv_val += LENOF("teletex"); + break; + } + if(( tmp.bv_len >= LENOF("telephone") ) && + ( strncasecmp(tmp.bv_val, "telephone", LENOF("telephone")) == 0 )) + { + tmp.bv_len -= LENOF("telephone"); + tmp.bv_val += LENOF("telephone"); + break; + } + return LDAP_INVALID_SYNTAX; + + case 'g': + case 'G': /* g3fax or g4fax */ + if(( tmp.bv_len >= LENOF("g3fax") ) && ( + ( strncasecmp(tmp.bv_val, "g3fax", LENOF("g3fax")) == 0 ) || + ( strncasecmp(tmp.bv_val, "g4fax", LENOF("g4fax")) == 0 ))) + { + tmp.bv_len -= LENOF("g3fax"); + tmp.bv_val += LENOF("g3fax"); + break; + } + return LDAP_INVALID_SYNTAX; + + case 'i': + case 'I': + if(( tmp.bv_len >= LENOF("ia5") ) && + ( strncasecmp(tmp.bv_val, "ia5", LENOF("ia5")) == 0 )) + { + tmp.bv_len -= LENOF("ia5"); + tmp.bv_val += LENOF("ia5"); + break; + } + return LDAP_INVALID_SYNTAX; + + case 'v': + case 'V': + if(( tmp.bv_len >= LENOF("videotex") ) && + ( strncasecmp(tmp.bv_val, "videotex", LENOF("videotex")) == 0 )) + { + tmp.bv_len -= LENOF("videotex"); + tmp.bv_val += LENOF("videotex"); + break; + } + return LDAP_INVALID_SYNTAX; + + default: + return LDAP_INVALID_SYNTAX; + } + + if( BER_BVISEMPTY( &tmp ) ) return LDAP_SUCCESS; + + while( !BER_BVISEMPTY( &tmp ) && ( tmp.bv_val[0] == ' ' ) ) { + tmp.bv_len--; + tmp.bv_val++; + } + if( !BER_BVISEMPTY( &tmp ) && ( tmp.bv_val[0] == '$' ) ) { + tmp.bv_len--; + tmp.bv_val++; + } else { + return LDAP_INVALID_SYNTAX; + } + while( !BER_BVISEMPTY( &tmp ) && ( tmp.bv_val[0] == ' ' ) ) { + tmp.bv_len--; + tmp.bv_val++; + } + + goto again; +} + +static int +nisNetgroupTripleValidate( + Syntax *syntax, + struct berval *val ) +{ + char *p, *e; + int commas = 0; + + if ( BER_BVISEMPTY( val ) ) { + return LDAP_INVALID_SYNTAX; + } + + p = (char *)val->bv_val; + e = p + val->bv_len; + + if ( *p != '(' /*')'*/ ) { + return LDAP_INVALID_SYNTAX; + } + + for ( p++; ( p < e ) && ( *p != /*'('*/ ')' ); p++ ) { + if ( *p == ',' ) { + commas++; + if ( commas > 2 ) { + return LDAP_INVALID_SYNTAX; + } + + } else if ( !AD_CHAR( *p ) ) { + return LDAP_INVALID_SYNTAX; + } + } + + if ( ( commas != 2 ) || ( *p != /*'('*/ ')' ) ) { + return LDAP_INVALID_SYNTAX; + } + + p++; + + if (p != e) { + return LDAP_INVALID_SYNTAX; + } + + return LDAP_SUCCESS; +} + +static int +bootParameterValidate( + Syntax *syntax, + struct berval *val ) +{ + char *p, *e; + + if ( BER_BVISEMPTY( val ) ) { + return LDAP_INVALID_SYNTAX; + } + + p = (char *)val->bv_val; + e = p + val->bv_len; + + /* key */ + for (; ( p < e ) && ( *p != '=' ); p++ ) { + if ( !AD_CHAR( *p ) ) { + return LDAP_INVALID_SYNTAX; + } + } + + if ( *p != '=' ) { + return LDAP_INVALID_SYNTAX; + } + + /* server */ + for ( p++; ( p < e ) && ( *p != ':' ); p++ ) { + if ( !AD_CHAR( *p ) ) { + return LDAP_INVALID_SYNTAX; + } + } + + if ( *p != ':' ) { + return LDAP_INVALID_SYNTAX; + } + + /* path */ + for ( p++; p < e; p++ ) { + if ( !SLAP_PRINTABLE( *p ) ) { + return LDAP_INVALID_SYNTAX; + } + } + + return LDAP_SUCCESS; +} + +static int +firstComponentNormalize( + slap_mask_t usage, + Syntax *syntax, + MatchingRule *mr, + struct berval *val, + struct berval *normalized, + void *ctx ) +{ + int rc; + struct berval comp; + ber_len_t len; + + if( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX( usage )) { + ber_dupbv_x( normalized, val, ctx ); + return LDAP_SUCCESS; + } + + if( val->bv_len < 3 ) return LDAP_INVALID_SYNTAX; + + if( ! ( val->bv_val[0] == '(' /*')'*/ + && val->bv_val[val->bv_len - 1] == /*'('*/ ')' ) + && ! ( val->bv_val[0] == '{' /*'}'*/ + && val->bv_val[val->bv_len - 1] == /*'('*/ '}' ) ) + { + return LDAP_INVALID_SYNTAX; + } + + /* trim leading white space */ + for( len=1; + len < val->bv_len && ASCII_SPACE(val->bv_val[len]); + len++ ) + { + /* empty */ + } + + /* grab next word */ + comp.bv_val = &val->bv_val[len]; + len = val->bv_len - len - STRLENOF(/*"{"*/ "}"); + for( comp.bv_len = 0; + !ASCII_SPACE(comp.bv_val[comp.bv_len]) && comp.bv_len < len; + comp.bv_len++ ) + { + /* empty */ + } + + if( mr == slap_schema.si_mr_objectIdentifierFirstComponentMatch ) { + rc = numericoidValidate( NULL, &comp ); + } else if( mr == slap_schema.si_mr_integerFirstComponentMatch ) { + rc = integerValidate( NULL, &comp ); + } else { + rc = LDAP_INVALID_SYNTAX; + } + + + if( rc == LDAP_SUCCESS ) { + ber_dupbv_x( normalized, &comp, ctx ); + } + + return rc; +} + +static char *country_gen_syn[] = { + "1.3.6.1.4.1.1466.115.121.1.15", /* Directory String */ + "1.3.6.1.4.1.1466.115.121.1.26", /* IA5 String */ + "1.3.6.1.4.1.1466.115.121.1.44", /* Printable String */ + NULL +}; + +#define X_BINARY "X-BINARY-TRANSFER-REQUIRED 'TRUE' " +#define X_NOT_H_R "X-NOT-HUMAN-READABLE 'TRUE' " + +static slap_syntax_defs_rec syntax_defs[] = { + {"( 1.3.6.1.4.1.1466.115.121.1.1 DESC 'ACI Item' " + X_BINARY X_NOT_H_R ")", + SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.2 DESC 'Access Point' " X_NOT_H_R ")", + 0, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.3 DESC 'Attribute Type Description' )", + 0, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.4 DESC 'Audio' " + X_NOT_H_R ")", + SLAP_SYNTAX_BLOB, NULL, blobValidate, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.5 DESC 'Binary' " + X_NOT_H_R ")", + SLAP_SYNTAX_BER, NULL, berValidate, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.6 DESC 'Bit String' )", + 0, NULL, bitStringValidate, NULL }, + {"( 1.3.6.1.4.1.1466.115.121.1.7 DESC 'Boolean' )", + 0, NULL, booleanValidate, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.8 DESC 'Certificate' " + X_BINARY X_NOT_H_R ")", + SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, + NULL, certificateValidate, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.9 DESC 'Certificate List' " + X_BINARY X_NOT_H_R ")", + SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, + NULL, certificateListValidate, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.10 DESC 'Certificate Pair' " + X_BINARY X_NOT_H_R ")", + SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, + NULL, sequenceValidate, NULL}, + {"( " attributeCertificateSyntaxOID " DESC 'X.509 AttributeCertificate' " + X_BINARY X_NOT_H_R ")", + SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, + NULL, attributeCertificateValidate, NULL}, +#if 0 /* need to go __after__ printableString */ + {"( 1.3.6.1.4.1.1466.115.121.1.11 DESC 'Country String' )", + 0, "1.3.6.1.4.1.1466.115.121.1.44", + countryStringValidate, NULL}, +#endif + {"( 1.3.6.1.4.1.1466.115.121.1.12 DESC 'Distinguished Name' )", + SLAP_SYNTAX_DN, NULL, dnValidate, dnPretty}, + {"( 1.2.36.79672281.1.5.0 DESC 'RDN' )", + 0, NULL, rdnValidate, rdnPretty}, +#ifdef LDAP_COMP_MATCH + {"( 1.2.36.79672281.1.5.3 DESC 'allComponents' )", + 0, NULL, allComponentsValidate, NULL}, + {"( 1.2.36.79672281.1.5.2 DESC 'componentFilterMatch assertion') ", + 0, NULL, componentFilterValidate, NULL}, +#endif + {"( 1.3.6.1.4.1.1466.115.121.1.13 DESC 'Data Quality' )", + 0, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.14 DESC 'Delivery Method' )", + 0, NULL, deliveryMethodValidate, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.15 DESC 'Directory String' )", + 0, NULL, UTF8StringValidate, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.16 DESC 'DIT Content Rule Description' )", + 0, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.17 DESC 'DIT Structure Rule Description' )", + 0, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.19 DESC 'DSA Quality' )", + 0, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.20 DESC 'DSE Type' )", + 0, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.21 DESC 'Enhanced Guide' )", + 0, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.22 DESC 'Facsimile Telephone Number' )", + 0, NULL, printablesStringValidate, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.23 DESC 'Fax' " X_NOT_H_R ")", + SLAP_SYNTAX_BLOB, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.24 DESC 'Generalized Time' )", + 0, NULL, generalizedTimeValidate, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.25 DESC 'Guide' )", + 0, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.26 DESC 'IA5 String' )", + 0, NULL, IA5StringValidate, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.27 DESC 'Integer' )", + 0, NULL, integerValidate, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.28 DESC 'JPEG' " X_NOT_H_R ")", + SLAP_SYNTAX_BLOB, NULL, blobValidate, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.29 DESC 'Master And Shadow Access Points' )", + 0, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.30 DESC 'Matching Rule Description' )", + 0, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.31 DESC 'Matching Rule Use Description' )", + 0, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.32 DESC 'Mail Preference' )", + 0, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.33 DESC 'MHS OR Address' )", + 0, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.34 DESC 'Name And Optional UID' )", + SLAP_SYNTAX_DN, NULL, nameUIDValidate, nameUIDPretty }, + {"( 1.3.6.1.4.1.1466.115.121.1.35 DESC 'Name Form Description' )", + 0, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.36 DESC 'Numeric String' )", + 0, NULL, numericStringValidate, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.37 DESC 'Object Class Description' )", + 0, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.38 DESC 'OID' )", + 0, NULL, numericoidValidate, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.39 DESC 'Other Mailbox' )", + 0, NULL, IA5StringValidate, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.40 DESC 'Octet String' )", + 0, NULL, blobValidate, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.41 DESC 'Postal Address' )", + 0, NULL, postalAddressValidate, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.42 DESC 'Protocol Information' )", + 0, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.43 DESC 'Presentation Address' )", + 0, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.44 DESC 'Printable String' )", + 0, NULL, printableStringValidate, NULL}, + /* moved here because now depends on Directory String, IA5 String + * and Printable String */ + {"( 1.3.6.1.4.1.1466.115.121.1.11 DESC 'Country String' )", + 0, country_gen_syn, countryStringValidate, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.45 DESC 'SubtreeSpecification' )", +#define subtreeSpecificationValidate UTF8StringValidate /* FIXME */ + 0, NULL, subtreeSpecificationValidate, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.49 DESC 'Supported Algorithm' " + X_BINARY X_NOT_H_R ")", + SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, NULL, berValidate, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.50 DESC 'Telephone Number' )", + 0, NULL, printableStringValidate, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.51 DESC 'Teletex Terminal Identifier' )", + 0, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.52 DESC 'Telex Number' )", + 0, NULL, printablesStringValidate, NULL}, +#ifdef SUPPORT_OBSOLETE_UTC_SYNTAX + {"( 1.3.6.1.4.1.1466.115.121.1.53 DESC 'UTC Time' )", + 0, NULL, utcTimeValidate, NULL}, +#endif + {"( 1.3.6.1.4.1.1466.115.121.1.54 DESC 'LDAP Syntax Description' )", + 0, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.55 DESC 'Modify Rights' )", + 0, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.56 DESC 'LDAP Schema Definition' )", + 0, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.57 DESC 'LDAP Schema Description' )", + 0, NULL, NULL, NULL}, + {"( 1.3.6.1.4.1.1466.115.121.1.58 DESC 'Substring Assertion' )", + 0, NULL, NULL, NULL}, + + /* RFC 2307 NIS Syntaxes */ + {"( 1.3.6.1.1.1.0.0 DESC 'RFC2307 NIS Netgroup Triple' )", + 0, NULL, nisNetgroupTripleValidate, NULL}, + {"( 1.3.6.1.1.1.0.1 DESC 'RFC2307 Boot Parameter' )", + 0, NULL, bootParameterValidate, NULL}, + + /* draft-zeilenga-ldap-x509 */ + {"( 1.3.6.1.1.15.1 DESC 'Certificate Exact Assertion' )", + SLAP_SYNTAX_HIDE, NULL, + serialNumberAndIssuerValidate, + serialNumberAndIssuerPretty}, + {"( 1.3.6.1.1.15.2 DESC 'Certificate Assertion' )", + SLAP_SYNTAX_HIDE, NULL, NULL, NULL}, + {"( 1.3.6.1.1.15.3 DESC 'Certificate Pair Exact Assertion' )", + SLAP_SYNTAX_HIDE, NULL, NULL, NULL}, + {"( 1.3.6.1.1.15.4 DESC 'Certificate Pair Assertion' )", + SLAP_SYNTAX_HIDE, NULL, NULL, NULL}, + {"( 1.3.6.1.1.15.5 DESC 'Certificate List Exact Assertion' )", + SLAP_SYNTAX_HIDE, NULL, + issuerAndThisUpdateValidate, + issuerAndThisUpdatePretty}, + {"( 1.3.6.1.1.15.6 DESC 'Certificate List Assertion' )", + SLAP_SYNTAX_HIDE, NULL, NULL, NULL}, + {"( 1.3.6.1.1.15.7 DESC 'Algorithm Identifier' )", + SLAP_SYNTAX_HIDE, NULL, NULL, NULL}, + {"( " attributeCertificateExactAssertionSyntaxOID " DESC 'AttributeCertificate Exact Assertion' )", + SLAP_SYNTAX_HIDE, NULL, + serialNumberAndIssuerSerialValidate, + serialNumberAndIssuerSerialPretty}, + {"( " attributeCertificateAssertionSyntaxOID " DESC 'AttributeCertificate Assertion' )", + SLAP_SYNTAX_HIDE, NULL, NULL, NULL}, + +#ifdef SLAPD_AUTHPASSWD + /* needs updating */ + {"( 1.3.6.1.4.1.4203.666.2.2 DESC 'OpenLDAP authPassword' )", + SLAP_SYNTAX_HIDE, NULL, NULL, NULL}, +#endif + + {"( 1.3.6.1.1.16.1 DESC 'UUID' )", + 0, NULL, UUIDValidate, UUIDPretty}, + + {"( 1.3.6.1.4.1.4203.666.11.2.1 DESC 'CSN' )", + SLAP_SYNTAX_HIDE, NULL, csnValidate, csnPretty }, + + {"( 1.3.6.1.4.1.4203.666.11.2.4 DESC 'CSN SID' )", + SLAP_SYNTAX_HIDE, NULL, sidValidate, sidPretty }, + + /* OpenLDAP Void Syntax */ + {"( 1.3.6.1.4.1.4203.1.1.1 DESC 'OpenLDAP void' )" , + SLAP_SYNTAX_HIDE, NULL, inValidate, NULL}, + + /* FIXME: OID is unused, but not registered yet */ + {"( 1.3.6.1.4.1.4203.666.2.7 DESC 'OpenLDAP authz' )", + SLAP_SYNTAX_HIDE, NULL, authzValidate, authzPretty}, + + {NULL, 0, NULL, NULL, NULL} +}; + +char *csnSIDMatchSyntaxes[] = { + "1.3.6.1.4.1.4203.666.11.2.1" /* csn */, + NULL +}; +char *certificateExactMatchSyntaxes[] = { + "1.3.6.1.4.1.1466.115.121.1.8" /* certificate */, + NULL +}; +char *certificateListExactMatchSyntaxes[] = { + "1.3.6.1.4.1.1466.115.121.1.9" /* certificateList */, + NULL +}; +char *attributeCertificateExactMatchSyntaxes[] = { + attributeCertificateSyntaxOID /* attributeCertificate */, + NULL +}; + +#ifdef LDAP_COMP_MATCH +char *componentFilterMatchSyntaxes[] = { + "1.3.6.1.4.1.1466.115.121.1.8" /* certificate */, + "1.3.6.1.4.1.1466.115.121.1.9" /* certificateList */, + attributeCertificateSyntaxOID /* attributeCertificate */, + NULL +}; +#endif + +char *directoryStringSyntaxes[] = { + "1.3.6.1.4.1.1466.115.121.1.11" /* countryString */, + "1.3.6.1.4.1.1466.115.121.1.44" /* printableString */, + "1.3.6.1.4.1.1466.115.121.1.50" /* telephoneNumber */, + NULL +}; +char *integerFirstComponentMatchSyntaxes[] = { + "1.3.6.1.4.1.1466.115.121.1.27" /* INTEGER */, + "1.3.6.1.4.1.1466.115.121.1.17" /* dITStructureRuleDescription */, + NULL +}; +char *objectIdentifierFirstComponentMatchSyntaxes[] = { + "1.3.6.1.4.1.1466.115.121.1.38" /* OID */, + "1.3.6.1.4.1.1466.115.121.1.3" /* attributeTypeDescription */, + "1.3.6.1.4.1.1466.115.121.1.16" /* dITContentRuleDescription */, + "1.3.6.1.4.1.1466.115.121.1.54" /* ldapSyntaxDescription */, + "1.3.6.1.4.1.1466.115.121.1.30" /* matchingRuleDescription */, + "1.3.6.1.4.1.1466.115.121.1.31" /* matchingRuleUseDescription */, + "1.3.6.1.4.1.1466.115.121.1.35" /* nameFormDescription */, + "1.3.6.1.4.1.1466.115.121.1.37" /* objectClassDescription */, + NULL +}; + +/* + * Other matching rules in X.520 that we do not use (yet): + * + * 2.5.13.25 uTCTimeMatch + * 2.5.13.26 uTCTimeOrderingMatch + * 2.5.13.31* directoryStringFirstComponentMatch + * 2.5.13.32* wordMatch + * 2.5.13.33* keywordMatch + * 2.5.13.36+ certificatePairExactMatch + * 2.5.13.37+ certificatePairMatch + * 2.5.13.40+ algorithmIdentifierMatch + * 2.5.13.41* storedPrefixMatch + * 2.5.13.42 attributeCertificateMatch + * 2.5.13.43 readerAndKeyIDMatch + * 2.5.13.44 attributeIntegrityMatch + * + * (*) described in RFC 3698 (LDAP: Additional Matching Rules) + * (+) described in draft-zeilenga-ldap-x509 + */ +static slap_mrule_defs_rec mrule_defs[] = { + /* + * EQUALITY matching rules must be listed after associated APPROX + * matching rules. So, we list all APPROX matching rules first. + */ + {"( " directoryStringApproxMatchOID " NAME 'directoryStringApproxMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )", + SLAP_MR_HIDE | SLAP_MR_EQUALITY_APPROX | SLAP_MR_EXT, NULL, + NULL, NULL, directoryStringApproxMatch, + directoryStringApproxIndexer, directoryStringApproxFilter, + NULL}, + + {"( " IA5StringApproxMatchOID " NAME 'IA5StringApproxMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )", + SLAP_MR_HIDE | SLAP_MR_EQUALITY_APPROX | SLAP_MR_EXT, NULL, + NULL, NULL, IA5StringApproxMatch, + IA5StringApproxIndexer, IA5StringApproxFilter, + NULL}, + + /* + * Other matching rules + */ + + {"( 2.5.13.0 NAME 'objectIdentifierMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )", + SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL, + NULL, NULL, octetStringMatch, + octetStringIndexer, octetStringFilter, + NULL }, + + {"( 2.5.13.1 NAME 'distinguishedNameMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )", + SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL, + NULL, dnNormalize, dnMatch, + octetStringIndexer, octetStringFilter, + NULL }, + + {"( 1.3.6.1.4.1.4203.666.4.9 NAME 'dnSubtreeMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )", + SLAP_MR_HIDE | SLAP_MR_EXT, NULL, + NULL, dnNormalize, dnRelativeMatch, + NULL, NULL, + NULL }, + + {"( 1.3.6.1.4.1.4203.666.4.8 NAME 'dnOneLevelMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )", + SLAP_MR_HIDE | SLAP_MR_EXT, NULL, + NULL, dnNormalize, dnRelativeMatch, + NULL, NULL, + NULL }, + + {"( 1.3.6.1.4.1.4203.666.4.10 NAME 'dnSubordinateMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )", + SLAP_MR_HIDE | SLAP_MR_EXT, NULL, + NULL, dnNormalize, dnRelativeMatch, + NULL, NULL, + NULL }, + + {"( 1.3.6.1.4.1.4203.666.4.11 NAME 'dnSuperiorMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )", + SLAP_MR_HIDE | SLAP_MR_EXT, NULL, + NULL, dnNormalize, dnRelativeMatch, + NULL, NULL, + NULL }, + + {"( 1.2.36.79672281.1.13.3 NAME 'rdnMatch' " + "SYNTAX 1.2.36.79672281.1.5.0 )", + SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL, + NULL, rdnNormalize, rdnMatch, + octetStringIndexer, octetStringFilter, + NULL }, + +#ifdef LDAP_COMP_MATCH + {"( 1.2.36.79672281.1.13.2 NAME 'componentFilterMatch' " + "SYNTAX 1.2.36.79672281.1.5.2 )", /* componentFilterMatch assertion */ + SLAP_MR_EXT|SLAP_MR_COMPONENT, componentFilterMatchSyntaxes, + NULL, NULL , componentFilterMatch, + octetStringIndexer, octetStringFilter, + NULL }, + + {"( 1.2.36.79672281.1.13.6 NAME 'allComponentsMatch' " + "SYNTAX 1.2.36.79672281.1.5.3 )", /* allComponents */ + SLAP_MR_EQUALITY|SLAP_MR_EXT|SLAP_MR_COMPONENT, NULL, + NULL, NULL , allComponentsMatch, + octetStringIndexer, octetStringFilter, + NULL }, + + {"( 1.2.36.79672281.1.13.7 NAME 'directoryComponentsMatch' " + "SYNTAX 1.2.36.79672281.1.5.3 )", /* allComponents */ + SLAP_MR_EQUALITY|SLAP_MR_EXT|SLAP_MR_COMPONENT, NULL, + NULL, NULL , directoryComponentsMatch, + octetStringIndexer, octetStringFilter, + NULL }, +#endif + + {"( 2.5.13.2 NAME 'caseIgnoreMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )", + SLAP_MR_EQUALITY | SLAP_MR_EXT, directoryStringSyntaxes, + NULL, UTF8StringNormalize, octetStringMatch, + octetStringIndexer, octetStringFilter, + directoryStringApproxMatchOID }, + + {"( 2.5.13.3 NAME 'caseIgnoreOrderingMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )", + SLAP_MR_ORDERING | SLAP_MR_EXT, directoryStringSyntaxes, + NULL, UTF8StringNormalize, octetStringOrderingMatch, + NULL, NULL, + "caseIgnoreMatch" }, + + {"( 2.5.13.4 NAME 'caseIgnoreSubstringsMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */ + SLAP_MR_SUBSTR, directoryStringSyntaxes, + NULL, UTF8StringNormalize, directoryStringSubstringsMatch, + octetStringSubstringsIndexer, octetStringSubstringsFilter, + "caseIgnoreMatch" }, + + {"( 2.5.13.5 NAME 'caseExactMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )", + SLAP_MR_EQUALITY | SLAP_MR_EXT, directoryStringSyntaxes, + NULL, UTF8StringNormalize, octetStringMatch, + octetStringIndexer, octetStringFilter, + directoryStringApproxMatchOID }, + + {"( 2.5.13.6 NAME 'caseExactOrderingMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )", + SLAP_MR_ORDERING | SLAP_MR_EXT, directoryStringSyntaxes, + NULL, UTF8StringNormalize, octetStringOrderingMatch, + NULL, NULL, + "caseExactMatch" }, + + {"( 2.5.13.7 NAME 'caseExactSubstringsMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */ + SLAP_MR_SUBSTR, directoryStringSyntaxes, + NULL, UTF8StringNormalize, directoryStringSubstringsMatch, + octetStringSubstringsIndexer, octetStringSubstringsFilter, + "caseExactMatch" }, + + {"( 2.5.13.8 NAME 'numericStringMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 )", + SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL, + NULL, numericStringNormalize, octetStringMatch, + octetStringIndexer, octetStringFilter, + NULL }, + + {"( 2.5.13.9 NAME 'numericStringOrderingMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 )", + SLAP_MR_ORDERING | SLAP_MR_EXT, NULL, + NULL, numericStringNormalize, octetStringOrderingMatch, + NULL, NULL, + "numericStringMatch" }, + + {"( 2.5.13.10 NAME 'numericStringSubstringsMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */ + SLAP_MR_SUBSTR, NULL, + NULL, numericStringNormalize, octetStringSubstringsMatch, + octetStringSubstringsIndexer, octetStringSubstringsFilter, + "numericStringMatch" }, + + {"( 2.5.13.11 NAME 'caseIgnoreListMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )", /* Postal Address */ + SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL, + NULL, postalAddressNormalize, octetStringMatch, + octetStringIndexer, octetStringFilter, + NULL }, + + {"( 2.5.13.12 NAME 'caseIgnoreListSubstringsMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */ + SLAP_MR_SUBSTR, NULL, + NULL, NULL, NULL, NULL, NULL, + "caseIgnoreListMatch" }, + + {"( 2.5.13.13 NAME 'booleanMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 )", + SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL, + NULL, NULL, booleanMatch, + octetStringIndexer, octetStringFilter, + NULL }, + + {"( 2.5.13.14 NAME 'integerMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )", + SLAP_MR_EQUALITY | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL, + NULL, NULL, integerMatch, + integerIndexer, integerFilter, + NULL }, + + {"( 2.5.13.15 NAME 'integerOrderingMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )", + SLAP_MR_ORDERING | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL, + NULL, NULL, integerMatch, + NULL, NULL, + "integerMatch" }, + + {"( 2.5.13.16 NAME 'bitStringMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.6 )", + SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL, + NULL, NULL, octetStringMatch, + octetStringIndexer, octetStringFilter, + NULL }, + + {"( 2.5.13.17 NAME 'octetStringMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )", + SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL, + NULL, NULL, octetStringMatch, + octetStringIndexer, octetStringFilter, + NULL }, + + {"( 2.5.13.18 NAME 'octetStringOrderingMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )", + SLAP_MR_ORDERING | SLAP_MR_EXT, NULL, + NULL, NULL, octetStringOrderingMatch, + NULL, NULL, + "octetStringMatch" }, + + {"( 2.5.13.19 NAME 'octetStringSubstringsMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )", + SLAP_MR_SUBSTR, NULL, + NULL, NULL, octetStringSubstringsMatch, + octetStringSubstringsIndexer, octetStringSubstringsFilter, + "octetStringMatch" }, + + {"( 2.5.13.20 NAME 'telephoneNumberMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 )", + SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL, + NULL, + telephoneNumberNormalize, octetStringMatch, + octetStringIndexer, octetStringFilter, + NULL }, + + {"( 2.5.13.21 NAME 'telephoneNumberSubstringsMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */ + SLAP_MR_SUBSTR, NULL, + NULL, telephoneNumberNormalize, octetStringSubstringsMatch, + octetStringSubstringsIndexer, octetStringSubstringsFilter, + "telephoneNumberMatch" }, + + {"( 2.5.13.22 NAME 'presentationAddressMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.43 )", + SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL, + NULL, NULL, NULL, NULL, NULL, NULL }, + + {"( 2.5.13.23 NAME 'uniqueMemberMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.34 )", /* Name And Optional UID */ + SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL, + NULL, uniqueMemberNormalize, uniqueMemberMatch, + uniqueMemberIndexer, uniqueMemberFilter, + NULL }, + + {"( 2.5.13.24 NAME 'protocolInformationMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.42 )", + SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL, + NULL, NULL, NULL, NULL, NULL, NULL }, + + {"( 2.5.13.27 NAME 'generalizedTimeMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )", + SLAP_MR_EQUALITY | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL, + NULL, generalizedTimeNormalize, octetStringMatch, + generalizedTimeIndexer, generalizedTimeFilter, + NULL }, + + {"( 2.5.13.28 NAME 'generalizedTimeOrderingMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )", + SLAP_MR_ORDERING | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL, + NULL, generalizedTimeNormalize, generalizedTimeOrderingMatch, + NULL, NULL, + "generalizedTimeMatch" }, + + {"( 2.5.13.29 NAME 'integerFirstComponentMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )", /* Integer */ + SLAP_MR_EQUALITY | SLAP_MR_EXT, + integerFirstComponentMatchSyntaxes, + NULL, firstComponentNormalize, integerMatch, + octetStringIndexer, octetStringFilter, + NULL }, + + {"( 2.5.13.30 NAME 'objectIdentifierFirstComponentMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )", /* OID */ + SLAP_MR_EQUALITY | SLAP_MR_EXT, + objectIdentifierFirstComponentMatchSyntaxes, + NULL, firstComponentNormalize, octetStringMatch, + octetStringIndexer, octetStringFilter, + NULL }, + + {"( 2.5.13.34 NAME 'certificateExactMatch' " + "SYNTAX 1.3.6.1.1.15.1 )", /* Certificate Exact Assertion */ + SLAP_MR_EQUALITY | SLAP_MR_EXT, certificateExactMatchSyntaxes, + NULL, certificateExactNormalize, octetStringMatch, + octetStringIndexer, octetStringFilter, + NULL }, + + {"( 2.5.13.35 NAME 'certificateMatch' " + "SYNTAX 1.3.6.1.1.15.2 )", /* Certificate Assertion */ + SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL }, + + {"( 2.5.13.38 NAME 'certificateListExactMatch' " + "SYNTAX 1.3.6.1.1.15.5 )", /* Certificate List Exact Assertion */ + SLAP_MR_EQUALITY | SLAP_MR_EXT, certificateListExactMatchSyntaxes, + NULL, certificateListExactNormalize, octetStringMatch, + octetStringIndexer, octetStringFilter, + NULL }, + + {"( 2.5.13.39 NAME 'certificateListMatch' " + "SYNTAX 1.3.6.1.1.15.6 )", /* Certificate List Assertion */ + SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL }, + + {"( 2.5.13.45 NAME 'attributeCertificateExactMatch' " + "SYNTAX " attributeCertificateExactAssertionSyntaxOID " )", + SLAP_MR_EQUALITY | SLAP_MR_EXT | SLAP_MR_HIDE, attributeCertificateExactMatchSyntaxes, + NULL, attributeCertificateExactNormalize, octetStringMatch, + octetStringIndexer, octetStringFilter, + NULL }, + + {"( 2.5.13.46 NAME 'attributeCertificateMatch' " + "SYNTAX " attributeCertificateAssertionSyntaxOID " )", + SLAP_MR_EQUALITY | SLAP_MR_EXT | SLAP_MR_HIDE, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL }, + + {"( 1.3.6.1.4.1.1466.109.114.1 NAME 'caseExactIA5Match' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )", + SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL, + NULL, IA5StringNormalize, octetStringMatch, + octetStringIndexer, octetStringFilter, + IA5StringApproxMatchOID }, + + {"( 1.3.6.1.4.1.1466.109.114.2 NAME 'caseIgnoreIA5Match' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )", + SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL, + NULL, IA5StringNormalize, octetStringMatch, + octetStringIndexer, octetStringFilter, + IA5StringApproxMatchOID }, + + {"( 1.3.6.1.4.1.1466.109.114.3 NAME 'caseIgnoreIA5SubstringsMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )", + SLAP_MR_SUBSTR, NULL, + NULL, IA5StringNormalize, directoryStringSubstringsMatch, + octetStringSubstringsIndexer, octetStringSubstringsFilter, + "caseIgnoreIA5Match" }, + + {"( 1.3.6.1.4.1.4203.1.2.1 NAME 'caseExactIA5SubstringsMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )", + SLAP_MR_SUBSTR, NULL, + NULL, IA5StringNormalize, directoryStringSubstringsMatch, + octetStringSubstringsIndexer, octetStringSubstringsFilter, + "caseExactIA5Match" }, + +#ifdef SLAPD_AUTHPASSWD + /* needs updating */ + {"( 1.3.6.1.4.1.4203.666.4.1 NAME 'authPasswordMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )", /* Octet String */ + SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL, + NULL, NULL, authPasswordMatch, + NULL, NULL, + NULL}, +#endif + + {"( 1.2.840.113556.1.4.803 NAME 'integerBitAndMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )", /* Integer */ + SLAP_MR_EXT, NULL, + NULL, NULL, integerBitAndMatch, + NULL, NULL, + "integerMatch" }, + + {"( 1.2.840.113556.1.4.804 NAME 'integerBitOrMatch' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )", /* Integer */ + SLAP_MR_EXT, NULL, + NULL, NULL, integerBitOrMatch, + NULL, NULL, + "integerMatch" }, + + {"( 1.3.6.1.1.16.2 NAME 'UUIDMatch' " + "SYNTAX 1.3.6.1.1.16.1 )", + SLAP_MR_EQUALITY | SLAP_MR_MUTATION_NORMALIZER, NULL, + NULL, UUIDNormalize, octetStringMatch, + octetStringIndexer, octetStringFilter, + NULL}, + + {"( 1.3.6.1.1.16.3 NAME 'UUIDOrderingMatch' " + "SYNTAX 1.3.6.1.1.16.1 )", + SLAP_MR_ORDERING | SLAP_MR_MUTATION_NORMALIZER, NULL, + NULL, UUIDNormalize, octetStringOrderingMatch, + octetStringIndexer, octetStringFilter, + "UUIDMatch"}, + + {"( 1.3.6.1.4.1.4203.666.11.2.2 NAME 'CSNMatch' " + "SYNTAX 1.3.6.1.4.1.4203.666.11.2.1 )", + SLAP_MR_HIDE | SLAP_MR_EQUALITY | SLAP_MR_ORDERED_INDEX, NULL, + NULL, csnNormalize, csnMatch, + csnIndexer, csnFilter, + NULL}, + + {"( 1.3.6.1.4.1.4203.666.11.2.3 NAME 'CSNOrderingMatch' " + "SYNTAX 1.3.6.1.4.1.4203.666.11.2.1 )", + SLAP_MR_HIDE | SLAP_MR_ORDERING | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL, + NULL, csnNormalize, csnOrderingMatch, + NULL, NULL, + "CSNMatch" }, + + {"( 1.3.6.1.4.1.4203.666.11.2.5 NAME 'CSNSIDMatch' " + "SYNTAX 1.3.6.1.4.1.4203.666.11.2.4 )", + SLAP_MR_HIDE | SLAP_MR_EQUALITY | SLAP_MR_EXT, csnSIDMatchSyntaxes, + NULL, csnSidNormalize, octetStringMatch, + octetStringIndexer, octetStringFilter, + NULL }, + + /* FIXME: OID is unused, but not registered yet */ + {"( 1.3.6.1.4.1.4203.666.4.12 NAME 'authzMatch' " + "SYNTAX 1.3.6.1.4.1.4203.666.2.7 )", /* OpenLDAP authz */ + SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL, + NULL, authzNormalize, authzMatch, + NULL, NULL, + NULL}, + + {NULL, SLAP_MR_NONE, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL } +}; + +int +slap_schema_init( void ) +{ + int res; + int i; + + /* we should only be called once (from main) */ + assert( schema_init_done == 0 ); + + for ( i=0; syntax_defs[i].sd_desc != NULL; i++ ) { + res = register_syntax( &syntax_defs[i] ); + + if ( res ) { + fprintf( stderr, "slap_schema_init: Error registering syntax %s\n", + syntax_defs[i].sd_desc ); + return LDAP_OTHER; + } + } + + for ( i=0; mrule_defs[i].mrd_desc != NULL; i++ ) { + if( mrule_defs[i].mrd_usage == SLAP_MR_NONE && + mrule_defs[i].mrd_compat_syntaxes == NULL ) + { + fprintf( stderr, + "slap_schema_init: Ignoring unusable matching rule %s\n", + mrule_defs[i].mrd_desc ); + continue; + } + + res = register_matching_rule( &mrule_defs[i] ); + + if ( res ) { + fprintf( stderr, + "slap_schema_init: Error registering matching rule %s\n", + mrule_defs[i].mrd_desc ); + return LDAP_OTHER; + } + } + + res = slap_schema_load(); + schema_init_done = 1; + return res; +} + +void +schema_destroy( void ) +{ + oidm_destroy(); + oc_destroy(); + at_destroy(); + mr_destroy(); + mru_destroy(); + syn_destroy(); + + if( schema_init_done ) { + ldap_pvt_thread_mutex_destroy( &ad_index_mutex ); + ldap_pvt_thread_mutex_destroy( &ad_undef_mutex ); + ldap_pvt_thread_mutex_destroy( &oc_undef_mutex ); + } +} diff --git a/servers/slapd/schema_prep.c b/servers/slapd/schema_prep.c new file mode 100644 index 0000000..72a5ddf --- /dev/null +++ b/servers/slapd/schema_prep.c @@ -0,0 +1,1614 @@ +/* schema_prep.c - load builtin schema */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/ctype.h> +#include <ac/string.h> +#include <ac/socket.h> + +#include "slap.h" + +#define OCDEBUG 0 + +int schema_init_done = 0; + +struct slap_internal_schema slap_schema; + +static int +oidValidate( + Syntax *syntax, + struct berval *in ) +{ + struct berval val = *in; + + if( val.bv_len == 0 ) { + /* disallow empty strings */ + return LDAP_INVALID_SYNTAX; + } + + if( DESC_LEADCHAR( val.bv_val[0] ) ) { + val.bv_val++; + val.bv_len--; + if ( val.bv_len == 0 ) return LDAP_SUCCESS; + + while( DESC_CHAR( val.bv_val[0] ) ) { + val.bv_val++; + val.bv_len--; + + if ( val.bv_len == 0 ) return LDAP_SUCCESS; + } + + } else { + int sep = 0; + while( OID_LEADCHAR( val.bv_val[0] ) ) { + val.bv_val++; + val.bv_len--; + + if ( val.bv_val[-1] != '0' ) { + while ( OID_LEADCHAR( val.bv_val[0] )) { + val.bv_val++; + val.bv_len--; + } + } + + if( val.bv_len == 0 ) { + if( sep == 0 ) break; + return LDAP_SUCCESS; + } + + if( !OID_SEPARATOR( val.bv_val[0] )) break; + + sep++; + val.bv_val++; + val.bv_len--; + } + } + + return LDAP_INVALID_SYNTAX; +} + + +static int objectClassPretty( + Syntax *syntax, + struct berval *in, + struct berval *out, + void *ctx ) +{ + ObjectClass *oc; + + if( oidValidate( NULL, in )) return LDAP_INVALID_SYNTAX; + + oc = oc_bvfind( in ); + if( oc == NULL ) return LDAP_INVALID_SYNTAX; + + ber_dupbv_x( out, &oc->soc_cname, ctx ); + return LDAP_SUCCESS; +} + +static int +attributeTypeMatch( + int *matchp, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *value, + void *assertedValue ) +{ + struct berval *a = (struct berval *) assertedValue; + AttributeType *at = at_bvfind( value ); + AttributeType *asserted = at_bvfind( a ); + + if( asserted == NULL ) { + if( OID_LEADCHAR( *a->bv_val ) ) { + /* OID form, return FALSE */ + *matchp = 1; + return LDAP_SUCCESS; + } + + /* desc form, return undefined */ + return LDAP_INVALID_SYNTAX; + } + + if ( at == NULL ) { + /* unrecognized stored value */ + return LDAP_INVALID_SYNTAX; + } + + *matchp = ( asserted != at ); + return LDAP_SUCCESS; +} + +static int +matchingRuleMatch( + int *matchp, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *value, + void *assertedValue ) +{ + struct berval *a = (struct berval *) assertedValue; + MatchingRule *mrv = mr_bvfind( value ); + MatchingRule *asserted = mr_bvfind( a ); + + if( asserted == NULL ) { + if( OID_LEADCHAR( *a->bv_val ) ) { + /* OID form, return FALSE */ + *matchp = 1; + return LDAP_SUCCESS; + } + + /* desc form, return undefined */ + return LDAP_INVALID_SYNTAX; + } + + if ( mrv == NULL ) { + /* unrecognized stored value */ + return LDAP_INVALID_SYNTAX; + } + + *matchp = ( asserted != mrv ); + return LDAP_SUCCESS; +} + +static int +objectClassMatch( + int *matchp, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *value, + void *assertedValue ) +{ + struct berval *a = (struct berval *) assertedValue; + ObjectClass *oc = oc_bvfind( value ); + ObjectClass *asserted = oc_bvfind( a ); + + if( asserted == NULL ) { + if( OID_LEADCHAR( *a->bv_val ) ) { + /* OID form, return FALSE */ + *matchp = 1; + return LDAP_SUCCESS; + } + + /* desc form, return undefined */ + return LDAP_INVALID_SYNTAX; + } + + if ( oc == NULL ) { + /* unrecognized stored value */ + return LDAP_INVALID_SYNTAX; + } + + *matchp = ( asserted != oc ); + return LDAP_SUCCESS; +} + +static int +objectSubClassMatch( + int *matchp, + slap_mask_t flags, + Syntax *syntax, + MatchingRule *mr, + struct berval *value, + void *assertedValue ) +{ + struct berval *a = (struct berval *) assertedValue; + ObjectClass *oc = oc_bvfind( value ); + ObjectClass *asserted = oc_bvfind( a ); + + if( asserted == NULL ) { + if( OID_LEADCHAR( *a->bv_val ) ) { + /* OID form, return FALSE */ + *matchp = 1; + return LDAP_SUCCESS; + } + + /* desc form, return undefined */ + return LDAP_INVALID_SYNTAX; + } + + if ( oc == NULL ) { + /* unrecognized stored value */ + return LDAP_INVALID_SYNTAX; + } + + if( SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX( flags ) ) { + *matchp = ( asserted != oc ); + } else { + *matchp = !is_object_subclass( asserted, oc ); + } + + return LDAP_SUCCESS; +} + +static int objectSubClassIndexer( + slap_mask_t use, + slap_mask_t mask, + Syntax *syntax, + MatchingRule *mr, + struct berval *prefix, + BerVarray values, + BerVarray *keysp, + void *ctx ) +{ + int rc, noc, i; + BerVarray ocvalues; + ObjectClass **socs; + + for( noc=0; values[noc].bv_val != NULL; noc++ ) { + /* just count em */; + } + + /* over allocate */ + socs = slap_sl_malloc( (noc+16) * sizeof( ObjectClass * ), ctx ); + + /* initialize */ + for( i=0; i<noc; i++ ) { + socs[i] = oc_bvfind( &values[i] ); + } + + /* expand values */ + for( i=0; i<noc; i++ ) { + int j; + ObjectClass *oc = socs[i]; + if( oc == NULL || oc->soc_sups == NULL ) continue; + + for( j=0; oc->soc_sups[j] != NULL; j++ ) { + int found = 0; + ObjectClass *sup = oc->soc_sups[j]; + int k; + + for( k=0; k<noc; k++ ) { + if( sup == socs[k] ) { + found++; + break; + } + } + + if( !found ) { + socs = slap_sl_realloc( socs, + sizeof( ObjectClass * ) * (noc+2), ctx ); + + assert( k == noc ); + socs[noc++] = sup; + } + } + } + + ocvalues = slap_sl_malloc( sizeof( struct berval ) * (noc+1), ctx ); + /* copy values */ + for( i=0; i<noc; i++ ) { + if ( socs[i] ) + ocvalues[i] = socs[i]->soc_cname; + else + ocvalues[i] = values[i]; + } + BER_BVZERO( &ocvalues[i] ); + + rc = octetStringIndexer( use, mask, syntax, mr, + prefix, ocvalues, keysp, ctx ); + + slap_sl_free( ocvalues, ctx ); + slap_sl_free( socs, ctx ); + return rc; +} + +#define objectSubClassFilter octetStringFilter + +static ObjectClassSchemaCheckFN rootDseObjectClass; +static ObjectClassSchemaCheckFN aliasObjectClass; +static ObjectClassSchemaCheckFN referralObjectClass; +static ObjectClassSchemaCheckFN subentryObjectClass; +#ifdef LDAP_DYNAMIC_OBJECTS +static ObjectClassSchemaCheckFN dynamicObjectClass; +#endif + +static struct slap_schema_oc_map { + char *ssom_name; + char *ssom_defn; + ObjectClassSchemaCheckFN *ssom_check; + slap_mask_t ssom_flags; + size_t ssom_offset; +} oc_map[] = { + { "top", "( 2.5.6.0 NAME 'top' " + "DESC 'top of the superclass chain' " + "ABSTRACT MUST objectClass )", + 0, 0, offsetof(struct slap_internal_schema, si_oc_top) }, + { "extensibleObject", "( 1.3.6.1.4.1.1466.101.120.111 " + "NAME 'extensibleObject' " + "DESC 'RFC4512: extensible object' " + "SUP top AUXILIARY )", + 0, SLAP_OC_OPERATIONAL, + offsetof(struct slap_internal_schema, si_oc_extensibleObject) }, + { "alias", "( 2.5.6.1 NAME 'alias' " + "DESC 'RFC4512: an alias' " + "SUP top STRUCTURAL " + "MUST aliasedObjectName )", + aliasObjectClass, SLAP_OC_ALIAS|SLAP_OC_OPERATIONAL, + offsetof(struct slap_internal_schema, si_oc_alias) }, + { "referral", "( 2.16.840.1.113730.3.2.6 NAME 'referral' " + "DESC 'namedref: named subordinate referral' " + "SUP top STRUCTURAL MUST ref )", + referralObjectClass, SLAP_OC_REFERRAL|SLAP_OC_OPERATIONAL, + offsetof(struct slap_internal_schema, si_oc_referral) }, + { "LDAProotDSE", "( 1.3.6.1.4.1.4203.1.4.1 " + "NAME ( 'OpenLDAProotDSE' 'LDAProotDSE' ) " + "DESC 'OpenLDAP Root DSE object' " + "SUP top STRUCTURAL MAY cn )", + rootDseObjectClass, SLAP_OC_OPERATIONAL, + offsetof(struct slap_internal_schema, si_oc_rootdse) }, + { "subentry", "( 2.5.17.0 NAME 'subentry' " + "DESC 'RFC3672: subentry' " + "SUP top STRUCTURAL " + "MUST ( cn $ subtreeSpecification ) )", + subentryObjectClass, SLAP_OC_SUBENTRY|SLAP_OC_OPERATIONAL, + offsetof(struct slap_internal_schema, si_oc_subentry) }, + { "subschema", "( 2.5.20.1 NAME 'subschema' " + "DESC 'RFC4512: controlling subschema (sub)entry' " + "AUXILIARY " + "MAY ( dITStructureRules $ nameForms $ dITContentRules $ " + "objectClasses $ attributeTypes $ matchingRules $ " + "matchingRuleUse ) )", + subentryObjectClass, SLAP_OC_OPERATIONAL, + offsetof(struct slap_internal_schema, si_oc_subschema) }, +#ifdef LDAP_COLLECTIVE_ATTRIBUTES + { "collectiveAttributeSubentry", "( 2.5.17.2 " + "NAME 'collectiveAttributeSubentry' " + "DESC 'RFC3671: collective attribute subentry' " + "AUXILIARY )", + subentryObjectClass, + SLAP_OC_COLLECTIVEATTRIBUTESUBENTRY|SLAP_OC_OPERATIONAL|SLAP_OC_HIDE, + offsetof( struct slap_internal_schema, + si_oc_collectiveAttributeSubentry) }, +#endif +#ifdef LDAP_DYNAMIC_OBJECTS + { "dynamicObject", "( 1.3.6.1.4.1.1466.101.119.2 " + "NAME 'dynamicObject' " + "DESC 'RFC2589: Dynamic Object' " + "SUP top AUXILIARY )", + dynamicObjectClass, SLAP_OC_DYNAMICOBJECT, + offsetof(struct slap_internal_schema, si_oc_dynamicObject) }, +#endif + { "glue", "( 1.3.6.1.4.1.4203.666.3.4 " + "NAME 'glue' " + "DESC 'Glue Entry' " + "SUP top STRUCTURAL )", + 0, SLAP_OC_GLUE|SLAP_OC_OPERATIONAL|SLAP_OC_HIDE, + offsetof(struct slap_internal_schema, si_oc_glue) }, + { "syncConsumerSubentry", "( 1.3.6.1.4.1.4203.666.3.5 " + "NAME 'syncConsumerSubentry' " + "DESC 'Persistent Info for SyncRepl Consumer' " + "AUXILIARY " + "MAY syncreplCookie )", + 0, SLAP_OC_SYNCCONSUMERSUBENTRY|SLAP_OC_OPERATIONAL|SLAP_OC_HIDE, + offsetof(struct slap_internal_schema, si_oc_syncConsumerSubentry) }, + { "syncProviderSubentry", "( 1.3.6.1.4.1.4203.666.3.6 " + "NAME 'syncProviderSubentry' " + "DESC 'Persistent Info for SyncRepl Producer' " + "AUXILIARY " + "MAY contextCSN )", + 0, SLAP_OC_SYNCPROVIDERSUBENTRY|SLAP_OC_OPERATIONAL|SLAP_OC_HIDE, + offsetof(struct slap_internal_schema, si_oc_syncProviderSubentry) }, + + { NULL, NULL, NULL, 0, 0 } +}; + +static AttributeTypeSchemaCheckFN rootDseAttribute; +static AttributeTypeSchemaCheckFN aliasAttribute; +static AttributeTypeSchemaCheckFN referralAttribute; +static AttributeTypeSchemaCheckFN subentryAttribute; +static AttributeTypeSchemaCheckFN administrativeRoleAttribute; +#ifdef LDAP_DYNAMIC_OBJECTS +static AttributeTypeSchemaCheckFN dynamicAttribute; +#endif + +static struct slap_schema_ad_map { + char *ssam_name; + char *ssam_defn; + AttributeTypeSchemaCheckFN *ssam_check; + slap_mask_t ssam_flags; + slap_syntax_validate_func *ssam_syn_validate; + slap_syntax_transform_func *ssam_syn_pretty; + slap_mr_convert_func *ssam_mr_convert; + slap_mr_normalize_func *ssam_mr_normalize; + slap_mr_match_func *ssam_mr_match; + slap_mr_indexer_func *ssam_mr_indexer; + slap_mr_filter_func *ssam_mr_filter; + size_t ssam_offset; +} ad_map[] = { + { "objectClass", "( 2.5.4.0 NAME 'objectClass' " + "DESC 'RFC4512: object classes of the entity' " + "EQUALITY objectIdentifierMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )", + NULL, SLAP_AT_FINAL, + oidValidate, objectClassPretty, + NULL, NULL, objectSubClassMatch, + objectSubClassIndexer, objectSubClassFilter, + offsetof(struct slap_internal_schema, si_ad_objectClass) }, + + /* user entry operational attributes */ + { "structuralObjectClass", "( 2.5.21.9 NAME 'structuralObjectClass' " + "DESC 'RFC4512: structural object class of entry' " + "EQUALITY objectIdentifierMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 " + "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )", + NULL, 0, + oidValidate, objectClassPretty, + NULL, NULL, objectSubClassMatch, + objectSubClassIndexer, objectSubClassFilter, + offsetof(struct slap_internal_schema, si_ad_structuralObjectClass) }, + { "createTimestamp", "( 2.5.18.1 NAME 'createTimestamp' " + "DESC 'RFC4512: time which object was created' " + "EQUALITY generalizedTimeMatch " + "ORDERING generalizedTimeOrderingMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 " + "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )", + NULL, SLAP_AT_MANAGEABLE, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_createTimestamp) }, + { "modifyTimestamp", "( 2.5.18.2 NAME 'modifyTimestamp' " + "DESC 'RFC4512: time which object was last modified' " + "EQUALITY generalizedTimeMatch " + "ORDERING generalizedTimeOrderingMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 " + "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )", + NULL, SLAP_AT_MANAGEABLE, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_modifyTimestamp) }, + { "creatorsName", "( 2.5.18.3 NAME 'creatorsName' " + "DESC 'RFC4512: name of creator' " + "EQUALITY distinguishedNameMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 " + "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )", + NULL, SLAP_AT_MANAGEABLE, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_creatorsName) }, + { "modifiersName", "( 2.5.18.4 NAME 'modifiersName' " + "DESC 'RFC4512: name of last modifier' " + "EQUALITY distinguishedNameMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 " + "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )", + NULL, SLAP_AT_MANAGEABLE, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_modifiersName) }, + { "hasSubordinates", "( 2.5.18.9 NAME 'hasSubordinates' " + "DESC 'X.501: entry has children' " + "EQUALITY booleanMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 " + "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )", + NULL, SLAP_AT_DYNAMIC, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_hasSubordinates) }, + { "subschemaSubentry", "( 2.5.18.10 NAME 'subschemaSubentry' " + "DESC 'RFC4512: name of controlling subschema entry' " + "EQUALITY distinguishedNameMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE " + "NO-USER-MODIFICATION USAGE directoryOperation )", + NULL, SLAP_AT_DYNAMIC, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_subschemaSubentry) }, +#ifdef LDAP_COLLECTIVE_ATTRIBUTES + { "collectiveAttributeSubentries", "( 2.5.18.12 " + "NAME 'collectiveAttributeSubentries' " + "DESC 'RFC3671: collective attribute subentries' " + "EQUALITY distinguishedNameMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 " + "NO-USER-MODIFICATION USAGE directoryOperation )", + NULL, SLAP_AT_HIDE, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_collectiveSubentries) }, + { "collectiveExclusions", "( 2.5.18.7 NAME 'collectiveExclusions' " + "DESC 'RFC3671: collective attribute exclusions' " + "EQUALITY objectIdentifierMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 " + "USAGE directoryOperation )", + NULL, SLAP_AT_HIDE, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_collectiveExclusions) }, +#endif + + { "entryDN", "( 1.3.6.1.1.20 NAME 'entryDN' " + "DESC 'DN of the entry' " + "EQUALITY distinguishedNameMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 " + "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )", + NULL, SLAP_AT_DYNAMIC, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_entryDN) }, + { "entryUUID", "( 1.3.6.1.1.16.4 NAME 'entryUUID' " + "DESC 'UUID of the entry' " + "EQUALITY UUIDMatch " + "ORDERING UUIDOrderingMatch " + "SYNTAX 1.3.6.1.1.16.1 " + "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )", + NULL, SLAP_AT_MANAGEABLE, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_entryUUID) }, + { "entryCSN", "( 1.3.6.1.4.1.4203.666.1.7 NAME 'entryCSN' " + "DESC 'change sequence number of the entry content' " + "EQUALITY CSNMatch " + "ORDERING CSNOrderingMatch " + "SYNTAX 1.3.6.1.4.1.4203.666.11.2.1{64} " + "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )", + NULL, SLAP_AT_HIDE, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_entryCSN) }, + { "namingCSN", "( 1.3.6.1.4.1.4203.666.1.13 NAME 'namingCSN' " + "DESC 'change sequence number of the entry naming (RDN)' " + "EQUALITY CSNMatch " + "ORDERING CSNOrderingMatch " + "SYNTAX 1.3.6.1.4.1.4203.666.11.2.1{64} " + "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )", + NULL, SLAP_AT_HIDE, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_namingCSN) }, + +#ifdef LDAP_SUPERIOR_UUID + { "superiorUUID", "( 1.3.6.1.4.1.4203.666.1.11 NAME 'superiorUUID' " + "DESC 'UUID of the superior entry' " + "EQUALITY UUIDMatch " + "ORDERING UUIDOrderingMatch " + "SYNTAX 1.3.6.1.1.16.1 " + "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )", + NULL, SLAP_AT_HIDE, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_superiorUUID) }, +#endif + + { "syncreplCookie", "( 1.3.6.1.4.1.4203.666.1.23 " + "NAME 'syncreplCookie' " + "DESC 'syncrepl Cookie for shadow copy' " + "EQUALITY octetStringMatch " + "ORDERING octetStringOrderingMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 " + "SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation )", + NULL, SLAP_AT_HIDE, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_syncreplCookie) }, + + { "contextCSN", "( 1.3.6.1.4.1.4203.666.1.25 " + "NAME 'contextCSN' " + "DESC 'the largest committed CSN of a context' " + "EQUALITY CSNMatch " + "ORDERING CSNOrderingMatch " + "SYNTAX 1.3.6.1.4.1.4203.666.11.2.1{64} " + "NO-USER-MODIFICATION USAGE dSAOperation )", + NULL, SLAP_AT_HIDE, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_contextCSN) }, + +#ifdef LDAP_SYNC_TIMESTAMP + { "syncTimestamp", "( 1.3.6.1.4.1.4203.666.1.26 NAME 'syncTimestamp' " + "DESC 'Time which object was replicated' " + "EQUALITY generalizedTimeMatch " + "ORDERING generalizedTimeOrderingMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 " + "SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation )", + NULL, 0, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_syncTimestamp) }, +#endif + + /* root DSE attributes */ + { "altServer", "( 1.3.6.1.4.1.1466.101.120.6 NAME 'altServer' " + "DESC 'RFC4512: alternative servers' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 USAGE dSAOperation )", + rootDseAttribute, 0, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_altServer) }, + { "namingContexts", "( 1.3.6.1.4.1.1466.101.120.5 " + "NAME 'namingContexts' " + "DESC 'RFC4512: naming contexts' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 USAGE dSAOperation )", + rootDseAttribute, 0, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_namingContexts) }, + { "supportedControl", "( 1.3.6.1.4.1.1466.101.120.13 " + "NAME 'supportedControl' " + "DESC 'RFC4512: supported controls' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 USAGE dSAOperation )", + rootDseAttribute, 0, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_supportedControl) }, + { "supportedExtension", "( 1.3.6.1.4.1.1466.101.120.7 " + "NAME 'supportedExtension' " + "DESC 'RFC4512: supported extended operations' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 USAGE dSAOperation )", + rootDseAttribute, 0, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_supportedExtension) }, + { "supportedLDAPVersion", "( 1.3.6.1.4.1.1466.101.120.15 " + "NAME 'supportedLDAPVersion' " + "DESC 'RFC4512: supported LDAP versions' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 USAGE dSAOperation )", + rootDseAttribute, 0, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_supportedLDAPVersion) }, + { "supportedSASLMechanisms", "( 1.3.6.1.4.1.1466.101.120.14 " + "NAME 'supportedSASLMechanisms' " + "DESC 'RFC4512: supported SASL mechanisms'" + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 USAGE dSAOperation )", + rootDseAttribute, 0, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_supportedSASLMechanisms) }, + { "supportedFeatures", "( 1.3.6.1.4.1.4203.1.3.5 " + "NAME 'supportedFeatures' " + "DESC 'RFC4512: features supported by the server' " + "EQUALITY objectIdentifierMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 " + "USAGE dSAOperation )", + rootDseAttribute, 0, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_supportedFeatures) }, + { "monitorContext", "( 1.3.6.1.4.1.4203.666.1.10 " + "NAME 'monitorContext' " + "DESC 'monitor context' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 " + "EQUALITY distinguishedNameMatch " + "SINGLE-VALUE NO-USER-MODIFICATION " + "USAGE dSAOperation )", + rootDseAttribute, SLAP_AT_HIDE, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_monitorContext) }, + { "configContext", "( 1.3.6.1.4.1.4203.1.12.2.1 " + "NAME 'configContext' " + "DESC 'config context' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 " + "EQUALITY distinguishedNameMatch " + "SINGLE-VALUE NO-USER-MODIFICATION " + "USAGE dSAOperation )", + rootDseAttribute, SLAP_AT_HIDE, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_configContext) }, + { "vendorName", "( 1.3.6.1.1.4 NAME 'vendorName' " + "DESC 'RFC3045: name of implementation vendor' " + "EQUALITY caseExactMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 " + "SINGLE-VALUE NO-USER-MODIFICATION " + "USAGE dSAOperation )", + rootDseAttribute, 0, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_vendorName) }, + { "vendorVersion", "( 1.3.6.1.1.5 NAME 'vendorVersion' " + "DESC 'RFC3045: version of implementation' " + "EQUALITY caseExactMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 " + "SINGLE-VALUE NO-USER-MODIFICATION " + "USAGE dSAOperation )", + rootDseAttribute, 0, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_vendorVersion) }, + + /* subentry attributes */ + { "administrativeRole", "( 2.5.18.5 NAME 'administrativeRole' " + "DESC 'RFC3672: administrative role' " + "EQUALITY objectIdentifierMatch " + "USAGE directoryOperation " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )", + administrativeRoleAttribute, SLAP_AT_HIDE, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_administrativeRole) }, + { "subtreeSpecification", "( 2.5.18.6 NAME 'subtreeSpecification' " + "DESC 'RFC3672: subtree specification' " + "SINGLE-VALUE " + "USAGE directoryOperation " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.45 )", + subentryAttribute, SLAP_AT_HIDE, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_subtreeSpecification) }, + + /* subschema subentry attributes */ + { "dITStructureRules", "( 2.5.21.1 NAME 'dITStructureRules' " + "DESC 'RFC4512: DIT structure rules' " + "EQUALITY integerFirstComponentMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.17 " + "USAGE directoryOperation ) ", + subentryAttribute, SLAP_AT_HIDE, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_ditStructureRules) }, + { "dITContentRules", "( 2.5.21.2 NAME 'dITContentRules' " + "DESC 'RFC4512: DIT content rules' " + "EQUALITY objectIdentifierFirstComponentMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.16 USAGE directoryOperation )", + subentryAttribute, SLAP_AT_HIDE, + oidValidate, NULL, + NULL, NULL, objectClassMatch, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_ditContentRules) }, + { "matchingRules", "( 2.5.21.4 NAME 'matchingRules' " + "DESC 'RFC4512: matching rules' " + "EQUALITY objectIdentifierFirstComponentMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.30 USAGE directoryOperation )", + subentryAttribute, 0, + oidValidate, NULL, + NULL, NULL, matchingRuleMatch, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_matchingRules) }, + { "attributeTypes", "( 2.5.21.5 NAME 'attributeTypes' " + "DESC 'RFC4512: attribute types' " + "EQUALITY objectIdentifierFirstComponentMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.3 USAGE directoryOperation )", + subentryAttribute, 0, + oidValidate, NULL, + NULL, NULL, attributeTypeMatch, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_attributeTypes) }, + { "objectClasses", "( 2.5.21.6 NAME 'objectClasses' " + "DESC 'RFC4512: object classes' " + "EQUALITY objectIdentifierFirstComponentMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.37 USAGE directoryOperation )", + subentryAttribute, 0, + oidValidate, NULL, + NULL, NULL, objectClassMatch, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_objectClasses) }, + { "nameForms", "( 2.5.21.7 NAME 'nameForms' " + "DESC 'RFC4512: name forms ' " + "EQUALITY objectIdentifierFirstComponentMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.35 USAGE directoryOperation )", + subentryAttribute, SLAP_AT_HIDE, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_nameForms) }, + { "matchingRuleUse", "( 2.5.21.8 NAME 'matchingRuleUse' " + "DESC 'RFC4512: matching rule uses' " + "EQUALITY objectIdentifierFirstComponentMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.31 USAGE directoryOperation )", + subentryAttribute, 0, + oidValidate, NULL, + NULL, NULL, matchingRuleMatch, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_matchingRuleUse) }, + + { "ldapSyntaxes", "( 1.3.6.1.4.1.1466.101.120.16 NAME 'ldapSyntaxes' " + "DESC 'RFC4512: LDAP syntaxes' " + "EQUALITY objectIdentifierFirstComponentMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.54 USAGE directoryOperation )", + subentryAttribute, 0, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_ldapSyntaxes) }, + + /* knowledge information */ + { "aliasedObjectName", "( 2.5.4.1 " + "NAME ( 'aliasedObjectName' 'aliasedEntryName' ) " + "DESC 'RFC4512: name of aliased object' " + "EQUALITY distinguishedNameMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE )", + aliasAttribute, SLAP_AT_FINAL, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_aliasedObjectName) }, + { "ref", "( 2.16.840.1.113730.3.1.34 NAME 'ref' " + "DESC 'RFC3296: subordinate referral URL' " + "EQUALITY caseExactMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 " + "USAGE distributedOperation )", + referralAttribute, 0, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_ref) }, + + /* access control internals */ + { "entry", "( 1.3.6.1.4.1.4203.1.3.1 " + "NAME 'entry' " + "DESC 'OpenLDAP ACL entry pseudo-attribute' " + "SYNTAX 1.3.6.1.4.1.4203.1.1.1 " + "SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation )", + NULL, SLAP_AT_HIDE, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_entry) }, + { "children", "( 1.3.6.1.4.1.4203.1.3.2 " + "NAME 'children' " + "DESC 'OpenLDAP ACL children pseudo-attribute' " + "SYNTAX 1.3.6.1.4.1.4203.1.1.1 " + "SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation )", + NULL, SLAP_AT_HIDE, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_children) }, + + /* access control externals */ + { "authzTo", "( 1.3.6.1.4.1.4203.666.1.8 " + "NAME ( 'authzTo' 'saslAuthzTo' ) " + "DESC 'proxy authorization targets' " + "EQUALITY authzMatch " + "SYNTAX 1.3.6.1.4.1.4203.666.2.7 " + "X-ORDERED 'VALUES' " + "USAGE distributedOperation )", + NULL, SLAP_AT_HIDE, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_saslAuthzTo) }, + { "authzFrom", "( 1.3.6.1.4.1.4203.666.1.9 " + "NAME ( 'authzFrom' 'saslAuthzFrom' ) " + "DESC 'proxy authorization sources' " + "EQUALITY authzMatch " + "SYNTAX 1.3.6.1.4.1.4203.666.2.7 " + "X-ORDERED 'VALUES' " + "USAGE distributedOperation )", + NULL, SLAP_AT_HIDE, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_saslAuthzFrom) }, + +#ifdef LDAP_DYNAMIC_OBJECTS + { "entryTtl", "( 1.3.6.1.4.1.1466.101.119.3 NAME 'entryTtl' " + "DESC 'RFC2589: entry time-to-live' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE " + "NO-USER-MODIFICATION USAGE dSAOperation )", + dynamicAttribute, SLAP_AT_MANAGEABLE, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_entryTtl) }, + { "dynamicSubtrees", "( 1.3.6.1.4.1.1466.101.119.4 " + "NAME 'dynamicSubtrees' " + "DESC 'RFC2589: dynamic subtrees' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 NO-USER-MODIFICATION " + "USAGE dSAOperation )", + rootDseAttribute, 0, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_dynamicSubtrees) }, +#endif + + /* userApplication attributes (which system schema depends upon) */ + { "distinguishedName", "( 2.5.4.49 NAME 'distinguishedName' " + "DESC 'RFC4519: common supertype of DN attributes' " + "EQUALITY distinguishedNameMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )", + NULL, SLAP_AT_ABSTRACT, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_distinguishedName) }, + { "name", "( 2.5.4.41 NAME 'name' " + "DESC 'RFC4519: common supertype of name attributes' " + "EQUALITY caseIgnoreMatch " + "SUBSTR caseIgnoreSubstringsMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} )", + NULL, SLAP_AT_ABSTRACT, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_name) }, + { "cn", "( 2.5.4.3 NAME ( 'cn' 'commonName' ) " + "DESC 'RFC4519: common name(s) for which the entity is known by' " + "SUP name )", + NULL, 0, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_cn) }, + { "uid", "( 0.9.2342.19200300.100.1.1 NAME ( 'uid' 'userid' ) " + "DESC 'RFC4519: user identifier' " + "EQUALITY caseIgnoreMatch " + "SUBSTR caseIgnoreSubstringsMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )", + NULL, 0, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_uid) }, + { "uidNumber", /* for ldapi:// */ + "( 1.3.6.1.1.1.1.0 NAME 'uidNumber' " + "DESC 'RFC2307: An integer uniquely identifying a user " + "in an administrative domain' " + "EQUALITY integerMatch " + "ORDERING integerOrderingMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )", + NULL, 0, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_uidNumber) }, + { "gidNumber", /* for ldapi:// */ + "( 1.3.6.1.1.1.1.1 NAME 'gidNumber' " + "DESC 'RFC2307: An integer uniquely identifying a group " + "in an administrative domain' " + "EQUALITY integerMatch " + "ORDERING integerOrderingMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )", + NULL, 0, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_gidNumber) }, + { "userPassword", "( 2.5.4.35 NAME 'userPassword' " + "DESC 'RFC4519/2307: password of user' " + "EQUALITY octetStringMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40{128} )", + NULL, 0, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_userPassword) }, + + { "labeledURI", "( 1.3.6.1.4.1.250.1.57 NAME 'labeledURI' " + "DESC 'RFC2079: Uniform Resource Identifier with optional label' " + "EQUALITY caseExactMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )", + NULL, 0, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_labeledURI) }, + +#ifdef SLAPD_AUTHPASSWD + { "authPassword", "( 1.3.6.1.4.1.4203.1.3.4 " + "NAME 'authPassword' " + "DESC 'RFC3112: authentication password attribute' " + "EQUALITY 1.3.6.1.4.1.4203.1.2.2 " + "SYNTAX 1.3.6.1.4.1.4203.1.1.2 )", + NULL, 0, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_authPassword) }, + { "supportedAuthPasswordSchemes", "( 1.3.6.1.4.1.4203.1.3.3 " + "NAME 'supportedAuthPasswordSchemes' " + "DESC 'RFC3112: supported authPassword schemes' " + "EQUALITY caseExactIA5Match " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} " + "USAGE dSAOperation )", + subschemaAttribute, 0, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_authPasswordSchemes) }, +#endif + + { "description", "( 2.5.4.13 NAME 'description' " + "DESC 'RFC4519: descriptive information' " + "EQUALITY caseIgnoreMatch " + "SUBSTR caseIgnoreSubstringsMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} )", + NULL, 0, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_description) }, + + { "seeAlso", "( 2.5.4.34 NAME 'seeAlso' " + "DESC 'RFC4519: DN of related object' " + "SUP distinguishedName )", + NULL, 0, + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + offsetof(struct slap_internal_schema, si_ad_seeAlso) }, + + { NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 } +}; + +static AttributeType slap_at_undefined = { + { "1.1.1", NULL, "Catchall for undefined attribute types", 1, NULL, + NULL, NULL, NULL, NULL, + 0, 0, 0, 1, LDAP_SCHEMA_DSA_OPERATION, NULL }, /* LDAPAttributeType */ + BER_BVC("UNDEFINED"), /* cname */ + NULL, /* sup */ + NULL, /* subtypes */ + NULL, NULL, NULL, NULL, /* matching rules routines */ + NULL, /* syntax (will be set later to "octetString") */ + NULL, /* schema check function */ + NULL, /* oidmacro */ + NULL, /* soidmacro */ + SLAP_AT_ABSTRACT|SLAP_AT_FINAL, /* mask */ + { NULL }, /* next */ + NULL /* attribute description */ + /* mutex (don't know how to initialize it :) */ +}; + +static AttributeType slap_at_proxied = { + { "1.1.1", NULL, "Catchall for undefined proxied attribute types", 1, NULL, + NULL, NULL, NULL, NULL, + 0, 0, 0, 0, LDAP_SCHEMA_USER_APPLICATIONS, NULL }, /* LDAPAttributeType */ + BER_BVC("PROXIED"), /* cname */ + NULL, /* sup */ + NULL, /* subtypes */ + NULL, NULL, NULL, NULL, /* matching rules routines (will be set later) */ + NULL, /* syntax (will be set later to "octetString") */ + NULL, /* schema check function */ + NULL, /* oidmacro */ + NULL, /* soidmacro */ + SLAP_AT_ABSTRACT|SLAP_AT_FINAL, /* mask */ + { NULL }, /* next */ + NULL /* attribute description */ + /* mutex (don't know how to initialize it :) */ +}; + +static struct slap_schema_mr_map { + char *ssmm_name; + size_t ssmm_offset; +} mr_map[] = { + { "caseExactIA5Match", + offsetof(struct slap_internal_schema, si_mr_caseExactIA5Match) }, + { "caseExactMatch", + offsetof(struct slap_internal_schema, si_mr_caseExactMatch) }, + { "caseExactSubstringsMatch", + offsetof(struct slap_internal_schema, si_mr_caseExactSubstringsMatch) }, + { "distinguishedNameMatch", + offsetof(struct slap_internal_schema, si_mr_distinguishedNameMatch) }, + { "dnSubtreeMatch", + offsetof(struct slap_internal_schema, si_mr_dnSubtreeMatch) }, + { "dnOneLevelMatch", + offsetof(struct slap_internal_schema, si_mr_dnOneLevelMatch) }, + { "dnSubordinateMatch", + offsetof(struct slap_internal_schema, si_mr_dnSubordinateMatch) }, + { "dnSuperiorMatch", + offsetof(struct slap_internal_schema, si_mr_dnSuperiorMatch) }, + { "integerMatch", + offsetof(struct slap_internal_schema, si_mr_integerMatch) }, + { "integerFirstComponentMatch", + offsetof(struct slap_internal_schema, + si_mr_integerFirstComponentMatch) }, + { "objectIdentifierFirstComponentMatch", + offsetof(struct slap_internal_schema, + si_mr_objectIdentifierFirstComponentMatch) }, + { "caseIgnoreMatch", + offsetof(struct slap_internal_schema, si_mr_caseIgnoreMatch) }, + { "caseIgnoreListMatch", + offsetof(struct slap_internal_schema, si_mr_caseIgnoreListMatch) }, + { NULL, 0 } +}; + +static struct slap_schema_syn_map { + char *sssm_name; + size_t sssm_offset; +} syn_map[] = { + { "1.3.6.1.4.1.1466.115.121.1.15", + offsetof(struct slap_internal_schema, si_syn_directoryString) }, + { "1.3.6.1.4.1.1466.115.121.1.12", + offsetof(struct slap_internal_schema, si_syn_distinguishedName) }, + { "1.3.6.1.4.1.1466.115.121.1.27", + offsetof(struct slap_internal_schema, si_syn_integer) }, + { "1.3.6.1.4.1.1466.115.121.1.40", + offsetof(struct slap_internal_schema, si_syn_octetString) }, + { "1.3.6.1.4.1.1466.115.121.1.3", + offsetof(struct slap_internal_schema, si_syn_attributeTypeDesc) }, + { "1.3.6.1.4.1.1466.115.121.1.16", + offsetof(struct slap_internal_schema, si_syn_ditContentRuleDesc) }, + { "1.3.6.1.4.1.1466.115.121.1.54", + offsetof(struct slap_internal_schema, si_syn_ldapSyntaxDesc) }, + { "1.3.6.1.4.1.1466.115.121.1.30", + offsetof(struct slap_internal_schema, si_syn_matchingRuleDesc) }, + { "1.3.6.1.4.1.1466.115.121.1.31", + offsetof(struct slap_internal_schema, si_syn_matchingRuleUseDesc) }, + { "1.3.6.1.4.1.1466.115.121.1.35", + offsetof(struct slap_internal_schema, si_syn_nameFormDesc) }, + { "1.3.6.1.4.1.1466.115.121.1.37", + offsetof(struct slap_internal_schema, si_syn_objectClassDesc) }, + { "1.3.6.1.4.1.1466.115.121.1.17", + offsetof(struct slap_internal_schema, si_syn_ditStructureRuleDesc) }, + { NULL, 0 } +}; + +int +slap_schema_load( void ) +{ + int i; + + for( i=0; syn_map[i].sssm_name; i++ ) { + Syntax ** synp = (Syntax **) + &(((char *) &slap_schema)[syn_map[i].sssm_offset]); + + assert( *synp == NULL ); + + *synp = syn_find( syn_map[i].sssm_name ); + + if( *synp == NULL ) { + fprintf( stderr, "slap_schema_load: Syntax: " + "No syntax \"%s\" defined in schema\n", + syn_map[i].sssm_name ); + return LDAP_INVALID_SYNTAX; + } + } + + for( i=0; mr_map[i].ssmm_name; i++ ) { + MatchingRule ** mrp = (MatchingRule **) + &(((char *) &slap_schema)[mr_map[i].ssmm_offset]); + + assert( *mrp == NULL ); + + *mrp = mr_find( mr_map[i].ssmm_name ); + + if( *mrp == NULL ) { + fprintf( stderr, "slap_schema_load: MatchingRule: " + "No matching rule \"%s\" defined in schema\n", + mr_map[i].ssmm_name ); + return LDAP_INAPPROPRIATE_MATCHING; + } + } + + slap_at_undefined.sat_syntax = slap_schema.si_syn_octetString; + slap_schema.si_at_undefined = &slap_at_undefined; + + slap_at_proxied.sat_equality = mr_find( "octetStringMatch" ); + slap_at_proxied.sat_approx = mr_find( "octetStringMatch" ); + slap_at_proxied.sat_ordering = mr_find( "octetStringOrderingMatch" ); + slap_at_proxied.sat_substr = mr_find( "octetStringSubstringsMatch" ); + slap_at_proxied.sat_syntax = slap_schema.si_syn_octetString; + slap_schema.si_at_proxied = &slap_at_proxied; + + ldap_pvt_thread_mutex_init( &ad_index_mutex ); + ldap_pvt_thread_mutex_init( &ad_undef_mutex ); + ldap_pvt_thread_mutex_init( &oc_undef_mutex ); + + for( i=0; ad_map[i].ssam_name; i++ ) { + assert( ad_map[i].ssam_defn != NULL ); + { + LDAPAttributeType *at; + int code; + const char *err; + + at = ldap_str2attributetype( ad_map[i].ssam_defn, + &code, &err, LDAP_SCHEMA_ALLOW_ALL ); + if ( !at ) { + fprintf( stderr, + "slap_schema_load: AttributeType \"%s\": %s before %s\n", + ad_map[i].ssam_name, ldap_scherr2str(code), err ); + return code; + } + + if ( at->at_oid == NULL ) { + fprintf( stderr, "slap_schema_load: " + "AttributeType \"%s\": no OID\n", + ad_map[i].ssam_name ); + ldap_attributetype_free( at ); + return LDAP_OTHER; + } + + code = at_add( at, 0, NULL, NULL, &err ); + if ( code ) { + ldap_attributetype_free( at ); + fprintf( stderr, "slap_schema_load: AttributeType " + "\"%s\": %s: \"%s\"\n", + ad_map[i].ssam_name, scherr2str(code), err ); + return code; + } + ldap_memfree( at ); + } + { + int rc; + const char *text; + Syntax *syntax = NULL; + + AttributeDescription ** adp = (AttributeDescription **) + &(((char *) &slap_schema)[ad_map[i].ssam_offset]); + + assert( *adp == NULL ); + + rc = slap_str2ad( ad_map[i].ssam_name, adp, &text ); + if( rc != LDAP_SUCCESS ) { + fprintf( stderr, "slap_schema_load: AttributeType \"%s\": " + "not defined in schema\n", + ad_map[i].ssam_name ); + return rc; + } + + if( ad_map[i].ssam_check ) { + /* install check routine */ + (*adp)->ad_type->sat_check = ad_map[i].ssam_check; + } + /* install flags */ + (*adp)->ad_type->sat_flags |= ad_map[i].ssam_flags; + + /* install custom syntax routines */ + if( ad_map[i].ssam_syn_validate || + ad_map[i].ssam_syn_pretty ) + { + Syntax *syn; + + syntax = (*adp)->ad_type->sat_syntax; + + syn = ch_malloc( sizeof( Syntax ) ); + *syn = *syntax; + + if( ad_map[i].ssam_syn_validate ) { + syn->ssyn_validate = ad_map[i].ssam_syn_validate; + } + if( ad_map[i].ssam_syn_pretty ) { + syn->ssyn_pretty = ad_map[i].ssam_syn_pretty; + } + + (*adp)->ad_type->sat_syntax = syn; + } + + /* install custom rule routines */ + if( syntax != NULL || + ad_map[i].ssam_mr_convert || + ad_map[i].ssam_mr_normalize || + ad_map[i].ssam_mr_match || + ad_map[i].ssam_mr_indexer || + ad_map[i].ssam_mr_filter ) + { + MatchingRule *mr = ch_malloc( sizeof( MatchingRule ) ); + *mr = *(*adp)->ad_type->sat_equality; + + if ( syntax != NULL ) { + mr->smr_syntax = (*adp)->ad_type->sat_syntax; + } + if ( ad_map[i].ssam_mr_convert ) { + mr->smr_convert = ad_map[i].ssam_mr_convert; + } + if ( ad_map[i].ssam_mr_normalize ) { + mr->smr_normalize = ad_map[i].ssam_mr_normalize; + } + if ( ad_map[i].ssam_mr_match ) { + mr->smr_match = ad_map[i].ssam_mr_match; + } + if ( ad_map[i].ssam_mr_indexer ) { + mr->smr_indexer = ad_map[i].ssam_mr_indexer; + } + if ( ad_map[i].ssam_mr_filter ) { + mr->smr_filter = ad_map[i].ssam_mr_filter; + } + + (*adp)->ad_type->sat_equality = mr; + } + } + } + + for( i=0; oc_map[i].ssom_name; i++ ) { + assert( oc_map[i].ssom_defn != NULL ); + { + LDAPObjectClass *oc; + int code; + const char *err; + + oc = ldap_str2objectclass( oc_map[i].ssom_defn, &code, &err, + LDAP_SCHEMA_ALLOW_ALL ); + if ( !oc ) { + fprintf( stderr, "slap_schema_load: ObjectClass " + "\"%s\": %s before %s\n", + oc_map[i].ssom_name, ldap_scherr2str(code), err ); + return code; + } + + if ( oc->oc_oid == NULL ) { + fprintf( stderr, "slap_schema_load: ObjectClass " + "\"%s\": no OID\n", + oc_map[i].ssom_name ); + ldap_objectclass_free( oc ); + return LDAP_OTHER; + } + + code = oc_add(oc,0,NULL,NULL,&err); + if ( code ) { + ldap_objectclass_free( oc ); + fprintf( stderr, "slap_schema_load: ObjectClass " + "\"%s\": %s: \"%s\"\n", + oc_map[i].ssom_name, scherr2str(code), err); + return code; + } + ldap_memfree(oc); + + } + { + ObjectClass ** ocp = (ObjectClass **) + &(((char *) &slap_schema)[oc_map[i].ssom_offset]); + + assert( *ocp == NULL ); + + *ocp = oc_find( oc_map[i].ssom_name ); + if( *ocp == NULL ) { + fprintf( stderr, "slap_schema_load: " + "ObjectClass \"%s\": not defined in schema\n", + oc_map[i].ssom_name ); + return LDAP_OBJECT_CLASS_VIOLATION; + } + + if( oc_map[i].ssom_check ) { + /* install check routine */ + (*ocp)->soc_check = oc_map[i].ssom_check; + } + /* install flags */ + (*ocp)->soc_flags |= oc_map[i].ssom_flags; + } + } + + return LDAP_SUCCESS; +} + +int +slap_schema_check( void ) +{ + /* we should only be called once after schema_init() was called */ + assert( schema_init_done == 1 ); + + /* + * cycle thru attributeTypes to build matchingRuleUse + */ + if ( matching_rule_use_init() ) { + return LDAP_OTHER; + } + + ++schema_init_done; + return LDAP_SUCCESS; +} + +static int rootDseObjectClass ( + Backend *be, + Entry *e, + ObjectClass *oc, + const char** text, + char *textbuf, size_t textlen ) +{ + *text = textbuf; + + if( e->e_nname.bv_len ) { + snprintf( textbuf, textlen, + "objectClass \"%s\" only allowed in the root DSE", + oc->soc_oid ); + return LDAP_OBJECT_CLASS_VIOLATION; + } + + /* we should not be called for the root DSE */ + assert( 0 ); + return LDAP_SUCCESS; +} + +static int aliasObjectClass ( + Backend *be, + Entry *e, + ObjectClass *oc, + const char** text, + char *textbuf, size_t textlen ) +{ + *text = textbuf; + + if( !SLAP_ALIASES(be) ) { + snprintf( textbuf, textlen, + "objectClass \"%s\" not supported in context", + oc->soc_oid ); + return LDAP_OBJECT_CLASS_VIOLATION; + } + + return LDAP_SUCCESS; +} + +static int referralObjectClass ( + Backend *be, + Entry *e, + ObjectClass *oc, + const char** text, + char *textbuf, size_t textlen ) +{ + *text = textbuf; + + if( !SLAP_REFERRALS(be) ) { + snprintf( textbuf, textlen, + "objectClass \"%s\" not supported in context", + oc->soc_oid ); + return LDAP_OBJECT_CLASS_VIOLATION; + } + + return LDAP_SUCCESS; +} + +static int subentryObjectClass ( + Backend *be, + Entry *e, + ObjectClass *oc, + const char** text, + char *textbuf, size_t textlen ) +{ + *text = textbuf; + + if( !SLAP_SUBENTRIES(be) ) { + snprintf( textbuf, textlen, + "objectClass \"%s\" not supported in context", + oc->soc_oid ); + return LDAP_OBJECT_CLASS_VIOLATION; + } + + if( oc != slap_schema.si_oc_subentry && !is_entry_subentry( e ) ) { + snprintf( textbuf, textlen, + "objectClass \"%s\" only allowed in subentries", + oc->soc_oid ); + return LDAP_OBJECT_CLASS_VIOLATION; + } + + return LDAP_SUCCESS; +} + +#ifdef LDAP_DYNAMIC_OBJECTS +static int dynamicObjectClass ( + Backend *be, + Entry *e, + ObjectClass *oc, + const char** text, + char *textbuf, size_t textlen ) +{ + *text = textbuf; + + if( !SLAP_DYNAMIC(be) ) { + snprintf( textbuf, textlen, + "objectClass \"%s\" not supported in context", + oc->soc_oid ); + return LDAP_OBJECT_CLASS_VIOLATION; + } + + return LDAP_SUCCESS; +} +#endif /* LDAP_DYNAMIC_OBJECTS */ + +static int rootDseAttribute ( + Backend *be, + Entry *e, + Attribute *attr, + const char** text, + char *textbuf, size_t textlen ) +{ + *text = textbuf; + + if( e->e_nname.bv_len ) { + snprintf( textbuf, textlen, + "attribute \"%s\" only allowed in the root DSE", + attr->a_desc->ad_cname.bv_val ); + return LDAP_OBJECT_CLASS_VIOLATION; + } + + /* we should not be called for the root DSE */ + assert( 0 ); + return LDAP_SUCCESS; +} + +static int aliasAttribute ( + Backend *be, + Entry *e, + Attribute *attr, + const char** text, + char *textbuf, size_t textlen ) +{ + *text = textbuf; + + if( !SLAP_ALIASES(be) ) { + snprintf( textbuf, textlen, + "attribute \"%s\" not supported in context", + attr->a_desc->ad_cname.bv_val ); + return LDAP_OBJECT_CLASS_VIOLATION; + } + + if( !is_entry_alias( e ) ) { + snprintf( textbuf, textlen, + "attribute \"%s\" only allowed in the alias", + attr->a_desc->ad_cname.bv_val ); + return LDAP_OBJECT_CLASS_VIOLATION; + } + + return LDAP_SUCCESS; +} + +static int referralAttribute ( + Backend *be, + Entry *e, + Attribute *attr, + const char** text, + char *textbuf, size_t textlen ) +{ + *text = textbuf; + + if( !SLAP_REFERRALS(be) ) { + snprintf( textbuf, textlen, + "attribute \"%s\" not supported in context", + attr->a_desc->ad_cname.bv_val ); + return LDAP_OBJECT_CLASS_VIOLATION; + } + + if( !is_entry_referral( e ) ) { + snprintf( textbuf, textlen, + "attribute \"%s\" only allowed in the referral", + attr->a_desc->ad_cname.bv_val ); + return LDAP_OBJECT_CLASS_VIOLATION; + } + + return LDAP_SUCCESS; +} + +static int subentryAttribute ( + Backend *be, + Entry *e, + Attribute *attr, + const char** text, + char *textbuf, size_t textlen ) +{ + *text = textbuf; + + if( !SLAP_SUBENTRIES(be) ) { + snprintf( textbuf, textlen, + "attribute \"%s\" not supported in context", + attr->a_desc->ad_cname.bv_val ); + return LDAP_OBJECT_CLASS_VIOLATION; + } + + if( !is_entry_subentry( e ) ) { + snprintf( textbuf, textlen, + "attribute \"%s\" only allowed in the subentry", + attr->a_desc->ad_cname.bv_val ); + return LDAP_OBJECT_CLASS_VIOLATION; + } + + return LDAP_SUCCESS; +} + +static int administrativeRoleAttribute ( + Backend *be, + Entry *e, + Attribute *attr, + const char** text, + char *textbuf, size_t textlen ) +{ + *text = textbuf; + + if( !SLAP_SUBENTRIES(be) ) { + snprintf( textbuf, textlen, + "attribute \"%s\" not supported in context", + attr->a_desc->ad_cname.bv_val ); + return LDAP_OBJECT_CLASS_VIOLATION; + } + + snprintf( textbuf, textlen, + "attribute \"%s\" not supported!", + attr->a_desc->ad_cname.bv_val ); + return LDAP_OBJECT_CLASS_VIOLATION; +} + +#ifdef LDAP_DYNAMIC_OBJECTS +static int dynamicAttribute ( + Backend *be, + Entry *e, + Attribute *attr, + const char** text, + char *textbuf, size_t textlen ) +{ + *text = textbuf; + + if( !SLAP_DYNAMIC(be) ) { + snprintf( textbuf, textlen, + "attribute \"%s\" not supported in context", + attr->a_desc->ad_cname.bv_val ); + return LDAP_OBJECT_CLASS_VIOLATION; + } + + if( !is_entry_dynamicObject( e ) ) { + snprintf( textbuf, textlen, + "attribute \"%s\" only allowed in dynamic object", + attr->a_desc->ad_cname.bv_val ); + return LDAP_OBJECT_CLASS_VIOLATION; + } + + return LDAP_SUCCESS; +} +#endif /* LDAP_DYNAMIC_OBJECTS */ diff --git a/servers/slapd/schemaparse.c b/servers/slapd/schemaparse.c new file mode 100644 index 0000000..f7bbcda --- /dev/null +++ b/servers/slapd/schemaparse.c @@ -0,0 +1,400 @@ +/* schemaparse.c - routines to parse config file objectclass definitions */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/ctype.h> +#include <ac/string.h> +#include <ac/socket.h> + +#include "slap.h" +#include "ldap_schema.h" +#include "config.h" + +static void oc_usage(void); +static void at_usage(void); + +static char *const err2text[] = { + "Success", + "Out of memory", + "ObjectClass not found", + "user-defined ObjectClass includes operational attributes", + "user-defined ObjectClass has inappropriate SUPerior", + "Duplicate objectClass", + "Inconsistent duplicate objectClass", + "AttributeType not found", + "AttributeType inappropriate matching rule", + "AttributeType inappropriate USAGE", + "AttributeType inappropriate SUPerior", + "AttributeType SYNTAX or SUPerior required", + "Duplicate attributeType", + "Inconsistent duplicate attributeType", + "MatchingRule not found", + "MatchingRule incomplete", + "Duplicate matchingRule", + "Syntax not found", + "Duplicate ldapSyntax", + "Superior syntax not found", + "Substitute syntax not specified", + "Substitute syntax not found", + "OID or name required", + "Qualifier not supported", + "Invalid NAME", + "OID could not be expanded", + "Duplicate Content Rule", + "Content Rule not for STRUCTURAL object class", + "Content Rule AUX contains inappropriate object class", + "Content Rule attribute type list contains duplicate", + NULL +}; + +char * +scherr2str(int code) +{ + if ( code < 0 || SLAP_SCHERR_LAST <= code ) { + return "Unknown error"; + } else { + return err2text[code]; + } +} + +/* check schema descr validity */ +int slap_valid_descr( const char *descr ) +{ + int i=0; + + if( !DESC_LEADCHAR( descr[i] ) ) { + return 0; + } + + while( descr[++i] ) { + if( !DESC_CHAR( descr[i] ) ) { + return 0; + } + } + + return 1; +} + + +/* OID Macros */ + +/* String compare with delimiter check. Return 0 if not + * matched, otherwise return length matched. + */ +int +dscompare(const char *s1, const char *s2, char delim) +{ + const char *orig = s1; + while (*s1++ == *s2++) + if (!s1[-1]) break; + --s1; + --s2; + if (!*s1 && (!*s2 || *s2 == delim)) + return s1 - orig; + return 0; +} + +static void +cr_usage( void ) +{ + fprintf( stderr, + "DITContentRuleDescription = \"(\" whsp\n" + " numericoid whsp ; StructuralObjectClass identifier\n" + " [ \"NAME\" qdescrs ]\n" + " [ \"DESC\" qdstring ]\n" + " [ \"OBSOLETE\" whsp ]\n" + " [ \"AUX\" oids ] ; Auxiliary ObjectClasses\n" + " [ \"MUST\" oids ] ; AttributeTypes\n" + " [ \"MAY\" oids ] ; AttributeTypes\n" + " [ \"NOT\" oids ] ; AttributeTypes\n" + " whsp \")\"\n" ); +} + +int +parse_cr( + struct config_args_s *c, + ContentRule **scr ) +{ + LDAPContentRule *cr; + int code; + const char *err; + char *line = strchr( c->line, '(' ); + + cr = ldap_str2contentrule( line, &code, &err, LDAP_SCHEMA_ALLOW_ALL ); + if ( !cr ) { + 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 ); + cr_usage(); + return 1; + } + + if ( cr->cr_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 ); + cr_usage(); + code = 1; + goto done; + } + + code = cr_add( cr, 1, scr, &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_contentrule_free( cr ); + + } else { + ldap_memfree( cr ); + } + + return code; +} + +int +parse_oc( + struct config_args_s *c, + ObjectClass **soc, + ObjectClass *prev ) +{ + LDAPObjectClass *oc; + int code; + const char *err; + char *line = strchr( c->line, '(' ); + + oc = ldap_str2objectclass(line, &code, &err, LDAP_SCHEMA_ALLOW_ALL ); + if ( !oc ) { + 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 ); + oc_usage(); + return 1; + } + + if ( oc->oc_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 ); + oc_usage(); + code = 1; + goto done; + } + + code = oc_add( oc, 1, soc, 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_objectclass_free( oc ); + + } else { + ldap_memfree( oc ); + } + + return code; +} + +static void +oc_usage( void ) +{ + fprintf( stderr, + "ObjectClassDescription = \"(\" whsp\n" + " numericoid whsp ; ObjectClass identifier\n" + " [ \"NAME\" qdescrs ]\n" + " [ \"DESC\" qdstring ]\n" + " [ \"OBSOLETE\" whsp ]\n" + " [ \"SUP\" oids ] ; Superior ObjectClasses\n" + " [ ( \"ABSTRACT\" / \"STRUCTURAL\" / \"AUXILIARY\" ) whsp ]\n" + " ; default structural\n" + " [ \"MUST\" oids ] ; AttributeTypes\n" + " [ \"MAY\" oids ] ; AttributeTypes\n" + " whsp \")\"\n" ); +} + +static void +at_usage( void ) +{ + fprintf( stderr, "%s%s%s", + "AttributeTypeDescription = \"(\" whsp\n" + " numericoid whsp ; AttributeType identifier\n" + " [ \"NAME\" qdescrs ] ; name used in AttributeType\n" + " [ \"DESC\" qdstring ] ; description\n" + " [ \"OBSOLETE\" whsp ]\n" + " [ \"SUP\" woid ] ; derived from this other\n" + " ; AttributeType\n", + " [ \"EQUALITY\" woid ] ; Matching Rule name\n" + " [ \"ORDERING\" woid ] ; Matching Rule name\n" + " [ \"SUBSTR\" woid ] ; Matching Rule name\n" + " [ \"SYNTAX\" whsp noidlen whsp ] ; see section 4.3\n" + " [ \"SINGLE-VALUE\" whsp ] ; default multi-valued\n" + " [ \"COLLECTIVE\" whsp ] ; default not collective\n", + " [ \"NO-USER-MODIFICATION\" whsp ]; default user modifiable\n" + " [ \"USAGE\" whsp AttributeUsage ]; default userApplications\n" + " ; userApplications\n" + " ; directoryOperation\n" + " ; distributedOperation\n" + " ; dSAOperation\n" + " whsp \")\"\n"); +} + +int +parse_at( + struct config_args_s *c, + AttributeType **sat, + AttributeType *prev ) +{ + LDAPAttributeType *at; + int code; + const char *err; + char *line = strchr( c->line, '(' ); + + at = ldap_str2attributetype( line, &code, &err, LDAP_SCHEMA_ALLOW_ALL ); + if ( !at ) { + 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 ); + at_usage(); + return 1; + } + + if ( at->at_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 ); + at_usage(); + code = 1; + goto done; + } + + /* operational attributes should be defined internally */ + if ( at->at_usage ) { + snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: \"%s\" is operational", + c->argv[0], at->at_oid ); + Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, + "%s %s\n", c->log, c->cr_msg, 0 ); + code = 1; + goto done; + } + + code = at_add( at, 1, sat, 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_attributetype_free( at ); + + } else { + ldap_memfree( at ); + } + + 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; +} + |