limoilux.org

Articles des members du comité des logiciels libres du Cégep Limoilou.

Browsing Posts in Uncategorized

Ca fait quelque temps que je travail sur mon encrypteur, et j’ai enfin réussi a fait de quoi qui marche.
Le code n’est pas complet, mais je croit que ca marche assez bien pour que j appelle ca un “proof of concept”.
Freya utilise un générateur de hasard pour creer les 16 sous-clés qui serve a son “réseau de Feistel”, ce qui le rend
en théorie invincible a une analyse des fréquences. Pour des raison pratique j’utilise une clé de 4 caractères de long,
mais ca serai facile de la rallonger.

Text.Java

package freyaCipher;

/* Preuve du Concept
 * Freya
 *
 * Encrypteur/décrypteur
 * La clef doit avoir 4 caracteres de long( pourrai eventuellement amilioré )
 * Par David Bernard (dbernard456@gmail.com)
 * Le 14 aout 2010
 *
 */

public class Text {
	private Key myKey;
	private Block[] myBlock;
	private char[] textBody;

	private int originalLength;
	private int adaptedLength;
	private int blockCount;

	public Text(String inputText) {
		textBody = inputText.toCharArray();
		originalLength = inputText.length();
		setParam();
		Encrypt("ABC5");
		Decrypt("ABC5");

	}

	public void Encrypt(String key) {
		prepare();
		myKey = new Key(key);
		for(int round = 0; round < 16; round++) {
			for(int i = 0; i < blockCount; i++) {
				myBlock[i].encode(myKey.getSubKey(round));
			}
		}
		readEachBlock();
		printText();
		printBlocks();

	}

	public void Decrypt(String key) {
		myKey = new Key(key);
		for(int round = 15; round >= 0; round--) {
			for(int i = 0; i < blockCount; i++) {
				myBlock[i].decode(myKey.getSubKey(round));
			}
		}
		revert();
		printText();
		printBlocks();
	}

	private void setParam() {
		setBlockCount();
		setAdaptedLength();
	}

	private void prepare() {
		adaptTextBody();
		setBlocks();
		permuteEachBlocks();
		readEachBlock();
	}

	private void readEachBlock() {
		for (int i = 0; i < blockCount; i++) {
			readBlock(i);
		}
	}

	private void readBlock(int blockIndex) {
		char[] buffer = myBlock[blockIndex].getBlock().toCharArray();
		int position;
		for(int i = 0; i < 8; i++) {
			position = i + (blockIndex * 8);
			textBody[position] = buffer[i];
		}
	}

	private void permuteEachBlocks() {
		for (int i = 0; i < blockCount; i++) {
			myBlock[i].permute();
		}
	}

	private void revert() {
		permuteEachBlocks();
		readEachBlock();
		unadaptTextBody();
	}

	private void unadaptTextBody() {
		char[] buffer;
		buffer = textBody;
		textBody = new char[originalLength];
		for(int i = 0; i < originalLength; i++) {
			textBody[i] = buffer[i];
		}
	}

	private void adaptTextBody() {
		char[] buffer;
		buffer = textBody;
		textBody = new char[adaptedLength];
		for(int i = 0; i < adaptedLength; i++) {
			textBody[i] = buffer[i % originalLength];
		}
	}

	private void setBlockCount() {
		blockCount = originalLength / 8;
		if((originalLength % 8 ) > 0) blockCount++;
	}

	private void setAdaptedLength() {
		adaptedLength = blockCount * 8;
	}

	private void setBlocks() {
		myBlock = new Block[blockCount];
		for(int i = 0; i < blockCount; i++) {
			createBlock(i);
		}
	}

	private void createBlock(int blockIndex) {
		char[] buffer = new char[8];
		String blockText;
		int position;
		for(int i = 0; i < 8; i++) {
			position = i + (blockIndex * 8);
			buffer[i] = textBody[position];
		}
		blockText = String.valueOf(buffer);
		myBlock[blockIndex] = new Block(blockText);
	}

	public String getText() {
		String output = String.valueOf(textBody);
		return output;
	}

	public void printText() {
		System.out.println(String.valueOf(textBody));
	}

	public void printBlocks() {

		for (int i = 0; i < blockCount; i++) {
			System.out.println("(" + i + ") " + myBlock[i].getBlock());
		}
		System.out.println();
	}
}

LCG.Java

package freyaCipher;

public class LCG {

	private int seed;
	private int modulus;
	private int multiplier;
	private int length;
	private int[] value;

	public LCG(int seed, int modulus, int multiplier, int length) {
		this.seed = seed;
		this.modulus = modulus;
		this.multiplier = multiplier;
		this.length = length;
		generate();
	}

	public int getValue(int position) {
		return value[position];
	}

	public void printSequency() {
		for(int i = 0; i < length ;i++) {
		System.out.print(value[i] + ", ");
		}
	}

	private void generate() {
		value = new int[length];
		value[0] = generateValue(seed);
		generateAllValue();
	}

	private void generateAllValue() {
		for(int i = 1; i < length; i++) {
			value[i] = generateValue(value[i - 1]);
		}
	}

