#!/usr/bin/env perl
#
# BEGIN COPYRIGHT BLOCK
# This Program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; version 2 of the License.
# 
# This Program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License along with
# this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
# Place, Suite 330, Boston, MA 02111-1307 USA.
# 
# In addition, as a special exception, Red Hat, Inc. gives You the additional
# right to link the code of this Program with code not covered under the GNU
# General Public License ("Non-GPL Code") and to distribute linked combinations
# including the two, subject to the limitations in this paragraph. Non-GPL Code
# permitted under this exception must only link to the code of this Program
# through those well defined interfaces identified in the file named EXCEPTION
# found in the source code files (the "Approved Interfaces"). The files of
# Non-GPL Code may instantiate templates or use macros or inline functions from
# the Approved Interfaces without causing the resulting work to be covered by
# the GNU General Public License. Only Red Hat, Inc. may make changes or
# additions to the list of Approved Interfaces. You must obey the GNU General
# Public License in all respects for all of the Program code and other code used
# in conjunction with the Program except the Non-GPL Code covered by this
# exception. If you modify this file, you may extend this exception to your
# version of the file, but you are not obligated to do so. If you do not wish to
# provide this exception without modification, you must delete this exception
# statement from your version and license this file solely under the GPL without
# exception. 
# 
# 
# Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
# Copyright (C) 2013 Red Hat, Inc.
# All rights reserved.
# END COPYRIGHT BLOCK
#

use lib qw(/usr/lib/aarch64-linux-gnu/dirsrv/perl);
use DSUtil;

# enable the use of our bundled perldap with our bundled ldapsdk libraries
# all of this nonsense can be omitted if the mozldapsdk and perldap are
# installed in the operating system locations (e.g. /usr/lib /usr/lib/perl5)

$ENV{'PATH'} = ":/usr/bin:/usr/lib64/mozldap/";
$ENV{'SHLIB_PATH'} = "$ENV{'LD_LIBRARY_PATH'}";

DSUtil::libpath_add("");
DSUtil::libpath_add("/usr/lib");
DSUtil::libpath_add("/usr/lib64");

# Add new password policy specific entries

#############################################################################
# enable the use of Perldap functions
require DynaLoader;

use Getopt::Std;
use Mozilla::LDAP::Conn;
use Mozilla::LDAP::Utils qw(:all);
use Mozilla::LDAP::API qw(:api :ssl :apiv3 :constant); # Direct access to C API

#############################################################################
# Default values of the variables

$opt_D = "";
$opt_p = "";
$opt_h = "";
$opt_Z = "";
$opt_v = 0;

#############################################################################

sub usage {
    print (STDERR "ns-newpwpolicy.pl [-Z serverID] [-v] [-D rootdn] { -w password | -j filename } [-P protocol]\n");
    print (STDERR "                  [-p port] [-h host] -U UserDN -S SuffixDN\n\n");
    print (STDERR "Arguments:\n");
    print (STDERR "        -?           - Display usage\n");
    print (STDERR "        -Z serverID  - Server instance identifier\n");
    print (STDERR "        -v           - Verbose output\n");
    print (STDERR "        -D rootdn    - Directory Manager DN\n");
    print (STDERR "        -w rootpw    - password for the Directory Manager DN\n");
    print (STDERR "        -j filename  - Read the Directory Manager's password from file\n");
    print (STDERR "        -p port      - Port\n");
    print (STDERR "        -h host      - Hostname\n");
    print (STDERR "        -U userDN    - User entry DN\n");
    print (STDERR "        -S suffixDN  - Suffix entry DN\n");
    print (STDERR "        -P protocol  - STARTTLS, LDAPS, LDAPI, LDAP (default: uses most secure protocol available)\n");
    exit 1;
}

