• bmortier's avatar
    Merge branch... · c59594ee
    bmortier authored
    Merge branch '5773-change-the-url-for-the-bug-tracker-into-the-fusiondirectory-manpages' into '1.3-dev'
    
    Resolve "Change the url for the bug tracker into the fusiondirectory manpages"
    
    Closes #5773
    
    See merge request fusiondirectory/fd!140
    
    (cherry picked from commit 0432aab6)
    
    fa775a3a :ambulance: fix(manpages): Change the url for the bug tracker into the fusiondirectory manpages
    c59594ee
fusiondirectory-setup 67.04 KiB
#!/usr/bin/perl
########################################################################
#  fusiondirectory-setup
#  Manage fusiondirectory installs from the command line
#  This code is part of FusionDirectory (http://www.fusiondirectory.org/)
#  Copyright (C) 2011-2018  FusionDirectory
#  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; either version 2 of the License, or
#  (at your option) any later version.
#  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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
########################################################################
use strict;
use warnings;
use 5.008;
# used to manage files
use Path::Class;
# used for checking config dirs rights (make the translation for lstat output)
use Fcntl ':mode';
# used to handle ldap connections
use Net::LDAP;
# used to base64 encode
use MIME::Base64;
# used to generate {SSHA} password (for LDAP)
use Digest::SHA;
use Crypt::CBC;
# used to uncompress tar.gz
use Archive::Extract;
# used to copy files
use File::Copy::Recursive qw(rcopy);
#XML parser
use XML::Twig;
# To hide password input
use Term::ReadKey;
use Data::Dumper;
# fd's directory and class.cache file's path declaration
my %vars = (
 fd_home          => "/var/www/fusiondirectory",
 fd_cache         => "/var/cache/fusiondirectory",
 fd_config_dir    => "/etc/fusiondirectory",
 fd_smarty_dir    => "/usr/share/php/smarty3",
 fd_spool_dir     => "/var/spool/fusiondirectory",
 ldap_conf        => "/etc/ldap/ldap.conf",
 config_file      => "fusiondirectory.conf",
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
secrets_file => "fusiondirectory.secrets", locale_dir => "locale", class_cache => "class.cache", locale_cache_dir => "locale", tmp_dir => "tmp", fai_log_dir => "fai", template_dir => "template" ); my ($fd_config,$fd_secrets,$locale_dir,$class_cache,$locale_cache_dir,$tmp_dir,$fai_log_dir,$template_dir); my (@root_config_dirs,@apache_config_dirs,@config_dirs); my @plugin_types = qw(addons admin personal); my $yes_flag = 0; my %classes_hash_result = (); my %i18n_hash_result = (); my $configrdn = "cn=config,ou=fusiondirectory"; my $userrdn = "ou=people"; my $aclrolerdn = "ou=aclroles"; my $grouprdn = "ou=groups"; my $systemrdn = "ou=systems"; my $dnsrdn = "ou=dns"; my $dhcprdn = "ou=dhcp"; my $workstationrdn = "ou=workstations,ou=systems"; my $winstationrdn = "ou=computers,ou=systems"; ################################################################################################################################################# # ask a question send as parameter, and return true if the answer is "yes" sub ask_yn_question { return 1 if ($yes_flag); my ($question) = @_; print ( "$question [Yes/No]?\n" ); while ( my $input = <STDIN> ) { # remove the \n at the end of $input chomp $input; # if user's answer is "yes" if ( lc($input) eq "yes" || lc($input) eq "y") { return 1; # else if he answer "no" } elsif ( lc($input) eq "no" || lc($input) eq "n") { return 0; } } } # function that ask for an user input and do some checks sub ask_user_input { my ($thing_to_ask, $default_answer, $hide_input) = @_; my $answer; if (defined $default_answer) { $thing_to_ask .= " [$default_answer]"; } print $thing_to_ask.":\n"; if (defined $hide_input && $hide_input) { ReadMode('noecho'); } do { if ($answer = <STDIN>) { chomp $answer; $answer =~ s/^\s+|\s+$//g;
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
} else { $answer = ""; } } while (($answer eq "") && (not defined $default_answer)); ReadMode('restore'); if ($answer eq "") { return $default_answer; } return $answer; } # Die on all LDAP error except for «No such object» sub die_on_ldap_errors { my ($mesg) = @_; if (($mesg->code != 0) && ($mesg->code != 32)) { die $mesg->error; } } { my $indice = 0; sub find_free_role_dn { my ($ldap,$base,$prefix) = @_; my ($cn,$dn,$mesg); do { $cn = $prefix.'-'.$indice; $dn = "cn=$cn,$aclrolerdn,$base"; $indice++; $mesg = $ldap->search( base => "$dn", scope => 'base', filter => '(objectClass=*)' ); die_on_ldap_errors($mesg); } while ($mesg->count); return $cn; } } sub create_role { my ($ldap,$base,$cn,$acl) = @_; my %role = ( 'cn' => "$cn", 'objectclass' => [ 'top', 'gosaRole' ], 'gosaAclTemplate' => "0:$acl" ); if (!branch_exists($ldap, "$aclrolerdn,$base")) { create_branch($ldap, $base, $aclrolerdn); } my $role_dn = "cn=$cn,$aclrolerdn,$base"; # Add the administator role object my @options = %role; my $role_add = $ldap->add( $role_dn, attr => \@options ); # send a warning if the ldap's admin's add didn't gone well $role_add->code && die "\n! failed to add LDAP's $role_dn entry - ".$role_add->error_name.": ".$role_add->error_text; return $role_dn; } ###################################################### Password encryption ######################################################################### sub cred_encrypt { my ($input, $password) = @_; my $cipher = Crypt::CBC->new( -key => $password, -cipher => 'Rijndael',
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
-salt => 1, -header => 'salt', ) || die "Couldn't create CBC object"; return $cipher->encrypt_hex($input); } sub cred_decrypt { my ($input, $password) = @_; my $cipher = Crypt::CBC->new( -key => $password, -cipher => 'Rijndael', -salt => 1, -header => 'salt', ) || die "Couldn't create CBC object"; return $cipher->decrypt_hex($input); } sub get_random_string { my ($size) = @_; $size = 32 if !$size; my @chars = ("A".."Z", "a".."z", '.', '/', 0..9); my $string; $string .= $chars[rand @chars] for 1..$size; return $string; } sub encrypt_passwords { if (!-e $fd_config) { die "Cannot find a valid configuration file ($fd_config)!\n"; } if (-e $fd_secrets) { die "There's already a file '$fd_secrets'. Cannot convert your existing fusiondirectory.conf - aborted\n"; } print "Starting password encryption\n"; print "* generating random master key\n"; my $master_key = get_random_string(); print "* creating '$fd_secrets'\n"; my $fp_file = file($fd_secrets); my $fp = $fp_file->openw() or die "! Unable to open '$fd_secrets' in write mode\n"; $fp->print("RequestHeader set FDKEY $master_key\n"); $fp->close or die "! Can't close '$fd_secrets'\n"; chmod 0600, $fd_secrets or die "! Unable to change '$fd_secrets' rights\n"; my $root_uid = getpwnam("root"); my $root_gid = getgrnam("root"); chown $root_uid,$root_gid,$fd_secrets or die "! Unable to change '$fd_secrets' owner\n"; # Move original fusiondirectory.conf out of the way and make it unreadable for the web user print "* creating backup in '$fd_config.orig'\n"; rcopy($fd_config, "$fd_config.orig"); chmod 0600, "$fd_config.orig" or die "! Unable to change '$fd_config.orig' rights\n"; chown $root_uid,$root_gid,"$fd_config.orig" or die "! Unable to change '$fd_config.orig' owner\n"; print "* loading '$fd_config'\n"; my $twig = XML::Twig->new(); # create the twig $twig->parsefile($fd_config); # build it # Locate all passwords inside the fusiondirectory.conf my @locs = $twig->root->first_child('main')->children('location'); foreach my $loc (@locs) { my $ref = $loc->first_child('referral'); print "* encrypting FusionDirectory password for: ".$ref->{'att'}->{'adminDn'}."\n"; $ref->set_att('adminPassword' => cred_encrypt($ref->{'att'}->{'adminPassword'}, $master_key)); } # Save print "* saving modified '$fd_config'\n"; $twig->print_to_file($fd_config, pretty_print => 'indented') or die "Cannot write modified $fd_config - aborted\n"; print "OK\n\n"; # Print reminder print "
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
Please adapt your http fusiondirectory location declaration to include the newly created $fd_secrets. Example: Alias /fusiondirectory /usr/share/fusiondirectory/html <Location /fusiondirectory> php_admin_flag engine on php_admin_flag register_globals off php_admin_flag allow_call_time_pass_reference off php_admin_flag expose_php off php_admin_flag zend.ze1_compatibility_mode off php_admin_flag register_long_arrays off php_admin_value upload_tmp_dir /var/spool/fusiondirectory/ php_admin_value session.cookie_lifetime 0 include /etc/fusiondirectory/fusiondirectory.secrets </Location> Please reload your httpd configuration after you've modified anything.\n"; } ####################################################### class.cache update ######################################################################### # function that scan recursivly a directory to find .inc and . php # then return a hash with class => path to the class file sub get_classes { my ($path) = @_; # if this function has been called without a parameter die ("! function get_classes called without parameter\n") if ( !defined($path) ); # create a "dir" object with the path my $dir = dir ($path) or die ("! Can't open $path\n"); my $contrib_dir = dir($vars{fd_home},"contrib"); if ("$dir" eq "$contrib_dir") { return; } # create an array with the content of $dir my @dir_files = $dir->children; foreach my $file (@dir_files) { # recursive call if $file is a directory if ( -d $file ) { get_classes($file); next; } # only process if $file is a .inc or a .php file if ( ( $file =~ /.*\.inc$/ ) && ( $file !~ /.*smarty.*/ ) ) { # put the entire content of the file pointed by $file in $data my @lines = $file->slurp; # modifing $file, to contains relative path, not complete one $file =~ s/^$vars{fd_home}//; foreach my $line ( @lines ) { # remove \n from the end of each line chomp $line; # only process for lines beginning with "class", and extracting the 2nd word (the class name) if ( $line =~ /^class\s*(\w+).*/ ) { # adding the values (class name and file path) to the hash $classes_hash_result{$1} = $file; } }
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
} } return %classes_hash_result; } # call get_classes and create /var/cache/fusiondirectory/class.cache sub rescan_classes { # hash that will contain the result of the "get_classes" function my %get_classes_result = get_classes ($vars{fd_home}); # create a "file" object with the $class_cache path my $file_class = file ($class_cache); # create the handler (write mode) for the file previoulsy created my $fhw = $file_class->openw() or die ("! Unable to open $class_cache in write mode\n"); # first lines of class.cache $fhw->print ("<?php\n\t\$class_mapping= array(\n"); # for each $key/$value, writting a new line to $class_cache while ( my($key,$value) = each %get_classes_result ) { $fhw->print ("\t\t\"$key\" => \"$value\",\n"); } # last line of classe.cache $fhw->print ("\t);\n?>"); $fhw->close or die ("! Can't close $class_cache\n"); } ###################################################### Internalisation's update #################################################################################### # function that create .mo files with .po for each language sub get_i18n { my ($path) = @_; # if this function has been called without a parameter die ("! function get_i18n called without parameter" ) if ( !defined($path) ); # create a "dir" object my $dir = dir ($path) or die ("! Can't open $path\n"); # create an array with the content of $dir my @dir_files = $dir->children; foreach my $file (@dir_files) { # recursive call if $file is a directory if (-d $file) { %i18n_hash_result = get_i18n ($file); next; } # if the file's directory is ???/language/fusiondirectory.po if ($file =~ qr{^.*/(\w+)/fusiondirectory.po$}) { # push the file's path in the language (fr/en/es/it...) array (wich is inside the hash pointed by $ref_result push @{$i18n_hash_result{$1}}, $file; } } return %i18n_hash_result; } # call get_i18n with the FusionDirectory's locales's directory and the hash that will contain the result in parameter sub rescan_i18n { # hash that will contain the result of the "get_i18n" function my %get_i18n_result = get_i18n ($locale_dir); while ( my ($lang, $files) = each %get_i18n_result ) {
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
# directory wich will contain the .mo file for each language my $lang_cache_dir = dir ("$locale_cache_dir/$lang/LC_MESSAGES"); # if $lang_cache_dir doesn't already exists, creating it if ( !-d $lang_cache_dir ) { $lang_cache_dir->mkpath or die ("! Can't create $locale_cache_dir/$lang/LC_MESSAGES"); } # glue .po files's names my $po_files = join(" ", @{$files}); chomp $po_files; # merging .po files system ( "msgcat --use-first ".$po_files.">".$lang_cache_dir."/fusiondirectory.po" ) and die ("! Unable to merge .po files for $lang with msgcat, is it already installed?\n"); # compiling .po files in .mo files system ( "msgfmt -o $lang_cache_dir/fusiondirectory.mo $lang_cache_dir/fusiondirectory.po && rm $lang_cache_dir/fusiondirectory.po" ) and die ("! Unable to compile .mo files with msgfmt, is it already installed?\n"); } } ############################################################# Directories checking ################################################################################### #get the apache user group name sub get_apache_group { my $apache_group = ""; # try to identify the running distribution, if it's not debian or rehat like, script ask for user input if (-e "/etc/debian_version") { $apache_group = "www-data"; } elsif ((-e "/etc/redhat-release") || (-e "/etc/mageia-release")) { $apache_group = "apache"; } elsif (-e "/etc/SuSE-release") { $apache_group = "www"; } elsif (-e "/etc/arch-release") { $apache_group = "http"; } else { print ("! Looks like you are not a Debian, Suse, Redhat or Mageia, I don't know your distribution !\n"); $apache_group = ask_user_input ("What is your apache group?"); } return $apache_group; } #check the rights of a directory or file, creates missing directory if needed sub check_rights { my ($dir,$user,$group,$rights,$create) = @_; my $user_uid = getpwnam ( $user ); my $group_gid = getgrnam ( $group ); # if the current dir exists if (-e $dir) { print("$dir exists…\n"); # retrieve dir's informations my @lstat = lstat ($dir); # extract the owner and the group of the directory my $dir_owner = getpwuid ( $lstat[4] ); my $dir_group = getgrgid ( $lstat[5] ); # extract the dir's rights my $dir_rights = S_IMODE( $lstat[2] ); if ( ($dir_owner ne $user) || ($dir_group ne $group) || ($dir_rights ne $rights) ) { if ( ask_yn_question ("$dir is not set properly, do you want to fix it ?: ") ) { chown ($user_uid,$group_gid,$dir) or die ("! Unable to change $dir owner\n") if ( ($dir_owner ne $user) || ($dir_group ne $group) ); chmod ( $rights, $dir ) or die ("! Unable to change $dir rights\n") if ($dir_rights ne $rights); } else { print ("Skipping...\n"); } } else {
491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
print("Rights on $dir are correct\n"); } } elsif ($create) { if ( ask_yn_question("Directory $dir doesn't exists, do you want to create it ?: ") ) { my $conf_dir = dir ($dir); # create the directory, and change the rights $conf_dir->mkpath (0,$rights); chmod ($rights, $dir); chown ($user_uid,$group_gid,$dir) or die ("Unable to change $dir rights\n"); } else { print ( "Skipping...\n" ); } } else { return 0; } return 1; } # function that check FusionDirectory's directories sub check_directories { my $apache_group = get_apache_group(); # for each config directory foreach my $dir (@config_dirs) { # if $dir is one of the dirs that remains to root if ( grep (/.*$dir.*/, @root_config_dirs) ) { check_rights($dir,"root","root",oct(755),1); # else if $dir is one of the dirs that remains to apache's user group, and the dir's owner is not root or the group is not the apache's user group, modifying owner } elsif ( grep ( /.*$dir.*/, @apache_config_dirs) ) { check_rights($dir,"root",$apache_group,oct(770),1); } } } # function that check FusionDirectory's config file sub check_config { my $apache_group = get_apache_group(); # check config file check_rights($fd_config,"root",$apache_group,oct(640),0) or die 'The config file does not exists!'; } ############################################################# Change install directories ################################################################################# sub write_vars { my $filecontent = <<eof; <?php require_once('variables_common.inc'); /*! \\file * Define common locations and variables * Generated by fusiondirectory-setup */ if (!defined("CONFIG_DIR")) { define ("CONFIG_DIR", "$vars{fd_config_dir}/"); /* FusionDirectory etc path */ } /* Allow setting the config file in the apache configuration e.g. SetEnv CONFIG_FILE fusiondirectory.conf 1.0 */ if (!defined("CONFIG_FILE")) { define ("CONFIG_FILE", "$vars{config_file}"); /* FusionDirectory filename */ } /* Path for smarty3 libraries */
561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
define("SMARTY", "$vars{fd_smarty_dir}"); /* Smarty compile dir */ define ("SPOOL_DIR", "$vars{fd_spool_dir}/"); /* FusionDirectory spool directory */ /* Global cache dir */ define ("CACHE_DIR", "$vars{fd_cache}/"); /* FusionDirectory var directory */ /* Global locale cache dir */ define ("LOCALE_DIR", "$locale_cache_dir/"); /* FusionDirectory locale directory */ /* Global tmp dir */ define ("TEMP_DIR", "$tmp_dir/"); /* FusionDirectory tmp directory */ /* Directory containing the configuration template */ define ("CONFIG_TEMPLATE_DIR", "$template_dir/"); /* FusionDirectory template directory */ /* Directory containing the fai logs */ define ("FAI_LOG_DIR", "$fai_log_dir/"); /* FusionDirectory fai directory */ /* Directory containing the supann files define ("SUPANN_DIR", "$vars{fd_config_dir}/supann/"); /* FusionDirectory supann template directory */ /* name of the class.cache file */ define("CLASS_CACHE", "$vars{class_cache}"); /* name of the class cache */ ?> eof my $variables_path = "$vars{fd_home}/include/variables.inc"; my $variables_file = file ($variables_path); my $vars_file = $variables_file->openw() or die ("! Unable to open $variables_path in write mode\n"); $vars_file->print($filecontent); $vars_file->close or die ("! Can't close $variables_file\n"); } ############################################################# LDAP conformity check ################################################################################# # function that add the FusionDirectory's admin account # return nothing is it a problem? sub add_ldap_admin { my ($base, $ldap, $admindns, $people_entries, $roles) = @_; # Get the configuration to know which attribute must be used in the dn my $mesg = $ldap->search( base => "$base", filter => "(&(objectClass=fusionDirectoryConf)(cn=fusiondirectory))", attrs => ['fdAccountPrimaryAttribute', 'fdForcePasswordDefaultHash', 'fdPasswordDefaultHash'] ); $mesg->code && die $mesg->error; my $attr; if ($mesg->count <= 0) { print "Could not find configuration object, using default value\n"; $attr = 'uid'; } elsif (($mesg->entries)[0]->exists('fdAccountPrimaryAttribute')) { $attr = ($mesg->entries)[0]->get_value('fdAccountPrimaryAttribute'); } else { $attr = 'uid'; } if ($mesg->count > 0) { if (($mesg->entries)[0]->exists('fdForcePasswordDefaultHash') && ($mesg->entries)[0]->exists('fdPasswordDefaultHash')) { if ((($mesg->entries)[0]->get_value('fdForcePasswordDefaultHash') eq 'TRUE') && (($mesg->entries)[0]->get_value('fdPasswordDefaultHash') ne 'ssha')) { warn "Warning: Administator password will be hashed with ssha instead of forced default ".($mesg->entries)[0]->get_value('fdPasswordDefaultHash')."\n"; } } } my $fd_admin_uid = ask_user_input ("Please enter a login for FusionDirectory's admin", "fd-admin"); # Does this user exists? my $dn = "";
631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
foreach my $entry (@$people_entries) { my $mesg = $ldap->search( base => "$entry", filter => "(&(objectClass=inetOrgPerson)(uid=$fd_admin_uid))", attrs => ['uid'] ); $mesg->code && die $mesg->error; if ($mesg->count) { print "User $fd_admin_uid already existing, adding admin acl to it\n"; $dn = ($mesg->entries)[0]->dn; last; } } if ($dn eq "") { my $fd_admin_pwd = ask_user_input ("Please enter FusionDirectory's admin password", undef, 1); my $fd_admin_pwd_confirm = ask_user_input ("Please enter it again", undef, 1); # while the confirmation password is not the same than the first one while ( ($fd_admin_pwd_confirm ne $fd_admin_pwd) && ($fd_admin_pwd_confirm ne "quit" ) ) { $fd_admin_pwd_confirm = ask_user_input ("! Inputs don't match, try again or type 'quit' to end this function"); } return -1 if ($fd_admin_pwd_confirm eq "quit"); my $ctx = Digest::SHA->new(1); my $salt = get_random_string(8); $ctx->add($fd_admin_pwd); $ctx->add($salt); my $hashedPasswd = '{SSHA}'.encode_base64($ctx->digest.$salt, ''); my %obj = ( 'cn' => 'System Administrator', 'sn' => 'Administrator', 'uid' => $fd_admin_uid, 'givenname' => 'System', 'objectclass' => [ 'top', 'person', 'organizationalPerson', 'inetOrgPerson' ], 'userPassword' => $hashedPasswd ); if (not defined $obj{$attr}) { print "Error : invalid account primary attribute $attr, using uid\n"; $attr = 'uid'; } $dn = "$attr=".$obj{$attr}.",$userrdn,$base"; # Add the administator user object my @options = %obj; my $admin_add = $ldap->add( $dn, attr => \@options ); # send a warning if the ldap's admin's add didn't gone well $admin_add->code && die "\n! failed to add LDAP's $dn entry - ".$admin_add->error_name.": ".$admin_add->error_text; } # Create admin role if not existing my $role; if (scalar @$roles == 0) { my $role_dn = create_role($ldap,$base,'admin','all;cmdrw'); $role = encode_base64($role_dn, ''); } else { $role = shift(@$roles); } # Add the assignment that make him an administrator my $acls = $ldap->search ( base => "$base", scope => 'base', filter => "(objectClass=*)", attrs => ['objectClass', 'gosaAclEntry'] ); $acls->code && die "\n! failed to search acls in '$base' - ".$acls->error_name.": ".$acls->error_text; ($acls->count == 0) && die "\n! failed to search acls in '$base' - base not found"; my $oclass = ($acls->entries)[0]->get_value("objectClass", asref => 1); # Add admin acl
701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
my $newacl = ["0:subtree:$role:".encode_base64($dn, '')]; if (not (grep $_ eq 'gosaAcl', @$oclass)) { push (@$oclass, 'gosaAcl'); } else { my $acl = ($acls->entries)[0]->get_value("gosaAclEntry", asref => 1); my $i = 1; if (defined $acl) { foreach my $line (@$acl) { # Reorder existing non-admin acls $line =~ s/^\d+:/$i:/; push (@$newacl, $line); $i++; } } } my $result = $ldap->modify ( $base, replace => { 'objectClass' => $oclass, 'gosaAclEntry' => $newacl } ); $result->code && warn "\n! failed to add ACL for admin on '$base' - ".$result->error_name.": ".$result->error_text; } # function that initiate the ldap connexion, and bind as the ldap's admin sub get_ldap_connexion { my %hash_result = (); my $bind_dn = ""; my $bind_pwd = ""; my $uri = ""; my $base = ""; my $tls = 0; # read ldap's server's info from /etc/fusiondirectory/fusiondirectory.conf if (-e $fd_config) { my $twig = XML::Twig->new(); # create the twig $twig->safe_parsefile($fd_config) or die("There is an error in $fd_config XML code: ".(split /\n/, $@)[1]."\n"); my @locs = $twig->root->first_child('main')->children('location'); my %locations = (); foreach my $loc (@locs) { my $ref = $loc->first_child('referral'); $locations{$loc->{'att'}->{'name'}} = { 'tls' => 0, 'uri' => $ref->{'att'}->{'URI'}, 'bind_dn' => $ref->{'att'}->{'adminDn'}, 'bind_pwd' => $ref->{'att'}->{'adminPassword'} }; if (defined $loc->{'att'}->{'ldapTLS'} and $loc->{'att'}->{'ldapTLS'} =~ m/true/i) { $locations{$loc->{'att'}->{'name'}}->{'tls'} = 1 } } my ($location) = keys(%locations); if (scalar(keys(%locations)) > 1) { my $question = "There are several locations in your config file, which one should be used : (".join(',',keys(%locations)).")"; my $answer; do { $answer = ask_user_input ($question, $location); } while (not exists($locations{$answer})); $location = $answer; } if ($locations{$location}->{'uri'} =~ qr|^(.*)/([^/]+)$|) { $uri = $1; $base = $2; } else { die '"'.$locations{$location}->{'uri'}.'" does not contain any base!'; } $bind_dn = $locations{$location}->{'bind_dn'};
771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
$bind_pwd = $locations{$location}->{'bind_pwd'}; $tls = $locations{$location}->{'tls'}; # if can't find fusiondirectory.conf } else { if ( ask_yn_question ("Can't find fusiondirectory.conf, do you want to specify LDAP's information yourself ?: ") ) { $uri = ask_user_input ("LDAP server's URI"); $base = ask_user_input ("Search base"); $hash_result{base} = $base; $bind_dn = ask_user_input ("Bind DN"); $bind_pwd = ask_user_input("Bind password", undef, 1); } else { return; } } # ldap connection my $ldap = Net::LDAP->new ($uri) or die ("! Can't contact LDAP server $uri\n"); $hash_result{ldap} = $ldap; $hash_result{base} = $base; # bind to the LDAP server if (-e $fd_secrets) { open(my $secrets, q{<}, $fd_secrets) || die ("Could not open $fd_secrets"); my $key = ""; while(<$secrets>) { if ($_ =~ m/RequestHeader set FDKEY ([^ \n]+)\n/) { $key = $1; last; } } close($secrets); $bind_pwd = cred_decrypt($bind_pwd, $key); } if ($tls) { # Read LDAP config file open (my $ldapconf, q{<}, $vars{ldap_conf}) or die ("! Failed to open ldap config file '$vars{ldap_conf}': $!\n"); my %tls_options = ( 'REQCERT' => 'require', 'CERT' => '', 'KEY' => '', 'CACERTDIR' => '', 'CACERT' => '', ); # Scan LDAP config while (<$ldapconf>) { /^\s*(#|$)/ && next; chomp; if (m/^TLS_(REQCERT|CERT|KEY|CACERTDIR|CACERT)\s+(.*)\s*$/i) { $tls_options{uc $1} = $2; } } close($ldapconf); $ldap->start_tls( verify => $tls_options{'REQCERT'}, clientcert => $tls_options{'CERT'}, clientkey => $tls_options{'KEY'}, capath => $tls_options{'CACERTDIR'}, cafile => $tls_options{'CACERT'} ); } my $bind = $ldap->bind ($bind_dn, password => $bind_pwd);
841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
# send a warning if the bind didn't gone well $bind->code && die ("! Failed to bind to LDAP server: ", $bind->error."\n"); return %hash_result; } # function that check if there is an admin sub check_admin { my ($base, $ldap, $people_entries) = @_; # search for FusionDirectory's admin account # search for admin role my $admin_roles = $ldap->search ( base => "$base", filter => "(&(objectClass=gosaRole)(gosaAclTemplate=*:all;cmdrw))", attrs => ['gosaAclTemplate'] ); $admin_roles->code && die $admin_roles->error; my @dns = (); my @roles = (); my $count = 0; while (my $entry = $admin_roles->shift_entry) { my $role_dn64 = encode_base64($entry->dn, ''); push @roles, $role_dn64; print ("Role ".$entry->dn." is an admin ACL role\n"); # Search for base-wide assignments my $assignments = $ldap->search ( base => "$base", scope => 'base', filter => "(&(objectClass=gosaAcl)(gosaAclEntry=*:subtree:$role_dn64:*))", attrs => ['gosaAclEntry'] ); $assignments->code && die $assignments->error; while (my $assignment = $assignments->shift_entry) { my $acl = $assignment->get_value("gosaAclEntry", asref => 1); foreach my $line (@$acl) { if ($line =~ m/^.:subtree:\Q$role_dn64\E/) { my @parts = split(':',$line,4); my @members = split(",",$parts[3]); foreach my $member (@members) { # Is this an existing user? my $dn = decode_base64($member); my $member_node = $ldap->search( base => $dn, scope => 'base', filter => "(objectClass=inetOrgPerson)" ); if ($member_node->count == 1) { print ("$dn is a valid admin\n"); return; } # Is this a group? $member_node = $ldap->search( base => $dn, scope => 'base', filter => "(objectClass=posixGroup)", attrs => ['memberUid'] ); if ($member_node->count == 1) { # Find group members my $member_entry = $member_node->shift_entry; my $memberUids = $member_entry->get_value("memberUid", asref => 1); my $filter = '(&(objectClass=inetOrgPerson)(|(uid='.join(')(uid=', @$memberUids).')))'; my $group_members = $ldap->search( base => $base, filter => $filter, ); $group_members->code && die $group_members->error; if (my $group_member_entry = $group_members->shift_entry) {
911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980
print ($group_member_entry->dn." is a valid admin\n"); return; } } else { push @dns, $dn; } } } } } $count++; } if ($count < 1) { print ("! There is no admin ACL role\n"); } foreach my $dn (@dns) { print ("! $dn is supposed to be admin but does not exists\n"); } if (ask_yn_question("No valid admin account found, do you want to create it ?")) { return add_ldap_admin($base, $ldap, \@dns, $people_entries, \@roles); } } sub create_branch { my ($ldap, $base, $ou) = @_; $ou =~ m/^ou=([^,]*),?$/ or die "Can’t create branch of unknown type $ou\n"; my $branch_add = $ldap->add( "$ou,$base", attr => [ 'ou' => $1, 'objectClass' => 'organizationalUnit' ] ); $branch_add->code && die "! failed to add LDAP's $ou,$base branch: ".$branch_add->error."\n"; } sub branch_exists { my ($ldap, $branch) = @_; # search for branch my $branch_mesg = $ldap->search (base => $branch, filter => '(objectClass=*)', scope => 'base'); if ($branch_mesg->code == 32) { return 0; } $branch_mesg->code && die $branch_mesg->error; my @entries = $branch_mesg->entries; return (defined ($entries[0])); } # function that check LDAP configuration sub check_ldap { # initiate the LDAP connexion my %hash_ldap_param = get_ldap_connexion(); # LDAP's connection's parameters my $base = $hash_ldap_param{base}; my $ldap = $hash_ldap_param{ldap}; my $admin_add = ""; # Collect existing people branches (even if main one may not exists); my $people = $ldap->search (base => $base, filter => $userrdn); $people->code && die $people->error; my @people_entries = $people->entries; @people_entries = map {$_->dn} @people_entries; # if people branch exists if ( branch_exists($ldap, "$userrdn,$base") ) { check_admin($base, $ldap, \@people_entries);
981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050
# if ou=people doesn't exists } else { print ( "! $userrdn,$base not found in your LDAP directory\n" ); # if user's answer is "yes", creating ou=people branch if ( ask_yn_question("Do you want to create it ?: ") ) { create_branch($ldap, $base, $userrdn); push @people_entries, "$userrdn,$base"; check_admin($base, $ldap, \@people_entries); } else { print ("Skipping...\n"); } } # if groups branch does not exist if (!branch_exists($ldap, "$grouprdn,$base")) { print ("! $grouprdn,$base not found in your LDAP directory\n"); # if user's answer is "yes", creating groups branch if ( ask_yn_question("Do you want to create it ?: ") ) { create_branch($ldap, $base, $grouprdn); } else { print ("Skipping...\n"); } } # search for workstations and object groups my $faiclasses = $ldap->search (base => "$base", filter => "(&(FAIclass=*)(!(objectClass~=FAIprofile)))" ); $faiclasses->code && die $faiclasses->error; my @faiclass_entries = $faiclasses->entries; foreach my $entry (@faiclass_entries) { my $faiclass = $entry->get_value('FAIclass'); my (@profiles) = split(' ',$faiclass); if (scalar @profiles > 2) { print "! System or group ".$entry->get_value('cn')." have more than one FAI profile : ".$faiclass."\n"; } elsif (scalar @profiles < 2) { print "! System or group ".$entry->get_value('cn')." have no release set in its FAIclass : ".$faiclass."\n"; } } # search for old config dn if (branch_exists($ldap, "cn=fusiondirectory,ou=configs,$base")) { print ("! There is a configuration in cn=fusiondirectory,ou=configs,$base in your LDAP directory\n"); print ("! The correct configuration dn is now cn=config,ou=fusiondirectory,$base\n"); print ("! FusionDirectory will not read your configuration at its current dn\n"); if ( ask_yn_question("Do you want to move and rename this entry? ") ) { if (!branch_exists($ldap, "ou=fusiondirectory,$base")) { create_branch($ldap, $base, 'ou=fusiondirectory'); } my $result = $ldap->moddn ( "cn=fusiondirectory,ou=configs,$base", newrdn => 'cn=config', deleteoldrdn => '1', newsuperior => "ou=fusiondirectory,$base" ); $result->code && die "Migration of configuration entry failed, LDAP error: ".$result->error."\n"; } else { print ("Skipping...\n"); } } # unbind to the LDAP server my $unbind = $ldap->unbind; $unbind->code && warn "! Unable to unbind from LDAP server: ", $unbind->error."\n"; }
1051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120
# function that check for duplicated uid or gid numbers sub check_id_numbers { # initiate the LDAP connexion my %hash_ldap_param = get_ldap_connexion(); # LDAP's connection's parameters my $ldap = $hash_ldap_param{ldap}; my $base = $hash_ldap_param{base}; check_id_numbers_generic($ldap, $base, 'posixAccount','uidNumber','users'); check_id_numbers_generic($ldap, $base, 'posixGroup','gidNumber','groups'); } sub check_id_numbers_generic { my ($ldap, $base, $objectClass, $attribute, $type) = @_; my $mesg = $ldap->search( filter => "(&(objectClass=$objectClass)($attribute=*))", base => $base, attrs => [$attribute] ); $mesg->code && die $mesg->error; my @entries = $mesg->entries; my %tmp = (); foreach my $entry (@entries) { if (not defined $tmp{$entry->get_value($attribute)}) { $tmp{$entry->get_value($attribute)} = (); } push @{$tmp{$entry->get_value($attribute)}}, $entry->dn(); } my $dups = 0; while (my ($id, $dns) = each %tmp) { if (scalar(@$dns) > 1) { $dups = 1; print "The following $type use the same $attribute $id:\n"; foreach my $dn (@$dns) { print "\t$dn\n"; } } } if ($dups == 0) { print "There are no duplicated ${attribute}s\n"; } } # function that create a directory and copy plugin files in it sub create_and_copy_plugin_dir { my ($plugin_dir,$dest_dir) = @_; if ( -e $plugin_dir ){ my $dir = dir ($dest_dir); $dir->mkpath() or warn ("! Unable to make ".$dest_dir."\n") if ( !-e $dest_dir); my $files_dirs_copied = rcopy($plugin_dir."/*", $dest_dir); } } # function that install all the FD's plugins from a directory sub install_plugins { # ask for the plugins archive my $plugins_archive = ask_user_input ("Where is your plugins archive ?"); die ("! ".$plugins_archive." doesn't exists") if (!-e $plugins_archive); # check the archive format $plugins_archive =~ /^.*\/(.*).tar.gz$/; my $name = $1 or die ("! Unkwnow archive $plugins_archive");
1121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190
# where the extract files will go my $tmp_plugins_dir = "/tmp"; print ("Installing plugins into $vars{fd_home}, please wait...\n"); my $dir = dir ($tmp_plugins_dir."/".$name); # extract the plugins archive my $archive = Archive::Extract->new (archive => $plugins_archive); my $extract = $archive->extract( to => "$tmp_plugins_dir" ) or die ("! Unable to extract $plugins_archive\n"); my @plugins = $dir->children; chdir ($dir) or die ("! Unable to move to $dir\n"); foreach my $plugin_path (@plugins) { $plugin_path =~ /^$tmp_plugins_dir\/$name\/(.*)$/; my $plugin = $1; # copy addons into plugins create_and_copy_plugin_dir($plugin_path."/addons/", $vars{fd_home}."/plugins/addons/"); # copy admin into plugins create_and_copy_plugin_dir($plugin_path."/admin/", $vars{fd_home}."/plugins/admin/"); # copy config into plugins create_and_copy_plugin_dir($plugin_path."/config/", $vars{fd_home}."/plugins/config/"); # copy personal into plugins create_and_copy_plugin_dir($plugin_path."/personal/", $vars{fd_home}."/plugins/personal/"); # copy extra HTML and images create_and_copy_plugin_dir($plugin_path."/html/", $vars{fd_home}."/html/"); # copy extra theme templates create_and_copy_plugin_dir($plugin_path."/ihtml/", $vars{fd_home}."/ihtml/"); # copy includes create_and_copy_plugin_dir($plugin_path."/include/", $vars{fd_home}."/include/"); # copy ldap schema create_and_copy_plugin_dir($plugin_path."/contrib/openldap/", $vars{fd_home}."/contrib/openldap/"); # copy etc files create_and_copy_plugin_dir($plugin_path."/contrib/etc/", $vars{fd_config_dir}."/".$plugin."/"); # copy doc create_and_copy_plugin_dir($plugin_path."/contrib/doc/", $vars{fd_home}."/contrib/doc/"); # copy the locales create_and_copy_plugin_dir($plugin_path."/locale/", $vars{fd_home}."/locale/plugins/".$plugin."/locale/"); } #finally update FusionDirectory's class.cache and locales rescan_classes(); rescan_i18n(); } # function that add object classes to people branch users sub migrate_users { # initiate the LDAP connexion my %hash_ldap_param = get_ldap_connexion(); # LDAP's connection's parameters my $base = $hash_ldap_param{base}; my $ldap = $hash_ldap_param{ldap}; my $mesg = $ldap->search( filter => '(&'. '(|'.
1191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260
'(objectClass=posixAccount)'. '(objectClass=person)'. '(objectClass=OpenLDAPperson)'. ')'. '(!(objectClass=ipHost))'. '(!(objectClass=inetOrgPerson))'. '(uid=*)'. ')', base => $base ); $mesg->code && die $mesg->error; if ($mesg->count > 0) { print ("The following users are missing objectClasses:\n"); my @entries = $mesg->entries; foreach my $entry (@entries) { print $entry->dn()."\n"; } if (ask_yn_question("Add the inetOrgPerson objectClass to all these entries?")) { foreach my $entry (@entries) { $mesg = $ldap->modify($entry->dn(), add => { "objectClass" => ["person","organizationalPerson","inetOrgPerson"]}); $mesg->code && print $mesg->error."\n"; } } } # unbind to the LDAP server my $unbind = $ldap->unbind; $unbind->code && warn "! Unable to unbind from LDAP server: ", $unbind->error."\n"; } # function that moves DHCP configurations from systems to ou=dhcp sub migrate_dhcp { # initiate the LDAP connexion my %hash_ldap_param = get_ldap_connexion(); # LDAP's connection's parameters my $base = $hash_ldap_param{base}; my $ldap = $hash_ldap_param{ldap}; # Search for DHCP configurations my $mesg = $ldap->search( base => "$base", filter => "(objectClass=dhcpService)", attrs => ['cn'] ); $mesg->code && die $mesg->error; my @entries = $mesg->entries; print "There are ".$mesg->count." DHCP configurations in the LDAP\n"; my @baddns = (); foreach my $entry (@entries) { if ($entry->dn() !~ m/$dhcprdn/) { if (scalar(@baddns) == 0) { print "The following are not in the DHCP branch ($dhcprdn):\n"; } print $entry->dn()."\n"; push @baddns, $entry->dn(); } } if (scalar(@baddns) == 0) { print "They all already are in the DHCP branch ($dhcprdn):\n"; } else {
1261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330
if (ask_yn_question("Move these entries to the DHCP branch ($dhcprdn)?")) { foreach my $entrydn (@baddns) { $entrydn =~ m/^([^,]+),(cn=([^,]+),.*$systemrdn,(.+))$/ or die "Could not parse dn ".$entrydn."\n"; my $rdn = $1; my $systemdn = $2; my $systemcn = $3; my $entrybase = $4; if (!branch_exists($ldap, "$dhcprdn,$entrybase")) { create_branch($ldap, $entrybase, $dhcprdn); } my $result = $ldap->moddn ( $entrydn, newrdn => "$rdn-$systemcn", deleteoldrdn => '1', newsuperior => "$dhcprdn,$entrybase" ); $result->code && die "Migration of DHCP configuration entry failed, LDAP error: ".$result->error."\n"; my $configDn = "$rdn-$systemcn,$dhcprdn,$entrybase"; # Add DHCP service on server $mesg = $ldap->search( base => $systemdn, scope => 'base', filter => '(objectClass=*)' ); $mesg->code && die $mesg->error; my $system_entry = $mesg->shift_entry; $system_entry->get_value('objectClass'); my @classes = $system_entry->get_value('objectClass'); if (grep {$_ eq 'dhcpServer'} @classes) { $result = $ldap->modify ( "$systemdn", replace => { 'dhcpServiceDN' => $configDn, } ); } else { $result = $ldap->modify ( "$systemdn", replace => { 'dhcpServiceDN' => $configDn, }, add => { 'objectClass' => 'dhcpServer' } ); } $result->code && print "Could not add DHCP service on $systemdn, LDAP error: ".$result->error."\nYou'll need to activate it yourself\n"; } } } } sub delete_gosa_locks { # initiate the LDAP connexion my %hash_ldap_param = get_ldap_connexion(); # LDAP's connection's parameters my $base = $hash_ldap_param{base}; my $ldap = $hash_ldap_param{ldap}; # Search for old formatted ACLs my $mesg = $ldap->search( base => "$base", filter => "(objectClass=gosaLockEntry)", attrs => ['dn'] ); $mesg->code && die $mesg->error;
1331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400
my @entries = $mesg->entries; foreach my $entry (@entries) { $mesg = $ldap->delete($entry); if ($mesg->code) { print "Failed to delete lock '".$entry->dn."': ".$mesg->error."\n"; } else { print "Deleted lock '".$entry->dn."'\n"; } } } # Get LDAP attributes which have been deprecated sub get_deprecated { # initiate the LDAP connexion my %hash_ldap_param = get_ldap_connexion(); # LDAP's connection's parameters my $base = $hash_ldap_param{base}; my $ldap = $hash_ldap_param{ldap}; my $schema_info = $ldap->schema(); my @attributes = $schema_info->all_attributes(); my @obsolete_attrs = (); foreach my $attribute (@attributes) { if ($attribute->{'obsolete'}) { push @obsolete_attrs, $attribute; } } my @ocs = $schema_info->all_objectclasses(); my @obsolete_classes = (); foreach my $oc (@ocs) { if ($oc->{'obsolete'}) { push @obsolete_classes, $oc; } } return (\@obsolete_attrs, \@obsolete_classes); } # function that migrates systems from FD<1.1 to FD>=1.1 sub migrate_systems { # initiate the LDAP connexion my %hash_ldap_param = get_ldap_connexion(); # LDAP's connection's parameters my $base = $hash_ldap_param{base}; my $ldap = $hash_ldap_param{ldap}; my @oldOCs = ('gotoWorkstation', 'goServer', 'gotoTerminal', 'gotoDevice'); my @newOCs = ('fdWorkstation', 'fdServer', 'fdTerminal', 'device'); foreach my $i (0 .. $#oldOCs) { my $oldOC = $oldOCs[$i]; my $newOC = $newOCs[$i]; my $mesg = $ldap->search( filter => "(objectClass=$oldOC)", base => $base ); $mesg->code && die $mesg->error; if ($mesg->count > 0) { print ("The following systems are using the obsolete $oldOC objectClass:\n"); my @entries = $mesg->entries; foreach my $entry (@entries) { print $entry->dn()."\n";
1401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470
} if (ask_yn_question("Migrate these entries to $newOC objectClass?")) { foreach my $entry (@entries) { my $tmprdn = "cn=".$entry->get_value('cn')."+ipHostNumber=".$entry->get_value('ipHostNumber'); my $newrdn = "cn=".$entry->get_value('cn'); my $dn_old = $entry->dn(); my $dn_new = $entry->dn(); $dn_new =~ s/^[^,]+,/$tmprdn,/; $entry->dn($dn_new); my @replace = ('ieee802Device', 'ipHost', $newOC); my @classes = $entry->get_value('objectClass'); foreach my $class (@classes) { if (($class ne $oldOC) && ($class ne 'GOhard')) { push(@replace, "$class"); } } $entry->replace("objectClass" => \@replace); if ($entry->exists('gotoMode')) { my $gotoMode = $entry->get_value('gotoMode'); if ($gotoMode eq 'locked') { $entry->add('fdMode' => 'locked'); } else { $entry->add('fdMode' => 'unlocked'); } $entry->delete('gotoMode'); } if ($entry->exists('argonautLdap2zoneAllowNotify')) { $entry->delete('argonautLdap2zoneAllowNotify'); } $mesg = $ldap->add($entry); if ($mesg->code) { print $entry->dn().": ".$mesg->error."\n"; next; } undef @replace; $mesg = $ldap->delete($dn_old); if ($mesg->code) { print $entry->dn().": ".$mesg->error."\n"; next; } $mesg = $ldap->moddn($dn_new, newrdn => $newrdn); if ($mesg->code) { print $entry->dn().": ".$mesg->error."\n"; next; } } } } else { print "Found no system using $oldOC\n"; } } # unbind to the LDAP server my $unbind = $ldap->unbind; $unbind->code && warn "! Unable to unbind from LDAP server: ", $unbind->error."\n"; } # function that migrates winstations from FD<1.1 to FD>=1.1 sub migrate_winstations { # initiate the LDAP connexion my %hash_ldap_param = get_ldap_connexion(); # LDAP's connection's parameters my $base = $hash_ldap_param{base}; my $ldap = $hash_ldap_param{ldap}; my $mesg = $ldap->search( filter => "(&(!(objectClass=fdWorkstation))(!(objectClass=inetOrgPerson))(!(objectClass=posixGroup))(uid=*\$)(objectClass=sambaSamAccount))",
1471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540
base => $base ); $mesg->code && die $mesg->error; if ($mesg->count > 0) { print ("The following winstations are not using the new fdWorkstation objectClass:\n"); my @entries = $mesg->entries; foreach my $entry (@entries) { print $entry->dn()."\n"; } if (ask_yn_question("Migrate these entries to fdWorkstation objectClass?")) { foreach my $entry (@entries) { my $cn = $entry->get_value('cn'); $cn =~ s/\$$//; $entry->replace('cn' => $cn); my $newrdn = "cn=".$cn; my $dn_old = $entry->dn(); my $entrybase; if ($dn_old =~ m/^[^,]+,$winstationrdn,(.+)$/) { $entrybase = $1; } elsif ($dn_old =~ m/^[^,]+,.*$systemrdn,(.+)$/) { $entrybase = $1; } elsif ($dn_old =~ m/^[^,]+,.*$winstationrdn,(.+)$/) { $entrybase = $1; } else { die "Could not parse dn ".$dn_old."\n"; } if (!branch_exists($ldap, "$workstationrdn,$entrybase")) { if ($workstationrdn =~ m/^([^,]+),([^,]+)$/) { if (!branch_exists($ldap, "$2,$entrybase")) { create_branch($ldap, $entrybase, $2); } create_branch($ldap, "$2,$entrybase", $1); } else { create_branch($ldap, $entrybase, $workstationrdn); } } my $dn_new = "$newrdn,$workstationrdn,$entrybase"; $entry->dn($dn_new); my @replace = ('fdWorkstation'); my @classes = $entry->get_value('objectClass'); foreach my $class (@classes) { if ($class ne 'account') { push(@replace, "$class"); } } $entry->replace("objectClass" => \@replace); $entry->add('fdMode' => 'unlocked'); $mesg = $ldap->add($entry); if ($mesg->code) { print $entry->dn().": ".$mesg->error."\n"; next; } undef @replace; $mesg = $ldap->delete($dn_old); if ($mesg->code) { print $entry->dn().": ".$mesg->error."\n"; next; } } } } else { print "Found no winstation to migrate\n"; } } # function that migrates phones from FD<1.1 to FD>=1.1
1541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610
sub migrate_phones { # initiate the LDAP connexion my %hash_ldap_param = get_ldap_connexion(); # LDAP's connection's parameters my $base = $hash_ldap_param{base}; my $ldap = $hash_ldap_param{ldap}; my $mesg = $ldap->search( filter => '(&(objectClass=fdPhone)(objectClass=device))', base => $base ); $mesg->code && die $mesg->error; if ($mesg->count > 0) { print ("The following phones are still using the device objectClass:\n"); my @entries = $mesg->entries; foreach my $entry (@entries) { print $entry->dn()."\n"; } if (ask_yn_question("Migrate these entries to fdPhone objectClass?")) { foreach my $entry (@entries) { my $tmprdn = "cn=".$entry->get_value('cn')."+objectClass=fdPhone"; my $newrdn = "cn=".$entry->get_value('cn'); my $dn_old = $entry->dn(); my $dn_new = $entry->dn(); $dn_new =~ s/^[^,]+,/$tmprdn,/; $entry->dn($dn_new); my @replace = (); my @classes = $entry->get_value('objectClass'); foreach my $class (@classes) { if ($class ne 'device') { push(@replace, "$class"); } } $entry->replace("objectClass" => \@replace); $mesg = $ldap->add($entry); if ($mesg->code) { print $entry->dn().": ".$mesg->error."\n"; next; } undef @replace; $mesg = $ldap->delete($dn_old); if ($mesg->code) { print $entry->dn().": ".$mesg->error."\n"; next; } $mesg = $ldap->moddn($dn_new, newrdn => $newrdn); if ($mesg->code) { print $entry->dn().": ".$mesg->error."\n"; next; } } } } # unbind to the LDAP server my $unbind = $ldap->unbind; $unbind->code && warn "! Unable to unbind from LDAP server: ", $unbind->error."\n"; } # List LDAP attributes which have been deprecated sub list_deprecated { my ($obsolete_attrs, $obsolete_classes) = get_deprecated(); print "Deprecated attributes:\n"; foreach my $attribute (@$obsolete_attrs) {
1611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680
printf(" %-30s\t%-60s\t- %s\n", $attribute->{'name'}, '('.$attribute->{'desc'}.')', $attribute->{'oid'}); } print "Deprecated objectClasses:\n"; foreach my $oc (@$obsolete_classes) { printf(" %-30s\t%-60s\t- %s\n", $oc->{'name'}, '('.$oc->{'desc'}.')', $oc->{'oid'}); } } # List LDAP entries using attributes which have been deprecated sub check_deprecated { my ($obsolete_attrs, $obsolete_classes) = get_deprecated(); my $filterAttrs = '(|'.join('', (map{ '('.$_->{'name'}.'=*)' } @$obsolete_attrs)).')'; # initiate the LDAP connexion my %hash_ldap_param = get_ldap_connexion(); # LDAP's connection's parameters my $base = $hash_ldap_param{base}; my $ldap = $hash_ldap_param{ldap}; my $entries = $ldap->search( base => "$base", filter => "$filterAttrs", ); $entries->code && die $entries->error; if ($entries->count > 0) { while (my $entry = $entries->shift_entry) { print $entry->dn." contains an obsolete attribute\n"; } } else { print "There are no entries in the LDAP using obsolete attributes\n"; } my $useobsoletes = 0; foreach my $class (@$obsolete_classes) { $entries = $ldap->search( base => "$base", filter => '(objectClass='.$class->{'name'}.')', ); $entries->code && die $entries->error; if ($entries->count > 0) { $useobsoletes = 1; while (my $entry = $entries->shift_entry) { print $entry->dn." uses the obsolete object class ".$class->{'name'}."\n"; } } } if (!$useobsoletes) { print "There are no entries in the LDAP using obsolete classes\n"; } } # Print a LDIF file removing attributes which have been deprecated sub ldif_deprecated { my ($obsolete_attrs, $obsolete_classes) = get_deprecated(); my $filterAttrs = '(|'.join('', (map{ '('.$_->{'name'}.'=*)' } @$obsolete_attrs)).')'; my $filterClasses = '(|'.join('', (map{ '(objectClass='.$_->{'name'}.')' } @$obsolete_classes)).')'; # initiate the LDAP connexion my %hash_ldap_param = get_ldap_connexion(); # LDAP's connection's parameters my $base = $hash_ldap_param{base}; my $ldap = $hash_ldap_param{ldap};
1681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750
my $entries = $ldap->search( base => "$base", filter => "$filterAttrs", ); $entries->code && die $entries->error; if ($entries->count > 0) { while (my $entry = $entries->shift_entry) { print 'dn:'.$entry->dn."\n"; print "changetype:modify\n"; foreach my $attr (@$obsolete_attrs) { if ($entry->exists($attr->{'name'})) { print "delete:".$attr->{'name'}."\n-\n"; } } print "\n"; } } else { print "# There are no entries in the LDAP using obsolete attributes\n"; } $entries = $ldap->search( base => "$base", filter => "$filterClasses", ); $entries->code && die $entries->error; if ($entries->count > 0) { print "# WARNING: There are entries in the LDAP using obsolete classes, you need to edit them manually\n"; } else { print "# There are no entries in the LDAP using obsolete classes\n"; } } # Read FD config in the LDAP sub read_ldap_config { # initiate the LDAP connexion my %hash_ldap_param = get_ldap_connexion(); # LDAP's connection's parameters my $base = $hash_ldap_param{base}; my $ldap = $hash_ldap_param{ldap}; my $mesg = $ldap->search (base => "$configrdn,$base", filter => '(objectClass=fusionDirectoryConf)', scope => 'base'); die_on_ldap_errors($mesg); if ($mesg->count > 0) { if (($mesg->entries)[0]->exists('fdUserRDN')) { $userrdn = ($mesg->entries)[0]->get_value('fdUserRDN'); } if (($mesg->entries)[0]->exists('fdOGroupRDN')) { $grouprdn = ($mesg->entries)[0]->get_value('fdOGroupRDN'); } if (($mesg->entries)[0]->exists('fdAclRoleRDN')) { $aclrolerdn = ($mesg->entries)[0]->get_value('fdAclRoleRDN'); } if (($mesg->entries)[0]->exists('fdSystemRDN')) { $systemrdn = ($mesg->entries)[0]->get_value('fdSystemRDN'); } if (($mesg->entries)[0]->exists('fdDnsRDN')) { $dnsrdn = ($mesg->entries)[0]->get_value('fdDnsRDN'); } if (($mesg->entries)[0]->exists('fdDhcpRDN')) { $dhcprdn = ($mesg->entries)[0]->get_value('fdDhcpRDN'); } if (($mesg->entries)[0]->exists('fdWorkstationRDN')) { $workstationrdn = ($mesg->entries)[0]->get_value('fdWorkstationRDN'); } if (($mesg->entries)[0]->exists('fdSambaMachineAccountRDN')) {
1751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820
$winstationrdn = ($mesg->entries)[0]->get_value('fdSambaMachineAccountRDN'); } } return ($mesg->entries)[0]; } sub show_ldap_config { my $config_node = read_ldap_config(); $config_node->dump(); } sub set_config_var { my ($var, $value) = @_; if (!($var =~ m/^fd/)) { $var = "fd$var"; } print "Setting configuration var $var to $value\n"; # initiate the LDAP connexion my %hash_ldap_param = get_ldap_connexion(); # LDAP's connection's parameters my $base = $hash_ldap_param{base}; my $ldap = $hash_ldap_param{ldap}; my $result = $ldap->modify ( "$configrdn,$base", replace => { $var => $value } ); $result->code && warn "! failed to set value for '".$var."' - ".$result->error_name.": ".$result->error_text; } sub show_version { my $variables_common_path = "$vars{fd_home}/include/variables_common.inc"; if (-e $variables_common_path) { open(my $vars, q{<}, $variables_common_path) || die ("Could not open $variables_common_path"); while(<$vars>) { if ($_ =~ m/^define \(["']FD_VERSION["'], "([^"]+)"\);/) { print "FusionDirectory version is $1\n"; last; } } close($vars); } else { print "File $variables_common_path does not exists, can’t find out FusionDirectory version\n"; } } # function that set useful vars based on user specified folders and files sub set_vars { $fd_config = $vars{fd_config_dir}."/".$vars{config_file}; $fd_secrets = $vars{fd_config_dir}."/".$vars{secrets_file}; $locale_dir = $vars{fd_home}."/".$vars{locale_dir}; $class_cache = $vars{fd_cache}."/".$vars{class_cache}; $locale_cache_dir = $vars{fd_cache}."/".$vars{locale_cache_dir}; $tmp_dir = $vars{fd_cache}."/".$vars{tmp_dir}; $fai_log_dir = $vars{fd_cache}."/".$vars{fai_log_dir}; $template_dir = $vars{fd_cache}."/".$vars{template_dir}; my $supann_dir = $vars{fd_cache}."/supann"; @root_config_dirs = ( $vars{fd_home}, $vars{fd_config_dir} ); @apache_config_dirs = ( $vars{fd_spool_dir}, $vars{fd_cache}, $tmp_dir, $fai_log_dir, $template_dir ); @config_dirs = ( @root_config_dirs, @apache_config_dirs ); }
1821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890
# function that list variables that can be modified by the user sub list_vars { while ( my ($key, $value) = each(%vars) ) { print "$key\t[$value]"."\n"; } } #################### main function ##################### #die if the user is not root die ("! You have to run this script as root\n") if ($<!=0); my @vars_keys = keys %vars; # $commands{<cli-option>} = [<description>, <function>, <needs-ldap-config>]; my %commands = (); $commands{"--update-cache"} = ["Updating class.cache", \&rescan_classes]; $commands{"--update-locales"} = ["Updating translations", \&rescan_i18n]; $commands{"--check-directories"} = ["Checking FusionDirectory's directories", \&check_directories]; $commands{"--check-config"} = ["Checking FusionDirectory's config file", \&check_config]; $commands{"--check-ldap"} = ["Checking your LDAP tree", \&check_ldap, 1]; $commands{"--check-ids"} = ["Checking for duplicated uid or gid numbers", \&check_id_numbers, 1]; $commands{"--migrate-users"} = ["Migrating your users", \&migrate_users, 1]; $commands{"--migrate-phones"} = ["Migrating your phones from FD < 1.1", \&migrate_phones, 1]; $commands{"--migrate-systems"} = ["Migrating your systems from FD < 1.1", \&migrate_systems, 1]; $commands{"--migrate-winstations"} = ["Migrating your winstations from FD < 1.1", \&migrate_winstations, 1]; $commands{"--migrate-dhcp"} = ["Migrating DHCP configurations for FD >= 1.0.17",\&migrate_dhcp, 1]; $commands{"--delete-gosa-locks"} = ["Delete lock tokens using old gosaLockEntry class", \&delete_gosa_locks]; $commands{"--install-plugins"} = ["Installing FusionDirectory's plugins", \&install_plugins]; $commands{"--encrypt-passwords"} = ["Encrypt passwords in fusiondirectory.conf", \&encrypt_passwords]; $commands{"--show-version"} = ["Show FusionDirectory version from variables_common.inc", \&show_version]; $commands{"--list-vars"} = ["List possible vars to give --set", \&list_vars]; $commands{"--write-vars"} = ["Choose FusionDirectory Directories", \&write_vars]; $commands{"--set-VAR=value"} = ["Set the variable VAR to value see --list-vars", \&die]; # Won't be called because it contains uppercase $commands{"--list-deprecated"} = ["List deprecated attributes and objectclasses", \&list_deprecated]; $commands{"--check-deprecated"} = ["List LDAP entries using deprecated attributes or objectclasses", \&check_deprecated]; $commands{"--ldif-deprecated"} = ["# Print an LDIF removing deprecated attributes",\&ldif_deprecated]; $commands{"--show-config"} = ["Show an LDAP dump of the FusionDirectory configuration", \&show_ldap_config]; $commands{"--set-config-VAR=value"} = ["Set the value in LDAP of a configuration field",\&set_config_var]; my $usage = 0; set_vars(); foreach my $arg ( @ARGV ) { if (( $arg =~ m/^--set-(.*)=(.*)$/ ) && (grep {$_ eq lc($1)} @vars_keys)) { $vars{lc($1)} = $2; print "Setting ".lc($1)." to $2\n"; set_vars(); } elsif ( $arg =~ m/^--set-config-(.*)=(.*)$/ ) { set_config_var($1, $2); } elsif ( defined $commands { lc ( $arg ) } ) { my @command = @{ $commands{ $arg } }; if ((defined $command[2]) && $command[2]) { read_ldap_config(); } print( $command[0]."\n" ); $command[1](); } elsif ( ( lc($arg) eq "--help" ) || ( lc($arg) eq "-h" ) ) { print ( "\nCommands:\n" ); while ( my ( $key,$value ) = each %commands ) { print ( "$key\t".$value->[0]."\n" ); } print ("--yes\t\t\tAlways answer yes to yes/no questions\n"); print ("--help\t\t\tShows this help\n\n"); } elsif (( lc($arg) eq "--yes" ) || ( lc($arg) eq "-y" )) { $yes_flag = 1; } else { print ("\nInvalid argument\n\n"); $usage = 1;
1891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960
} } if( $usage || ( @ARGV <= 0 ) ) { print ( "Usage : $0 [--yes]" ); foreach my $command ( keys ( %commands )) { print ( " [$command]" ); } print "\n\n"; } exit 0; __END__ =head1 NAME fusiondirectory-setup - FusionDirectory configuration management tool =head1 DESCRIPTION This script is designed to perform multiple checks on your FusionDirectory/LDAP architecture, and fix usual misconfiguration. Some extra features allow you to install FusionDirectory's plugins, and change destinations directories. =head2 Options =over 4 =item --update-cache This option update the /var/cache/fusiondirectory/class.cache file. Which contain PHP classes used in FusionDirectory, and their location. =item --update-locales This option update internalization, by generating a new .mo locales file for each language, with every .po files it found. Needs I<msgcat> and I<msgfmt> to be installed. =item --check-directories This option perform a check on all FusionDirectory's files or directories. =item --check-config This option perform a check on FusionDirectory's config file. =item --check-ldap This option check your LDAP tree. Looking for admin account, and groups or people branch. If one of those don't exists, the script will ask you what to do. =item --check-ids This option check your LDAP tree for duplicated uidNumber or gidNumber among users and groups. =item --migrate-users This option add FusionDirectory attributes to the people branch. =item --migrate-phones This option removes device objectClass for phones as fdPhones is now structural since FD 1.1. =item --migrate-systems This option replace old systems objectClasses by new objectClasses from FD 1.1. =item --migrate-winstations This option replace old winstations objectClasses by new objectClasses from FD 1.1. =item --migrate-dhcp