	private int generateValue(int arg) {
		return (arg * multiplier) % modulus;
	}
}

Block.Java

package freyaCipher;

public class Block {

	private char[] collection = new char[8];

	public Block(String collection) {
		this.collection = collection.toCharArray();
	}

	public String getBlock() {
		return String.valueOf(collection);
	}

	public void permute() {
		char[] buffer;
		buffer = collection;
		collection = new char[8];
		collection[0] = buffer[0];
		collection[1] = buffer[3];
		collection[2] = buffer[5];
		collection[3] = buffer[1];
		collection[4] = buffer[6];
		collection[5] = buffer[2];
		collection[6] = buffer[4];
		collection[7] = buffer[7];
	}

	public void encode(String subKey) {
		char[] key = subKey.toCharArray();
		char[] buffer = collection;
		collection = new char[8];
		collection[0] = (char)((buffer[4] ^ key[0])^buffer[0]);
		collection[1] = (char)((buffer[5] ^ key[1])^buffer[1]);
		collection[2] = (char)((buffer[6] ^ key[2])^buffer[2]);
		collection[3] = (char)((buffer[7] ^ key[3])^buffer[3]);
		collection[4] = buffer[0];
		collection[5] = buffer[1];
		collection[6] = buffer[2];
		collection[7] = buffer[3];
	}

	public void decode(String subKey) {
		char[] key = subKey.toCharArray();
		char[] buffer = collection;
		collection = new char[8];
		collection[0] = buffer[4];
		collection[1] = buffer[5];
		collection[2] = buffer[6];
		collection[3] = buffer[7];
		collection[4] = (char)((buffer[4]^buffer[0])^key[0]);
		collection[5] = (char)((buffer[5]^buffer[1])^key[1]);
		collection[6] = (char)((buffer[6]^buffer[2])^key[2]);
		collection[7] = (char)((buffer[7]^buffer[3])^key[3]);
	}
}

Key.Java

package freyaCipher;

public class Key {
	int[] seed = new int[4];
	char[][] subKey = new char[4][16];
	char[] key;
	int modulus = 251;
	int multiplier = 13;

	public Key(String key) {
		this.key = key.toCharArray();
		generateSubKey();
	}

	private void generateSubKey() {
		generateAllSeed();
		LCG[] myLCG = new LCG[4];
		for(int i = 0; i < 4; i++)  {
			myLCG[i] = new LCG(seed[i], modulus, multiplier, 16);
			for(int j = 0; j < 16; j++) {
				subKey[i][j] = (char)myLCG[i].getValue(j);
			}
		}
	}

	private void generateAllSeed() {
		for(int i = 0; i < 4; i++) {
			generateSeed(i);
		}
	}

	private void generateSeed(int seedIndex) {
		seed[seedIndex]	= (int)key[seedIndex];
	}

	public String getSubKey(int roundIndex) {
		char[] buffer = new char[4];
		buffer[0] = subKey[0][roundIndex];
		buffer[1] = subKey[1][roundIndex];
		buffer[2] = subKey[2][roundIndex];
		buffer[3] = subKey[3][roundIndex];
		return String.valueOf(buffer);
	}
}

Il arrive assez souvent qu’il faut attribuer un type à un objet. Lorsque vient le temps de restreindre les valeurs attribuables pour ce type, c’est plus compliqué. Je m’explique à l’aide d’un exemple. J’ai une type d’espresso qui supporte uniquement le “simple”, le “double” et l’”allongé”. Je finirai toujours par utiliser un attribut quelconque pour représenter ce type. Comment m’assurer que seulement les valeurs supportées soient attribuées à cet attribut? ou comment être sur que personne ne mettra une valeur bidon dans cet attribut? Bien sûr il y a la validation “standard” avec des if, mais voici une solution qui abaisse très peu les performances de l’application, qui ne nécessite aucun test et beaucoup plus facile à maintenir (expliqué plus bas).

Le but est de restreindre l’accèes au constructeur à l’objet lui-même (constructeur private). On crée alors une interface pour notre type qui sera implémentée à toutes les calsses ayant ce type comme attribut. On crée ensuite une classe embarquée dans l’interface qui défini le type comme d’habitude (attribut, constructeur…). Pour restreindre l’accès à l’attribut, il suffit de ne pas définir de mutateur (set) et de mettre le constructeur “private”. Cela aura pour effet que seul l’interface pourra construire le type et par le faite même, donner des valeures à son attribut. Finalement, dans l’interface, on définit comme constante toutes les valeures permises pour le type, l’acesseur et le mutateur à l’objet. Un exemple précisera sans doute :P :

HasEspressoType.java

public interface HasEspressoType
{
    public static class EspressoTypeConstant
    {
        private String espressoTypeValue;

        private EspressoTypeConstant(String espressoTypeValue)
        {
            this.espressoTypeValue = espressoTypeValue;
        }

        public String getEspressoTypeValue()
        {
            return this.espressoTypeValue;
        }
    }

    public static final EspressoTypeConstant SIMPLE =
            new PhoneNumberTypeConstant("simple");