# Process the command line arguments
{
    usage() if (!getopts('vD:w:j:p:P:h:U:S:Z:'));

    ($opt_Z, $confdir) = DSUtil::get_server_id($opt_Z, "/etc/default");
    %info = DSUtil::get_info($confdir, $opt_h, $opt_p, $opt_D);
    $info{rootdnpw} = $opt_w;
    $info{protocol} = $opt_P;
    if ($opt_j ne ""){
        die "Error, cannot open password file $opt_j\n" unless (open (RPASS, $opt_j));
        $opt_w = <RPASS>;
        $info{rootdnpw} = chomp($opt_w);
        close(RPASS);
    } 
    
    usage() if( $opt_w eq "" );
    if ($opt_U eq "" && $opt_S eq "") {
        print (STDERR "Please provide at least -S or -U option.\n\n");
    }

    # Now, check if the user/group exists

    if ($opt_S) {
        my $esc_opt_S = $opt_S;
         $esc_opt_S =~ s/,/\\,/g;
         $esc_opt_S =~ s/=/\\=/g;
        print (STDERR "host = $info{host}, port = $info{port}, suffixDN = \"$opt_S\"\n\n") if $opt_v;

        $container="dn: cn=nsPwPolicyContainer,$opt_S\nobjectclass: top\nobjectclass: nsContainer\n\n";
        $pwpolicy="dn: cn=cn\\=nsPwPolicyEntry\\,$esc_opt_S,cn=nsPwPolicyContainer,$opt_S\n" . 
                  "objectclass: top\nobjectclass: ldapsubentry\nobjectclass: passwordpolicy\n\n";
        $template="dn: cn=cn\\=nsPwTemplateEntry\\,$esc_opt_S,cn=nsPwPolicyContainer,$opt_S\n" . 
                  "objectclass: top\nobjectclass: extensibleObject\nobjectclass: costemplate\n" . 
                  "objectclass: ldapsubentry\ncosPriority: 1\n" . 
                  "pwdpolicysubentry: cn=cn\\=nsPwPolicyEntry\\,$esc_opt_S,cn=nsPwPolicyContainer,$opt_S\n\n";
        $cos="dn: cn=nsPwPolicy_cos,$opt_S\nobjectclass: top\nobjectclass: LDAPsubentry\n" . 
             "objectclass: cosSuperDefinition\nobjectclass: cosPointerDefinition\n" . 
             "cosTemplateDn: cn=cn\\=nsPwTemplateEntry\\,$esc_opt_S,cn=nsPwPolicyContainer,$opt_S\n" . 
             "cosAttribute: pwdpolicysubentry default operational-default\n\n";
        $entries = $container . $pwpolicy . $template . $cos;

        $info{args} = "-c -a";
        $retcode = DSUtil::ldapmod($entries, %info);
        if ( $retcode != 0 && $retcode != 68 ) {
            print( STDERR "Error $retcode while adding pwpolicy entries. Exiting.\n" );
            exit ($retcode);
        } else {
            print( STDERR "Successfully added pwpolicy entries\n\n") if $opt_v;
        }
        
        $info{args} = "";
        $modConfig = "dn:cn=config\nchangetype: modify\nreplace:nsslapd-pwpolicy-local\nnsslapd-pwpolicy-local: on\n\n";
        $retcode = DSUtil::ldapmod($modConfig, %info);
        if ( $retcode != 0 ) {
            print( STDERR "Error $retcode while modifing \"cn=config\". Exiting.\n" );
            exit ($retcode);
        } else {
            print( STDERR "Entry \"cn=config\" modified\n\n") if $opt_v;
        }
    } # end of $opt_S

    if ($opt_U) {
        my $norm_opt_U = normalizeDN($opt_U);
        my $esc_opt_U = $norm_opt_U;
        $esc_opt_U =~ s/,/\\,/g;
        $esc_opt_U =~ s/=/\\=/g;
        print (STDERR "host = $info{host}, port = $info{port}, userDN = \"$norm_opt_U\"\n\n") if $opt_v;
        $info{base} = $norm_opt_U;
        $info{filter} = "";
        $info{scope} = "base";
        $info{attrs} = "";
        $retcode = DSUtil::ldapsrch_ext(%info);
        if ($retcode != 0 ) {
            print( STDERR "the user entry $norm_opt_U does not exist. Error $retcode\n");
            exit ($retcode);
        }
        
        print( STDERR "the user entry $norm_opt_U found..\n\n") if $opt_v;
        
        # Now, get the parentDN 
        @rdns = ldap_explode_dn($norm_opt_U, 0);
        shift @rdns;
        $parentDN = join(',', @rdns);

        print (STDERR "parentDN is $parentDN\n\n") if $opt_v;

        $info{args} = "-c -a";
        my $containers="dn: cn=nsPwPolicyContainer,$parentDN\n" . 
                       "objectclass: top\n" . 
                       "objectclass: nsContainer\n\n" .
                       "dn: cn=cn\\=nsPwPolicyEntry\\,$esc_opt_U,cn=nsPwPolicyContainer,$parentDN\n" . 
                       "objectclass: top\n" . 
                       "objectclass: ldapsubentry\nobjectclass: passwordpolicy\n";
        $retcode = DSUtil::ldapmod($containers, %info);
        if ( $retcode != 0 && $retcode != 68 ) {
            print( STDERR "Error $retcode while adding container entries.\n" );
            exit ($retcode);
        } else {
            print (STDERR "Container entries added.\n");
        }
        
        $info{args} = "";
        $target = "cn=cn\\=nsPwPolicyEntry\\,$esc_opt_U,cn=nsPwPolicyContainer,$parentDN";
        $modConfig = "dn: $norm_opt_U\nchangetype: modify\nreplace:pwdpolicysubentry\npwdpolicysubentry: $target\n\n";
        $retcode = DSUtil::ldapmod($modConfig, %info);
        if ( $retcode != 0 ) {
            print( STDERR "Error $retcode while modifing $norm_opt_U. Exiting.\n" );
            exit ($retcode);
        } else {
            print( STDERR "Entry \"$norm_opt_U\" modified\n\n") if $opt_v;
        }

        $modConfig = "dn:cn=config\nchangetype: modify\nreplace:nsslapd-pwpolicy-local\nnsslapd-pwpolicy-local: on\n\n";
        $retcode = DSUtil::ldapmod($modConfig, %info);
        if( $retcode != 0 ) {
            print( STDERR "Error $retcode while modifing \"cn=config\"." );
            exit ($retcode);
        } else {
            print( STDERR "Entry \"cn=config\" modified\n\n") if $opt_v;
        }
    } # end of $opt_U
}
