mirror of
https://git.openldap.org/openldap/openldap.git
synced 2024-12-21 03:10:25 +08:00
82fad7d0c8
Includes rewriting of URLs where the DN of the referral object and the DN of the ref attribute attribute are not the same. Also, always returns explicit DN and scope. Currently, back-ldbm only. Needs to be ported to back-bdb.
402 lines
9.9 KiB
C
402 lines
9.9 KiB
C
/* $OpenLDAP$ */
|
|
/*
|
|
* Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
|
|
* COPYING RESTRICTIONS APPLY, see COPYRIGHT file
|
|
*/
|
|
/*
|
|
* Copyright (c) 1995 Regents of the University of Michigan.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms are permitted
|
|
* provided that this notice is preserved and that due credit is given
|
|
* to the University of Michigan at Ann Arbor. The name of the University
|
|
* may not be used to endorse or promote products derived from this
|
|
* software without specific prior written permission. This software
|
|
* is provided ``as is'' without express or implied warranty.
|
|
*/
|
|
|
|
#include "portable.h"
|
|
|
|
#include <stdio.h>
|
|
#include <ac/string.h>
|
|
#include <ac/time.h>
|
|
#include <ac/socket.h>
|
|
|
|
#include "ldap_pvt.h"
|
|
#include "slap.h"
|
|
|
|
static int slap_mods2entry(
|
|
Modifications *mods,
|
|
Entry **e,
|
|
const char **text );
|
|
|
|
int
|
|
do_add( Connection *conn, Operation *op )
|
|
{
|
|
BerElement *ber = op->o_ber;
|
|
char *dn, *ndn, *last;
|
|
ber_len_t len;
|
|
ber_tag_t tag;
|
|
Entry *e;
|
|
Backend *be;
|
|
LDAPModList *modlist = NULL;
|
|
LDAPModList **modtail = &modlist;
|
|
Modifications *mods = NULL;
|
|
const char *text;
|
|
int rc = LDAP_SUCCESS;
|
|
int manageDSAit;
|
|
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG(( "operation", LDAP_LEVEL_ENTRY,
|
|
"do_add: conn %d enter\n", conn->c_connid ));
|
|
#else
|
|
Debug( LDAP_DEBUG_TRACE, "do_add\n", 0, 0, 0 );
|
|
#endif
|
|
/*
|
|
* Parse the add request. It looks like this:
|
|
*
|
|
* AddRequest := [APPLICATION 14] SEQUENCE {
|
|
* name DistinguishedName,
|
|
* attrs SEQUENCE OF SEQUENCE {
|
|
* type AttributeType,
|
|
* values SET OF AttributeValue
|
|
* }
|
|
* }
|
|
*/
|
|
|
|
/* get the name */
|
|
if ( ber_scanf( ber, "{a", /*}*/ &dn ) == LBER_ERROR ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG(( "operation", LDAP_LEVEL_ERR,
|
|
"do_add: conn %d ber_scanf failed\n", conn->c_connid ));
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY, "do_add: ber_scanf failed\n", 0, 0, 0 );
|
|
#endif
|
|
send_ldap_disconnect( conn, op,
|
|
LDAP_PROTOCOL_ERROR, "decoding error" );
|
|
return -1;
|
|
}
|
|
|
|
ndn = ch_strdup( dn );
|
|
|
|
if ( dn_normalize( ndn ) == NULL ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG(( "operation", LDAP_LEVEL_ERR,
|
|
"do_add: conn %d invalid dn (%s)\n", conn->c_connid, dn ));
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY, "do_add: invalid dn (%s)\n", dn, 0, 0 );
|
|
#endif
|
|
send_ldap_result( conn, op, LDAP_INVALID_DN_SYNTAX, NULL,
|
|
"invalid DN", NULL, NULL );
|
|
free( dn );
|
|
free( ndn );
|
|
return LDAP_INVALID_DN_SYNTAX;
|
|
}
|
|
|
|
e = (Entry *) ch_calloc( 1, sizeof(Entry) );
|
|
|
|
e->e_dn = dn;
|
|
e->e_ndn = ndn;
|
|
e->e_attrs = NULL;
|
|
e->e_private = NULL;
|
|
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG(( "operation", LDAP_LEVEL_ARGS,
|
|
"do_add: conn %d ndn (%s)\n", conn->c_connid, e->e_ndn ));
|
|
#else
|
|
Debug( LDAP_DEBUG_ARGS, "do_add: ndn (%s)\n", e->e_ndn, 0, 0 );
|
|
#endif
|
|
|
|
/* get the attrs */
|
|
for ( tag = ber_first_element( ber, &len, &last ); tag != LBER_DEFAULT;
|
|
tag = ber_next_element( ber, &len, last ) )
|
|
{
|
|
LDAPModList *mod = (LDAPModList *) ch_malloc( sizeof(LDAPModList) );
|
|
mod->ml_op = LDAP_MOD_ADD;
|
|
mod->ml_next = NULL;
|
|
|
|
rc = ber_scanf( ber, "{a{V}}", &mod->ml_type, &mod->ml_bvalues );
|
|
|
|
if ( rc == LBER_ERROR ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG(( "operation", LDAP_LEVEL_ERR,
|
|
"do_add: conn %d decoding error \n", conn->c_connid ));
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY, "do_add: decoding error\n", 0, 0, 0 );
|
|
#endif
|
|
send_ldap_disconnect( conn, op,
|
|
LDAP_PROTOCOL_ERROR, "decoding error" );
|
|
rc = -1;
|
|
free( mod );
|
|
goto done;
|
|
}
|
|
|
|
if ( mod->ml_bvalues == NULL ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG(( "operation", LDAP_LEVEL_INFO,
|
|
"do_add: conn %d no values for type %s\n",
|
|
conn->c_connid, mod->ml_type ));
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY, "no values for type %s\n",
|
|
mod->ml_type, 0, 0 );
|
|
#endif
|
|
send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR,
|
|
NULL, "no values for attribute type", NULL, NULL );
|
|
free( mod->ml_type );
|
|
free( mod );
|
|
goto done;
|
|
}
|
|
|
|
*modtail = mod;
|
|
modtail = &mod->ml_next;
|
|
}
|
|
|
|
if ( ber_scanf( ber, /*{*/ "}") == LBER_ERROR ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG(( "operation", LDAP_LEVEL_ERR,
|
|
"do_add: conn %d ber_scanf failed\n", conn->c_connid ));
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY, "do_add: ber_scanf failed\n", 0, 0, 0 );
|
|
#endif
|
|
send_ldap_disconnect( conn, op,
|
|
LDAP_PROTOCOL_ERROR, "decoding error" );
|
|
rc = -1;
|
|
goto done;
|
|
}
|
|
|
|
if( (rc = get_ctrls( conn, op, 1 )) != LDAP_SUCCESS ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG(( "operation", LDAP_LEVEL_INFO,
|
|
"do_add: conn %d get_ctrls failed\n", conn->c_connid ));
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY, "do_add: get_ctrls failed\n", 0, 0, 0 );
|
|
#endif
|
|
goto done;
|
|
}
|
|
|
|
if ( modlist == NULL ) {
|
|
send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR,
|
|
NULL, "no attributes provided", NULL, NULL );
|
|
goto done;
|
|
}
|
|
|
|
Statslog( LDAP_DEBUG_STATS, "conn=%ld op=%d ADD dn=\"%s\"\n",
|
|
op->o_connid, op->o_opid, e->e_ndn, 0, 0 );
|
|
|
|
if( e->e_ndn == NULL || *e->e_ndn == '\0' ) {
|
|
/* protocolError may be a more appropriate error */
|
|
send_ldap_result( conn, op, rc = LDAP_ALREADY_EXISTS,
|
|
NULL, "root DSE already exists",
|
|
NULL, NULL );
|
|
goto done;
|
|
|
|
#if defined( SLAPD_SCHEMA_DN )
|
|
} else if ( strcasecmp( ndn, SLAPD_SCHEMA_DN ) == 0 ) {
|
|
/* protocolError may be a more appropriate error */
|
|
send_ldap_result( conn, op, rc = LDAP_ALREADY_EXISTS,
|
|
NULL, "subschema subentry already exists",
|
|
NULL, NULL );
|
|
goto done;
|
|
#endif
|
|
}
|
|
|
|
manageDSAit = get_manageDSAit( op );
|
|
|
|
/*
|
|
* We could be serving multiple database backends. Select the
|
|
* appropriate one, or send a referral to our "referral server"
|
|
* if we don't hold it.
|
|
*/
|
|
be = select_backend( e->e_ndn, manageDSAit );
|
|
if ( be == NULL ) {
|
|
struct berval **ref = referral_rewrite( default_referral,
|
|
NULL, e->e_dn, LDAP_SCOPE_DEFAULT );
|
|
|
|
send_ldap_result( conn, op, rc = LDAP_REFERRAL,
|
|
NULL, NULL, ref ? ref : default_referral, NULL );
|
|
|
|
ber_bvecfree( ref );
|
|
goto done;
|
|
}
|
|
|
|
/* check restrictions */
|
|
rc = backend_check_restrictions( be, conn, op, NULL, &text ) ;
|
|
if( rc != LDAP_SUCCESS ) {
|
|
send_ldap_result( conn, op, rc,
|
|
NULL, text, NULL, NULL );
|
|
goto done;
|
|
}
|
|
|
|
/* check for referrals */
|
|
rc = backend_check_referrals( be, conn, op, e->e_dn, e->e_ndn );
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* do the add if 1 && (2 || 3)
|
|
* 1) there is an add function implemented in this backend;
|
|
* 2) this backend is master for what it holds;
|
|
* 3) it's a replica and the dn supplied is the updatedn.
|
|
*/
|
|
if ( be->be_add ) {
|
|
/* do the update here */
|
|
int repl_user = (be->be_update_ndn != NULL &&
|
|
strcmp( be->be_update_ndn, op->o_ndn ) == 0);
|
|
#ifndef SLAPD_MULTIMASTER
|
|
if ( be->be_update_ndn == NULL || repl_user )
|
|
#endif
|
|
{
|
|
int update = be->be_update_ndn != NULL;
|
|
char textbuf[SLAP_TEXT_BUFLEN];
|
|
size_t textlen = sizeof textbuf;
|
|
|
|
rc = slap_modlist2mods( modlist, update, &mods, &text,
|
|
textbuf, textlen );
|
|
|
|
if( rc != LDAP_SUCCESS ) {
|
|
send_ldap_result( conn, op, rc,
|
|
NULL, text, NULL, NULL );
|
|
goto done;
|
|
}
|
|
|
|
if ( (be->be_lastmod == ON || (be->be_lastmod == UNDEFINED &&
|
|
global_lastmod == ON)) && !repl_user )
|
|
{
|
|
Modifications **modstail;
|
|
for( modstail = &mods;
|
|
*modstail != NULL;
|
|
modstail = &(*modstail)->sml_next )
|
|
{
|
|
assert( (*modstail)->sml_op == LDAP_MOD_ADD );
|
|
assert( (*modstail)->sml_desc != NULL );
|
|
}
|
|
rc = slap_mods_opattrs( op, modstail, &text );
|
|
if( rc != LDAP_SUCCESS ) {
|
|
send_ldap_result( conn, op, rc,
|
|
NULL, text, NULL, NULL );
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
rc = slap_mods2entry( mods, &e, &text );
|
|
if( rc != LDAP_SUCCESS ) {
|
|
send_ldap_result( conn, op, rc,
|
|
NULL, text, NULL, NULL );
|
|
goto done;
|
|
}
|
|
|
|
if ( (*be->be_add)( be, conn, op, e ) == 0 ) {
|
|
#ifdef SLAPD_MULTIMASTER
|
|
if ( !repl_user )
|
|
#endif
|
|
{
|
|
replog( be, op, e->e_dn, e );
|
|
}
|
|
be_entry_release_w( be, conn, op, e );
|
|
e = NULL;
|
|
}
|
|
|
|
#ifndef SLAPD_MULTIMASTER
|
|
} else {
|
|
struct berval **defref = be->be_update_refs
|
|
? be->be_update_refs : default_referral;
|
|
struct berval **ref = referral_rewrite( defref,
|
|
NULL, e->e_dn, LDAP_SCOPE_DEFAULT );
|
|
|
|
send_ldap_result( conn, op, rc = LDAP_REFERRAL, NULL, NULL,
|
|
ref ? ref : defref, NULL );
|
|
|
|
ber_bvecfree( ref );
|
|
#endif
|
|
}
|
|
} else {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG(( "operation", LDAP_LEVEL_INFO,
|
|
"do_add: conn %d no backend support\n", conn->c_connid ));
|
|
#else
|
|
Debug( LDAP_DEBUG_ARGS, " do_add: no backend support\n", 0, 0, 0 );
|
|
#endif
|
|
send_ldap_result( conn, op, rc = LDAP_UNWILLING_TO_PERFORM,
|
|
NULL, "operation not supported within namingContext", NULL, NULL );
|
|
}
|
|
|
|
done:
|
|
if( modlist != NULL ) {
|
|
slap_modlist_free( modlist );
|
|
}
|
|
if( mods != NULL ) {
|
|
slap_mods_free( mods );
|
|
}
|
|
if( e != NULL ) {
|
|
entry_free( e );
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int slap_mods2entry(
|
|
Modifications *mods,
|
|
Entry **e,
|
|
const char **text )
|
|
{
|
|
Attribute **tail = &(*e)->e_attrs;
|
|
assert( *tail == NULL );
|
|
|
|
for( ; mods != NULL; mods = mods->sml_next ) {
|
|
Attribute *attr;
|
|
|
|
assert( mods->sml_op == LDAP_MOD_ADD );
|
|
assert( mods->sml_desc != NULL );
|
|
|
|
attr = attr_find( (*e)->e_attrs, mods->sml_desc );
|
|
|
|
if( attr != NULL ) {
|
|
#define SLURPD_FRIENDLY
|
|
#ifdef SLURPD_FRIENDLY
|
|
ber_len_t i,j;
|
|
|
|
for( i=0; attr->a_vals[i]; i++ ) {
|
|
/* count them */
|
|
}
|
|
for( j=0; mods->sml_bvalues[j]; j++ ) {
|
|
/* count them */
|
|
}
|
|
j++; /* NULL */
|
|
|
|
attr->a_vals = ch_realloc( attr->a_vals,
|
|
sizeof( struct berval * ) * (i+j) );
|
|
|
|
/* should check for duplicates */
|
|
AC_MEMCPY( &attr->a_vals[i], mods->sml_bvalues,
|
|
sizeof( struct berval * ) * j );
|
|
|
|
/* trim the mods array */
|
|
ch_free( mods->sml_bvalues );
|
|
mods->sml_bvalues = NULL;
|
|
|
|
continue;
|
|
#else
|
|
*text = "attribute provided more than once";
|
|
return LDAP_TYPE_OR_VALUE_EXISTS;
|
|
#endif
|
|
}
|
|
|
|
attr = ch_calloc( 1, sizeof(Attribute) );
|
|
|
|
/* move ad to attr structure */
|
|
attr->a_desc = mods->sml_desc;
|
|
mods->sml_desc = NULL;
|
|
|
|
/* move values to attr structure */
|
|
/* should check for duplicates */
|
|
attr->a_vals = mods->sml_bvalues;
|
|
mods->sml_bvalues = NULL;
|
|
|
|
*tail = attr;
|
|
tail = &attr->a_next;
|
|
}
|
|
|
|
return LDAP_SUCCESS;
|
|
}
|