    public static final EspressoTypeConstant DOUBLE =
            new PhoneNumberTypeConstant("double");

    public static final EspressoTypeConstant LUNGO =
            new PhoneNumberTypeConstant("allongé");

    public EspressoTypeConstant getEspressoType();
    public void setEspressoType(EspressoTypeConstant e);
}

Espresso.java

public class Espresso implements HasEspressoType
{
    private EspressoTypeConstant espressoType;

    public Espresso(EspressoTypeConstant e)
    {
        this.espressoType = e;
    }

    public EspressoTypeConstant getEspressoType()
    {
        return this.espressoType;
    }

    public void setEspressoType(EspressoTypeConstant e)
    {
        this.espressoType = e;
    }
}

Barista.java

public class Barista
{
    void main()
    {
        Espresso e1 = new Espresso(Espresso.LUNGO);
        Espresso e2 = new Espresso(Espresso.DOUBLE);
        Espresso e3 = new Espresso(Espresso.DOUBLE);
        Espresso e4 = new Espresso(Espresso.SIMPLE);
        System.out.println(e1.getEspressoType().getEspressoTypeValue());
        System.out.println(e2.getEspressoType().getEspressoTypeValue());
        System.out.println(e3.getEspressoType().getEspressoTypeValue());
        System.out.println(e4.getEspressoType().getEspressoTypeValue());
    }

On pourraiît très facilement ajouter des attributs à notre type d’espresso (température, quandtité de café, etc.).

Plus tôt, je vous parlais de maintenance. En utilisant ce procédé, il est possible de définir une méthode getAllEspressoTypeConstant qui retourne toutes les valeurs possible (simple, double et allongé dans ce cas-ci). Cette méthode sera très pratique si nous avons plusieurs vues (grossièrement la partie affichée à l’utilisateur). Lorsque notre Barista apprendra un autre type d’espresso (… pas besoin on a le simple et le double :S), nous pourrons simplement ajouter une constante à notre interface et modifier la méthode “getAllEspressoTypeConstant” de la classe Espresso. Pas besoin de trouver toutes les vues avec les valeurs “hardcoded” (je vous met au défi de traduire ce terme) pour les modifier.

MyView.java

public class MyView()
{
    ...
    public MyView()
    {
        ListBox l = new ListBox()
        for (EspressoTypeConstant e : Espresso.getAllEspressoTypeConstant())
        {
                l.add(e.getEspressoTypeValue());
        }
        ...
    }
}

Source: http://code.google.com/p/google-web-toolkit/source/browse/trunk/user/src/com/google/gwt/user/client/ui/HasVerticalAlignment.java?r=7746

J’ai eu quelques problèmes avec la libraire de Logging standard à Java (java.util.logging).

  • Aucun support pour les fichiers de configuration
  • Fin du logging lors de la supression ou de la modification du fichier de log

J’ai donc codé un wrapper pour me faciliter la vie et réduire le code dans chaque classes qui veut logger quelque chose. Ce wrapper résout les problèmes ennoncés plus haut et supporte le reload à chaud. Lorsqu’on change un paramètre dans le fichier de configuration, celui-ci est automatiquement chargé lors du prochain log. Il détecte la modfication à l’aide de la classe ConfigFile.

Il est maintenant super simple de l’appeller. Dans n’importe quelle classe du projet:

LogFactory.log("org.limoilux.demo", Level.WARNING, "Need a coffee");

Voici la source de LogFactory, ConfigFile et le fichier de configurations.

LogFactory.java

package org.limoilux.demo;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;

public class LogFactory {

    private static final String LOG_CONF_FILE_PATH = "conf/log.properties";
    private static final ConfigFile confFile = new ConfigFile();
    private static String logFileName = "log/demo.log";
    private static long logFileLength = 0;

    public static void log(String logger_name, Level l, String msg)
    {
        try
        {
            if (!LogFactory.isInitialized())
            {
                LogFactory.initialize();
            }
        }
        catch (Exception e)
        {
            Logger.getLogger(logger_name).log(Level.SEVERE, e.toString());
        }

        Logger.getLogger(logger_name).log(l, msg);
        try
        {
            LogFactory.logFileLength =
                        new File(LogFactory.logFileName).length();
        }
        catch (Exception e)
        {
            Logger.getLogger(logger_name).log(Level.SEVERE, e.toString() +
                    " cannot get logger file length");
        }
    }

   private static boolean isInitialized() throws Exception
   {
      if (!LogFactory.confFile.isInitialized(LogFactory.LOG_CONF_FILE_PATH))
      {
          return false;
      }
      try
      {
        if (!LogFactory.logFileExists())
        {
          return false;
        }
        if (LogFactory.logFileChanged())
        {
           return false;
        }
      }
      catch (Exception ex)
      {
        throw ex;
      }
      return true;
   }

   private static boolean logFileExists() throws Exception
   {
       try
       {
            LogFactory.refreshLogFileName();
       }
       catch (IOException ex)
       {
            throw ex;
       }
       catch (Exception e)
       {
           throw e;
       }

       if (!new File(LogFactory.logFileName).exists())
       {
            return false;
       }
       return true;
   }

