Latest Entries »

Recently, I needed to interconnect two private networks using on one side, a SOHO Cisco 871 router (because it’s silent, and people don’t want a desktop appliance to be as loud as an aircraft), and on the other side, an existing linux box with some services we want to connect to.

The main purpose of this setup is not to get optimal performances nor security, but to test interoperability on the two involved IPSec stack.

NAT configuration on the cisco side will be given as a bonus, at the end of this article, as it might be tricky to deal with simultaneous NAT and VPN.

You will find below the schema of our example setup.

netschemaIn our example, let’s assume our PSK is qFcOx72WVERsNobVsimx

Before we begin to overlook the configuration, let’s recall some points on IPSec.

IPSec is a quite complete protocol that can be used in a vast number of use cases: site to site VPNs, roadwarrior remote access, host to host security, with a focus on either integrity or integrity and confidentiality enforcement.

IPSec is thus commonly considered as a complex technology: its features are described and standardized by over 30 IETF RFCs, and it’s modularity reaches such a point that different implementations may not interoperate out of the box as we will see below. Some bonus features are not even standard (e.g. Opportunistic Encryption).

When two endpoints establish a security association (SA), the endpoint that attempt to establish the SA is called the initiator.

To summarize, the protocol works in two phases:

  • Phase 1:the security association and key management, where the two IPSec endpoints mutually authenticate and exchange keys that will be used on phase 2.
  • Phase 2: the security policy(ies) setup, where the two IPSec endpoints decide to do either encryption or authentication of the secured payload, and if they want to secure host to host, or network to network communications.

Here is the list of the different components that are involved in my sample setup:

  • Debian wheezy with a stock 3.2.54-2 kernel and the racoon and ipsec-tools packages from the official repository (version 1:0.8.0-14 for both these packages).
  • Cisco 871 with a Cisco IOS C870 Software (C870-ADVIPSERVICESK9-M), Version 12.4(15)T7, RELEASE (fc3)

Racoon configuration

To begin with, the configuration of racoon was not especially tricky until I experienced a strange issue: when the tunnel was initiated by the linux box, the phase 1 handshake worked properly, but the phase 2 failed to bring up, with a NO-PROPOSAL-CHOSEN error even if sa parameters were matching. If you have more feedback on this, you’re welcome to contribute in the comments. Edit: I have found what was the problem: I forgot to include the second sainfo section in racoon.conf, and I also made a mistake in the cisco configuration. Refer to the appropriate section for further details.

To avoid getting stuck in this case, I managed to make the linux box passive, and to bring up automatically the tunnel using a trick on the cisco side.

Racoon-issued dead peer detection also made my phase 2 die after timeout, as the cisco agent did not send appropriate replies. I addressed this issue by configuring racoon as a passive DPD responder.

/etc/racoon/racoon.conf

path pre_shared_key "/etc/racoon/psk.txt";
path certificate "/etc/racoon/certs";
log notify;

listen
{
	isakmp 198.51.100.37 [500];
}

remote 192.0.2.13 {
	exchange_mode aggressive,main;
	generate_policy off;
	my_identifier address 198.51.100.37;
	peers_identifier address 192.0.2.13;
	lifetime time 3600 sec;	
	passive on;
	
	proposal {
		encryption_algorithm 3des;
                authentication_method pre_shared_key;
                hash_algorithm sha1;
                dh_group 2;
		lifetime time 3600 sec;
	}
}


