Entry * str2entry2( char *s, int checkvals ) { int rc; Entry *e; struct berval *type, *vals, *nvals; char *freeval; AttributeDescription *ad, *ad_prev; const char *text; char *next; int attr_cnt; int i, lines; Attribute ahead, *atail; /* * LDIF is used as the string format. * An entry looks like this: * * dn: <dn>\n * [<attr>:[:] <value>\n] * [<tab><continuedvalue>\n]* * ... * * If a double colon is used after a type, it means the * following value is encoded as a base 64 string. This * happens if the value contains a non-printing character * or newline. */ Debug( LDAP_DEBUG_TRACE, "=> str2entry: \"%s\"\n", s ? s : "NULL" ); e = entry_alloc(); if( e == NULL ) { Debug( LDAP_DEBUG_ANY, "<= str2entry NULL (entry allocation failed)\n" ); return( NULL ); } /* initialize entry */ e->e_id = NOID; /* dn + attributes */ atail = &ahead; ahead.a_next = NULL; ad = NULL; ad_prev = NULL; attr_cnt = 0; next = s; lines = ldif_countlines( s ); type = ch_calloc( 1, (lines+1)*3*sizeof(struct berval)+lines ); vals = type+lines+1; nvals = vals+lines+1; freeval = (char *)(nvals+lines+1); i = -1; /* parse into individual values, record DN */ while ( (s = ldif_getline( &next )) != NULL ) { int freev; if ( *s == '\n' || *s == '\0' ) { break; } i++; if (i >= lines) { Debug( LDAP_DEBUG_TRACE, "<= str2entry ran past end of entry\n" ); goto fail; } rc = ldif_parse_line2( s, type+i, vals+i, &freev ); freeval[i] = freev; if ( rc ) { Debug( LDAP_DEBUG_TRACE, "<= str2entry NULL (parse_line)\n" ); continue; } if ( bvcasematch( &type[i], &dn_bv ) ) { if ( e->e_dn != NULL ) { Debug( LDAP_DEBUG_ANY, "str2entry: " "entry %ld has multiple DNs \"%s\" and \"%s\"\n", (long) e->e_id, e->e_dn, vals[i].bv_val ); goto fail; } rc = dnPrettyNormal( NULL, &vals[i], &e->e_name, &e->e_nname, NULL ); if( rc != LDAP_SUCCESS ) { Debug( LDAP_DEBUG_ANY, "str2entry: " "entry %ld has invalid DN \"%s\"\n", (long) e->e_id, vals[i].bv_val ); goto fail; } if ( freeval[i] ) free( vals[i].bv_val ); vals[i].bv_val = NULL; i--; continue; } } lines = i+1; /* check to make sure there was a dn: line */ if ( BER_BVISNULL( &e->e_name )) { Debug( LDAP_DEBUG_ANY, "str2entry: entry %ld has no dn\n", (long) e->e_id ); goto fail; } /* Make sure all attributes with multiple values are contiguous */ if ( checkvals ) { int j, k; struct berval bv; int fv; for (i=0; i<lines; i++) { for ( j=i+1; j<lines; j++ ) { if ( bvcasematch( type+i, type+j )) { /* out of order, move intervening attributes down */ if ( j != i+1 ) { bv = vals[j]; fv = freeval[j]; for ( k=j; k>i; k-- ) { type[k] = type[k-1]; vals[k] = vals[k-1]; freeval[k] = freeval[k-1]; } k++; type[k] = type[i]; vals[k] = bv; freeval[k] = fv; } i++; } } } } if ( lines > 0 ) { for ( i=0; i<=lines; i++ ) { ad_prev = ad; if ( !ad || ( i<lines && !bvcasematch( type+i, &ad->ad_cname ))) { ad = NULL; rc = slap_bv2ad( type+i, &ad, &text ); if( rc != LDAP_SUCCESS ) { int wtool = ( slapMode & (SLAP_TOOL_MODE|SLAP_TOOL_READONLY) ) == SLAP_TOOL_MODE; Debug( wtool ? LDAP_DEBUG_ANY : LDAP_DEBUG_TRACE, "<= str2entry: str2ad(%s): %s\n", type[i].bv_val, text ); if( wtool ) { goto fail; } rc = slap_bv2undef_ad( type+i, &ad, &text, 0 ); if( rc != LDAP_SUCCESS ) { Debug( LDAP_DEBUG_ANY, "<= str2entry: slap_str2undef_ad(%s): %s\n", type[i].bv_val, text ); goto fail; } } /* require ';binary' when appropriate (ITS#5071) */ if ( slap_syntax_is_binary( ad->ad_type->sat_syntax ) && !slap_ad_is_binary( ad ) ) { Debug( LDAP_DEBUG_ANY, "str2entry: attributeType %s #%d: " "needs ';binary' transfer as per syntax %s\n", ad->ad_cname.bv_val, 0, ad->ad_type->sat_syntax->ssyn_oid ); goto fail; } } if (( ad_prev && ad != ad_prev ) || ( i == lines )) { int j, k; atail->a_next = attr_alloc( NULL ); atail = atail->a_next; atail->a_flags = 0; atail->a_numvals = attr_cnt; atail->a_desc = ad_prev; atail->a_vals = ch_malloc( (attr_cnt + 1) * sizeof(struct berval)); if( ad_prev->ad_type->sat_equality && ad_prev->ad_type->sat_equality->smr_normalize ) atail->a_nvals = ch_malloc( (attr_cnt + 1) * sizeof(struct berval)); else atail->a_nvals = NULL; k = i - attr_cnt; for ( j=0; j<attr_cnt; j++ ) { if ( freeval[k] ) atail->a_vals[j] = vals[k]; else ber_dupbv( atail->a_vals+j, &vals[k] ); vals[k].bv_val = NULL; if ( atail->a_nvals ) { atail->a_nvals[j] = nvals[k]; nvals[k].bv_val = NULL; } k++; } BER_BVZERO( &atail->a_vals[j] ); if ( atail->a_nvals ) { BER_BVZERO( &atail->a_nvals[j] ); } else { atail->a_nvals = atail->a_vals; } attr_cnt = 0; /* FIXME: we only need this when migrating from an unsorted DB */ if ( atail->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL ) { rc = slap_sort_vals( (Modifications *)atail, &text, &j, NULL ); if ( rc == LDAP_SUCCESS ) { atail->a_flags |= SLAP_ATTR_SORTED_VALS; } else if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) { Debug( LDAP_DEBUG_ANY, "str2entry: attributeType %s value #%d provided more than once\n", atail->a_desc->ad_cname.bv_val, j ); goto fail; } } if ( i == lines ) break; } if ( BER_BVISNULL( &vals[i] ) ) { Debug( LDAP_DEBUG_ANY, "str2entry: attributeType %s #%d: " "no value\n", ad->ad_cname.bv_val, attr_cnt ); goto fail; } if ( ad->ad_type->sat_equality && ad->ad_type->sat_equality->smr_normalize ) { rc = ordered_value_normalize( SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX, ad, ad->ad_type->sat_equality, &vals[i], &nvals[i], NULL ); if ( rc ) { Debug( LDAP_DEBUG_ANY, "<= str2entry NULL (smr_normalize %s %d)\n", ad->ad_cname.bv_val, rc ); goto fail; } } attr_cnt++; } } free( type ); atail->a_next = NULL; e->e_attrs = ahead.a_next; Debug(LDAP_DEBUG_TRACE, "<= str2entry(%s) -> 0x%lx\n", e->e_dn, (unsigned long) e ); return( e ); fail: for ( i=0; i<lines; i++ ) { if ( freeval[i] ) free( vals[i].bv_val ); free( nvals[i].bv_val ); } free( type ); entry_free( e ); return NULL; }
/* * Do basic attribute type checking and syntax validation. */ int slap_mods_check( Operation *op, Modifications *ml, const char **text, char *textbuf, size_t textlen, void *ctx ) { int rc; for( ; ml != NULL; ml = ml->sml_next ) { AttributeDescription *ad = NULL; /* convert to attribute description */ if ( ml->sml_desc == NULL ) { rc = slap_bv2ad( &ml->sml_type, &ml->sml_desc, text ); if( rc != LDAP_SUCCESS ) { if ( get_no_schema_check( op )) { rc = slap_bv2undef_ad( &ml->sml_type, &ml->sml_desc, text, 0 ); } } if( rc != LDAP_SUCCESS ) { snprintf( textbuf, textlen, "%s: %s", ml->sml_type.bv_val, *text ); *text = textbuf; return rc; } } ad = ml->sml_desc; if( slap_syntax_is_binary( ad->ad_type->sat_syntax ) && !slap_ad_is_binary( ad )) { /* attribute requires binary transfer */ snprintf( textbuf, textlen, "%s: requires ;binary transfer", ml->sml_type.bv_val ); *text = textbuf; return LDAP_UNDEFINED_TYPE; } if( !slap_syntax_is_binary( ad->ad_type->sat_syntax ) && slap_ad_is_binary( ad )) { /* attribute does not require binary transfer */ snprintf( textbuf, textlen, "%s: disallows ;binary transfer", ml->sml_type.bv_val ); *text = textbuf; return LDAP_UNDEFINED_TYPE; } if( slap_ad_is_tag_range( ad )) { /* attribute requires binary transfer */ snprintf( textbuf, textlen, "%s: inappropriate use of tag range option", ml->sml_type.bv_val ); *text = textbuf; return LDAP_UNDEFINED_TYPE; } #if 0 if ( is_at_obsolete( ad->ad_type ) && (( ml->sml_op != LDAP_MOD_REPLACE && ml->sml_op != LDAP_MOD_DELETE ) || ml->sml_values != NULL )) { /* * attribute is obsolete, * only allow replace/delete with no values */ snprintf( textbuf, textlen, "%s: attribute is obsolete", ml->sml_type.bv_val ); *text = textbuf; return LDAP_CONSTRAINT_VIOLATION; } #endif if ( ml->sml_op == LDAP_MOD_INCREMENT && #ifdef SLAPD_REAL_SYNTAX !is_at_syntax( ad->ad_type, SLAPD_REAL_SYNTAX ) && #endif !is_at_syntax( ad->ad_type, SLAPD_INTEGER_SYNTAX ) ) { /* * attribute values must be INTEGER or REAL */ snprintf( textbuf, textlen, "%s: attribute syntax inappropriate for increment", ml->sml_type.bv_val ); *text = textbuf; return LDAP_CONSTRAINT_VIOLATION; } /* * check values */ if( ml->sml_values != NULL ) { ber_len_t nvals; slap_syntax_validate_func *validate = ad->ad_type->sat_syntax->ssyn_validate; slap_syntax_transform_func *pretty = ad->ad_type->sat_syntax->ssyn_pretty; if( !pretty && !validate ) { *text = "no validator for syntax"; snprintf( textbuf, textlen, "%s: no validator for syntax %s", ml->sml_type.bv_val, ad->ad_type->sat_syntax->ssyn_oid ); *text = textbuf; return LDAP_INVALID_SYNTAX; } /* * check that each value is valid per syntax * and pretty if appropriate */ for ( nvals = 0; !BER_BVISNULL( &ml->sml_values[nvals] ); nvals++ ) { struct berval pval; if ( pretty ) { rc = ordered_value_pretty( ad, &ml->sml_values[nvals], &pval, ctx ); } else { rc = ordered_value_validate( ad, &ml->sml_values[nvals], ml->sml_op ); } if( rc != 0 ) { snprintf( textbuf, textlen, "%s: value #%ld invalid per syntax", ml->sml_type.bv_val, (long) nvals ); *text = textbuf; return LDAP_INVALID_SYNTAX; } if( pretty ) { ber_memfree_x( ml->sml_values[nvals].bv_val, ctx ); ml->sml_values[nvals] = pval; } } ml->sml_values[nvals].bv_len = 0; ml->sml_numvals = nvals; /* * a rough single value check... an additional check is needed * to catch add of single value to existing single valued attribute */ if ((ml->sml_op == LDAP_MOD_ADD || ml->sml_op == LDAP_MOD_REPLACE) && nvals > 1 && is_at_single_value( ad->ad_type )) { snprintf( textbuf, textlen, "%s: multiple values provided", ml->sml_type.bv_val ); *text = textbuf; return LDAP_CONSTRAINT_VIOLATION; } /* if the type has a normalizer, generate the * normalized values. otherwise leave them NULL. * * this is different from the rule for attributes * in an entry - in an attribute list, the normalized * value is set equal to the non-normalized value * when there is no normalizer. */ if( nvals && ad->ad_type->sat_equality && ad->ad_type->sat_equality->smr_normalize ) { ml->sml_nvalues = ber_memalloc_x( (nvals+1)*sizeof(struct berval), ctx ); for ( nvals = 0; !BER_BVISNULL( &ml->sml_values[nvals] ); nvals++ ) { rc = ordered_value_normalize( SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX, ad, ad->ad_type->sat_equality, &ml->sml_values[nvals], &ml->sml_nvalues[nvals], ctx ); if ( rc ) { Debug( LDAP_DEBUG_ANY, "<= str2entry NULL (ssyn_normalize %d)\n", rc, 0, 0 ); snprintf( textbuf, textlen, "%s: value #%ld normalization failed", ml->sml_type.bv_val, (long) nvals ); *text = textbuf; BER_BVZERO( &ml->sml_nvalues[nvals] ); return rc; } } BER_BVZERO( &ml->sml_nvalues[nvals] ); } /* check for duplicates, but ignore Deletes. */ if( nvals > 1 && ml->sml_op != LDAP_MOD_DELETE ) { int i; rc = slap_sort_vals( ml, text, &i, ctx ); if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) { /* value exists already */ snprintf( textbuf, textlen, "%s: value #%d provided more than once", ml->sml_desc->ad_cname.bv_val, i ); *text = textbuf; } if ( rc ) return rc; } } else { ml->sml_numvals = 0; } } return LDAP_SUCCESS; }
int slap_bv2ad( struct berval *bv, AttributeDescription **ad, const char **text ) { int rtn = LDAP_UNDEFINED_TYPE; AttributeDescription desc, *d2; char *name, *options, *optn; char *opt, *next; int ntags; int tagslen; /* hardcoded limits for speed */ #define MAX_TAGGING_OPTIONS 128 struct berval tags[MAX_TAGGING_OPTIONS+1]; #define MAX_TAGS_LEN 1024 char tagbuf[MAX_TAGS_LEN]; assert( ad != NULL ); assert( *ad == NULL ); /* temporary */ if( bv == NULL || BER_BVISNULL( bv ) || BER_BVISEMPTY( bv ) ) { *text = "empty AttributeDescription"; return rtn; } /* make sure description is IA5 */ if( ad_keystring( bv ) ) { *text = "AttributeDescription contains inappropriate characters"; return rtn; } /* find valid base attribute type; parse in place */ desc.ad_cname = *bv; desc.ad_flags = 0; BER_BVZERO( &desc.ad_tags ); name = bv->bv_val; options = ber_bvchr( bv, ';' ); if ( options != NULL && (unsigned) ( options - name ) < bv->bv_len ) { /* don't go past the end of the berval! */ desc.ad_cname.bv_len = options - name; } else { options = NULL; } desc.ad_type = at_bvfind( &desc.ad_cname ); if( desc.ad_type == NULL ) { *text = "attribute type undefined"; return rtn; } if( is_at_operational( desc.ad_type ) && options != NULL ) { *text = "operational attribute with options undefined"; return rtn; } /* * parse options in place */ ntags = 0; tagslen = 0; optn = bv->bv_val + bv->bv_len; for( opt=options; opt != NULL; opt=next ) { int optlen; opt++; next = strchrlen( opt, optn, ';', &optlen ); if( optlen == 0 ) { *text = "zero length option is invalid"; return rtn; } else if ( optlen == STRLENOF("binary") && strncasecmp( opt, "binary", STRLENOF("binary") ) == 0 ) { /* binary option */ if( slap_ad_is_binary( &desc ) ) { *text = "option \"binary\" specified multiple times"; return rtn; } if( !slap_syntax_is_binary( desc.ad_type->sat_syntax )) { /* not stored in binary, disallow option */ *text = "option \"binary\" not supported with type"; return rtn; } desc.ad_flags |= SLAP_DESC_BINARY; continue; } else if ( ad_find_option_definition( opt, optlen ) ) { int i; if( opt[optlen-1] == '-' || ( opt[optlen-1] == '=' && msad_range_hack )) { desc.ad_flags |= SLAP_DESC_TAG_RANGE; } if( ntags >= MAX_TAGGING_OPTIONS ) { *text = "too many tagging options"; return rtn; } /* * tags should be presented in sorted order, * so run the array in reverse. */ for( i=ntags-1; i>=0; i-- ) { int rc; rc = strncasecmp( opt, tags[i].bv_val, (unsigned) optlen < tags[i].bv_len ? (unsigned) optlen : tags[i].bv_len ); if( rc == 0 && (unsigned)optlen == tags[i].bv_len ) { /* duplicate (ignore) */ ntags--; goto done; } else if ( rc > 0 || ( rc == 0 && (unsigned)optlen > tags[i].bv_len )) { AC_MEMCPY( &tags[i+2], &tags[i+1], (ntags-i-1)*sizeof(struct berval) ); tags[i+1].bv_val = opt; tags[i+1].bv_len = optlen; goto done; } } if( ntags ) { AC_MEMCPY( &tags[1], &tags[0], ntags*sizeof(struct berval) ); } tags[0].bv_val = opt; tags[0].bv_len = optlen; done:; tagslen += optlen + 1; ntags++; } else { *text = "unrecognized option"; return rtn; } } if( ntags > 0 ) { int i; if( tagslen > MAX_TAGS_LEN ) { *text = "tagging options too long"; return rtn; } desc.ad_tags.bv_val = tagbuf; tagslen = 0; for( i=0; i<ntags; i++ ) { AC_MEMCPY( &desc.ad_tags.bv_val[tagslen], tags[i].bv_val, tags[i].bv_len ); tagslen += tags[i].bv_len; desc.ad_tags.bv_val[tagslen++] = ';'; } desc.ad_tags.bv_val[--tagslen] = '\0'; desc.ad_tags.bv_len = tagslen; } /* see if a matching description is already cached */ for (d2 = desc.ad_type->sat_ad; d2; d2=d2->ad_next) { if( d2->ad_flags != desc.ad_flags ) { continue; } if( d2->ad_tags.bv_len != desc.ad_tags.bv_len ) { continue; } if( d2->ad_tags.bv_len == 0 ) { break; } if( strncasecmp( d2->ad_tags.bv_val, desc.ad_tags.bv_val, desc.ad_tags.bv_len ) == 0 ) { break; } } /* Not found, add new one */ while (d2 == NULL) { size_t dlen = 0; ldap_pvt_thread_mutex_lock( &desc.ad_type->sat_ad_mutex ); /* check again now that we've locked */ for (d2 = desc.ad_type->sat_ad; d2; d2=d2->ad_next) { if (d2->ad_flags != desc.ad_flags) continue; if (d2->ad_tags.bv_len != desc.ad_tags.bv_len) continue; if (d2->ad_tags.bv_len == 0) break; if (strncasecmp(d2->ad_tags.bv_val, desc.ad_tags.bv_val, desc.ad_tags.bv_len) == 0) break; } if (d2) { ldap_pvt_thread_mutex_unlock( &desc.ad_type->sat_ad_mutex ); break; } /* Allocate a single contiguous block. If there are no * options, we just need space for the AttrDesc structure. * Otherwise, we need to tack on the full name length + * options length, + maybe tagging options length again. */ if (desc.ad_tags.bv_len || desc.ad_flags != SLAP_DESC_NONE) { dlen = desc.ad_type->sat_cname.bv_len + 1; if (desc.ad_tags.bv_len) { dlen += 1 + desc.ad_tags.bv_len; } if ( slap_ad_is_binary( &desc ) ) { dlen += 1 + STRLENOF(";binary") + desc.ad_tags.bv_len; } } d2 = ch_malloc(sizeof(AttributeDescription) + dlen); d2->ad_next = NULL; d2->ad_type = desc.ad_type; d2->ad_flags = desc.ad_flags; d2->ad_cname.bv_len = desc.ad_type->sat_cname.bv_len; d2->ad_tags.bv_len = desc.ad_tags.bv_len; ldap_pvt_thread_mutex_lock( &ad_index_mutex ); d2->ad_index = ++ad_count; ldap_pvt_thread_mutex_unlock( &ad_index_mutex ); if (dlen == 0) { d2->ad_cname.bv_val = d2->ad_type->sat_cname.bv_val; d2->ad_tags.bv_val = NULL; } else { char *cp, *op, *lp; int j; d2->ad_cname.bv_val = (char *)(d2+1); strcpy(d2->ad_cname.bv_val, d2->ad_type->sat_cname.bv_val); cp = d2->ad_cname.bv_val + d2->ad_cname.bv_len; if( slap_ad_is_binary( &desc ) ) { op = cp; lp = NULL; if( desc.ad_tags.bv_len ) { lp = desc.ad_tags.bv_val; while( strncasecmp(lp, "binary", STRLENOF("binary")) < 0 && (lp = strchr( lp, ';' )) != NULL ) ++lp; if( lp != desc.ad_tags.bv_val ) { *cp++ = ';'; j = (lp ? (unsigned) (lp - desc.ad_tags.bv_val - 1) : strlen( desc.ad_tags.bv_val )); cp = lutil_strncopy(cp, desc.ad_tags.bv_val, j); } } cp = lutil_strcopy(cp, ";binary"); if( lp != NULL ) { *cp++ = ';'; cp = lutil_strcopy(cp, lp); } d2->ad_cname.bv_len = cp - d2->ad_cname.bv_val; if( desc.ad_tags.bv_len ) ldap_pvt_str2lower(op); j = 1; } else { j = 0; } if( desc.ad_tags.bv_len ) { lp = d2->ad_cname.bv_val + d2->ad_cname.bv_len + j; if ( j == 0 ) *lp++ = ';'; d2->ad_tags.bv_val = lp; strcpy(lp, desc.ad_tags.bv_val); ldap_pvt_str2lower(lp); if( j == 0 ) d2->ad_cname.bv_len += 1 + desc.ad_tags.bv_len; } } /* Add new desc to list. We always want the bare Desc with * no options to stay at the head of the list, assuming * that one will be used most frequently. */ if (desc.ad_type->sat_ad == NULL || dlen == 0) { d2->ad_next = desc.ad_type->sat_ad; desc.ad_type->sat_ad = d2; } else { d2->ad_next = desc.ad_type->sat_ad->ad_next; desc.ad_type->sat_ad->ad_next = d2; } ldap_pvt_thread_mutex_unlock( &desc.ad_type->sat_ad_mutex ); } if( *ad == NULL ) { *ad = d2; } else { **ad = *d2; } return LDAP_SUCCESS; }