   private static void refreshLogFileName() throws IOException, Exception
   {
        Properties properties = new Properties();
        try
        {
            properties.load(new FileInputStream(LogFactory.LOG_CONF_FILE_PATH));

            String filename = properties.
                getProperty("java.util.logging.FileHandler.pattern");

            if (filename == null)
            {
                throw new Exception(
                        "No filename found in log configuration file");
            }

            LogFactory.logFileName = filename;
            LogFactory.logFileLength = new File(filename).length();
        }
        catch (IOException e)
        {
            throw e;
        }
   }

   public static void initialize() throws Exception
   {
      Properties prop = System.getProperties();
      prop.setProperty("java.util.logging.config.file",
      LogFactory.LOG_CONF_FILE_PATH);
      try
      {
        LogManager.getLogManager().readConfiguration();
      }
      catch (Exception e)
      {
        throw e;
      }
   }

    public static boolean logFileChanged() throws Exception
    {
       boolean has_changed = true;
       long new_file_length = 0;

       if (LogFactory.logFileLength == 0)
       {
           return true;
       }

       try
       {
            LogFactory.refreshLogFileName();

            new_file_length =
                    new File(LogFactory.logFileName).length();

            if (new_file_length == LogFactory.logFileLength)
            {
                has_changed = false;
            }
       }
       catch (IOException ex)
       {
            throw ex;
       }
       catch (Exception e)
       {
           throw e;
       }
       return has_changed;
    }
}

ConfigFile.java

package org.limoilux.demo;

import java.io.File;

/**
 *
 * @author avezina
 */
public class ConfigFile {
    private File configFile;

    public ConfigFile()
    {
        this.configFile = null;
    }

    /**
     * This will initialize the ConfigFile if it was not
     * @param filename
     * @return
     */
    public boolean isInitialized(String filename)
    {
       File f = new File(filename);
       if(this.configFile == null)
       {
           this.configFile = f;
           return false;
       }

       if (this.configFile.compareTo(f) != 0)
       {
           return false;
       }
       this.configFile = f;
       return true;
   }

    public void initialize(String filename)
    {
       this.configFile = new File(filename);
    }
}

Pour configurer le logger, il suffit de créer le fichier log.properties dans le répertoire conf. LogFactory s’occupera de détecter les changements et de charger les paramètres.

conf/log.properties

handlers = java.util.logging.FileHandler
.level = WARNING

java.util.logging.FileHandler.pattern = log/demo.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.append = true

org.vencomm.demo.level = INFO
C’est un petit défi que je m’était donné, comprendre conment un peut faire une addition avec seulement des OR et des AND. J ai dessiner des transistor et j’ai voulu les tester avec des transistors virtuels. Bien que j’aille trouver dans les livre une version pas mal plus simple du circuit, je suis quand même fier de moi d’avoir trouver quelque chose qui marchait tout seul, même si c’est loin d’être efficace.
————————–

package logicAdder;

public class AdderUnit {

	// Additioneur logique revision 1 20100521, par David Bernard, dbernard456@gmail.com

	public static boolean[] calculateSum (boolean inputA[], boolean inputB[], int outputLength) {

		boolean output[] = new boolean[outputLength];
		boolean buffer[] = new boolean[2];
		boolean remain   = false;    

		for(int i = 0; i < outputLength; i++) {
			buffer    = AdderUnit.compute(inputA[i], inputB[i], remain);
			output[i] = buffer[0];
			remain    = buffer[1];
		}

		return output;
	}

	private static boolean[] compute(boolean a, boolean b, boolean c) {
		boolean output[] = new boolean[2]; 

		boolean midVal = AdderUnit.allTrueUnit(a, b, c);
		boolean[] leftVal = AdderUnit.sumBody(a, b, c);
		boolean[] rightVal = AdderUnit.residBody(a, b, c);
		output[0] = AdderUnit.orUnit(leftVal, midVal);
		output[1] = AdderUnit.orUnit(rightVal, midVal);
		return output;
	}
	private static boolean[] sumBody(boolean a, boolean b, boolean c) {
		boolean output[] = new boolean[3];

		output[0] = AdderUnit.sumUnit(a, b, c);
		output[1] = AdderUnit.sumUnit(b, c, a);
		output[2] = AdderUnit.sumUnit(c, a, b);
		return output;
		}

	private static boolean[] residBody(boolean a, boolean b, boolean c) {
		boolean output[] = new boolean[3];

		output[0] = AdderUnit.residUnit(b, c, a);
		output[1] = AdderUnit.residUnit(c, a, b);
		output[2] = AdderUnit.residUnit(a, b, c);
		return output;
		}

	private static boolean residUnit(boolean input1,boolean input2, boolean input3){
		boolean buffer = false;
		if(input1 && input2) buffer = true;
		if(buffer && !input3)
			return true; else return false;
	}

	private static boolean sumUnit(boolean input1, boolean input2,boolean input3){
		boolean buffer = false;
		if(input1 || input2) buffer = true;
		if(!buffer && input3)
			return true; else return false;
	}

