All Downloads are FREE. Search and download functionalities are using the official Maven repository.

es.gob.afirma.standalone.ui.restoreconfig.RestoreConfigMacOSX Maven / Gradle / Ivy

There is a newer version: 1.8.2
Show newest version
/* Copyright (C) 2011 [Gobierno de Espana]
 * This file is part of "Cliente @Firma".
 * "Cliente @Firma" 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.
 *   - or The European Software License; either version 1.1 or (at your option) any later version.
 * You may contact the copyright holder at: [email protected]
 */

package es.gob.afirma.standalone.ui.restoreconfig;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFilePermission;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JOptionPane;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import es.gob.afirma.core.AOException;
import es.gob.afirma.core.misc.AOUtil;
import es.gob.afirma.core.misc.Base64;
import es.gob.afirma.core.misc.BoundedBufferedReader;
import es.gob.afirma.keystores.mozilla.MozillaKeyStoreUtilities;
import es.gob.afirma.keystores.mozilla.MozillaKeyStoreUtilitiesOsX;
import es.gob.afirma.keystores.mozilla.apple.AppleScript;
import es.gob.afirma.standalone.AutoFirmaUtil;
import es.gob.afirma.standalone.SimpleAfirmaMessages;
import es.gob.afirma.standalone.ui.restoreconfig.CertUtil.CertPack;
import es.gob.afirma.standalone.ui.restoreconfig.RestoreConfigFirefox.MozillaProfileNotFoundException;

/**
 * Clase que contiene la lógica para realizar las tareas de restauración
 * de la configuración de navegadores para el sistema operativo MacOsX.
 */
final class RestoreConfigMacOSX implements RestoreConfig {

	static final Logger LOGGER = Logger.getLogger("es.gob.afirma"); //$NON-NLS-1$

	private static final String KS_FILENAME = "/autofirma.pfx"; //$NON-NLS-1$
	private static final String SSL_CA_CER_FILENAME = "/AutoFirma_ROOT.cer";//$NON-NLS-1$
	private static final String SSL_CER_FILENAME = "/autofirma.cer"; //$NON-NLS-1$
	private static final String KS_PASSWORD = "654321"; //$NON-NLS-1$
	private static final String CERT_CN = "127.0.0.1"; //$NON-NLS-1$
	private static final String CERT_CN_ROOT = "'AutoFirma ROOT'"; //$NON-NLS-1$
	static final String GET_USER_SCRIPT = "/getUsers.sh";//$NON-NLS-1$
	private static final String KEYCHAIN_PATH = "/Library/Keychains/System.keychain"; //$NON-NLS-1$
	private static final String OSX_SEC_COMMAND = "security add-trusted-cert -d -r trustRoot -k %KEYCHAIN% %CERT%"; //$NON-NLS-1$
	private static final String OSX_SEC_KS_CERT_COMMAND = "security add-trusted-cert -d -r trustAsRoot -k %KEYCHAIN% %CERT%"; //$NON-NLS-1$
	static final String OSX_GET_USERS_COMMAND = "dscacheutil -q user"; //$NON-NLS-1$
	private final static String USER_DIR_LINE_PREFIX = "dir: "; //$NON-NLS-1$
	static final String MAC_SCRIPT_NAME = "/installCerScript"; //$NON-NLS-1$
	static final String MAC_SCRIPT_EXT = ".sh"; //$NON-NLS-1$
	static final String EXPORT_PATH = "export PATH=$PATH:";//$NON-NLS-1$
	static final String EXPORT_LIBRARY_LD = "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:";//$NON-NLS-1$
	private static final String TRUST_SETTINGS_COMMAND = "security trust-settings-import -d "; //$NON-NLS-1$
	private static final String TRUST_SETTINGS_FILE = "/trust_settings.plist"; //$NON-NLS-1$
	private static final String OSX_RESOURCES = "/osx"; //$NON-NLS-1$


	static final String GET_USERS_COMMAND = "dscacheutil -q user"; //$NON-NLS-1$
	private static final String GET_USER_SCRIPTS_NAME = "scriptGetUsers";//$NON-NLS-1$
	private static final String SCRIPT_EXT = ".sh";//$NON-NLS-1$

	static String mac_script_path;

	private static List userDirs = null;

	private static File sslCerFile;

