DHCP sous Windows – Callout

Les serveurs DHCP Microsoft, à partir de Windows 2003 Server, disposent d’une fonctionnalité très intéressante et rarement utilisée : le callout

Cette fonctionnalité, très bien documentée (http://msdn.microsoft.com/library/windows/desktop/aa363372.aspx), permet de notifier des librairies externes du traitement de requêtes DHCP.
Une fois n’est pas coutume, Microsoft laisse l’opportunité à ces librairies d’altérer les paquets, requêtes, et même de stopper le traitement d’une requête !

Le besoin

Pour une raison quelconque, j’ai du réfléchir à une problématique : comment ne pas distribuer d’adresses IP à une certaine catégorie de machines pouvant se brancher sur le même réseau physique/logique que des machines légitimes.

Des solutions existent déjà :

  • IpSec
  • NAC

et même d’autres moins efficaces (*) :

  • IP fixe pour tout le réseau
  • Blacklistage des adresses MAC sur les équipements frontaux
  • Réservations DHCP pour tous les postes

…Inapplicables pour diverses raisons sur l’environnement ciblé.
(*) L’on part ici du principe qu’une personne prenant volontairement une IP du réseau d’entreprise ou changeant son adresse MAC, commet une action malveillante. Cela arrive rarement par hasard.

Un début de solution

Quelques recherches Google mènent obligatoirement vers cette entrée Technet : http://blogs.technet.com/b/teamdhcp/archive/2007/10/03/dhcp-server-callout-dll-for-mac-address-based-filtering.aspx

L’on y apprend que le blacklistage d’adresses MAC au niveau DHCP n’est pas un besoin loufoque et que Microsoft a « développé » une librairie pouvant prendre en charge une liste noire ou blanche d’adresses.
A priori celle-ci est maintenant devenue une fonctionnalité native du service DHCP de Windows Server 2008 r2.

Vu le nombre de commentaires exprimant des problématiques de mise en service, et ce genre d’avertissements Microsoft :

The current callout DLL shall no longer be available after December 15, 2010.
[…]
Known Issue:

  1. This callout dll may not work on localized builds (non english builds).

… cela n’encourage guère à son utilisation !

Un peu de code

Grâce au MSDN : http://msdn.microsoft.com/library/windows/desktop/aa363373.aspx, l’on peut rapidement coder sa propre librairie (ici sans fichier de log ou de paramétrages … (pas envie, pas besoin…)

/*	Benjamin DELPY `gentilkiwi`
	http://blog.gentilkiwi.com
	benjamin@gentilkiwi.com
	Licence : http://creativecommons.org/licenses/by-nc-sa/3.0/fr/
*/
#include <windows.h>
#include <dhcpssdk.h>

#define MAC_ADDRESS_SIZE			6
#define MAC_SOURCE_ADDRESS_OFFSET	28

const BYTE macToBlack[][MAC_ADDRESS_SIZE] = {
	{0x00, 0x0c, 0x29, 0x00, 0x00, 0x00},
	{0x00, 0x50, 0x56, 0x00, 0x00, 0x00}
};

HMODULE nextLibrary = NULL;
LPDHCP_NEWPKT nextLibraryCalloutNewPkt = NULL;

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
	if(ul_reason_for_call == DLL_PROCESS_DETACH && nextLibrary)
		FreeLibrary(nextLibrary);

	return TRUE;
}

DWORD CALLBACK DhcpNewPktHook(IN OUT LPBYTE *Packet, IN OUT DWORD *PacketSize, IN DWORD IpAddress, IN LPVOID Reserved, IN OUT LPVOID *PktContext, OUT LPBOOL ProcessIt)
{
	DWORD retour = ERROR_SUCCESS, m;
	*ProcessIt = TRUE;

	for(m = 0; m < sizeof(macToBlack) / MAC_ADDRESS_SIZE; m++)
	{
		if(RtlEqualMemory(*Packet + MAC_SOURCE_ADDRESS_OFFSET, macToBlack[m], MAC_ADDRESS_SIZE / 2))
		{
			*ProcessIt = FALSE;
			retour = DHCP_DROP_INVALID;
			break;
		}
	}

	if(*ProcessIt && nextLibraryCalloutNewPkt)
		retour = nextLibraryCalloutNewPkt(Packet, PacketSize, IpAddress, Reserved, PktContext, ProcessIt);
	
	return retour;
}

DWORD CALLBACK DhcpServerCalloutEntry(IN LPWSTR ChainDlls, IN DWORD CalloutVersion, IN OUT LPDHCP_CALLOUT_TABLE CalloutTbl)
{
	LPDHCP_ENTRY_POINT_FUNC nextEntry;

	RtlZeroMemory(CalloutTbl, sizeof(DHCP_CALLOUT_TABLE));

	if(nextLibrary = LoadLibrary(ChainDlls))
		if(nextEntry = (LPDHCP_ENTRY_POINT_FUNC) GetProcAddress(nextLibrary, DHCP_CALLOUT_ENTRY_POINT))
			nextEntry(ChainDlls + wcslen(ChainDlls) + 1, CalloutVersion, CalloutTbl);

	if(CalloutTbl->DhcpNewPktHook)
		nextLibraryCalloutNewPkt = CalloutTbl->DhcpNewPktHook;
	CalloutTbl->DhcpNewPktHook = DhcpNewPktHook;

	return ERROR_SUCCESS;
}

Sans oublier notre export C d’un __stdcall

LIBRARY
EXPORTS
	DhcpServerCalloutEntry = DhcpServerCalloutEntry

Cette librairie rejette ainsi les demandes émanant d’adresse MAC commençant par 00:0c:29 ou 00:50:56, soit provenant de machines virtuelles VMware.

Installation

Sous HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\DHCPServer\Parameters

  • CalloutEnabled de type DWORD, à 1 pour activer les Callouts, 0 pour les désactiver
  • CalloutDlls de type REG_MULTI_SZ, contenant la liste des librairies de Callouts (séparées par un saut de ligne)

dhcp_callout

Le service DHCP (DHCPServer) doit être redémarré pour prendre en charge ces modifications.

Test

C:\Documents and Settings\Administrateur>ipconfig /renew

Configuration IP de Windows

Une erreur s'est produite lors du renouvellement de l'interface Connexion au réseau local : impossible de contacter votre serveur DHC. Le délai d'attente de la demande est dépassé.

Diaboliquement efficace pour quelques lignes :)