#!/bin/bash set -e # ===== Dumping and reloading using LDIF files ========================== {{{ # # If incompatible changes are done to the database underlying a LDAP # directory we need to dump the contents and reload the data into a newly # created database after the new server was installed. The following # functions deal with this functionality. # ----- Configuration of this component -------------------------------- {{{ # # Dumping the database can have negative effects on the system we are # running on. If there is a lot of data dumping it might fill a partition # for example. Therefore we must give the user exact control over what we # are doing. # Check if the user has enabled database dumping for the current situation. # Return success if yes. # Usage: if database_dumping_enabled; then ... fi database_dumping_enabled() { # {{{ dialog --radiolist "Before upgrading to a new version of the OpenLDAP server, the data from your LDAP directories can be dumped into plain text files in the standard LDAP Data Interchange Format.\n\nDump databases to file on upgrade?" 0 0 0 "always" "" off "when needed" "" on "never" "" off 2>/tmp/answer RET=$(cat /tmp/answer) case "$RET" in always) ;; "when needed") database_format_changed || return 1 ;; never) return 1 ;; *) echo >&2 "Unknown value for slapd/dump_database: $RET" echo >&2 "Please report!" exit 1 ;; esac } # }}} # Check if the database format has changed since the old installed version # Return success if yes. # Usage: if database_format_changed; then database_format_changed() { # {{{ if compare-versions "$OLD_VERSION" lt 2.4.14; then return 0 else return 1 fi } # }}} # Figure out the directory we are dumping the database to and create it # if it does not exist. # Usage: destdir=`database_dumping_destdir` database_dumping_destdir() { # {{{ local dir dialog --inputbox "Please specify the directory where the LDAP databases will be exported. In this directory, several LDIF files will be created which correspond to the search bases located on the server. Make sure you have enough free space on the partition where the directory is located. The first occurrence of the string \"VERSION\" is replaced with the server version you are upgrading from.\n\nDirectory to use for dumped databases:" 0 0 "/var/openldap/backups/VERSION" 0 0 2>/tmp/answer dir=`$(cat /tmp/answer) | sed -e "s/VERSION/$OLD_VERSION/"` mkdir -p -m 700 "$dir" echo $dir } # }}} create_new_user() { # {{{ if [ -z "`getent group openldap`" ]; then groupadd -g 49 openldap fi if [ -z "`getent passwd openldap`" ]; then echo -n " Creating new user openldap... " >&2 useradd -M -u 49 -d /var/openldap/openldap-data -r -s /bin/false -g openldap openldap echo "done." >&2 fi } # }}} create_ldap_directories() { # {{{ if [ ! -d /var/openldap/openldap-data ]; then mkdir /var/openldap/openldap-data fi if [ ! -d /var/openldap/run ]; then mkdir /var/openldap/run fi update_permissions /var/openldap/openldap-data update_permissions /var/openldap/run chmod 0755 /var/openldap/run } # }}} update_permissions() { # {{{ dir="$1" [ -z "${SLAPD_USER}" ] || chown -R "${SLAPD_USER}" "${dir}" [ -z "${SLAPD_GROUP}" ] || chgrp -R "${SLAPD_GROUP}" "${dir}" chmod -R u=rwX,g=rX,o-rwx "${dir}" } # }}} update_databases_permissions() { # {{{ for suffix in `get_suffix`; do dbdir=`get_directory $suffix` update_permissions "$dbdir" done } # }}} # }}} # ----- Dumping and loading the data ------------------------------------ {{{ # If the user wants us to dump the databases they are dumped to the # configured directory. dump_databases() { # {{{ local db suffix file dir failed slapcat_opts database_dumping_enabled || return 0 dir=`database_dumping_destdir` echo >&2 " Dumping to $dir: " for suffix in `get_suffix`; do file="$dir/$suffix.ldif" echo -n " - directory $suffix... " >&2 # Need to support slapd.d migration from preinst if [ -f "${SLAPD_CONF}" ]; then slapcat_opts="-f ${SLAPD_CONF}" else slapcat_opts="-F ${SLAPD_CONF}" fi slapcat ${slapcat_opts} -b "$suffix" > "$file" || failed=1 if [ "$failed" ]; then rm -f "$file" echo failed. >&2 exit 1 fi echo "done." >&2 done } # }}} load_databases() { # {{{ local dir file db dbdir backupdir dir=`database_dumping_destdir` echo >&2 " Loading from $dir: " for suffix in `get_suffix`; do dbdir=`get_directory $suffix` if ! is_empty_dir "$dbdir"; then echo >&2 \ " Directory $dbdir for $suffix not empty, aborting." exit 1 fi file="$dir/$suffix.ldif" echo -n " - directory $suffix... " >&2 # If there is an old DB_CONFIG file, restore it before # running slapadd backupdir=`compute_backup_path -n "$dbdir" "$suffix"` if [ -e "$backupdir"/DB_CONFIG ]; then cp -a "$backupdir"/DB_CONFIG "$dbdir"/ else copy_example_DB_CONFIG "$dbdir"/ fi capture_diagnostics slapadd -F "${SLAPD_CONF}" \ -q -b "$suffix" -l "$file" || failed=1 if [ "$failed" ]; then rm -f "$dbdir"/* echo failed. >&2 echo >&2 cat <<-EOF Loading the database from the LDIF dump failed with the following error while running slapadd: EOF release_diagnostics " " exit 1 fi echo "done." >&2 if [ -n "$SLAPD_USER" ] || [ -n "$SLAPD_GROUP" ]; then echo -n " - chowning database directory ($SLAPD_USER:$SLAPD_GROUP)... " [ -z "$SLAPD_USER" ] || \ chown -R "$SLAPD_USER" "$dbdir" [ -z "$SLAPD_GROUP" ] || \ chgrp -R "$SLAPD_GROUP" "$dbdir" echo "done"; fi done } # }}} move_incompatible_databases_away() { # {{{ echo >&2 " Moving old database directories to /var/backups:" for suffix in `get_suffix`; do dbdir=`get_directory $suffix` move_old_database_away "$dbdir" "$suffix" done } # }}} # The following functions need to support slapd.conf installations # as long as upgrading from slapd.conf environment is supported. # They're used to dump database in preinst which may have a slapd.conf file. # {{{ get_suffix() { # {{{ if [ -f "${SLAPD_CONF}" ]; then for f in `get_all_slapd_conf_files`; do grep '^suffix ' ${f} | sed 's/^suffix[[:space:]]\+\(.\+\)/\1/' | sed 's/"//g' done else grep -h olcSuffix ${SLAPD_CONF}/cn\=config/olcDatabase* | cut -d: -f 2 fi } # }}} get_directory() { # {{{ # Returns the db directory for a given suffix if [ -d "${SLAPD_CONF}" ] && echo `get_suffix` | grep -q "$1" ; then grep "olcDbDirectory:" `grep -l "olcSuffix: $1" ${SLAPD_CONF}/cn\=config/olcDatabase*` | cut -d: -f 2 | sed 's/^ *//g' elif [ -f "${SLAPD_CONF}" ]; then # Extract the directory for the given suffix ($1) for f in `get_all_slapd_conf_files`; do awk ' BEGIN { DB=0; SUF=""; DIR="" } ; /^database/ { DB=1; SUF=""; DIR="" } ; DB==1 && /^suffix[ \t]+"?'$1'"?$/ { SUF=$2 ; } ; DB==1 && /^directory/ { DIR=$2 ;} ; DB==1 && SUF!="" && DIR!="" { sub(/^"/,"",DIR) ; sub(/"$/,"",DIR) ; print DIR; SUF=""; DIR="" }' "${f}" done else return 1 fi } # }}} # Returns the list of all the config files: slapd.conf and included files. get_all_slapd_conf_files() { # {{{ echo ${SLAPD_CONF} awk ' BEGIN { I=0 } /^include/ { sub(/include/," "); I=1; } I==1 && /^[ \t]+/ { split($0,F) ; for (f in F) if (!match(F[f],/schema/)) { print F[f] }; next; } I==1 { I=0 } ' ${SLAPD_CONF} } # }}} # }}} # Compute the path to backup a database directory # Usage: compute_backup_path [-n] compute_backup_path() { # {{{ local dirname basedn ok_exists if [ "$1" = "-n" ]; then ok_exists=yes shift fi dirname="$1" basedn="$2" # Computing the name of the backup directory from the old version, # the suffix etc. all makes me feel worried. I'd rather have a # directory name which is not going to exist. So the simple # scheme we are using now is to compute the filename from the # directory name and appending date and time. And we check if it # exists to be really sure... -- Torsten local target local id id="$OLD_VERSION" [ -n "$id" ] || id=`date +%Y%m%d-%H%M%S` target="/var/backups/$basedn-$id.ldapdb" # Configuration via dpkg-reconfigure. # The backup directory already exists when reconfigured # twice or more: append a timestamp. if [ -e "${target}" ] && ([ "$MODE" = reconfigure ] || [ "$DEBCONF_RECONFIGURE" ]); then target="$target-`date +%Y%m%d-%H%M%S`" fi if [ -e "$target" ] && [ -z "$ok_exists" ]; then echo >&2 echo >&2 " Backup path $target exists. Giving up..." exit 1 fi echo "$target" } # }}} # Move the old database away if it is still there # # In fact this function makes sure that the database directory is empty # and can be populated with a new database. If something is in the way # it is moved to a backup directory if the user accepted the debconf # option slapd/move_old_database. Otherwise we output a warning and let # the user fix it himself. # Usage: move_old_database_away [] move_old_database_away() { # {{{ local databasedir backupdir databasedir="$1" suffix="${2:-unknown}" if [ ! -e "$databasedir" ] || is_empty_dir "$databasedir"; then return 0 fi # Note that we can't just move the database dir as it might be # a mount point. Instead me move the content which might # include mount points as well anyway, but it's much less likely. dialog --yesno "There are still files in $databasedir which will probably break the configuration process. If you enable this option, the maintainer scripts will move the old database files out of the way before.\n\nMove old database?" 0 0 RET=$? if [ $RET -eq 0 ]; then backupdir=`compute_backup_path "$databasedir" "$suffix"` echo -n " - directory $suffix... " >&2 mkdir -p "$backupdir" find "$databasedir" -mindepth 1 -maxdepth 1 \ -exec mv {} "$backupdir" \; echo done. >&2 else cat >&2 < copy_example_DB_CONFIG() { # {{{ return 1 local directory srcdir directory="$1" srcdir="/usr/share/openldap" if ! [ -f "${directory}/DB_CONFIG" ] && [ -d "$directory" ]; then cp $srcdir/DB_CONFIG "${directory}/DB_CONFIG" fi } # }}} # Create a new configuration and directory create_new_configuration() { # {{{ local basedn dc backend # For the domain really.argh.org we create the basedn # dc=really,dc=argh,dc=org with the dc entry dc: really dialog --inputbox "The DNS domain name is used to construct the base DN of the LDAP directory. For example, 'foo.example.org' will create the directory with 'dc=foo, dc=example, dc=org' as base DN.\n\nDNS domain name:" 0 0 2>/tmp/answer RET=$(cat /tmp/answer) local basedn="dc=`echo $RET | sed 's/^\.//; s/\./,dc=/g'`" dc="`echo $RET | sed 's/^\.//; s/\..*$//'`" dialog --radiolist "The HDB backend is recommended. HDB and BDB use similar storage formats, but HDB adds support for subtrrenames. Both support the same configuration options.\n\nDatabase backend to use:" 0 0 0 BDB "" off HDB "" on 2>/tmp/answer RET=$(cat /tmp/answer) backend="`echo $RET|tr A-Z a-z`" # Looks like the following code is not needed as slapd is unconfigured # first and stopped at that time. So no need to stop slapd at all here. if [ -e "/var/openldap/openldap-data" ] && ! is_empty_dir /var/openldap/openldap-data; then echo >&2 " Moving old database directory to /var/backups:" move_old_database_away /var/openldap/openldap-data fi create_ldap_directories create_new_slapd_conf "$basedn" "$backend" create_new_directory "$basedn" "$dc" # Put the right permissions on this directory. update_permissions /var/openldap/openldap-data } # }}} # Creates a new slapd.d configuration for the suffix given # Usage: create_new_slapd_conf create_new_slapd_conf() { # {{{ local basedn backend conf_template basedn="$1" backend="$2" # Get the admin password for the cn=config tree dialog --insecure --passwordbox "Enter the admin password:" 0 0 2>/tmp/answer # adminpw can have / character which would break sed # further down. adminpass=$(cat /tmp/answer | sed -e 's|/|\\/|g') adminpass=`crypt_admin_pass $adminpass` echo -n " Creating initial slapd configuration... " >&2 [ -e "${SLAPD_CONF}" ] && rm -rf "${SLAPD_CONF}" if [ "${METHOD}" == "old style" ] then echo "ittvagyok" conf_template="/usr/share/openldap/slapd.init.conf" sed <"$conf_template" \ -e "s/@RootPW@/$adminpass/g" \ -e "s/@backend@/$backend/g" \ -e "s/@SUFFIX@/$basedn/g" \ -e "s/@ADMIN@/cn=admin,$basedn/g" \ > ${SLAPD_CONF} else mkdir "${SLAPD_CONF}" # Need to have a version of the backend name with the first # letter capitalized (ex: olcBdbConfig or olcHdbConfig) to set # the correct objectClass attribute in the db configuration. local backend1="$(echo ${backend:0:1} | tr a-z A-Z)${backend:1}" conf_template="/usr/share/openldap/slapd.init.ldif" init_ldif=`mktemp /tmp/slapd_init.ldif.XXXXXXXXXX` sed <"$conf_template" \ -e "s/@olcRootPW@/olcRootPW: $adminpass/g" \ -e "s/@backend@/$backend/g" \ -e "s/@Backend@/$backend1/g" \ -e "s/@SUFFIX@/$basedn/g" \ -e "s/@ADMIN@/cn=admin,$basedn/g" \ > ${init_ldif} if [ "$adminpass" = "" ]; then sed -i -e '/^olcRootPW: / d' ${init_ldif} fi capture_diagnostics slapadd -F "${SLAPD_CONF}" \ -b "cn=config" -l ${init_ldif} || failed=1 rm "${init_ldif}" fi if [ "$failed" ]; then cat <<-EOF Loading the initial configuration from the ldif file (${init_ldif}) failed with the following error while running slapadd: EOF release_diagnostics " " exit 1 fi [ -f ${init_ldif} ] && rm -f ${init_ldif} update_permissions "${SLAPD_CONF}" echo "done." >&2 } # }}} # Make the value utf8 encoded. Takes one argument and utf8 encode it. # Usage: val=`encode_utf8 ` encode_utf8() { # {{{ perl -e 'use Encode; print encode_utf8($ARGV[0]);' "$1" } # }}} # Create a new directory. Takes the basedn and the dc value of that entry. # Other information is extracted from debconf. # Usage: create_new_directory create_new_directory() { # {{{ local basedn dc organization basedn="$1" dc="$2" # Encode to utf8 and base64 encode the organization. dialog --inputbox "Please enter the organization to use in the base DN of your LDAP directory.\n\nOrganization name:" 0 0 2>/tmp/answer RET=$(cat /tmp/answer) organization=`encode_utf8 "$RET"` echo -n " Creating initial LDAP directory... " >&2 init_ldif=`mktemp /tmp/slapd_init_dir.ldif.XXXXXXXXXX` echo "dn: $basedn objectClass: top objectClass: dcObject objectClass: organization o: $organization dc: $dc dn: cn=admin,$basedn objectClass: simpleSecurityObject objectClass: organizationalRole cn: admin description: LDAP administrator userPassword: $adminpass " > ${init_ldif} capture_diagnostics slapadd $confplace "${SLAPD_CONF}" -b "${basedn}" -l ${init_ldif} || failed=1 if [ "$failed" ]; then echo failed. >&2 echo >&2 cat <<-EOF Loading the initial directory structure from the ldif file (${init_ldif}) failed with the following error while running slapadd: EOF release_diagnostics " " exit 1 fi rm "${init_ldif}" echo "done." >&2 } # }}} # Adds the "allow bind_v2" directive to the configuration if the user decided # he wants to have ldap v2 enabled. configure_v2_protocol_support() { # {{{ local new_conf dialog --defaultno --yesno "The obsolete LDAPv2 protocol is disabled by default in slapd. Programs and users should upgrade to LDAPv3. If you have old programs which can't use LDAPv3, you should select this option and 'olcAllows: bind_v2' will be added to your cn=config directory.\n\nAllow LDAPv2 protocol?" 0 0 RET=$? if [ "$RET" -ne 0 ] then if [ -f ${SLAPD_CONF} ] then sed -i ${SLAPD_CONF} -e "s/#@allow_v2@/#allow bind_v2/g" fi return 0 else echo -n " Enabling LDAPv2 support... " >&2 if [ -d "$SLAPD_CONF" ]; then if ! grep -q -E '^olcAllows:[[:space:]]+bind_v2' "${SLAPD_CONF}/cn=config.ldif"; then echo "olcAllows: bind_v2" >> "${SLAPD_CONF}/cn=config.ldif" fi echo "done" >&2 return 0 elif [ -f "${SLAPD_CONF}" ] then sed -i ${SLAPD_CONF} -e "s/#@allow_v2@/allow bind_v2/g" echo "done" >&2 return 0 else return 1 fi fi echo . >&2 } # }}} # Create a backup of the current configuration files. # Usage: backup_config_once backup_config_once() { # {{{ local backupdir if [ -z "$FLAG_CONFIG_BACKED_UP" ]; then backupdir=`database_dumping_destdir` if [ -e "$SLAPD_CONF" ]; then cp -a "$SLAPD_CONF" "$backupdir" fi FLAG_CONFIG_BACKED_UP=yes fi } # }}} # Store the encrypted admin password into the debconf db # Usage: crypt_admin_pass clear_password # XXX: This is the standard unix crypt. Maybe we can get something stronger? crypt_admin_pass() { # {{{ if [ ! -z "$1" ] then echo `slappasswd -h "{CRYPT}" -s "$1"` fi } # }}} # Check if a path refers to an empty directory # Usage: if is_empty_dir "$dir"; then ... fi is_empty_dir() { # {{{ output=`find "$1" -type d -maxdepth 0 -empty 2>/dev/null` if [ "$output" ]; then return 0 else return 1 fi } # }}} # }}} # ----- Handling diagnostic output ------------------------------------ {{{ # # Often you want to run a program while you are showing progress # information to the user. If the program you are running outputs some # diagnostics it will mess up your screen. # # This is what the following functions are designed for. When running the # program, use capture_diagnostics to store what the program outputs to # stderr and use release_diagnostics to write out the captured output. # Run the command passed and capture the diagnostic output in a temporary # file. You can dump that file using release_diagnostics. capture_diagnostics() { # {{{ # Create the temporary file local tmpfile tmpfile=`mktemp` exec 7<>"$tmpfile" rm "$tmpfile" # Run the program and capture stderr. If the program fails the # function fails with the same status. "$@" 2>&7 || return $? } # }}} # Dump the diagnostic output captured via capture_diagnostics, optionally # prefixing each line. # Usage: release_diagnostics "prefix" release_diagnostics() { # {{{ local script script=' seek STDIN, 0, 0; print "$ARGV[0]$_" while ();'; perl -e "$script" "$1" <&7 } # }}} # }}} # Configure slapd for the first time (when first installed) # Usage: postinst_initial_configuration postinst_initial_configuration() { # {{{ if manual_configuration_wanted; then echo " Omitting slapd configuration as requested." >&2 else create_new_configuration configure_v2_protocol_support fi } # }}} # }}} # ===== Global variables ================================================ {{{ # # At some points we need to know which version we are upgrading from if # any. More precisely we only care about the configuration and data we # might have laying around. Some parts also want to know which mode the # script is running in. # Source the init script configuration # See example file debian/slapd.default for variables defined here if [ -f "/etc/sysconfig/slapd" ]; then . /etc/sysconfig/slapd fi adminpass= dialog --radiolist "Which configuration method do you want to use:" 0 0 0 "old style" "" on "new style" "" off 2>/tmp/answer METHOD=$(cat /tmp/answer) # Load the default location of the slapd config file if [ "$METHOD" == "old style" ] then SLAPD_CONF="/etc/openldap/slapd.conf" confplace="-f" else SLAPD_CONF="/etc/openldap/slapd.d/" confplace="-F" fi # Create a new user. Don't create the user, however, if the local # administrator has already customized slapd to run as a different user. is_user_exist=$(getent passwd openldap | wc -l) if [ "openldap" = "$SLAPD_USER" ] && [ $is_user_exist -eq 0 ]; then create_new_user fi postinst_initial_configuration # }}} # vim: set sw=8 foldmethod=marker: