Provisioning for new DSfW Domain Controller fails with -614 error during the Assign Rights task

  • 7009930
  • 27-Dec-2011
  • 19-Sep-2012

Environment

Novell Open Enterprise Server 11
Domain Services for Windows

Situation

During the the provisioning of a newly installed OES2SP3 or OES11 Domain Services for Windows Server, the DSfW provisioning wizard fails with a -614 (duplicate value) error during the 'Assign Rights' task.
The issue is only seen in a name mapped installation scenario where the container to which the domain is mapped, was previously specified as LUM base container for any other OES server in the tree.

Resolution

The issue occurs because of duplicate ACL values being added during the DSfW 'Assign rights' provisioning task. 

There are two possible resolutions to this problem depending on whether the 'Assign Rights' Provisioning task has been executed (and has failed) or the 'Assign Rights' task has not been executed at all.

If the 'Assign Rights' has not been executed at all, the following steps can be executed prior to executing DSfW 'Assign rights' provisioning task in the DSfW Provisioning Wizard.

- Login into to iManager
- Select iManager Role 'Rights'
- Select Task 'Modify Trustees'
- Select the container to which the DSfW forest domain is mapped.
- Locate the [Public] Trustee Name
- Select Assigned Rights.
- Select and remove the following properties:

- gecos
- gidNumber
- homeDirectory
- memberUid
- LoginShell
- uidNumber

Followed by Done and OK.

Now the DSfW Provisioning Wizard should be able to avoid the -614 error being reported and the 'Assign Rights' task should complete.


In case the DSfW 'Assign rights' provisioning task already has been executed and has failed, a script called fix_assign_rights_task.pl which can be executed outside the DSfW provisioning Wizard is available. Please open a Service Request with Novell Technical Services to request the above mentioned script.

The script requires the ADM_PASSWD and NDSEXISTINGADMINPASSWD variables to be exported.
export ADM_PASSWD=<domain-administrator-passwd>
export NDSEXISTINGADMINPASSWD=<tree-admin-passwd>

After exporting the ADM_PASSWD and NDSEXISTINGADMINPASSWD, the script has to be executed using the command below:
perl fix_assign_rights_task.pl

After completion of the script, the DSfW 'Assign rights' provisioning task in the DSfW Provisioning wizard has to be skipped.
All subsequent provisioning tasks have to be executed using the DSfW Provisioning wizard.

Status

Reported to Engineering

Additional Information

Affect OES 11 servers
Resolved with OES11 SP1 
The /var/opt/novell/xad/log/provisioning.log shows the following relevant information:

Importing /var/opt/novell/xad/ds/domain/nds-domain-acls.ldif
ldap_modify: Type or value exists (20)
    additional info: NDS error: duplicate value (-614)