	@Override
	public void restore(final RestoreConfigPanel configPanel) {

		userDirs = getSystemUsersHomes();

		// Tomamos como directorio de aplicacion aquel en el que podemos generar
		// los certificados SSL para despues usarlos
		final File appDir = AutoFirmaUtil.getMacOsXAlternativeAppDir();

		configPanel.appendMessage(SimpleAfirmaMessages.getString("RestoreConfigMacOSX.3", appDir.getAbsolutePath())); //$NON-NLS-1$

		// Verifica si se tiene permisos para escribir en el directorio de instalacion
		// y establece como directorio de trabajo otro distinto en caso de no tenerlos
		if (!appDir.isDirectory() && !appDir.mkdirs()) {
			configPanel.appendMessage(SimpleAfirmaMessages.getString("RestoreConfigMacOSX.1")); //$NON-NLS-1$
			LOGGER.severe("No se puede utilizar el directorio alternativo de trabajo por no ser un directorio y no poder crearse"); //$NON-NLS-1$
		}

		// Generamos un fichero que utilizaremos para guardar y ejecutar AppleScripts
		try {
			mac_script_path = File.createTempFile(MAC_SCRIPT_NAME, MAC_SCRIPT_EXT).getAbsolutePath();
		}
		catch (final Exception e) {
			configPanel.appendMessage(SimpleAfirmaMessages.getString("RestoreConfigMacOSX.15")); //$NON-NLS-1$
			LOGGER.severe("Error creando script temporal. Se aborta la operacion: " + e); //$NON-NLS-1$
		}

		// Iniciamos la restauracion de los certificados SSL
		restoreSslCertificates(appDir, configPanel);

		// Iniciamos la restauracion de la confianza de Chrome en el protocolo afirma
		configPanel.appendMessage(SimpleAfirmaMessages.getString("RestoreConfigMacOSX.16")); //$NON-NLS-1$
		closeChrome();
		RestoreRemoveChromeWarning.removeChromeWarningsMac(appDir, userDirs);

		configPanel.appendMessage(SimpleAfirmaMessages.getString("RestoreConfigMacOSX.8")); //$NON-NLS-1$
		LOGGER.info("Finalizado" ); //$NON-NLS-1$

	}

	/** Restaura la configuración de los certificados SSL para la comunicación
	 * por sockets.
	 * @param appDir Directorio en donde se crearan los ficheros necesarios para
	 *                   llevar a cabo la restauración.
	 * @param configPanel Panel de configuración sobre el que se irán
	 *                    imprimiendo los mensajes con el estado de la operación. */
	private static void restoreSslCertificates(final File appDir, final RestoreConfigPanel configPanel) {

		final File sslRootCertFile;
		if (!checkSSLKeyStoreGenerated(appDir)) {
			// Damos permisos al script
			addExexPermissionsToAllFilesOnDirectory(appDir);

			try {
				configureSSL(appDir, configPanel);
			}
			catch (final IOException e) {
				configPanel.appendMessage(SimpleAfirmaMessages.getString("RestoreConfigMacOSX.7")); //$NON-NLS-1$
				LOGGER.log(Level.SEVERE, "Error al copiar a disco los certificados SSL. Se aborta la operacion", e); //$NON-NLS-1$
				return;
			}
			catch (final GeneralSecurityException e) {
				configPanel.appendMessage(SimpleAfirmaMessages.getString("RestoreConfigMacOSX.9")); //$NON-NLS-1$
				LOGGER.log(Level.SEVERE, "Error al generar los certificados SSL. Se aborta la operacion", e); //$NON-NLS-1$
				return;
			}
			sslRootCertFile = new File(appDir, SSL_CA_CER_FILENAME);
		}
		else if (!checkSSLRootCertificateGenerated(appDir)) {
			// Damos permisos al script
			addExexPermissionsToAllFilesOnDirectory(appDir);

			sslRootCertFile = new File(appDir, SSL_CA_CER_FILENAME);
			try {
				final Certificate[] sslCertChain;
				try (FileInputStream fis = new FileInputStream(new File(appDir, KS_FILENAME))) {
					final KeyStore ks = KeyStore.getInstance("PKCS12"); //$NON-NLS-1$
					ks.load(fis, KS_PASSWORD.toCharArray());
					sslCertChain = ks.getCertificateChain(ks.aliases().nextElement());
				}

				// Generacion del certificado raiz .cer
				RestoreConfigUtil.installFile(
					sslCertChain[sslCertChain.length - 1].getEncoded(),
					sslRootCertFile
				);
			}
			catch (final Exception e) {
				configPanel.appendMessage(SimpleAfirmaMessages.getString("RestoreConfigLinux.4")); //$NON-NLS-1$
				LOGGER.severe("Error al copiar los certificados SSL a disco: " + e); //$NON-NLS-1$
				return;
			}
		}
		else {
			LOGGER.info("Los certificados SSL existen y no se crearan ni instalaran" ); //$NON-NLS-1$
			configPanel.appendMessage(SimpleAfirmaMessages.getString("RestoreConfigMacOSX.14")); //$NON-NLS-1$
			sslRootCertFile = new File(appDir, SSL_CA_CER_FILENAME);
		}

		try {
			installRootCA(appDir, sslRootCertFile, configPanel);
		}
		catch (final SecurityException e) {
			configPanel.appendMessage(SimpleAfirmaMessages.getString("RestoreConfigMacOSX.10")); //$NON-NLS-1$
			LOGGER.severe("No se tienen permisos para realizar la instalacion de los certificados SSL. Se aborta la operacion: " + e); //$NON-NLS-1$
			return;
		}
		catch (final KeyStoreException | IOException e) {
			configPanel.appendMessage(SimpleAfirmaMessages.getString("RestoreConfigMacOSX.11")); //$NON-NLS-1$
			LOGGER.log(Level.SEVERE, "Error al instalar los certificados SSL en los almacenes de confianza. Se aborta la operacion: " + e, e); //$NON-NLS-1$
			return;
		}
	}