	private static boolean allTrueUnit(boolean input1,boolean input2,boolean input3){
		boolean buffer = false;
		if(input1 && input2) buffer = true;
		if(buffer && input3)
			return true; else return false;
	}

	private static boolean orUnit(boolean[] args, boolean input) {

		boolean buffer[] = new boolean[2];

		if(args[0] || args[1])
			buffer[0] = true; else buffer[0] = false;

		if(args[2] || input)
			buffer[1] = true; else buffer[1] = false;

		if(buffer[0] || buffer[1])
			return true; else return false;
	}

}

Si vous avez un parc avec des postes Windows, vous aimerez surement appliquer des GPO un jours ou l’autre en fonction des groupes. Voici comment j’ai réussi à émuler quelque chose de semblable.

J’ai suivi cette procédure premièrement pour monter le DC: http://www.howtoforge.com/openldap-samba-domain-controller-ubuntu7.10

Activer un logon script
Dans smb.conf, assurez vous d’avoir:

[global]
...
logon script = scripts\logon.bat
...
[netlogon]
comment = The domain logon service
path = /home/netlogon
public = no
writeable = no
browsable = no

Logon script

Dans /home/netlogon/scripts/logon.bat

@echo off

echo %time%  > C:\time.txt

REM Yaay! Let's print an ascii art
type \\<THIS_SERVER>\netlogon\scripts\ascii_arts\tux.txt

REM Things are easier in VBS
cscript \\<THIS_SERVER>\netlogon\scripts\logon.vbs
echo %time% >> C:\time.txt

Je me suis fait demander: “Pourquoi VBS?”. La réponse est que c’est la seule façon de travailler avec Microsoft Office sur le sens du monde… malheureusement.

Avec le script suivant, nous pouvons lancer des sous scripts dans l’order désiré (ordre alphabétique). Dans /home/netlogon/scripts/logon.vbs

'
' This script runs every scripts in folder_name sorted by name
'
'On Error Resume Next
Main
Sub Main()
  Dim fso, folder, files, sFolder
  Dim objFile, strScriptCode

  Set fso = CreateObject("Scripting.FileSystemObject")
  Set objShell = Wscript.CreateObject("Wscript.Shell")

  folder_name = "\\<THIS_SERVER>\netlogon\scripts\scripts.d\"

  set fold = fso.getFolder(folder_name)
  fileCount = fold.files.count
  dim fNames()
  redim fNames(fileCount)
  cFcount = 0
  for each file in fold.files
    cFcount = cFcount + 1
    fNames(cFcount) = lcase(file.name)
  next
  for tName = 1 to fileCount
    for nName = (tName + 1) to fileCount
      if strComp(fNames(tName),fNames(nName),0)=1 then
        buffer = fNames(nName)
          fNames(nName) = fNames(tName)
          fNames(tName) = buffer
      end if
    next
  next
  for i = 1 to fileCount
    script_name = folder_name & fNames(i)
    wscript.stdout.writeline script_name

    Set objFile = fso.OpenTextFile(script_name, 1)
    strScriptCode = objFile.ReadAll
    objFile.Close

    Execute strScriptCode
  next
End Sub

Le répertoire /home/netlogon/scripts/scripts.d contient les sous scripts. Un bon truc pour l’ordre alphabétique est de mettre des numéros devant les noms selon l’ordre dans lequel on veut qu’il soit exécuté (merci rc.d).

Émulation des GPO
Les GPO sont en fait des clé de registres appliqués sur une machine. Soit par utilisateur (HKEY_CURRENT_USER) ou pour toutes les machines (HKEY_LOCAL_MACHINE).

Donc pour appliquer les GPO, il suffit de faire un script qui applique des clés de registre. Encore une fois, le trie par ordre alphabétique est utile. Disons /home/netlogon/scripts/scripts.d/0001_update_registry.vbs

'
' This script runs every scripts in folder_name sorted by name
'
On Error Resume Next
update_registry
Sub update_registry()
  Dim fso, folder, files, sFolder

  Set fso = CreateObject("Scripting.FileSystemObject")
  Set objShell = Wscript.CreateObject("Wscript.Shell")

  folder_name = "\\<THIS_SERVER>\netlogon\scripts\registry_keys.d\"