Could not import /var/opt/novell/xad/ds/domain/nds-domain-acls.ldif at
/opt/novell/xad/lib64/perl/Install/frd_install.pm line 551.
 at /opt/novell/xad/lib64/perl/Logger.pm line 119
    Logger::_err('Could not import
/var/opt/novell/xad/ds/domain/nds-domain-acl...') called at
/opt/novell/xad/lib64/perl/Logger.pm line 206
    Logger::Log(0, 'Could not import
/var/opt/novell/xad/ds/domain/nds-domain-acl...') called at
/opt/novell/xad/lib64/perl/Install/frd_install.pm line 570
    frd_install::import_nds_domain_acl('frd_install=HASH(0x792cd8)') called at
/opt/novell/xad/share/dcinit/provision/provision_config_acl.pl line 71

Contact Novell Support and reference this SR for the fix_assign_rights_task.pl or create the script by copying and pasting the below information.

#!/usr/bin/perl -I. -I/opt/novell/xad/lib64/perl -I/opt/novell/xad/lib/perl
################################################################################
# Domain Services for Windows Script
#
# This will take care of handling Assign Rights Task failure due to
# presence of ACLs which are applied with nds-domain-acls.ldif
#
# See the file LICENSE for redistribution information.
#
# Copyright (c) 2001-2012
#       Novell, Inc.  All rights reserved.
################################################################################

$ENV{'PATH'} = "/opt/novell/xad/share/dcinit:$ENV{'PATH'}";
use Getopt::Long;
use XAD::registry;
use Install::frd_install;
use util;
use XAD::secure;
use Logger qw(:DEFAULT);
use File::Copy;
use strict;
use warnings;
use Install::check;

####################
#####Variables######
####################

my %DSfW_Acls = ();
my %DSfW_Original_Acls = ();
my %Existing_Acls = ();
my %Final_Acls = ();
my ($IP_Address, $Admin_Name, $Admin_Passwd, $Mapped_Container) = "";
my $Upgrade = registry::getReg("UPGRADE");
my $ifUpgrade = 0;
my $rc;
my $lower_Trustee = "";

#####################
#####Functions#######
#####################

sub Read_DSfW_ACL_Templates() {

my $Line = "";

# Opening the first template file to read the DSfW specific ACLs
        if($ifUpgrade)
{
   open(TFILE, "/opt/novell/xad/share/dcinit/templates/nds-domain-upgrade-acls.ini") or die "Cannot open the file /opt/novell/xad/share/dcinit/templates/nds-domain-upgrade-acls.ini:$!";
}
else
{
   open(TFILE, "/opt/novell/xad/share/dcinit/templates/nds-domain-acls.ini") or die "Cannot open the file /opt/novell/xad/share/dcinit/templates/nds-domain-acls.ini:$!";
}

while($Line = <TFILE>)
{
chomp $Line;
if($Line  =~ /^ACL/)
{
Create_Acl_Hash("$Line");
}

}

# Closing the first template file
close(TFILE);
# Open the second template file to read the DSfW specific ACLs
if(!$ifUpgrade)
        {
   open(TFILE, "/opt/novell/xad/share/dcinit/templates/nds-domain-lum-acls.ini") or die "Cannot open file '/opt/novell/xad/share/dcinit/templates/nds-domain-lum-acls.ini':$!";

   while($Line = <TFILE>)
   {
   chomp $Line;
   if($Line  =~ /^ACL/)
   {       
   Create_Acl_Hash("$Line");
   }
   }

   # Closing the second template file
   close(TFILE);
}
}

sub Compute_Existing_Acls() {

my ($Adm_Passwd, $Ldap_Admin_Name, $ACL_Entry) = "";

$Ldap_Admin_Name = $Admin_Name;
$Ldap_Admin_Name =~ s/\.([a-z,A-Z,0-9]+)=/,$1=/g;

        if($ifUpgrade != 1)
        {
   if($Admin_Passwd eq "Env:NDSEXISTINGADMINPASSWD")
   {
   if($ENV{'NDSEXISTINGADMINPASSWD'} ne "")
   {
   $Adm_Passwd = $ENV{'NDSEXISTINGADMINPASSWD'};
   }

   if($Adm_Passwd eq "")
   {
   print "\n Administrator password is not set as environment variable\n";
   exit();
   }
   }
   else
   {
   $Adm_Passwd = $Admin_Passwd;
   }
       }
       my $Ldapsearch_filter = ' | sed " /^ / {; H; d; }; /^ /! {; x; s/\n //; }; "';
       my $Command = "";
       if($ifUpgrade)
       {
       $Command = 'LDAPCONF=/etc/opt/novell/xad/openldap/ldap.conf /usr/bin/ldapsearch  -Y EXTERNAL -b "'.$Mapped_Container.'" -s base -LLL '.$Ldapsearch_filter.' | grep "ACL"> /tmp/Existing_domain_Acls';
       }
       else
       {
   $Command = '/opt/novell/eDirectory/bin/ldapsearch -x -h '.$IP_Address.' -D "'.$Ldap_Admin_Name.'" -w "'.$Adm_Passwd.'" -b "'.$Mapped_Container.'" -s base -LLL '.$Ldapsearch_filter.' | grep "ACL"> /tmp/Existing_domain_Acls';
       }

       $rc = `$Command`;

print "The command output [$rc]: [$Command] \n";
if($? != 0)
{
die "Failed to contact LDAP server:$!";
}

# Open the existing acls file to compute the existing acls
open(EFILE,"/tmp/Existing_domain_Acls") or die "Cannot open the '/tmp/Existing_domain_Acls' file:$!";

while($ACL_Entry = <EFILE>)
{
chomp $ACL_Entry;
if($ACL_Entry =~ /^ACL/)
{
# remove the trailing part seperated by semicolon
$ACL_Entry =~ s/\;.+//g;
Create_Existing_Acl_Hash("$ACL_Entry");
}
}

# Closing the file
close(EFILE);

# Delete the file
unlink("/tmp/Existing_domain_Acls");

}

sub Create_Acl_Hash() {

my ($ACL_Entry) = @_;
my ($ACL_Rights, $Temp_Trustee, $Trustee) = "";
my @Tokens = "";

@Tokens = split("=", $ACL_Entry,2);
$ACL_Rights = substr("$Tokens[1]", 0, 1);
$Temp_Trustee = reverse $Tokens[1];
chop $Temp_Trustee;
$Trustee = reverse $Temp_Trustee;
$lower_Trustee = lc $Trustee;
$DSfW_Acls{"$lower_Trustee"} = "$ACL_Rights";
$DSfW_Original_Acls{"$lower_Trustee"} = "$Trustee";
}

sub Create_Existing_Acl_Hash() {

my ($ACL_Entry) = @_;
my ($ACL_Rights, $Temp_Trustee, $Trustee) = "";
my @Tokens = "";

@Tokens = split(":", $ACL_Entry);
$ACL_Rights = substr("$Tokens[1]", 0, 2);
$Temp_Trustee = reverse $Tokens[1];
chop $Temp_Trustee;
# to remove the extra space
chop $Temp_Trustee;
$Trustee = reverse $Temp_Trustee;

# Using Temp_Trustee to hold the Acl_Rights value
$Temp_Trustee = reverse $ACL_Rights;
chop $Temp_Trustee;
$ACL_Rights = reverse $Temp_Trustee;
$lower_Trustee = lc $Trustee;
$Existing_Acls{"$lower_Trustee"} = "$ACL_Rights";
}

sub Compute_Effective_Acls() {

# Compare the keys of DSfW_Acls hash and Existing_Acls hash,
# if the keys match, compare the value associated with the keys
# if the values do not match, take an OR of the two values and store
# the result as the net effective acls rights. Then write an ini and/or 
# ldif to add these effective acls.
my ($temp_key1, $temp_key2, $Final_Acl_Value, $Temp_Acl_Value, $original_temp_key) = "";

foreach $temp_key1 (keys %Existing_Acls)
{
if(defined($DSfW_Acls{"$temp_key1"}))
{
# OR the values associated with the $temp_key1 in the
# DSfW_Acls hash and Existing_Acls hash, and then push the
# resultant union value in the final hash, and then delete the 
# same key-value pair from DSfW_Acls
# Comparing the two values associated with the key in two hashes
if($Existing_Acls{"$temp_key1"} eq $DSfW_Acls{"$temp_key1"})
{
# Leaving the ACLS with matching 'rights' values intact
}
else
{
# OR the two values and append the final hash with 'OR'ed
# value and key
$Final_Acl_Value = $DSfW_Acls{"$temp_key1"} | $Existing_Acls{"$temp_key1"};
$original_temp_key = $DSfW_Original_Acls{"$temp_key1"};
$Final_Acls{"$original_temp_key"} = "$Final_Acl_Value";
}

# Delete the entry from the DSfW_Acls hash
delete $DSfW_Acls{"$temp_key1"};
}
else
{
# Leaving the existing ACLs as they are, as we
# are not going to add/alter them as per DSfW perspective
}
}

# Append the left-over key-value pairs of DSfW_Acls hash to final hash, as 
# no match was found for them.

if(eval(scalar(%DSfW_Acls)))
{
foreach $temp_key2 (keys %DSfW_Acls)
{
$Temp_Acl_Value = $DSfW_Acls{"$temp_key2"};
$original_temp_key = $DSfW_Original_Acls{"$temp_key2"};
$Final_Acls{"$original_temp_key"} = "$Temp_Acl_Value";
}
}
}

sub Write_Effective_Acls() {

my ($keys, $old_value, $new_value, $update, $Value, $Child_Name) = "";
my (@Tokens) = "";
open(FILE, ">/tmp/effective_acls") or die "Cannot open file '/tmp/effective_acls':$!";

print FILE "[DEFAULTROOTDOMAIN]\n";

foreach $keys (keys %Final_Acls)
{
@Tokens = split("#", $keys);
if($Tokens[3] =~ /\[/)
{
# do nothing
$Child_Name = $Tokens[3];
$Child_Name =~ s/\[//g;
$Child_Name =~ s/\]//g;
}
else
{
$Child_Name = $Tokens[3];
}
if($Child_Name ne "")
{
print FILE "CHILD=$Child_Name\n";
}
}

@Tokens = "";
print FILE "\n";

foreach $keys (keys %Final_Acls)
{
@Tokens = split("#", $keys);
if($Tokens[3] =~ /\[/)
{
$Value = $Tokens[3];
}
else
{
$Value = "[" . $Tokens[3] . "]";
}

if($keys =~ /^#/)
{
$new_value = $Final_Acls{$keys} . $keys;
}

my $lower_keys = lc $keys;
if(defined($Existing_Acls{"$lower_keys"}))
{
$old_value = $Existing_Acls{$lower_keys} . $keys;
$update = <<"EOU";
$Value
RDN-Of-Object=DEFAULTROOTDOMAIN
changetype=modify
delete=ACL
ACL=$old_value
-
add=ACL
ACL=$new_value

EOU
}
else
{
$update = <<"EOU";
$Value
RDN-Of-Object=DEFAULTROOTDOMAIN
changetype=modify
add=ACL
ACL=$new_value

EOU
}
print FILE $update;
}
close(FILE);
}

sub create_effective_acls()
{
Read_DSfW_ACL_Templates();
Compute_Existing_Acls();
Compute_Effective_Acls();
Write_Effective_Acls();
}

sub complete_assign_right_task() {

my $type = registry::getReg('INSTALL_TYPE');
my $is_replica = registry::getReg("IS_REPLICA");
my $is_mapped = int(registry::getReg("MAPPED"));
my $retain_policy = lc(registry::getReg("XADRETAINPOLICIES"));
my $inst;
my $msg; 
my $status;

if (not $is_mapped and $type eq "frd" and $is_replica eq "FALSE") {
Log (ERR, "This script has to be run only on Name Mapped Forest Root Server Installation");
}

`grep -q "CONFIGACL:ERROR" /etc/opt/novell/xad/provisioning.xml`;
if ($? == 0) {
Log (ERR, "This script is meant for the failure during Assign Rights Task");
}

$IP_Address = registry::getReg("IP Address");
$Admin_Name = registry::getReg("LDAPADMINNAME");
$Mapped_Container = registry::getReg("Mapped Domain NC");

$Admin_Passwd = $ENV {NDSEXISTINGADMINPASSWD};
if (not defined $Admin_Passwd) {
Log (ERR, "The password not supplied in the environment variable {NDSEXISTINGADMINPASSWD} for [$Admin_Name]\n");
}
if((defined($IP_Address) && defined($Admin_Name) && defined($Admin_Passwd) && defined($Mapped_Container)))
{
create_effective_acls();
}
else {
Log (ERR, "The required parameters are not supplied. \n");
}

my $live_dsstatedir = registry::getReg("LIVE_DSSTATEDIR");

stage::gen_ldif("/tmp/effective_acls", '>',  "$live_dsstatedir/domain/nds-domain-acls_new.ldif", "--defaultrootdomain");
move ("/tmp/effective_acls", "/tmp/effective_acls_new");

# Import the ldif file generated now 
my $output = `LDAPCONF=/etc/opt/novell/xad/openldap/ldap.conf /usr/bin/ldapmodify -Y EXTERNAL -Q -f $live_dsstatedir/domain/nds-domain-acls_new.ldif 2>&1`;
if ($? != 0 ) {
Log (ERR, "ACL apply failed [$output]: $live_dsstatedir/domain/nds-domain-acls_new.ldif \n");
}

$inst = frd_install->new();
if ($is_mapped == 1 and $retain_policy eq "no") {
$inst->import_nds_policies()
}

secure::associate_passwd_policy($retain_policy);

# Stage1 of add_custom_objects
$inst->add_custom_objects ("stage2");

util::send_domain_update_now_signal ();

($status, $msg) = check::post_check ("ACLCONFIGURATION");
Log("Post-check of ACL configuration Passed") if ($status);

}

complete_assign_right_task;