	/** Pide al usuario que cierre el navegador Google Chrome y no permite continuar hasta que lo hace. */
	private static void closeChrome() {
		while (isChromeOpen()) {
			JOptionPane.showMessageDialog(
					null,
					SimpleAfirmaMessages.getString("RestoreAutoFirma.8"), //$NON-NLS-1$
					SimpleAfirmaMessages.getString("RestoreAutoFirma.9"), //$NON-NLS-1$
					JOptionPane.WARNING_MESSAGE);
		}
	}

	/** Detecta si el proceso de Firefox está abierto.
	 * @return true si el proceso de Firefox está abierto,
	 *         false en caso contrario. */
	private static boolean isChromeOpen() {

		// Listamos los procesos abiertos y buscamos uno que contenga una cadena identificativa de Firefox
		try {
			final ProcessBuilder psProcessBuilder = new ProcessBuilder("ps", "aux"); //$NON-NLS-1$ //$NON-NLS-2$
			final Process ps = psProcessBuilder.start();

			String line;
			try (
					final InputStream resIs = ps.getInputStream();
					final BufferedReader resReader = new BoundedBufferedReader(
							new InputStreamReader(resIs),
							256, // Maximo 256 lineas de salida
							1024 // Maximo 1024 caracteres por linea
							);
					) {
				while ((line = resReader.readLine()) != null) {
					if (line.contains("Google Chrome.app")) { //$NON-NLS-1$
						return true;
					}
				}
			}
		}
		catch (final IOException e) {
			LOGGER.warning("No se pudo completar la deteccion del proceso de Firefox. Se considerara que no esta en ejecucion: " + e); //$NON-NLS-1$
		}

		return false;
	}

	/** Comprueba si ya existe un almacén de certificados generado.
	 * @param appConfigDir Directorio de configuración de la aplicación.
	 * @return {@code true} si ya existe un almacen de certificados SSL,
	 *         {@code false} en caso contrario. */
	private static boolean checkSSLKeyStoreGenerated(final File appConfigDir) {
		return new File(appConfigDir, KS_FILENAME).exists();
	}

	/** Comprueba si ya existe un certificado raíz generado.
	 * @param appDir Directorio de la aplicación.
	 * @return {@code true} si ya existe un certificado raíz .cer, {@code false} en caso contrario. */
	private static boolean checkSSLRootCertificateGenerated(final File appDir) {
		return new File(appDir, SSL_CA_CER_FILENAME).exists();
	}

	/** Comprueba si ya existe una plantilla de confianzas instalada en el
	 * directorio de la aplicación.
	 * @param appDir Directorio de la aplicación.
	 * @return {@code true} si ya existe una plantilla de confianza, {@code false} en caso contrario. */
	private static boolean checkTrutsTemplateInstalled(final File appDir) {
		return new File(appDir, TRUST_SETTINGS_FILE).exists();
	}

	/**
	 * Genera y copia a disco los certificados SSL para la comunicación con la aplicación.
	 * @param workingDir Directorio en el que almacenar los certificados de la aplicación.
	 * @param configPanel Panel de configuración con las trazas de ejecución.
	 * @throws IOException Cuando ocurre un error en el proceso de instalación.
	 * @throws GeneralSecurityException Cuando ocurre un error al generar el certificado SSL.
	 */
	private static void configureSSL(final File workingDir, final RestoreConfigPanel configPanel) throws IOException, GeneralSecurityException {

		configPanel.appendMessage(SimpleAfirmaMessages.getString("RestoreConfigMacOSX.5")); //$NON-NLS-1$

		// Generamos los certificados de CA y SSL
		final CertPack certPack = CertUtil.getCertPackForLocalhostSsl(
			RestoreConfigUtil.CERT_ALIAS,
			KS_PASSWORD
		);

		deleteInstalledCertificates(workingDir);

		// Copiamos los certificados CA, SSL y el PKCS#12 a disco
		RestoreConfigUtil.installFile(
        		certPack.getCaCertificate().getEncoded(),
        		new File(workingDir, SSL_CA_CER_FILENAME));

		RestoreConfigUtil.installFile(
			certPack.getPkcs12(),
			new File(workingDir, KS_FILENAME)
		);
	}