  set fold = fso.getFolder(folder_name)
  fileCount = fold.files.count
  dim fNames()
  redim fNames(fileCount)
  cFcount = 0
  for each file in fold.files
      cFcount = cFcount + 1
      fNames(cFcount) = lcase(file.name)
  next
  for tName = 1 to fileCount
    for nName = (tName + 1) to fileCount
      if strComp(fNames(tName),fNames(nName),0)=1 then
        buffer = fNames(nName)
          fNames(nName) = fNames(tName)
          fNames(tName) = buffer
      end if
    next
  next
  for i = 1 to fileCount
    ' First param is the command to run
    ' Second param is window visibility (0 hidden, 1 visible)
    ' Third param is if we need to wait the return of the command to continue
    ' http://msdn.microsoft.com/en-us/library/d5fk67ky(VS.85).aspx
    objShell.Run "regedit.exe /S " & folder_name & fNames(i), 0, true
  next
End Sub

Pour trouver quelle clé de registre nous devons changer pour appliquer un certain paramètre, il suffit d’utiliser l’outil Process Monitor de Microsoft. Il permet de voir en temps réel quels sont les accèes en lecture et en écriture sur les registres. En “sniffant” et en cliquand sur la “bonne case à cocher” de n’importe quelle application ou outil de configuration Windows, on voit quelle clé se modifie. On peut donc l’exporter avec l’outil regedit et l’appliquer avec notre script. L’avantage de cette technique est que contrairement aux GPO Microsoft il est possible d’appliquer n’importe quel changement sur n’importe quel paramètre de n’importe quelle application.

Voici un exemple qui active la prise de contrôle à distance sur les postes clients. Dans /home/netlogon/scripts/registry_keys.d/ créer un fichier (0050_enable_remote_desktop).

Windows Registry Editor Version 5.00</code>

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server]
"fDenyTSConnections"=dword:00000000

*On note ici que ce n’est pas activate, mais deny… WTF?

Scripts par groupes
Parfois, il est utile de “connecter un lecteur réseau” sur une machine en fonction de son groupe ou d’appliquer tel paramètre sur une application que seul tel groupe pourrait avoir. Voici un script qui fait une requête dans l’arbre LDAP pour trouver tous les groupes de l’utilisateur courant pour ensuite lancer tous les sous scripts de ce groupe (encore avec l’orde alphabétique)

Dans /home/netlogon/scripts/scripts.d créer un nouveau script (0089_group_logon.vbs)

group_logon
Sub group_logon()
  Set objShell = wscript.createObject("wscript.shell")
  Set fso = CreateObject("Scripting.FileSystemObject")
  username =  objShell.ExpandEnvironmentStrings("%USERNAME%")

  sUser = "logon_scripts"
  sPassword = "PASSWORD"
  sDN = "cn=" & sUser & ",dc=example,dc=com"
  sRoot = "LDAP://<LDAP_SERVER>/dc=example,dc=com"

  Dim oDS: Set oDS = GetObject("LDAP:")
  Dim oAuth: Set oAuth = oDS.OpenDSObject(sRoot, sDN, sPassword, &H0200)

  Dim oConn: Set oConn = CreateObject("ADODB.Connection")
  oConn.Provider = "ADSDSOObject"
  oConn.Open "Ads Provider", sDN, sPassword

  dim fNames()

  Dim rs
  Set rs = oConn.Execute("<" & sRoot & ">;(&(objectClass=sambaGroupMapping)(memberUid="&username&"));cn;subtree")

  Do while not rs.EOF
    For Each vValue in RS.Fields(0).value
      folder_name = "\\<THIS_SERVER>\netlogon\scripts\groups\"&vValue&".d\"
      If fso.FolderExists(folder_name) Then
        set fold = fso.getFolder(folder_name)
        fileCount = fold.files.count
        redim fNames(fileCount)
        cFcount = 0
        for each file in fold.files
          cFcount = cFcount + 1
          fNames(cFcount) = lcase(file.name)
        next
        for tName = 1 to fileCount
          for nName = (tName + 1) to fileCount
            if strComp(fNames(tName),fNames(nName),0)=1 then
              buffer = fNames(nName)
                fNames(nName) = fNames(tName)
                fNames(tName) = buffer
            end if
          next
        next
        for i = 1 to fileCount
          script_name = folder_name & fNames(i)
          wscript.stdout.writeline script_name

          Set objFile = fso.OpenTextFile(script_name, 1)
          strScriptCode = objFile.ReadAll
          objFile.Close

          Execute strScriptCode
        next
      End if
    next
  rs.moveNext
  Loop
End Sub

Ensuite on créer les répertoires en fonction des groupes. Si on ne veut pas de logon script pour un groupe, on ne crée pas de répertoire. Aussi simple que ça. Tout ça grâce à la ligne: If fso.FolderExists(folder_name) Then

Exemple de connexion de deux lecteurs réseau (le home et un autre) pour le groupe DouchEbag. Dans /home/netlogon/scripts/groups/DouchEbag.d (Un peu de respect pour la casse. On est sous Linux):

mount_netdrives
Sub mount_netdrives()
  ' Verifiy if drives are mounted, unmount them and remount them.
  Dim objShell, objNet, checkDrive, objUser, CurrentUser, strGroup

  Set objShell = WScript.CreateObject("WScript.Shell")
  Set objNet = WScript.CreateObject("WScript.Network")
  Set checkDrive = objNet.EnumNetworkDrives()

  'Declare static drives letters here
  Dim vecDriveLetter(2)
  vecDriveLetter(0) = "J:"
  vecDriveLetter(1) = "Z:"

  'Declare static drives paths here
  Dim vecRemotePath(2)
  vecRemotePath(0) = "\\<FILE_SERVER>\"&objShell.ExpandEnvironmentStrings("%USERNAME%")
  vecRemotePath(1) = "\\<MAYBE_ANOTHER_SERVER>\<SHARE>"

  'Mount every drive (persistent mode)
  For i = 0 To ubound(vecDriveLetter) - 1
    ' See http://msdn.microsoft.com/en-us/library/8kst88h6(VS.85).aspx
    objNet.MapNetworkDrive vecDriveLetter(i), vecRemotePath(i), true
  Next