sainfo address 10.0.0.0/24[any] any address 10.224.9.0/24[any] any {
{
        pfs_group modp1024;
        encryption_algorithm 3des;
        authentication_algorithm hmac_sha1;
        compression_algorithm deflate;
        lifetime time 3600 sec;
}

sainfo address 10.224.9.0/24[any] any address 10.0.0.0/24[any] any {
{
        pfs_group modp1024;
        encryption_algorithm 3des;
        authentication_algorithm hmac_sha1;
        compression_algorithm deflate;
        lifetime time 3600 sec;
}

/etc/racoon/psk.conf

192.0.2.13 	qFcOx72WVERsNobVsimx

/etc/ipsec-tools.conf

#!/usr/sbin/setkey -f

flush;
spdflush;

spdadd 10.0.0.0/24 10.224.9.0/24 any -P out ipsec
    esp/tunnel/198.51.100.37-192.0.2.13/require;

spdadd 10.224.9.0/24 10.0.0.0/24 any -P in ipsec
    esp/tunnel/192.0.2.13-198.51.100.37/require;

Cisco 871 configuration

To get a nailed-up IPSec tunnel at boot time, I decided to set up a permanent ping probe using the ip sla feature of my IOS.

Edit: There was a mistake in the « crypto isakmp profile » section: when you use the match identity host directive, the identifier that follows is a fqdn, not an IP address. If you want to match IP addresses, use the match identity address directive. This is extremely important, as the phase 2 negociation might screw up because of this.

!
version 12.4
no service pad
service timestamps debug datetime msec
service timestamps log datetime msec
no service password-encryption
!
hostname nettest
!
boot-start-marker
boot-end-marker
!
!
no aaa new-model
!
!
dot11 syslog
ip cef
!
!
no ip dhcp use vrf connected
ip dhcp excluded-address 10.224.9.1
!
ip dhcp pool POOL_VLAN1
   network 10.224.9.0 255.255.255.0
   default-router 10.224.9.1 
!
!
ip domain name example.local
!
multilink bundle-name authenticated
!
!
username admin privilege 15 secret 0 youradminpassword
! 
!
crypto isakmp policy 1
 encr 3des
 authentication pre-share
 group 2
 lifetime 3600
crypto isakmp key qFcOx72WVERsNobVsimx address 198.51.100.37 no-xauth
crypto isakmp keepalive 10 3 periodic
crypto isakmp profile 1
   keyring default
   self-identity address
   match identity address 198.51.100.37
   keepalive 25 retry 3
!
!
crypto ipsec transform-set MyTransformSet esp-3des esp-sha-hmac 
!         
crypto map MyMap local-address FastEthernet4
crypto map MyMap isakmp-profile 1
crypto map MyMap 10 ipsec-isakmp 
 set peer 198.51.100.37
 set transform-set MyTransformSet 
 set pfs group2
 set isakmp-profile 1
 match address 150
!
archive
 log config
  hidekeys
!
!
ip tftp source-interface Vlan1
!
!
!
interface FastEthernet0
!
interface FastEthernet1
!
interface FastEthernet2
!
interface FastEthernet3
!
interface FastEthernet4
 description WAN interface
 ip address 192.0.2.13 255.255.255.0
 duplex auto
 speed auto
 crypto map MyMap
!
interface Vlan1
 description Internal interface
 ip address 10.224.9.1 255.255.255.0
 no autostate
!
ip forward-protocol nd
ip classless
ip route 0.0.0.0 0.0.0.0 192.0.2.13
!
!
no ip http server
no ip http secure-server
!
ip sla 10
 icmp-echo 10.0.0.1 source-interface Vlan1
 timeout 1000
 frequency 1
ip sla schedule 10 life forever start-time now
access-list 150 permit ip 10.224.9.0 0.0.0.255 10.0.0.0 0.0.0.255
!
!
!
!         
control-plane
!
banner motd ^C
*************************************************************
$(hostname) - VPN tests cisco router
Contact: Geoffroy GRAMAIZE
*************************************************************
^C
!
line con 0
 logging synchronous
 login local
 no modem enable
line aux 0
line vty 0 4
 logging synchronous
 login local
!
scheduler max-task-time 5000
end

As promised, If you want your internal network, on the cisco side, to communicate with the internet, you should add the following commands to the above configuration:

interface FastEthernet4
 ip nat outside
!
interface Vlan1
 ip nat inside
!
ip access-list extended NAT_list
 deny   ip 10.0.0.0 0.0.0.255 10.224.9.0 0.0.0.255
 deny   ip 10.224.9.0 0.0.0.255 10.0.0.0 0.0.0.255
 permit ip 10.224.9.0 0.0.0.255 any
!
ip nat inside source list NAT_list interface FastEthernet4 overload

I’ve recently purchased an Edgerouter PoE from Ubiquiti, which is a great deal regarding its price and performance. The only caveat was related to the lack of native support for load balancing and failover. This lack has been fixed with the release of the 1.4.0 firmware, which embeds a load balancing functionnality with native connection tracking.

For this example, I’ll take a generic scenario of a dual WAN setup, in a failover configuration with some policy routes, as we assume some ISP specific services are not available from the internet (e.g. administration interfaces, SMTP and DNS servers…)

Let’s also assume your ISP CPEs are configured in bridge mode.  To show all the potential of the router, the IP address we’ll get from ISP 1 is dynamic and the one from ISP 2 is static, but both are acquired from ISP’s DHCP server (yes, my ISP are serious people, and they don’t use PPPo[E|A] ^.^).

I also use an internal autonomous DNS server to avoid unreachability delays during failover, and to have a trusted DNSSEC anchor. You will find below the schema for this scenario. The fqdn and IP addresses in this scenario have been changed to protect the innocents.

Our sample topology.

Our sample topology.

To begin with, set up your 3 interfaces on the router, the dhcp on the inside part, and the DNAT rules.

interfaces {
    ethernet eth0 {
    address dhcp
        description ISP_1
        duplex auto
        poe {
            output off
        }
        speed auto
    }
    ethernet eth1 {
        address dhcp
        description ISP_2
        duplex auto
        poe {
            output off
        }
        speed auto
    }
    switch switch0 {
        address 192.168.0.254/24
        switch-port {
            interface eth2
            interface eth3
            interface eth4
        }
    }
}
service {
    dhcp-server {
        disabled false
        hostfile-update disable
        shared-network-name Home {
            authoritative disable
            subnet 192.168.0.0/24 {
                default-router 192.168.0.254
                dns-server 192.168.0.252
                lease 86400
                start 192.168.0.1 {
                    stop 192.168.0.50
                }
            }
        }
    }
}
nat {
    rule 5000 {
            description ISP_1_NAT
            log disable
            outbound-interface eth0
            protocol all
            type masquerade
    }
    rule 5001 {
            description ISP_2_NAT
            log disable
            outbound-interface eth1
            type masquerade
    }
}

Next, we’ll setup the load balancer to use ISP1 as our primary access and ISP2 as our failover access. I decided to change some of the check parameters to show you how powerful the tool is. As we are in a failover setup, I won’t use the weight command, which you would use for load balancing scenarios, to adjust the percentage of traffic you’d like to send to the corresponding interface.

load-balance {
    group lb-output {
        interface eth0 {
            route-test {
                count {
                    failure 3
                    success 4
                }
                interval 5
                type {
                    ping {
                        target 203.0.113.42
                    }
                }
            }
        }
        interface eth1 {
            failover-only
        }
    }
}

As told at the beginning of this article, the load balancer will take care of tracking and marking the connection, to avoid that a current session gets in and out by different IP addresses. This is especially useful if you decide to use SNAT rules. As shown above, I decided to check ISP 1 connectivity against a specific IP address, but by default, the equipment will run the check against « ping.ubnt.com ».

Next, we’ll configure the fwr-lbalance firewall modifier group to set the policy routes. This firewall modifier will be used to let the trafic through the load balancer « lb-output », expect for:

  • RFC1918 networks which we will route through the main routing table.
  • 192.0.2.129 which is only reachable via ISP 1 (we’ll set up the target VRF table 10 for this case).
  • 198.51.100.192/28 which is only reachable via ISP 2. (we’ll assume our gateway is 198.51.100.62, and we’ll set up another target VRF table 20 for this case).

Edit: ubnt-stig advised me to use a firewall group to define the RFC1918 in the comment, so you will find an updated version below.

And here is the associated configuration:

firewall {
    group {
        network-group RFC1918 {
            network 10.0.0.0/8
            network 172.16.0.0/12
            network 192.168.0.0/16
        }
    }
    modify fwr-lbalance {
        rule 1 {
            action modify
            destination {
                group {
                    network-group RFC1918
                }
            }
            modify {
                table main
            }
        }
        rule 100 {
            action modify
            destination {
                address 192.0.2.129
            }
            modify {
                table 10
            }
        }
        rule 200 {
            action modify
            destination {
                address 198.51.100.192/28
            }
            modify {
                table 20
            }
        }
        rule 500 {
            action modify
            modify {
                lb-group lb-output
            }
        }
    }
} 
protocols {
    static {
        table 10 {
            interface-route 0.0.0.0/0 {
                next-hop-interface eth0 {
                }
            }
        }
        table 20 {
            route 0.0.0.0/0 {
                next-hop 198.51.100.62 {
                }
            }
        }    
    }
}

Now, you need to tell the router to apply the firewall modifier instance to your internal interfaces:

interfaces {
    switch switch0 {
        address 192.168.0.254/24
        firewall {
            in {
                modify fwr-lbalance
            }
        }
        switch-port {
            interface eth2
            interface eth3
            interface eth4
        }
    }
}

And finally, you’re done! Your dual wan setup is operationnal. Now you can configure SNAT rules for your publicly available services. If you want to use different a different load balancing policy, create another load-balancer group with the appropriate settings, and add a new rule into the firewall modifier group. Before exiting configuration mode, don’t forget to commit the configuration, and to save the configuration if it fits your requirement.

Edit: On the following screenshot, you can see the output of the load-balancer status commands.

Load Balander status

Load Balander status

Hi,In this article, I’ll talk about some problems you might encounter while working on a Cisco device:

When I plug a device on my Cisco CPE, I have some issues to get a DHCP lease.

This problem is mainly caused by the following points:

  • Your CPE ports are configured with the default spanning-tree behaviour. With the default behaviour, after being plugged, a switch port is temporarily put in a blocking state for 30 to 50 seconds, to acquire and calculate STP topology. If this behaviour is absolutely safe, some early network applications – like DHCP – will be subject to timeout.
  • When plugging your first equipment, if your CPE uses a level 2 switch, and has « vlan » interfaces , the vlan management interface takes some time to toggle from down/down to up/up state. The DHCP server is bound to this interface, so it won’t be able to process a request until the associated vlan interface is in up/up state.

To deal with the first issue, you have either to use the following commands to switch the level 2 port in STP portfast mode:

interface fastethernet X
 spanning-tree portfast

or to disable the spanning tree for your VLAN by typing the following in configuration mode:

no spanning-tree vlan <VID>

To mitigate the second issue, you must stick the vlan interface to the up/up state. You can do so using the following commands:

interface vlan X
 no autostate

I have screwed while flashing my Cisco device! Rommon tells me that it cannot find a bootable image.

keepcalm

Using tftpdnld, you can load a bootable image from a TFTP server from the rommon prompt. To do so, hook up your Cisco device to a network with a TFTP server, and type the following commands in the rommon prompt:

IP_ADDRESS=X.X.X.X
IP_SUBNET_MASK=X.X.X.X
DEFAULT_GATEWAY=X.X.X.X
TFTP_SERVER=<tftp_server_IPv4_address>
TFTP_FILE=<path_to_your_IOS_image_on_tftp_server>
tftpdnld -r

Once your image has booted, copy again your image from TFTP to flash, then check its integrity by computing and checking the resulting hash.

copy tftp://<tftp_server_addr>/<path_to_IOS_image> flash:<image_filename>
verify /md5 flash:<image_filename>

Then, configure the bootloader to load your image at boot time:

configure terminal
 boot system flash:<image_filename>
 exit
copy running-config startup-config

Now, you can reboot safely and enjoy your fresh IOS image.

I don’t remember my login/enable password, how to recover it?

To begin with, you need a serial console client which support the break signal: this is something you can’t emulate only with your keyboard. If you don’t know what the break signal is, please refer to the following document: Cisco Standard Break Key Sequence Combinations

As the recovery procedures is model dependent, visit the Password Recovery Procedures web page where you will find the detailed instructions for your device.

Standard TLS sessions have a big issue: they are vulnerable to the « wiretap then crack » attack scheme: any intercepted communication can be stored and deciphered when you’ll have/find/factorize the server private key. Network traffic analysers often provide an option to perform such an operation for debug/protocol validation purposes. « Perfect » forward secrecy has been designed to fight against this issue: when two peers want to establish a TLS tunnel with PFS, after performing the server (or the mutual) authentication, they agree on an ephemeral session key.

TLS perfect forward secrecy can be supported in all recent browsers with Apache 2.3+. Version 2.4 has recently been migrated to Debian Jessie. The configuration you will find below has been made with the Qualys SSL Server Test. This test suite is quite useful to review the configuration of your TLS server: it checks the validity of your certificates, the strength of the cipher suite your server offers and gives you information on how common browsers will behave on your website.

Though the grade I get is not perfect, the configuration that I posted below seems to be, at this i’m writing this post, both the most interoperable and the most robust configuration you can create with Apache.


Replace (or add if applicable) the following configuration directives in your SSL module configuration file (most likely to be found in /etc/apache2/mods-enabled/ssl.conf).

SSLProtocol +TLSv1.2 +TLSv1.1 +TLSv1
SSLCompression off
SSLHonorCipherOrder on
SSLCipherSuite ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:AES256-GCM-SHA384:AES256-SHA256:CAMELLIA256-SHA:ECDHE-RSA-AES128-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:CAMELLIA128-SHA

then restart your apache server.

Please refer to the configuration at the bottom of this post


UPDATE: Qualys has updated its browser test suite, integrating Opera 12.15 and Firefox 21 on fedora 19, which are both known for not supporting TLS_ECDHE_RSA_WITH_RC4_128_SHA (0xC011).

Fedora community is well known for building FIPS 140-2 compliant cryptographic modules, and FIPS 140-2 happens not to support TLS_ECDHE_RSA_WITH_RC4_128_SHA.

This federal standard is known to only whitelist some cipher suites. In fact FIPS 140-2 doesn’t allow the use of « next » generation algorhithms (such as ECDHE for ephemeral key exchange, or as Galois/Counter Mode for block ciphers) despite the fact they bring significant improvements in terms of bandwidth and computing overhead.

Considering nowadays cryptographic ecosystem, this standard is to be considered as obsolete, except if you plan to sell any cryptographic product to the US goverment.

Ignoring that fact, today’s main security issue is caused by Apple Safari that still has not mitigated the BEAST vulnerability using the (1/n-1) split record trick.

So here is quite embarassing tradeoff you have to deal with:
1. Let your apple clients be vulnerable to BEAST while supporting PFS for any other browser by recommending CBC modes block ciphers and refusing use of RC4 (this will enhance the security for every browser except Safari),
2. Protect everyone against BEAST by using a crippled stream cipher and support PFS with a best-effort policy.

Bonus reminder: even if 3DES-EDE cipher suites use a 168 bit key, real key strength is 112 bits because of its vulnerability to the meet-in-the-middle attack. You may advertise for legacy support purposes, but if you do so, put these suites at the bottom of your server cipherlist, but do not add export or ADH suites are they are respectively weak and vulnerable.


UPDATE 2: After some browser behaviour analysis, I have achieved to get full PFS support for modern browsers, as shown on the pictures below. To do so, you need to properly enable some Diffie-Hellman ciphers. Doing so doesn’t trip the BEAST vulnerability flag and enables PFS for FIPS-compliant browsers that were not supported. The « Key exchange » subgrade in the Qualys SSL Server Test will decrease because DH parameters length are not great, even if they give a sufficient level of security for now.

This grade deterioration is an apache-specific issue: Apache developpers currently assumes that (EC)DH parameter choice is to be build-specific. According to me, this is not an acceptable solution as it makes long term support more difficult. You should consider bumping (voting for) this bugtracker feature request to get a more flexible way to control DH/ECDH parameters in the future.

Replace (or add if applicable) the following configuration directives in your SSL module configuration file (most likely to be found in /etc/apache2/mods-enabled/ssl.conf).

SSLProtocol +TLSv1.2 +TLSv1.1 +TLSv1
SSLCompression off
SSLHonorCipherOrder on
SSLCipherSuite ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:RC4-SHA:AES256-GCM-SHA384:AES256-SHA256:CAMELLIA256-SHA:ECDHE-RSA-AES128-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:CAMELLIA128-SHA

then restart your apache server.

Here is the grade I’ve got with the last configuration:

Below, you can see which cipher suites are selected by your clients.

Expert tip: The (1/n-1) split record trick is implemented in Firefox since a while. Thus, you can safely disable RC4 on Firefox in the advanced configuration menu. To do so, enter ‘about:config’ in your address bar, then search for ‘rc4′ and toggle all the found values to ‘false’. If you experience connections issues, toggle back those parameters to true.

À l’attention des personnes qui éditent leur site en direct (c’est très mal) et/ou qui oublient de supprimer leurs fichiers de sauvegarde avant de mettre en production, ce tweet devrait vous faire réfléchir…

Un hack de brute tout simple.

Un hack de brute bête et méchant.

Si vous éditez rapidement vos pages sur place et/ou que vous faites une copie de sauvegarde, les fichiers de sauvegarde que vous créez restent disponibles par défaut, et ne seront pas interprétés comme des scripts. Il devient facile d’accéder à votre code source et donc, éventuellement à des données sensibles comme les crédentiels de votre base de données par exemple.

Si vous êtes sur Apache, vous pouvez ajouter ceci dans votre fichier de configuration principal pour colmater cette vulnérabilité:

<Files ~ "(\.(bak|old)|\~)$">
    Order allow,deny
    Deny from all
    Satisfy all
</Files>

Pour terminer, relancez l’indien (^_–)  ~ ☆

La matrice de Walsh-Hadamard dispose de certaines propriétés mathématiques intéressantes, comme l’orthogonalité 2 à 2 des lignes de la matrice qui est exploitée dans les systèmes à accès multiple par répartition de codes (CDMA). Cependant, les méthodes de génération conventionnelles font assez facilement appel à la multiplication de deux matrices.

Je me suis donc intéressé à une méthode de génération qui utilise quasi exclusivement des opérations de base (décalage de bits pour les puissances de 2, additions et branchements logiques). Elle présente toutefois l’inconvénient de consommer 2 fois la mémoire nécessaire à stocker la matrice.

Le code qui vous est proposé est optimisable, notamment en terme de stockage, mais il a été réalisé dans un objectif d’universalité. Il est fonctionnel et a été vérifié avec les matrices H2, H4 et H1024. Attention, le code source suivant est en C99, n’oubliez pas de configurer correctement votre compilateur.

Fichier hadamard.h

#ifndef __HADAMARD_H
#define __HADAMARD_H

typedef struct matrix {
	size_t width;
	size_t height;
	unsigned char *data;
} matrix;

typedef struct smatrix {
	size_t width;
	size_t height;
	char *data;
} smatrix;

void print_smatrix( smatrix *mtx);

void __hadamard_increment( size_t iteration, matrix *mtx);

// Generates the hadamard matrix of dimention 2^k
smatrix* gen_hadamard_matrix( size_t k);

#endif // __HADAMARD_H

Fichier hadamard.c

#include <stdio.h>
#include <stdlib.h>
#include "hadamard.h"

void print_smatrix( smatrix *mtx) {
	for( size_t i = 0; i<mtx->height; ++i)
	{
		printf( "|");

		for( size_t j=0; j<mtx->width; ++j)
		{
			printf( ((int)mtx->data[mtx->width*i+j] < 0 ? " %d" : " +%d"), (int)mtx->data[mtx->width*i+j]);
		}
		printf( " |\n");
	}
}

void __hadamard_increment( size_t iteration, matrix *mtx)
{
	size_t powOfTwo = 1 << iteration;

	for( size_t y=0; y<mtx->height; ++y)
	{
		// If selected bit is not set, skip this column
		if( (powOfTwo & y) != powOfTwo )
			continue;

		for( size_t x=0; x<mtx->height; ++x)
		{
			if( (powOfTwo & x) != powOfTwo)
				continue;
			++(mtx->data[mtx->height*y+x]);
		}
	}
}

// Generates the hadamard matrix of dimention 2^k
smatrix* gen_hadamard_matrix( size_t k)
{
	matrix mtx;
	smatrix* smat = malloc( sizeof(smatrix));

	// To generate a hadamard matrix, without using the multiplication,
	// we use an additive transition matrix.

	mtx.width = mtx.height = 1 << k;
        mtx.data = malloc(sizeof(unsigned char)*mtx.width*mtx.height);

	size_t mtxSZ2D = smat->width*smat->height;
        for (size_t i=0; i<mtxSZ2D; ++i)
        {
                mtx.data[i] = 0;
        }

	// We iteratively apply the incrementation on cells where the k_th bit
	// is set on both y and x coordinates. Apply k times for a 2^k by 2^k
	// Hadamard matrix

        for( size_t i=0; i<k; ++i)
                __hadamard_increment( i, &mtx);

        smat->width = mtx.width;
        smat->height = mtx.height;
        smat->data = malloc(sizeof(char)*smat->width*smat->height);

	// Then we generate the hadamard matrix by converting odd cells to '-1'
	// from the transition matrix and even cells to '+1'.

	size_t smatSZ2D = smat->width*smat->height;
        for ( size_t i=0; i<smatSZ2D; ++i)
        {
                smat->data[i] = (char)( ((mtx.data[i]) & 1) == 0 ? 1 : -1 );
        }

	free(mtx.data);
	return smat;
}

Fichier main.c

#include <stdio.h>
#include <stdlib.h>
#include "hadamard.h"

int main(void)
{
	size_t k;
	matrix mtx;

	printf( "Calcul de H(2^k). Valeur de k? ");
	scanf( "%i", &k);

	smatrix* smat = NULL;

	smat = gen_hadamard_matrix(k);
	print_smatrix( smat);

	free(smat->data);
	free(smat);
	return 0;
}

Les explications seront publiées prochainement.

Récemment, je cherchais une solution libre pour faire proprement et efficacement des diagrammes de séquence de message pour un rapport. Qui dit diagrammes dit généralement clicodrômes chronophages, ennuyeux, et où l’on passe perd son temps à peaufiner des détails cosmétiques.

Lorsque j’ai lu la documentation de mscgen (pour Message Sequence Chart GENerator) que je vais vous présenter ultérieurement, non seulement j’ai compris que j’avais affaire à une merveille respectueuse du principe KISS, mais qu’il existait également des possibilités d’interfaçage intéressantes.

Et le meilleur concurrent de la souris est…

… la combinaison d’un clavier et d’un langage efficace et simple à prendre en main, lisible et éditable à la fois par un humain et une machine! Pour vous montrer la puissance de mscgen, entrons directement dans la matière avec la source d’un petit exemple (qui est loin d’exploiter la totalité les fonctionnalités) qui illustre le mécanisme de retransmission sur rejet utilisé par le protocole LAPB:

msc {
    arcgradient = 10;
    tx[label="Transmitter"], rx[label="Receiver"];
    tx => rx [label="(I,0)"];
    tx -x rx [label="(I,1)"];
    ...;
    tx => rx [label="(I,2)"];
    rx => tx [label="(REJ,1)", textbgcolor="#FFAF5F"];
    tx => rx [label="(I,1)"];
    tx => rx [label="(I,2)"];
}

Ci dessous, on trouve le résultat associé à la source précédente

Diagramme généré par MSCGEN

Mscgen vous permet de produire vos graphiques aux formats png, svg, eps ou ismap sous Windows et Linux (il est même dans le dépôt main de Debian). La source MSC peut être fournie via l’entrée standard ou par un fichier. Enfin, il est possible de modifier la police par défaut.

Le seul inconvénient de ce logiciel est qu’il n’est utilisable qu’en ligne de commande, au grand dam de nos amis Windowsiens. Je vous invite à consulter la page web du développeur pour consulter la documentation du langage MSC et vous faire une idée plus complète des possibilités offertes. Pour le téléchargement, les liens sont disponibles en bas de la page de documentation.

Intégration à des préprocesseurs

Le choix de créer un langage simple et exploitable à la fois par un humain et une machine a permet le développement facile de « préprocesseurs » et de générateurs de trace. L’exemple le plus pratique que j’ai en tête est pcap2msc qui permet de convertir un fichier PCAP (trace d’échanges réseau) en une source MSC. Le résultat du traitement n’est pas parfait, mais la correction est facile et le gain de temps par rapport à l’utilisation une solution graphique est non négligeable, surtout quand les échanges sont longs.

Lors de la phase de conception, de débuggage et de documentation d’un logiciel, on peut utiliser le langage MSC pour aider à journaliser et rendre facilement lisibles les communications entre différents composants/threads/processus/nœuds d’une application. Si j’ai un peu de temps libre, ou le besoin de réaliser un tel générateur, je vous tiendrai au courant en temps voulu.

Récemment, j’ai eu besoin de générer des écrans à afficher sur une gameboy advance. On pourrait se dire « oui, c’est facile, tu n’as qu’à faire chauffer imagemagick pour les conversions », mais ce génial outil ne supporte pas le mode d’affichage de la console. En effet, Nintendo a utilisé l’espace de couleur RGB15 Big endian (tant qu’à faire, c’est plus amusant).

Du coup, j’ai le petit programme suivant en C89 pour l’occasion. Il permet de convertir une image bitmap de dimensions 240×160 (résolution de la GBA) avec des couleurs dans l’espace RGB24 vers un fichier RAW (les pixels écrits en dur dans le fichier sans en-tête) encodé avec le bon espace (le format binaire est décrit dans les sources).

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Screen size for a GBA */
#define GBA_WIDTH    240
#define GBA_HEIGHT    160

typedef unsigned char	uint8;
typedef unsigned short	uint16;

const int BmpHeaderSize = 54;

/* BMP file info */
typedef struct
{
    char ID[2];
    long FileSize;
    long RESERVED;
    long BitmapDataOffset;
    long BitmapHeaderSize;
    long Width;
    long Height;
    short NrOfPlanes;
    short BitsPerPixel;
    long Compression;
    long BitmapDataSize;
    long HorizontalResolution;
    long VerticalResolution;
    long Colors;
    long ImportantColors;
} BmpHeader;

typedef struct
{
    uint8 lsb;
    uint8 msb;
} TwoBytes;

/* Convert 24bit RGB to 15BPP Big endian format */
TwoBytes rgb_to_gba_be ( uint8 r, uint8 g, uint8 b)
{
    TwoBytes resp;

    /* GBA Pixel are Big endian and described as follow :
     *
     *        -------LSB-------   -------MSB-------
     * Color  G G G B   B B B B   0 R R R   R R G G
     * Order  2 1 0 4   3 2 1 0   X 4 3 2   1 0 4 3
     *
     */

    /* Initialize pixel */
    resp.msb = 0;
    resp.lsb = 0;

    /* Red component */
    resp.msb = resp.msb | ( (r>>1) & 0x7C ) ;

    /* Green component */
    resp.msb = resp.msb | ( (g>>6) & 0x03 ) ;
    resp.lsb = resp.lsb | ( (g<<5) & 0xE0 ) ;

    /* Blue component */
    resp.lsb = resp.lsb | ( (b>>3) & 0x1F ) ;

    return resp;
}

int main( int argc, char** argv)
{
    FILE *pInFile, *pOutRaw;

    BmpHeader* imgProps = NULL;

    uint8 r=0,g=0,b=0;
    TwoBytes gbapix;
    int line, col;
    long i;
    const long image_sz = GBA_HEIGHT * GBA_WIDTH * 2;

    /* Check args  */
    if( argc != 3 )
    {
        printf( "Usage: %s infile outfile\n", argv[0]);
        exit(1);
    }

    /* Open source file */
    pInFile = (FILE *) fopen( argv[1], "rb");
    if( pInFile == NULL )
    {
        printf( "Unable to open input file '%s' for reading.\n", argv[1]);
        exit(1);
    }

    /* Open target file */
    pOutRaw = (FILE *) fopen( argv[2], "wb");
    if( pOutRaw == NULL )
    {
        printf( "Unable to open output file '%s' for writing.\n", argv[2]);
        fclose(pInFile);
        exit(2);
    }

    /* Check source file */
    imgProps = (BmpHeader*)malloc(sizeof(BmpHeader));

    fread( &(imgProps->ID[0]),            1, 1, pInFile);
    fread( &(imgProps->ID[1]),             1, 1, pInFile);
    fread( &(imgProps->FileSize),             4, 1, pInFile);
    fread( &(imgProps->RESERVED),             4, 1, pInFile);
    fread( &(imgProps->BitmapDataOffset),         4, 1, pInFile);
    fread( &(imgProps->BitmapHeaderSize),         4, 1, pInFile);
    fread( &(imgProps->Width),             4, 1, pInFile);
    fread( &(imgProps->Height),             4, 1, pInFile);

    fread( &(imgProps->NrOfPlanes),         2, 1, pInFile);
    fread( &(imgProps->BitsPerPixel),         2, 1, pInFile);

    fread( &(imgProps->Compression),         4, 1, pInFile);
    fread( &(imgProps->BitmapDataSize),        4, 1, pInFile);
    fread( &(imgProps->HorizontalResolution),    4, 1, pInFile);
    fread( &(imgProps->VerticalResolution),     4, 1, pInFile);
    fread( &(imgProps->Colors),             4, 1, pInFile);
    fread( &(imgProps->ImportantColors),         4, 1, pInFile);

    if(     imgProps->ID[0] != 'B' || imgProps->ID[1] != 'M' ||
        imgProps->Width != 240 || imgProps->Height != 160 ||
        imgProps->Compression != 0 || imgProps->BitsPerPixel != 24 )
    {
        printf("*** Invalid input image! ***\n");
        printf("Signature: '%c%c' Expected: 'BM'.\n", imgProps->ID[0], imgProps->ID[1] );
        printf("Width: %ld Expected: %d\n", imgProps->Width, GBA_WIDTH);
        printf("Height: %ld Expected: %d\n", imgProps->Height, GBA_HEIGHT);
        printf("Compression: %s Expected: no\n", (imgProps->Compression != 0)?"yes":"no");
        printf("Color depth: %hd Expected: %d\n", imgProps->BitsPerPixel , 24);
        fclose(pInFile);
        fclose(pOutRaw);
        exit(3);
    }

    /* Fill the output with the appropriate size */
    fseek( pOutRaw, 0, SEEK_SET);
    for ( i=0; i<image_sz; ++i)
        fputc( 0x00, pOutRaw);
    fseek( pOutRaw, 0, SEEK_SET);

    /* Convert image and write to target */
    fseek( pInFile, BmpHeaderSize, SEEK_SET);

    for( line=0; line<GBA_HEIGHT ; ++line)
    {
        fseek( pOutRaw, (GBA_HEIGHT-1-line)*GBA_WIDTH*2, SEEK_SET);

        for( col=0; col<GBA_WIDTH ; ++col)
        {
            b=fgetc( pInFile);
            g=fgetc( pInFile);
            r=fgetc( pInFile);

            gbapix = rgb_to_gba_be ( r, g, b);
            fputc( gbapix.lsb, pOutRaw);
            fputc( gbapix.msb, pOutRaw);
        }
    }

    fclose(pInFile);
    fclose(pOutRaw);
    free(imgProps);
    return 0;
}

Le code travaille ligne à ligne (attention au format BMP qui stocke les lignes dans l’ordre inversé!), et convertit chaque pixel à la volée. Il est à noter que ce code a été conçu pour mon usage spécifique (vérification du format du fichier et de la taille). La taille prise en charge peut être rendue dynamique moyennant un peu de code supplémentaire. Si ça peut vous être utile, servez-vous et happy hacking ^.^

Vous ne supportez pas de devoir stocker les secrets de vos utilisateurs en clair dans vos fichiers sip.conf sous asterisk?

Pas de soucis, il est en effet possible de stocker les secrets au format MD5. En effet, SIP prévoit que l’on puisse effectuer une authentification de type Digest similaire à celle présente dans HTTP.

Cependant, n’oubliez pas de bien tester la compatibilité avec les clients de votre parc avant d’effectuer un déploiement complet: bien que l’authentification par Digest soit présente dans la spécification de SIP (RFC 3261), on n’est jamais à l’abri de surprises.

Dans un premier temps, il faut définir le realm qui va servir pendant l’authentification. Le realm doit être globalement unique, selon la RFC 3261. Pour la suite de l’exemple, je vais supposer que le realm est siptests.

Dans le fichier sip.conf, allez dans la section [global] décommentez et éditez (ou ajoutez le cas échéant) la ligne avec realm pour y mettre le realm que vous avez choisi:

[global]
...
realm=siptests

Ensuite, rendez vous dans la section où sont stockés vos utilisateurs.

Une entrée d’utilisateur va ressembler plus ou moins à la structure proposée ci-dessous:

[1337](default-phone)
    callerid=Leet user <1337>
    mailbox=1337
    secret=pastressecret
    username=1337

La forme en clair du hash à stocker est la suivante: <username>:<realm>:<secret> soit pour mon cas d’exemple 1337:siptests:pastressecret . Sous linux, vous pouvez générer très simplement le hash grace à la commande suivante:

$ echo -n "1337:siptests:pastressecret" | md5sum
bac2cb492e4eb751544add8b5a2a3d24  -
$

Veillez bien à ne pas oublier le paramètre -n pour ne pas ajouter un retour à la ligne. Dans le cas contraire, le hash calculé serait invalide.

Maintentant que vous avez votre hash, supprimez la ligne secret du profil et ajoutez la ligne md5secret comme suit:

[1337](default-phone)
    callerid=Leet user <1337>
    mailbox=1337
    md5secret=bac2cb492e4eb751544add8b5a2a3d24
    username=1337

Il ne vous reste plus qu’a recharger asterisk et vérifier que le client se connecte bien.

Au cours de mes différentes expérimentations, j’ai voulu renforcer l’authentification de certains de mes services en m’appuyant sur une infrastructure à clés publiques dont je disposais déjà. Étant donné que la configuration du VirtualHost sur Apache n’a pas forcément été simple à mettre en place, je vous propose par la suite un template de Virtualhost que j’ai conçu. Les aspects relatifs à la gestion d’une PKI ou de la génération de certificats ne sera pas traitée dans ce billet.

Prérequis

La présente configuration suppose que vous disposiez au préalable:

  • d’un serveur Apache fonctionnel avec un virtualhost qui vous permettra de accueillir les utilisateurs dont l’authentification a échoué
  • des modules apache suivants (liste non exhaustive): mod_ssl, mod_rewrite, mod_headers
  • de certificats SSL serveurs et clients valides.

Gestion des erreurs

Un des problèmes principaux de l’authentification SSL repose sur le fait que le client se retrouve sur une page d’erreur du navigateur et non sur une page d’erreur de votre site web. La configuration suivante vous permettra de rediriger le client vers une autre page lui proposant éventuellement d’autres modes d’authentification (forte de préférence). La redirection mise en place est de type HTTP/307 et est complètement standard.

Pour notre exemple, je vous fournis une mire d’échec très simpliste en PHP qui permettra au client de relancer la procédure d’authentification vers le bon site une fois les problèmes corrigés de son côté. Pour la suite de ce billet, on supposera cette page accessible via l’URL suivante: https://login.yourdomain.com/sslfail.php

<html>
    <head>
        <title>FAIL</title>
    </head>
    <body>
        <p>SSL authentication failed. <?php
            if(isset($_GET["from"]))
            {
                echo("<a href=\"https://". $_GET["from"] .".yourdomain.com/\">Try Again.</a>");
            }
        ?></p>
    </body>
</html>

Le fichier VirtualHost

Pour la suite, on va considérer que le service à protéger sera accessible aux clients sur secureservice.yourdomain.com.

<IfModule mod_ssl.c>
    <VirtualHost *:443>
        ServerName secureservice.yourdomain.com
        DocumentRoot /var/secureservice-wwws

        # Configuration SSL côté serveur
        SSLEngine on
        SSLCertificateFile    /path/to/ca/certs/secureservice.pem
        SSLCertificateKeyFile /path/to/ca/private/secureservice.key
        SSLCertificateChainFile /path/to/ca/keystore/ca.crt

        # Configuration SSL côté client
        SSLCACertificateFile /path/to/ca/certs/ca.pem
        SSLCARevocationFile /path/to/ca/crl/ca-crl.crl
        SSLVerifyDepth  10

        <IfModule mod_rewrite.c>
            # If mod_rewrite is available, redirect clients on failure
            SSLVerifyClient optional

            <IfModule mod_headers.c>
                Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
                Header set Pragma "no-cache"
                Header set Expires "Wed, 11 Jan 1984 05:00:00 GMT"
            </IfModule>
            RewriteEngine On
            RewriteCond %{SSL:SSL_CLIENT_VERIFY} !^SUCCESS$
            RewriteRule .* https://login.yourdomain.com/sslfail.php?from=secureservice [r=307]
        </IfModule>
        <IfModule !mod_rewrite.c>
            # If mod_rewrite is not available, just deny clients on failure
            SSLVerifyClient require
        </IfModule>

        BrowserMatch "MSIE [2-6]" \
            nokeepalive ssl-unclean-shutdown \
            downgrade-1.0 force-response-1.0
        # MSIE 7 and newer should be able to use keepalive
        BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
    </VirtualHost>
</IfModule>

Concernant les attributs côté client, je tiens à vous préciser quelque points: en plus de la configuration classique du certificat serveur, 3 lignes sont impératives pour mettre en place l’authentification SSL:

  • La ligne qui dit au serveur où chercher les certificats des CA qui ont signé les certificats client (SSLCACertificateFile). Le chemin doit impérativement pointer sur un fichier encodé au format PEM. Si vous avez plusieurs CA à prendre en compte, concaténez vos différents fichiers PEM et faites pointer cette ligne sur le fichier résultant.
  • La ligne qui indique combien de niveaux de validation sont supportés pour les certificats clients (SSLVerifyDepth).  Si la valeur associée vaut zéro, le certificat client doit être auto-signé. Si la valeur vaut 1, le certificat doit être validé par le certificat d’une autorité présente dans le fichier des autorité client. Si la valeur vaut 2 ou plus, le certificat peut avoir été signé par une autorité intermédiaire valide. Avec 2, le client peut avoir été signé par une CA signée par une CA présente dans le fichier des autorités.
  • La ligne qui va spécifier le comportement à tenir en cas d’échec. Lorsque SSLVerifyClient est positionné sur require, la négociation SSL/TLS sera annulée si le certificat de l’utilisateur n’est pas validé par le serveur. Le client sera informé par son navigateur que l’opération a échoué. Si par contre SSLVerifyClient est positionné sur optional, et que le certificat de l’utilisateur n’est toujours pas valide, la connexion s’établit quand même, mais il sera, dans notre cas, redirigé vers la page d’échec (http://login.tourdomain.com/sslfail.php?from=secureservice) par le biais d’une réponse HTTP/307. À ce sujet, il est très important d’ajouter le module headers à apache, car les navigateurs ont tendance à mettre en cache les redirections. Si une redirection est mise en cache, votre client pourrait croire que son authentification échoue en permanence, ce qui n’est pas souhaitable.

La spécification d’une liste de révocation de certificats n’est pas toujours nécessaire, mais il reste préférable de l’utiliser car elle vous permettra d’assurer une gestion des droits d’accès plus propre. La ligne avec SSLCARevocationFile doit pointer sur un fichier qui contient la concaténation de toutes les listes de révocation des autorités client encodées au format PEM.

Suivre

Recevez les nouvelles publications par mail.