    /** Devuelve un listado con todos los directorios de usuario del sistema.
	 * @return Listado de directorios. */
	private static List getSystemUsersHomes() {

		if (userDirs != null) {
			return userDirs;
		}
		try {
			final File getUsersScriptFile = createGetUsersScript();
			final Object o = executeScriptFile(getUsersScriptFile.getAbsolutePath(), false, true);
			userDirs = new ArrayList<>();
			try (
					final InputStream resIs = new ByteArrayInputStream(o.toString().getBytes());
					final BufferedReader resReader = new BoundedBufferedReader(
							new InputStreamReader(resIs),
							2048, // Maximo 2048 lineas de salida (256 perfiles)
							2048 // Maximo 2048 caracteres por linea
							);
					) {
				String line;
				while ((line = resReader.readLine()) != null) {
					if (line.startsWith(USER_DIR_LINE_PREFIX)){
						userDirs.add(line.substring(USER_DIR_LINE_PREFIX.length()));
					}
				}
			}
		}
		catch (final IOException | InterruptedException e) {
			LOGGER.severe("Error al generar el listado perfiles de Firefox del sistema: " + e); //$NON-NLS-1$
			userDirs = null;
		}

		return userDirs;
	}


	/** Crea un fichero de script para la obtención de los usuarios del sistema.
	 * @return Fichero de script para la obtención de los usuarios del sistema.
	 * @throws IOException Cuando no se pueda crear el fichero de script. */
	private static File createGetUsersScript() throws IOException {
		final StringBuilder script = new StringBuilder(GET_USERS_COMMAND);
		final File scriptFile = File.createTempFile(GET_USER_SCRIPTS_NAME, SCRIPT_EXT);
		try {
			ConfiguratorMacUtils.writeScriptFile(script, scriptFile.getAbsolutePath(), true);
		}
		catch (final IOException e) {
			LOGGER.log(Level.WARNING, "Ha ocurrido un error al generar el script de obtencion de usuarios: " + e, e); //$NON-NLS-1$
		}
		ConfiguratorMacUtils.addExexPermissionsToFile(scriptFile);
		return scriptFile;
	}

	/** Instala los certificados de comunicación en el almacén de Mozilla y Apple.
	 * @param appDir Directorio de aplicación.
	 * @param workingDir Directorio en el que almacenar los ficheros de la aplicación.
	 * @param rootCertFile Certificado raíz a instalar.
	 * @param configPanel Panel de configuración con las trazas de ejecución.
	 * @throws IOException Si ocurre algún problema durante el proceso.
	 * @throws SecurityException Cuando no se tengan permisos para realizar la instalación.
	 * @throws KeyStoreException Cuando ocurre un error durante la importación.
	 */
	private static void installRootCA(final File appDir, final File rootCertFile, final RestoreConfigPanel configPanel)
			throws IOException, SecurityException, KeyStoreException {

		// Cerramos el almacen de firefox si esta abierto
		closeFirefox();

		// Desinstalamos de los almacenes cualquier certificado anterior generado para este proposito
		LOGGER.info("Desinstalacion de versiones anteriores del certificado raiz del almacen de MacOSX"); //$NON-NLS-1$
		try {
			uninstallRootCAMacOSXKeyStore();
		}
		catch (final IOException e) {
			LOGGER.log(Level.SEVERE, "Se ha producido un error durante la busqueda y desinstalacion de versiones anteriores del certificado SSL en el llavero de macOS: " + e, e); //$NON-NLS-1$
		}

		// Copiamos en disco certUtil para la configuracion de los certificados en el almacen de Firefox
		RestoreConfigFirefox.copyConfigurationFiles(appDir);

		LOGGER.info("Desinstalacion de versiones anteriores del certificado raiz del almacen de Firefox"); //$NON-NLS-1$
		try {
			uninstallRootCAFirefoxKeyStore(appDir);
		}
		catch (final IOException e) {
			LOGGER.log(Level.SEVERE, "Se ha producido un error durante la busqueda y desinstalacion de versiones anteriores del certificado SSL en Firefox: " + e, e); //$NON-NLS-1$
		}


		// Se instalan los certificados en el almacen de Mozilla
		try {

			configPanel.appendMessage(SimpleAfirmaMessages.getString("RestoreConfigMacOSX.13")); //$NON-NLS-1$

			// Instalar el certificado en Mozilla
			RestoreConfigFirefox.installRootCAMozillaKeyStore(
				appDir,
				rootCertFile,
				userDirs
			);

			LOGGER.info("Configuracion de NSS"); //$NON-NLS-1$
			MozillaKeyStoreUtilitiesOsX.configureMacNSS(MozillaKeyStoreUtilities.getSystemNSSLibDir());
		}
		catch (final MozillaProfileNotFoundException e) {
			LOGGER.warning("No se ha encontrado el perfil de Mozilla en macOS: " + e); //$NON-NLS-1$
			configPanel.appendMessage(SimpleAfirmaMessages.getString("RestoreConfigMacOSX.12")); //$NON-NLS-1$
		}
		catch (final AOException e1) {
			LOGGER.warning("La configuracion de NSS para macOS X ha fallado: " + e1); //$NON-NLS-1$
		}

		// Se instalan los certificados en el almacen de Apple
		configPanel.appendMessage(SimpleAfirmaMessages.getString("RestoreConfigMacOSX.6")); //$NON-NLS-1$
		try {
			createScriptToImportCARootOnMacOSXKeyStore(appDir);
			addExexPermissionsToFile(new File(mac_script_path));
			executeScript(mac_script_path, true, true);
		}
		catch (final Exception e1) {
			LOGGER.log(Level.WARNING, "Error en la importacion del certificado de confianza en el llavero del sistema operativo: " + e1, e1); //$NON-NLS-1$
		}
		finally {
			if (sslCerFile != null) {
				LOGGER.info("Elimino .cer del certificado SSL: " + sslCerFile.delete()); //$NON-NLS-1$
			}
		}
	}