End Sub

Il serait aussi possible d’appliquer le script de registres pour les groupes.

Déploiement de logiciels
Je vous conseille fortement WPKG.

Personellement je n’utilise pas leur agent (car il faut le déployer pour déployer…), mais j’utilise un script. Je le lance en dernier pour être certain que l’installation ne nuit pas trop au temps de login.

wpkg_command = "cscript \\<THIS_SERVER>\netlogon\wpkg\wpkg.js /synchronize /quiet /nonotify"
Set objShell = wscript.createObject("wscript.shell")
objShell.Run wpkg_command, 0, FALSE

Il ne resterait qu’à coder un GUI pour atteindre beaucoup de petites entreprises. Personnellement je trouve cette solution assez charmante, mais elle ne fera pas l’unanimité au niveau des sysadmins.

#!/bin/bash
BASE_DIR="/home/avezina/test/bkp/"

# Keep 2 directories
DIR_TO_REMOVE=`ls -C1 -t $BASE_DIR | awk 'NR&gt;2'`
for DIR in $DIR_TO_REMOVE
do
  rm -Rf $BASE_DIR/$DIR
done

Malheureusement je n’ai pas trouvé comment mettre une variable pour le 2. Mais bon ce n’est pas trop simple à changer.

L’informatique est maintenant au coeur des entreprises et institutions partout au travers le monde. Cette technologie augmente la productivité dans presque tous les domaines. Les investissements sont faramineux et il est normal de se demander si tous ces millions sont toujours justifiés. La réponse est simple: non. Trop souvent, des entreprises et gouvernements perdent des milliers de dollars en achats de licences quand des solutions autant sinon plus efficaces sont disponibles gratuitement sur le marché. Ils font partie de la famille des logiciels libres et nous devrions clairement les utiliser en entreprise.

D’entrée de jeu, je tiens à préciser que les logiciels libres ne sont plus du tout ce qu’ils étaient il y a dix ou même cinq ans. Ils ne sont plus réservés uniquement aux petits génies informatiques emprisonnés dans leur sous-sol. Pas plus aux gauchistes prêts à tout sacrifier pour avoir un monde totalement égalitaire. Ces logiciels sont souvent développés par des personnes mordues « désireuses de bénéficier de créations collectives et/ou souhaitant maximiser la diffusion de ses œuvres (logiciels, textes, images, vidéos, musiques) » 1. Il n’est pas rare de voir, dans les équipes de développement, une personne qui ne connaît absolument rien à l’informatique. Il s’agit souvent d’une personne passionnée qui désire avoir un outil adapté à ses besoins. C’est pour cette raison que les logiciels libres sont souvent mieux adaptés et plus performants que les solutions propriétaires. Par ailleurs, dans le monde de l’entreprise où toute décision se prend en fonction de l’argent, il est totalement absurde de dépenser des millions de dollars pour des licences d’utilisations de logiciels quand l’équivalent est disponible totalement gratuitement. Bien sûr, le processus de migration et de formation coûtera quelques dizaines de milliers de dollars, mais ces coûts s’amortiront facilement au bout de quelques années d’économies de licences. La France à déjà commencé le processus de migration et pour le ministère de la Gendarmerie nationale et leurs débuts « montrent une économie de 20 % par rapport aux solutions propriétaires Windows, grâce à l’absence des coûts de licences. » 2 Un autre atout des logiciels libres est leur capacité à s’adapter aux besoins de l’utilisateur. La réalité en entreprise est que chacune d’entre elles a des besoins spécifiques, mais elles doivent se plier aux fonctionnalités du logiciel. L’ajout ou la modification de fonctionnalités est presque impossible avec des solutions propriétaires, mais « disposer du code source permet à tout problème qui pourrait se poser d’être résolu rapidement, et de pouvoir ajouter facilement de nouvelles fonctionnalités. » 3 La création d’emplois pour le développement spécifique sur ces logiciels ouvrirait une gamme d’emploi totalement nouvelle et tous les coûts de ceux-ci seraient encore transparents pour des coûts justifiés.

