diff --git a/README.org b/README.org index cd6f7d0..78aea47 100644 --- a/README.org +++ b/README.org @@ -5,6 +5,21 @@ Script de instalación de firma digital. Busca ser compatible con el estándar d shell de POSIX a nivel de sintaxis, pero su funcionalidad está limitada a Linux y macOS. +*Probado en estas distribuciones:* + +- [X] Debian 12 +- [ ] Fedora 40 +- [ ] Arch Linux [date] + +* TODO [0/4] + +- [ ] Check if installation is successful +- [ ] Check if already installed +- [ ] Uninstall +- [ ] Allow to use already set variables + +* Shebang + #+begin_src shell #!/bin/sh #+end_src @@ -147,7 +162,6 @@ set_menu() { MENU="term" fi } - #+end_src ** =menu= @@ -165,7 +179,7 @@ menu() { MODE="$1" PROMPT="$2" if [ "$MENU" = "zenity" ] ; then - echo_debug "MENU: $MENU MODE: $MODE PROMPT: $PROMPT" > /dev/stderr # DEBUG + echo_debug "MENU: $MENU MODE: $MODE PROMPT: $PROMPT" > /dev/stderr [ "$MODE" = "info" ] && zenity --title "$TITLE" --text "$PROMPT" --info [ "$MODE" = "error" ] && zenity --title "$TITLE" --text "$PROMPT" --error > /dev/stderr [ "$MODE" = "entry" ] && zenity --title "$TITLE" --text "$PROMPT" --entry @@ -193,3 +207,531 @@ menu() { } #+end_src + +* Lenguaje + +=/etc/default/locale= contiene información del lenguage por defecto, obtiene el +lenguaje de esta locación si no está previamente definido (por el sistema o por +el usuario utilizando, por ejemplo, =LANG=es=). Utiliza español por defecto +si logra definir el lenguaje. + +#+begin_src shell +set_lang() { + # See: /var/lib/AccountsService/users/ + ! [ "$LANG" ] && [ -f "/etc/default/locale" ] && . /etc/default/locale + SLANG="${LANG%%_*}" + [ -z "$SLANG" ] && SLANG="es" + + if [ "$SLANG" = "es" ] ; then + TITLE="Instalador firma digital" + PROMPT_WELCOME="Bienvenido al asistente de instalación de certificados para firma digial." + PROMPT_ERR_DEPS="Error, la instalación requiere de los siguientes programas no presentes: " + PROMPT_SERIAL="Ingrese el número serial impreso al reverso de la tarjeta." + PROMPT_ERR_SERIAL="Error al obtener el número serial de la tarjeta, abortando." + PROMPT_DOWNLOAD="Descargando desde Centro de Soporte Firma Digital..." + PROMPT_DEPS_INSTALL="Instalando dependencias y otros complementos..." + PROMPT_PASS_DEPS_INSTALL="Ingrese la contraseña del equipo para instalar los componentes" + PROMPT_ERR_DEPS_INSTALL="Error instalando dependencias." + PROMPT_ERR_DOWNLOAD="Error al descargar el fichero, abortando." + PROMPT_END_SUCCESS="El instalador ha concluido." + + elif [ "$SLANG" = "en" ] ; then + TITLE="Digital signature installer" + PROMPT_WELCOME="Welcome to the digital signing certificate installation wizard." + PROMPT_ERR_DEPS="Error, installation requires the following programs not present: " + PROMPT_SERIAL="Enter the serial number printed on the back of the card." + PROMPT_ERR_SERIAL="Error obtaining the card serial number, aborting." + PROMPT_DOWNLOAD="Downloading from the Digital Signature Support Center..." + PROMPT_DEPS_INSTALL="Installing dependencies and other components..." + PROMPT_PASS_DEPS_INSTALL="Enter your computer password to install components" + PROMPT_ERR_DEPS_INSTALL="Error installing dependencies." + PROMPT_ERR_DOWNLOAD="Error downloading file, aborting." + PROMPT_END_SUCCESS="The installer has completed." + + elif [ "$SLANG" = "fr" ] ; then + TITLE="Installation de signature" + PROMPT_WELCOME="Bienvenue dans l'assistant d'installation du certificat de signature." + PROMPT_ERR_DEPS="Erreur, l'installation requise des programmes suivants ne présente pas: " + PROMPT_SERIAL="Entrez le numéro de série imprimé au dos de la carte." + PROMPT_ERR_SERIAL="Erreur d'obtention du numéro de série de la carte, abandon." + PROMPT_DOWNLOAD="Téléchargement du fichier depuis le Centre de support des signatures numériques..." + PROMPT_DEPS_INSTALL="Installation des dépendances et d'autres composants..." + PROMPT_PASS_DEPS_INSTALL="Entrez le mot de passe de votre ordinateur pour installer les composants" + PROMPT_ERR_DEPS_INSTALL="Erreur lors de l'installation des dépendances." + PROMPT_ERR_DOWNLOAD="Erreur de téléchargement du fichier, abandon." + PROMPT_END_SUCCESS="Le programme d'installation est terminé." + + fi +} +#+end_src + +* Instalación + +Proceso de instalación para dependiendo de su base, ya sea con el =$ID= o +=$ID_LIKE=, esto se define en =get_version=. + + +Se deshabilita =SC2016= ya que esta parte debe correr sin expandir las +variables. + +** Debian + +#+begin_src shell +debian_install_certs() { + # Source: https://fran.cr/instalar-firma-digital-costa-rica-gnu-linux-ubuntu-debian/ + + echo_debug "Instalando dependencias" + pseudo apt-get install -y unzip binutils p11-kit pcscd bubblewrap icedtea-netx > /dev/null || return 1 + + echo_debug "Extraer fichero" + [ -z "$SAVE_DIR" ] || [ -z "$SAVE_FILE" ] && return 1 + (cd "$SAVE_DIR" && unzip -u "$SAVE_FILE" > /dev/null) || return 1 + + echo_debug "Copiar certificados" + for cert in "$(find "$SAVE_DIR" -name "Certificados")"/* ; do + certname="${cert##*/}" + pseudo cp "$cert" /usr/local/share/ca-certificates/"${certname%.cer}.crt" + done + + echo_debug "Extraer módulo privativo" + PACKAGE="$(find "$SAVE_DIR" -name "idprotectclient[-_]*.deb")" + PACKAGE_DIR="${PACKAGE%/*}" + PACKAGE="${PACKAGE##*/}" + [ -z "$PACKAGE_DIR" ] || [ -z "$PACKAGE" ] && return 1 + (cd "$PACKAGE_DIR" && ar p "$PACKAGE" data.tar.gz | tar zx ./usr/lib/x64-athena/libASEP11.so) + pseudo cp -p "$PACKAGE_DIR"/usr/lib/x64-athena/libASEP11.so /usr/lib/x86_64-linux-gnu/ + + echo_debug "Symlinks y componentes..." + # shellcheck disable=SC2016 + pseudo sh -c ' +# --- Certificados --- +for file in /usr/local/share/ca-certificates/*.crt ; do openssl x509 -inform DER -in "$file" -out "$file.tmp" 2> /dev/null ; done +find /usr/local/share/ca-certificates/ -type f -empty -delete +for i in /usr/local/share/ca-certificates/*.tmp ; do mv "$i" "${i%.tmp}" ; done +update-ca-certificates --fresh > /dev/null +# --- Instalación del módulo PKCS#11 --- +mkdir -p /usr/lib/x64-athena +mkdir -p /Firma_Digital/LIBRERIAS +ln -sf /usr/lib/x86_64-linux-gnu/libASEP11.so /usr/lib/x64-athena/ +ln -sf /usr/lib/x86_64-linux-gnu/libASEP11.so /usr/lib/ +ln -sf /usr/lib/x86_64-linux-gnu/libASEP11.so /usr/local/lib/ +ln -sf /usr/lib/x86_64-linux-gnu/libASEP11.so /Firma_Digital/LIBRERIAS/ +ln -sf /usr/local/share/ca-certificates /Firma_Digital/CERTIFICADOS +systemctl enable --now pcscd.socket > /dev/null + ' + + echo_debug "Configurando IDPClientDB" + pseudo sh -c " +mkdir -p /etc/Athena +echo \" + + + + + + 3BDC00FF8091FE1FC38073C821106600000000000000 + FFFF00FFF0FFFFFFFFFFFFFFFFF0FF00000000000000 + + + + + + + 3BEA00008131FE450031C173C840000090007A + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + + +\" > /etc/Athena/IDPClientDB.xml +" + + echo_debug "Configurando p11-kit/modules" + pseudo sh -c " +mkdir -p /usr/share/p11-kit/modules +echo 'remote: |bwrap --unshare-all --dir /tmp --ro-bind /etc/Athena /etc/Athena --proc /proc --dev /dev --ro-bind /usr /usr --ro-bind /lib /lib --ro-bind /lib64 /lib64 --ro-bind /var/run/pcscd /var/run/pcscd --ro-bind /run/pcscd /run/pcscd p11-kit remote /usr/lib/x86_64-linux-gnu/libASEP11.so' > /usr/share/p11-kit/modules/firma-digital.module +" + + echo_debug "Configurando p11-kit update symlinks" + pseudo sh -c " +mkdir -p /usr/local/sbin +echo \"#!/bin/sh + +FIREFOX_LIB=/usr/lib/firefox/libnssckbi.so +FIREFOX_ESR_LIB=/usr/lib/firefox-esr/libnssckbi.so +THUNDERBIRD_LIB=/usr/lib/thunderbird/libnssckbi.so +NSS_LIB=/usr/lib/x86_64-linux-gnu/nss/libnssckbi.so + +if [ -e \"\$FIREFOX_LIB\" ] +then + if ! [ -L \"\$FIREFOX_LIB\" ] + then + echo \"Firefox libnssckbi.so is not a symlink. Fixing...\" + mv -f \"\$FIREFOX_LIB\" \"\$FIREFOX_LIB\".bak + ln -s /usr/lib/x86_64-linux-gnu/p11-kit-proxy.so \"\$FIREFOX_LIB\" + fi +fi + +if [ -e \"\$FIREFOX_ESR_LIB\" ] +then + if ! [ -L \"\$FIREFOX_ESR_LIB\" ] + then + echo \"Firefox ESR libnssckbi.so is not a symlink. Fixing...\" + mv -f \"\$FIREFOX_ESR_LIB\" \"\$FIREFOX_ESR_LIB\".bak + ln -s /usr/lib/x86_64-linux-gnu/p11-kit-proxy.so \"\$FIREFOX_ESR_LIB\" + fi +fi + +if [ -e \"\$THUNDERBIRD_LIB\" ] +then + if ! [ -L \"\$THUNDERBIRD_LIB\" ] + then + echo \"Thunderbird libnssckbi.so is not a symlink. Fixing...\" + mv -f \"\$THUNDERBIRD_LIB\" \"\$THUNDERBIRD_LIB\".bak + ln -s /usr/lib/x86_64-linux-gnu/p11-kit-proxy.so \"\$THUNDERBIRD_LIB\" + fi +fi + +if [ -e \"\$NSS_LIB\" ] +then + if ! [ -L \"\$NSS_LIB\" ] + then + echo \"NSS libnssckbi.so is not a symlink. Fixing...\" + mv -f \"\$NSS_LIB\" \"\$NSS_LIB\".bak + ln -s /usr/lib/x86_64-linux-gnu/p11-kit-proxy.so \"\$NSS_LIB\" + fi +fi\" > /usr/local/sbin/update-p11-kit-symlinks +chmod +x /usr/local/sbin/update-p11-kit-symlinks +" + + echo_debug "Configurando módulo mantenimiento systemd" + pseudo sh -c " +mkdir -p /etc/systemd/system +echo \"[Unit] +Description=mantenimiento de enlaces a p11-kit-proxy + +[Service] +Type=oneshot +ExecStart=/usr/local/sbin/update-p11-kit-symlinks + +[Install] +WantedBy=multi-user.target +\" > /etc/systemd/system/p11-kit-proxy-updater.service +systemctl enable --now p11-kit-proxy-updater.service > /dev/null +" + + echo_debug "Instalando trust module pk11" + pseudo sh -c " +mkdir -p /etc/pkcs11/modules +echo 'disable-in:' > /etc/pkcs11/modules/p11-kit-trust.module +" +} +#+end_src + +** Fedora + +#+begin_src shell +fedora_install_certs() { + # Source: https://fran.cr/instalar-firma-digital-costa-rica-gnu-linux-fedora/ + + echo_debug "Instalando dependencias" + pseudo dnf -y install unzip pcsc-lite icedtea-web > /dev/null || return 1 + + echo_debug "Extraer fichero" + [ -z "$SAVE_DIR" ] || [ -z "$SAVE_FILE" ] && return 1 + (cd "$SAVE_DIR" && unzip -u "$SAVE_FILE" > /dev/null) || return 1 + + echo_debug "Copiar certificados" + pseudo cp -p "$(find "$SAVE_DIR" -name "Certificados")"/* /usr/share/pki/ca-trust-source/anchors/ + pseudo update-ca-trust + + echo_debug "Extraer módulo privativo" + PACKAGE="$(find "$SAVE_DIR" -name "idprotectclient[-_]*.rpm")" + PACKAGE_DIR="${PACKAGE%/*}" + PACKAGE="${PACKAGE##*/}" + [ -z "$PACKAGE_DIR" ] || [ -z "$PACKAGE" ] && return 1 + (cd "$PACKAGE_DIR" && + rm -r ./usr/lib/x64-athena/libASEP11.so + rpm2cpio "$PACKAGE" | cpio -dim ./usr/lib/x64-athena/libASEP11.so) || return 1 + pseudo cp -p "$PACKAGE_DIR"/usr/lib/x64-athena/libASEP11.so /usr/lib64/ + + echo_debug "Symlinks y componentes..." + pseudo sh -c ' +mkdir -p /usr/lib/x64-athena/ +mkdir -p /Firma_Digital/LIBRERIAS/ +ln -sf /usr/lib64/libASEP11.so /usr/lib/x64-athena/ +ln -sf /usr/lib64/libASEP11.so /usr/lib/ +ln -sf /usr/lib64/libASEP11.so /usr/local/lib/ +ln -sf /usr/lib64/libASEP11.so /Firma_Digital/LIBRERIAS/ +ln -sf /usr/share/pki/ca-trust-source/anchors /Firma_Digital/CERTIFICADOS +systemctl enable --now pcscd.socket > /dev/null +' + + echo_debug "Configurando IDPClientDB" + pseudo sh -c " +mkdir -p /etc/Athena +echo \" + + + + + + 3BDC00FF8091FE1FC38073C821106600000000000000 + FFFF00FFF0FFFFFFFFFFFFFFFFF0FF00000000000000 + + + + + + + 3BEA00008131FE450031C173C840000090007A + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + + +\" > /etc/Athena/IDPClientDB.xml +" + + echo_debug "Configurando p11-kit/modules" + pseudo sh -c " +mkdir -p /usr/share/p11-kit/modules +echo 'remote: |bwrap --unshare-all --dir /tmp --proc /proc --dev /dev --ro-bind /etc/Athena /etc/Athena --ro-bind /usr /usr --ro-bind /var/run/pcscd /var/run/pcscd --ro-bind /run/pcscd /run/pcscd --symlink /usr/lib64 /lib64 p11-kit remote /usr/lib64/libASEP11.so' > /usr/share/p11-kit/modules/firma-digital.module +" +} +#+end_src + +** Arch Linux + +#+begin_src shell + arch_install_certs() { + echo_debug "Instalando dependencias" + pseudo pacman -S --noconfirm --needed unzip cpio rpm-tools pcsclite ccid jre8-openjdk icedtea-web > /dev/null || return 1 + + echo_debug "Extraer fichero" + [ -z "$SAVE_DIR" ] || [ -z "$SAVE_FILE" ] && return 1 + (cd "$SAVE_DIR" && unzip -u "$SAVE_FILE" > /dev/null) + + echo_debug "Copiar certificados" + pseudo cp -p "$(find "$SAVE_DIR" -name "Certificados")"/* /usr/share/ca-certificates/trust-source/anchors/ + pseudo update-ca-trust + + echo_debug "Extraer módulo privativo" + [ -z "$PACKAGE_DIR" ] || [ -z "$PACKAGE" ] && return 1 + (cd "$PACKAGE_DIR" && rpm2cpio "$PACKAGE" | cpio -dim ./usr/lib/x64-athena/libASEP11.so) + pseudo cp -p "$PACKAGE_DIR"/usr/lib/x64-athena/libASEP11.so /usr/lib/ + + echo_debug "Symlinks y componentes..." + pseudo sh -c ' +mkdir -p /usr/lib/x64-athena/ +mkdir -p /Firma_Digital/LIBRERIAS/ +ln -sf /usr/lib/libASEP11.so /usr/lib/x64-athena/ +ln -sf /usr/lib/libASEP11.so /usr/local/lib/ +ln -sf /usr/lib/libASEP11.so /Firma_Digital/LIBRERIAS/ +ln -sf /usr/share/ca-certificates/trust-source/anchors /Firma_Digital/CERTIFICADOS +ln -sf /usr/lib/p11-kit-proxy.so /usr/lib/firefox/libosclientcerts.so +systemctl enable --now pcscd.socket > /dev/null +' + + echo_debug "Configurando IDPClientDB" + pseudo sh -c " +mkdir -p /etc/Athena +echo \" + + + + + + 3BDC00FF8091FE1FC38073C821106600000000000000 + FFFF00FFF0FFFFFFFFFFFFFFFFF0FF00000000000000 + + + + + + + 3BEA00008131FE450031C173C840000090007A + FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + + + +\" > /etc/Athena/IDPClientDB.xml +" + + echo_debug "Configurando p11-kit/modules" + pseudo sh -c " +mkdir -p /usr/share/p11-kit/modules +echo 'remote: |bwrap --unshare-all --dir /tmp --proc /proc --dev /dev --ro-bind /etc/Athena /etc/Athena --ro-bind /usr /usr --ro-bind /lib64 /lib64 --ro-bind /run/pcscd /run/pcscd p11-kit remote /usr/lib/libASEP11.so' > /usr/share/p11-kit/modules/firma-digital.module +" +} +#+end_src + +** Función wrapper + +Encargarse de la instalación simplemente llamando =install_certs=. + +#+begin_src shell +install_certs() { + [ -z "$SUDO_PASSWORD" ] && return 1 + [ -z "$SAVE_FILE" ] && return 1 + SAVE_DIR="${SAVE_FILE%/*}" + + if [ "$ID" = "macos" ] ; then + open "$SAVE_FILE" || return 1 + + elif [ "$ID" = "debian" ] ; then + debian_install_certs || return 1 + + elif [ "$ID" = "fedora" ] ; then + fedora_install_certs || return 1 + + elif [ "$ID" = "arch" ] ; then + arch_install_certs || return 1 + + elif [ "$ID" = "centos" ] ; then + echo || return 1 + + fi +} +#+end_src + +* Main + +** TODO Inicializar [0/2] + +- [ ] Tirar =exit 1= en caso de no poder seleccionar una versión o un menu. +- [ ] Agregar otras dependencias mínimas, o que no conviene instalarlas en el script (sudo) + +#+begin_src shell +set_lang +set_version +set_menu + +if ! command -v curl > /dev/null ; then + menu error "$PROMPT_ERR_DEPS curl" + exit 1 +fi + +menu info "$PROMPT_WELCOME" +#+end_src + +** No. Serial + +#+begin_src shell +SERIAL="${SERIAL:=$(menu entry "$PROMPT_SERIAL")}" + +if [ -z "$SERIAL" ] ; then + menu error "$PROMPT_ERR_SERIAL" + exit 1 +fi +#+end_src + +** Proceso de descarga + +*** Obtener URL e información del fichero + +#+begin_src shell +ARCHIVE="$(get_archive)" +TEMPKEY="${ARCHIVE##* }" +FILE="${ARCHIVE%% *}" + +DOWNLOAD_URL="$( + printf 'https://soportefirmadigital.com/sfdj/getiso.aspx?tempkey=%s' \ + "$TEMPKEY" +)" + +SIZE="$(curl -sI "$DOWNLOAD_URL" | + sed '/[Cc]ontent-[Ll]ength/!d;s/^.*: //g' | + awk '{$1/=1024;printf "%d",$1}' +)" + +if [ "$SIZE" -lt 500 ] || [ -z "$FILE" ] ; then + menu error "$PROMPT_ERR_DOWNLOAD" + exit 1 +fi +#+end_src + +*** Descarga + +Se desactiva =SC2009= para asegurarse de tener el ID correcto, ya que terminará +el proceso en caso de seguir activo. + +#+begin_src shell +SAVE_DIR="/tmp/soportefirmadigital" +SAVE_FILE="$SAVE_DIR/$FILE" +mkdir -p "$SAVE_DIR" + +if [ "$MENU" = "zenity" ] ; then + (curl -sL "$DOWNLOAD_URL" -o "$SAVE_FILE") & + while true ; do + sleep 0.5 + DOWN="$(du "$SAVE_FILE" 2>/dev/null | awk '{print $1}')" + [ -z "$DOWN" ] && DOWN=0 + r=$(((DOWN*10000)/SIZE)) + printf '%d\n' ${r%??} + done | zenity --title "$TITLE" --text "$PROMPT_DOWNLOAD" --progress --auto-close + +elif [ "$MENU" = "term" ] ; then + menu info "$PROMPT_DOWNLOAD" && echo + curl "$DOWNLOAD_URL" -o "$SAVE_FILE" --progress-bar + +fi + +# shellcheck disable=SC2009 +ACTIVE="$(ps -t | grep 'curl.*soportefirmadigital' | sed '/grep/d')" +ACTIVE="${ACTIVE# }" +ACTIVEID="${ACTIVE%% *}" + +if [ -n "$ACTIVE" ] ; then + menu error "$PROMPT_ERR_DOWNLOAD" + echo_debug "Killing process ID: $ACTIVEID from: $ACTIVE" # DEBUG + kill "$ACTIVEID" + exit 1 +fi +#+end_src + +** Proceso de instalación + +*** Permisos elevados + +#+begin_src shell +SUDO_PASSWORD="${SUDO_PASSWORD:=$(menu pass "$PROMPT_PASS_DEPS_INSTALL")}" + +if [ -z "$SUDO_PASSWORD" ] || ! pseudo whoami >/dev/null 2>&1 ; then + menu error "$PROMPT_ERR_DEPS_INSTALL" + exit 1 +fi +#+end_src + +*** Instalación + +#+begin_src shell +if [ "$MENU" = "zenity" ] ; then + # Attempt to install, forward output to program + # but keep exit code of install function + ( ( ( (install_certs; echo $? >&3) | + zenity --title "$TITLE" --text "$PROMPT_DEPS_INSTALL" --progress --pulsate --auto-close >&4) 3>&1 ) | + (read -r xs; exit "$xs") ) 4>&1 + #install_certs # Just run this instead to see debug info # DEBUG + # Ignore as this is needed for this "workaround" + # shellcheck disable=SC2181 + if [ "$?" != "0" ] ; then + menu error "$PROMPT_ERR_DEPS_INSTALL" + exit 1 + fi + +elif [ "$MENU" = "term" ] ; then + menu info "$PROMPT_DEPS_INSTALL" && echo + ! install_certs && menu error "$PROMPT_ERR_DEPS_INSTALL" && exit 1 + +fi +#+end_src + +** Finalización + +#+begin_src shell +menu info "$PROMPT_END_SUCCESS\n" + +exit 0 +#+end_src