	/** Genera el comando de instalación del certificado en el almacén de Apple en el
	 * script de instalación.
	 * @param appDir Directorio de la aplicación, donde está el certificado.
	 * @param workingDir Directorio actual de trabajo.
	 * @param appDir Directorio de aplicación.
	 * @throws GeneralSecurityException Se produce si hay un problema de seguridad durante el proceso.
	 * @throws IOException Se produce cuando hay un error en la creación del fichero. */
	static void createScriptToImportCARootOnMacOSXKeyStore(final File appDir) throws GeneralSecurityException, IOException {

		// Creamos el script para la instalacion del certificado SSL en el almacen de confianza de Apple
		final File certFile = new File(appDir, SSL_CA_CER_FILENAME);
		final String cmd = OSX_SEC_COMMAND.replace(
			"%KEYCHAIN%", //$NON-NLS-1$
			KEYCHAIN_PATH
			).replace(
				"%CERT%", //$NON-NLS-1$
				certFile.getAbsolutePath().replace(" ", "\\ ") //$NON-NLS-1$ //$NON-NLS-2$
		);
		LOGGER.info("Comando de instalacion del certificado de CA en el almacen de confianza de Apple: " + cmd); //$NON-NLS-1$
		writeScriptFile(mac_script_path, new StringBuilder(cmd), true);

		// Creamos el script para la instalacion del certificado SSL en el almacen de confianza de Apple
		final File pfx = new File(appDir, KS_FILENAME);
		final KeyStore ks;
		try (final InputStream is = new FileInputStream(pfx)) {
			ks = KeyStore.getInstance("PKCS12"); //$NON-NLS-1$
			ks.load(is, KS_PASSWORD.toCharArray());
		}
		final X509Certificate certPfx = (X509Certificate) ks.getCertificate(RestoreConfigUtil.CERT_ALIAS);
		final byte[] buf = certPfx.getEncoded();

		sslCerFile = new File(appDir, SSL_CER_FILENAME);
		try (
			final FileOutputStream os = new FileOutputStream(sslCerFile);
		) {
			os.write(buf);
		}

		final String cmdKs = OSX_SEC_KS_CERT_COMMAND.replace(
			"%KEYCHAIN%", //$NON-NLS-1$
			KEYCHAIN_PATH
			).replace(
				"%CERT%", //$NON-NLS-1$
				sslCerFile.getAbsolutePath().replace(" ", "\\ ") //$NON-NLS-1$ //$NON-NLS-2$
		);
		LOGGER.info("Comando de instalacion del certificado SSL en el almacen de confianza de Apple: " + cmd); //$NON-NLS-1$
		writeScriptFile(mac_script_path, new StringBuilder(cmdKs), true);

		// Creamos el fichero de perfil y el script necesario para que se confie automaticamente en los nuevos certificados
		final X509Certificate root;
		try (final InputStream is = new FileInputStream(certFile)) {
			root = (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(is); //$NON-NLS-1$
		}

		final String snRoot = AOUtil.hexify(root.getSerialNumber().toByteArray(), false);
		final String sha1Root = AOUtil.hexify(MessageDigest.getInstance("SHA1").digest(root.getEncoded()), false); //$NON-NLS-1$
		final String snCer = AOUtil.hexify(certPfx.getSerialNumber().toByteArray(), false);
		final String sha1Cer =  AOUtil.hexify(MessageDigest.getInstance("SHA1").digest(certPfx.getEncoded()), false); //$NON-NLS-1$

		editTrustFile(appDir, sha1Root, sha1Cer, snRoot, snCer);

		final String trustCmd = TRUST_SETTINGS_COMMAND
			+ appDir.getAbsolutePath().replace(" ", "\\ ") //$NON-NLS-1$ //$NON-NLS-2$
			+ TRUST_SETTINGS_FILE
		;
		LOGGER.info("Comando de instalacion de ajustes de confianza: " + trustCmd); //$NON-NLS-1$
		writeScriptFile(mac_script_path, new StringBuilder(trustCmd), true);

	}

	/** Ejecuta un script en OS X.
	 * @param path Ruta donde se encuentra el script.
	 * @param administratorMode true el script se ejecuta como permisos de adminsitrador, false en caso contrario.
	 * @param delete true se borra el fichero después de haberse ejecutado.
	 * @return El objeto que da como resultado el script.
	 * @throws IOException Excepción lanzada en caso de ocurrir algún error en la ejecución del script.
	 * @throws InterruptedException Cuando se interrumpe la ejecución del script. */
	public static Object executeScript(final String path, final boolean administratorMode, final boolean delete) throws IOException, InterruptedException {

		LOGGER.info("Se ejecuta el fichero: " + path); //$NON-NLS-1$

		String result;
		final AppleScript appleScript = new AppleScript(new File(path), delete);
		if (administratorMode) {
			result = appleScript.runAsAdministrator();
		}
		else {
			result = appleScript.run();
		}

		return result;
	}

	/**
	 * Da permisos de ejecución a todos los ficheros de un directorio dado.
	 * @param dir Directorio al que dar permiso.
	 */
	public static void addExexPermissionsToAllFilesOnDirectory(final File dir) {

		for (final File fileEntry : dir.listFiles()) {
			addExexPermissionsToFile(fileEntry);
		}
	}

	/**
	 * Cambia los permisos de un fichero para permitir su ejecución
	 * @param f Fichero sobre el cual se cambian los permisos
	 */
	static void addExexPermissionsToFile(final File f) {
		final Set perms = new HashSet<>();
		perms.add(PosixFilePermission.OWNER_EXECUTE);
		perms.add(PosixFilePermission.GROUP_EXECUTE);
		perms.add(PosixFilePermission.OTHERS_EXECUTE);
		perms.add(PosixFilePermission.OWNER_READ);
		perms.add(PosixFilePermission.GROUP_READ);
		perms.add(PosixFilePermission.OTHERS_READ);
		perms.add(PosixFilePermission.OWNER_WRITE);
		perms.add(PosixFilePermission.GROUP_WRITE);
		perms.add(PosixFilePermission.OTHERS_WRITE);

		try {
			Files.setPosixFilePermissions(
				Paths.get(f.getAbsolutePath()),
				perms
			);
		}
		catch (final Exception e) {
			LOGGER.warning(
				"No se ha podido dar permiso de ejecucion a '" + f.getAbsolutePath() + "': " + e//$NON-NLS-1$ //$NON-NLS-2$
			);
		}
	}


	/**
	 * Genera el script de desinstalación del llavero OS X mediante AppleScript del certificado generado
	 * y elimina los links simbólicos.
	 * @throws IOException Se produce cuando hay un error en la creación del fichero.
	 */
	private static void uninstallRootCAMacOSXKeyStore() throws IOException {
		LOGGER.info("Desinstalamos los certificados y eliminamos los enlaces simbolicos:"); //$NON-NLS-1$
		// Creamos comandos para eliminar enlaces simbolicos de firfox y certificados del llavero
		final String deleteLinks = "ls -ln /usr/local/lib | grep Firefox | awk '{print $9}' | xargs -I {} rm /usr/local/lib/{}"; //$NON-NLS-1$
		final String deleteCaCerts = "security find-certificate -c " + CERT_CN + " -a -Z|grep SHA-1|awk '{ print $NF }' | xargs -I {} security delete-certificate -Z {}"; //$NON-NLS-1$ //$NON-NLS-2$
		final String deleteKsCerts = "security find-certificate -c " + CERT_CN_ROOT + " -a -Z|grep SHA-1|awk '{ print $NF }' | xargs -I {} security delete-certificate -Z {}"; //$NON-NLS-1$ //$NON-NLS-2$
		final StringBuilder sb = new StringBuilder();
		sb.append(deleteLinks);
		sb.append(";"); //$NON-NLS-1$
		sb.append(deleteCaCerts);
		sb.append(";"); //$NON-NLS-1$
		sb.append(deleteKsCerts);
		writeScriptFile(mac_script_path, sb, true);

	}

	private static void editTrustFile(final File appDir, final String sha1Root, final String sha1Cer, final String snRoot, final String snCer) {

		try {
			deleteTrustTemplate(appDir);
		} catch (final Exception e) {
			LOGGER.warning("No ha podido eliminarse la anterior plantilla de configuracion de confianza: " + e); //$NON-NLS-1$
		}
		try {
			exportResource(OSX_RESOURCES,TRUST_SETTINGS_FILE, appDir.getAbsolutePath());
		} catch (final Exception e) {
			LOGGER.severe("No ha podido copiarse la plantilla de configuracion de confianza: " + e); //$NON-NLS-1$
		}

		final String sha1RootOrig = "%CA_SHA1%"; //$NON-NLS-1$
		final String sha1CerOrig = "%SSL_SHA1%"; //$NON-NLS-1$
		final String snRootOrig = "%CA_SERIALNUMBER%"; //$NON-NLS-1$
		final String snCerOrig = "%SSL_SERIALNUMBER%"; //$NON-NLS-1$

		try (final InputStream in = new FileInputStream(new File(appDir, TRUST_SETTINGS_FILE));) {

			final DocumentBuilderFactory docFactory =
			DocumentBuilderFactory.newInstance();
			final DocumentBuilder docBuilder =
			docFactory.newDocumentBuilder();
			final Document doc = docBuilder.parse(in);
			final Node dict = doc.getElementsByTagName("dict").item(1); //$NON-NLS-1$
			final NodeList list = dict.getChildNodes();

			for (int i = 0; i < list.getLength(); i++) {
		         final Node node = list.item(i);
		         if (node.getNodeType() == Node.ELEMENT_NODE) {
		        	 final Element element = (Element) node;
		        	 if (element.getNodeName().equals("key")) { //$NON-NLS-1$
		        		 if (element.getTextContent().equals(sha1RootOrig)) {
		        			 element.setTextContent(sha1Root);
		        		 }
		        		 else if (element.getTextContent().equals(sha1CerOrig)) {
		        			 element.setTextContent(sha1Cer);
		        		 }
		        	 }
		        	 else if (element.getNodeName().equals("dict")) { //$NON-NLS-1$
		        		 final NodeList certList = element.getChildNodes();
		        		 for (int j = 0; j < certList.getLength(); j++) {
		        			 final Node n = certList.item(j);
		        			 if (n.getNodeType() == Node.ELEMENT_NODE) {
		        				 final Element el = (Element) n;
		        				 if (el.getNodeName().equals("data")) { //$NON-NLS-1$
		        					 if (AOUtil.hexify(Base64.decode(el.getTextContent()), false).equals(snRootOrig)) {
		        						 el.setTextContent(Base64.encode(hexStringToByteArray(snRoot)));
		        					 }
		        					 else if (AOUtil.hexify(Base64.decode(el.getTextContent()), false).equals(snCerOrig)) {
		        						 el.setTextContent(Base64.encode(hexStringToByteArray(snCer)));
		        					 }
			   		        	}
		        			}
		        		 }
		        	 }
		         }
		    }

			final TransformerFactory transformerFactory = TransformerFactory.newInstance();
			final Transformer transformer = transformerFactory.newTransformer();
			final DOMSource domSource = new DOMSource(doc);
			final StreamResult streamResult = new StreamResult(
				new File(appDir, TRUST_SETTINGS_FILE)
			);
			transformer.transform(domSource, streamResult);

		}
		catch (final Exception e) {
			LOGGER.severe("Error analizando el PList: " + e); //$NON-NLS-1$
		}
	}

	private static byte[] hexStringToByteArray(final String s) {
	    final int len = s.length();
	    final byte[] data = new byte[len / 2];
	    for (int i = 0; i < len; i += 2) {
	        data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
	                             + Character.digit(s.charAt(i+1), 16));
	    }
	    return data;
	}