D’un autre côté, beaucoup d’entreprises se plaignent que les logiciels libres ne sont pas compatibles avec tous les logiciels et toutes les pièces disponibles sur le marché. Ceci peut souvent causer des problèmes lors d’implantation de nouvelles technologies. Le partage de l’information et la centralisation des données amènent des besoins gigantesques pour les ressources informatiques. Les entreprises n’ont pas d’autres choix que de suivre le mouvement et si une technologie n’est pas compatible avec les logiciels libres, ils n’ont pas d’autres choix que de payer pour utiliser les logiciels compatibles. « Selon IDC, le marché du stockage sur disque profite de bonnes perspectives jusqu’en 2010. Le cabinet d’analyses prévoit en effet que ce marché génère un chiffre d’affaires de 29,8 Md$ en 2010. » 4. En plus, n’oublions pas que les développeurs de logiciels libres font souvent le développement à temps perdu et qu’ils ne gagnent aucun salaire pour leur travail. Il est donc prévisible de voir une vitesse de développement plus limitée qu’un logiciel propriétaire avec une équipe de plusieurs dizaines de développeurs qui travaillent à temps complet sur le logiciel. Il est assez fréquent de voir aussi un bon logiciel libre où l’équipe cesse complètement le développement par un manque de temps ou de motivation. Linus Torvalds, le fondateur lui-même de Linux et un grand participant aux logiciels libres affirme lui même: “Le seul projet à long terme que j’aie jamais eu, et conserve aujourd’hui encore, est d’améliorer Linux » 5. Finalement, les chefs d’entreprises dépensent beaucoup d’argent en salaire d’employés. Ils s’attendent à ce que leurs employés soient compétents dans leur domaine. Le problème est que les institutions ne font qu’effleurer les logiciels libres dans leurs formations. Ils se concentrent sur les besoins des entreprises qui sont le support et la maintenance des logiciels propriétaires. Il suffit de regarder la liste des cours d’informatique de divers Cégeps dans la région.

Les contre arguments énoncés si hauts ont du vrai, mais il est évident, qu’en creusant un peu plus profond, que ceux-ci manquent littéralement de sens. Les logiciels libres se conforment avec les standards définis par les autorités internationales (W3C, ISO, IEEE…). De plus, les logiciels libres s’efforcent même de comprendre les formats propriétaires afin d’offrir aux utilisateurs une compatibilité la plus élargie que possible. « Toutes les versions d’OpenOffice.org permettent l’import des documents enregistrés dans les anciens formats binaires (extensions en .doc, .xls, .ppt), et disposent également d’une option d’enregistrement dans ces mêmes formats afin de permettre l’interopérabilité avec les utilisateurs ne disposant pas d’une suite bureautique compatible OpenDocument, notamment les utilisateurs de MicrosoftOffice. » 6. Pour ce qui est de l’ajout des fonctionnalités, j’ai vu beaucoup d’exemples d’un logiciel libre en offrant beaucoup plus qu’un logiciel propriétaire. L’explication est simple, les développeurs sont des mordus et passionnés de défis. Ils ont aussi souvent eux-mêmes besoin d’utiliser le logiciel et ont une idée très précise de ce qui pourrait améliorer leur utilisation. Ils développent donc des logiciels plus adaptés et performants. Il est absurde de penser que quelqu’un puisse répondre parfaitement à un besoin sans avoir expérimenté ce besoin. Finalement, si l’éducation n’est pas adaptée aux logiciels libres, c’est que les entreprises ne font pas de demandes dans le domaine. C’est une roue sans fin qui doit être brisée dans un sens ou de l’autre. La France et le Brésil ont décidé de le faire dans un sens, à nous maintenant de décider qui brisera ce non-sens interminable.

Pour finir, je crois que tout le monde peut voir à quel point les logiciels libres répondent aux besoins de la majorité des entreprises. Le changement semble risqué à première vu pour tous chefs d’entreprises, mais en creusant un peu plus, on se rend compte que ce n’est qu’un manque d’information et qu’il faudrait travailler au niveau de la sensibilisation. Après cette mauvaise période, nous pourrions peut-être réinvestir nos impôts dans des domaines plus en danger.

1. « Qu’est-ce qu’un logiciel libre? », Association Francophone des Utilisateurs de Logiciels Libres, http://aful.org/ressources/logiciel-libre (page consultée le mardi 27 avril 2010)

2. « Logiciel libre : une porte pour le changement», cyberpresse.ca, http://www.cyberpresse.ca/le-soleil/actualites/science-et-technologie/201003/26/01-4264855-logiciel-libre-une-porte-pour-le-changement.php (page consultée le mardi 27 avril 2010)

3. « Logiciel libre et viabilité commerciale», gnu.org, http://www.gnu.org/philosophy/software-libre-commercial-viability.fr.html (page consultée le mardi 27 avril 2010)

4. « Croissance attendue du marché du stockage sur disque jusqu’en 2010», reseaux-telecoms.net, http://www.reseaux-telecoms.net/actualites/lire-croissance-attendue-du-marche-du-stockage-sur-disque-jusqu-en-2010-13556.html (page consultée le mardi 27 avril 2010)

5. « Le pragmatiste du logiciel libre : entretien avec Linus Torvalds. », linux-france.org, http://www.linux-france.org/article/these/interview/torvalds/pragmatist-fr.html (page consultée le mardi 27 avril 2010)

6. « http://fr.wikipedia.org/wiki/OpenOffice.org#Import_des_documents_au_format_Microsoft_Office», Wikipedia.org, http://fr.wikipedia.org/wiki/OpenOffice.org#Import_des_documents_au_format_Microsoft_Office (page consultée le mardi 27 avril 2010)

Drupal nous a causé beaucoup de problèmes tant au niveau de la stabilité que des ressources. Nous essayons donc WordPress qui semble être plus rapide et simple.