	/**
	 * Elimina los ficheros de certificado raíz y almacén SSL del disco
	 * como paso previo a volver a generarlos
	 * @param appDir Directorio en donde se encontran los certificados.
	 * @throws IOException Si hay problemas borrando los ficheros. */
	private static void deleteInstalledCertificates(final File appDir) throws IOException {

		if (checkSSLKeyStoreGenerated(appDir)) {
			final File sslKey = new File(appDir, KS_FILENAME);
			if (!sslKey.delete()) {
				throw new IOException("No puedo eliminar autofirma.pfx"); //$NON-NLS-1$
			}

		}

		if (checkSSLRootCertificateGenerated(appDir)) {
			final File sslRoot = new File(appDir, SSL_CA_CER_FILENAME);
			if (!sslRoot.delete()) {
				throw new IOException("No puedo eliminar AutoFirma_ROOT.cer"); //$NON-NLS-1$
			}

		}
	}

	/** Elimina los ficheros de certificado raíz y almacén SSL del disco
	 * como paso previo a volver a generarlos.
	 * @param appDir Ruta del directorio de la aplicación.
	 * @throws IOException Si hay problemas borrando los ficheros. */
	private static void deleteTrustTemplate(final File appDir) throws IOException {

		if (checkTrutsTemplateInstalled(appDir)) {

			final File sslKey = new File(appDir, TRUST_SETTINGS_FILE);

			if (!sslKey.delete()) {
				throw new IOException("No puedo eliminar " + TRUST_SETTINGS_FILE); //$NON-NLS-1$
			}

		}

	}

	/**
	 * Desinstala el certificado de CA del almacen de autoridades de confianza de Mozilla Firefox.
	 * @param workingDir Directorio en el que almacenar los ficheros necesarios para la
	 * ejecución de la función.
	 * @throws IOException Se produce cuando hay un error en la creación del fichero.
	 */
	private static void uninstallRootCAFirefoxKeyStore(final File workingDir) throws IOException {

		// Generamos script para borrar el almacen certificados firefox
		RestoreConfigFirefox.generateUninstallScriptMac(workingDir, userDirs);
		// Le damos permisos para poder ejecutarlo
		addExexPermissionsToAllFilesOnDirectory(workingDir);
	}

	/** Escribe un script en un fichero dado.
	 * @param path Ruta donde se escribirá el script.
	 * @param data Datos a escribir.
	 * @param append true permite contatenar el contenido del fichero con lo que se va a escribir. false el fichero se sobrescribe.
	 * @throws IOException Se produce cuando hay un error en la creación del fichero. */
	static void writeScriptFile(final String path, final StringBuilder data, final boolean append ) throws IOException{
		LOGGER.info("Se escribira en fichero el siguiente comando:\n" + data.toString()); //$NON-NLS-1$
		final File macScript = new File(path);
		data.append("\n"); //$NON-NLS-1$
		try (final FileOutputStream fout = new FileOutputStream(macScript, append);) {
			fout.write(data.toString().getBytes());
		}
	}

	/**
	 * Pide al usuario que cierre el navegador Mozilla Firefox y no permite continuar hasta que lo hace.
	 */
	private static void closeFirefox() {

		while (isFirefoxOpen()) {
			JOptionPane.showMessageDialog(
					null,
					SimpleAfirmaMessages.getString("RestoreAutoFirma.7"), //$NON-NLS-1$
					SimpleAfirmaMessages.getString("RestoreAutoFirma.9"), //$NON-NLS-1$
					JOptionPane.WARNING_MESSAGE);
		}
	}

	/** Detecta si el proceso de Firefox está abierto.
	 * @return true si el proceso de Firefox está abierto,
	 *         false en caso contrario. */
	private static boolean isFirefoxOpen() {

		// Listamos los procesos abiertos y buscamos uno que contenga una cadena identificativa de Firefox
		try {
			final ProcessBuilder psProcessBuilder = new ProcessBuilder("ps", "aux"); //$NON-NLS-1$ //$NON-NLS-2$
			final Process ps = psProcessBuilder.start();

			String line;
			try (
					final InputStream resIs = ps.getInputStream();
					final BufferedReader resReader = new BoundedBufferedReader(
							new InputStreamReader(resIs),
							256, // Maximo 256 lineas de salida
							1024 // Maximo 1024 caracteres por linea
							);
					) {
				while ((line = resReader.readLine()) != null) {
					if (line.contains("Firefox.app") //$NON-NLS-1$
							|| line.contains("FirefoxNightly.app") //$NON-NLS-1$
							|| line.contains("FirefoxDeveloperEdition.app")) { //$NON-NLS-1$
						return true;
					}
				}
			}
		}
		catch (final IOException e) {
			LOGGER.warning("No se pudo completar la deteccion del proceso de Firefox. Se considerara que no esta en ejecucion: " + e); //$NON-NLS-1$
		}

		return false;
	}

	/** Copia un recurso desde dentro del JAR hacia una ruta externa.
     * @param pathToResource Carpeta del recurso dentro del JAR.
     * @param resourceName Nombre del recurso a copiar.
     * @param destinationPath Ruta externa destino.
     * @return Ruta completa del recurso copiado.
	 * @throws IOException En cualquier error. */
    static public String exportResource(final String pathToResource,
    		                            final String resourceName,
    		                            final String destinationPath) throws IOException {
        try (
    		final OutputStream resStreamOut = new FileOutputStream(destinationPath + resourceName);
        	final InputStream stream = RestoreConfigMacOSX.class.getResourceAsStream(pathToResource + resourceName)
		) {
            if(stream == null) {
                throw new IOException("No ha podido obtenerse el recurso \"" + resourceName + "\" del JAR."); //$NON-NLS-1$ //$NON-NLS-2$
            }
            int readBytes;
            final byte[] buffer = new byte[4096];
            while ((readBytes = stream.read(buffer)) > 0) {
                resStreamOut.write(buffer, 0, readBytes);
            }
        }
        return destinationPath + resourceName;
    }

    /** Ejecuta un fichero de scripts.
	 * @param path Ruta donde se encuentra el script.
	 * @param administratorMode true el script se ejecuta con permisos de adminsitrador, false en caso contrario.
	 * @param delete true se borra el fichero después de haberse ejecutado.
	 * @return El objeto que da como resultado el script.
	 * @throws IOException Excepción lanzada en caso de ocurrir algún error en la ejecución del script.
     * @throws InterruptedException Cuando el proceso se ve interrumpido. */
	public static Object executeScriptFile(final String path, final boolean administratorMode, final boolean delete) throws IOException, InterruptedException {

		final AppleScript script = new AppleScript(new File(path), delete);

		LOGGER.info("Path del script: " + path); //$NON-NLS-1$
		try {
			Object o;
			if (administratorMode) {
				o = script.runAsAdministrator();
			}
			else {
				o = script.run();
			}
			return o;
		}
		catch (final IOException e) {
			throw new IOException("Error en la ejecucion del script via AppleScript: " + e, e); //$NON-NLS-1$
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy