windows-nt/Source/XPSP1/NT/net/dhcp/client/rogue/dhcploc.c

1099 lines
23 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1994 Microsoft Corporation
Module Name:
dhcploc.c
Abstract:
This apps is used to detect the rogue DHCP server on a subnet.
To build, 'nmake UMTEST=dhcpcli'
Author:
Madan Appiah (madana) 21-Oct-1993
Environment:
User Mode - Win32
Revision History:
Oct 1996, (a-martih) Martin Holladay
Corrected AV bug when passed an unknown cmd-line parameter.
--*/
#include <dhcpcli.h>
#include <conio.h>
#include <locmsg.h>
#include <time.h>
#include <lmcons.h>
#include <lmmsg.h>
#define RECEIVE_TIMEOUT 5 // in secs. 5 secs
#define THREAD_TERMINATION_TIMEOUT 10000 // in msecs. 10 secs
#define SOCKET_RECEIVE_BUFFER_SIZE 1024 * 4 // 4K max.
#define AUTH_SERVERS_MAX 64
#define SMALL_BUFFER_SIZE 32
#define ALERT_INTERVAL 5 * 60 // 5 mins
#define ALERT_MESSAGE_LENGTH 256
#define MAX_ALERT_NAMES 256
DWORD GlobalAuthServers[AUTH_SERVERS_MAX];
BOOL GlobalNoAuthPrint = FALSE;
DWORD GlobalAuthServersCount = 0;
HANDLE GlobalRecvThreadHandle = NULL;
BOOL GlobalTerminate = FALSE;
DWORD GlobalIpAddress = 0;
time_t GlobalLastAlertTime = 0;
DWORD GlobalAlertInterval = ALERT_INTERVAL;
LPWSTR GlobalAlertNames[MAX_ALERT_NAMES];
DWORD GlobalAlertNamesCount = 0;
#if DBG
VOID
DhcpPrintRoutine(
IN DWORD DebugFlag,
IN LPSTR Format,
...
)
{
#define MAX_PRINTF_LEN 1024 // Arbitrary.
va_list arglist;
char OutputBuffer[MAX_PRINTF_LEN];
ULONG length = 0;
//
// Put a the information requested by the caller onto the line
//
va_start(arglist, Format);
length += (ULONG) vsprintf(&OutputBuffer[length], Format, arglist);
va_end(arglist);
DhcpAssert(length <= MAX_PRINTF_LEN);
//
// Output to the debug terminal,
//
printf( "%s", OutputBuffer);
}
#endif // DBG
DWORD
OpenSocket(
SOCKET *Socket,
DWORD IpAddress,
DWORD Port
)
{
DWORD Error;
SOCKET Sock;
DWORD OptValue;
struct sockaddr_in SocketName;
//
// Create a socket
//
Sock = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP );
if ( Sock == INVALID_SOCKET ) {
Error = WSAGetLastError();
goto Cleanup;
}
//
// Make the socket share-able
//
OptValue = TRUE;
Error = setsockopt(
Sock,
SOL_SOCKET,
SO_REUSEADDR,
(LPBYTE)&OptValue,
sizeof(OptValue) );
if ( Error != ERROR_SUCCESS ) {
Error = WSAGetLastError();
goto Cleanup;
}
OptValue = TRUE;
Error = setsockopt(
Sock,
SOL_SOCKET,
SO_BROADCAST,
(LPBYTE)&OptValue,
sizeof(OptValue) );
if ( Error != ERROR_SUCCESS ) {
Error = WSAGetLastError();
goto Cleanup;
}
OptValue = SOCKET_RECEIVE_BUFFER_SIZE;
Error = setsockopt(
Sock,
SOL_SOCKET,
SO_RCVBUF,
(LPBYTE)&OptValue,
sizeof(OptValue) );
if ( Error != ERROR_SUCCESS ) {
Error = WSAGetLastError();
goto Cleanup;
}
SocketName.sin_family = PF_INET;
SocketName.sin_port = htons( (unsigned short)Port );
SocketName.sin_addr.s_addr = IpAddress;
RtlZeroMemory( SocketName.sin_zero, 8);
//
// Bind this socket to the DHCP server port
//
Error = bind(
Sock,
(struct sockaddr FAR *)&SocketName,
sizeof( SocketName )
);
if ( Error != ERROR_SUCCESS ) {
Error = WSAGetLastError();
goto Cleanup;
}
*Socket = Sock;
Error = ERROR_SUCCESS;
Cleanup:
if( Error != ERROR_SUCCESS ) {
//
// if we aren't successful, close the socket if it is opened.
//
if( Sock != INVALID_SOCKET ) {
closesocket( Sock );
}
}
return( Error );
}
BOOL
IsAuthServer(
DWORD IpAddress
)
{
DWORD i;
for( i = 0; i < GlobalAuthServersCount; i++ ) {
if( IpAddress == GlobalAuthServers[i] ){
return( TRUE );
}
}
return( FALSE );
}
VOID
ExtractOptions1(
POPTION Option,
PDHCP_OPTIONS DhcpOptions,
DWORD MessageSize
)
{
POPTION start = Option;
POPTION nextOption;
LPBYTE MagicCookie;
//
// initialize option data.
//
RtlZeroMemory( DhcpOptions, sizeof( DHCP_OPTIONS ) );
if ( MessageSize == 0 ) {
return;
}
//
// check magic cookie.
//
MagicCookie = (LPBYTE) Option;
if( (*MagicCookie != (BYTE)DHCP_MAGIC_COOKIE_BYTE1) ||
(*(MagicCookie+1) != (BYTE)DHCP_MAGIC_COOKIE_BYTE2) ||
(*(MagicCookie+2) != (BYTE)DHCP_MAGIC_COOKIE_BYTE3) ||
(*(MagicCookie+3) != (BYTE)DHCP_MAGIC_COOKIE_BYTE4)) {
return;
}
Option = (LPOPTION) (MagicCookie + 4);
while ( Option->OptionType != OPTION_END ) {
if ( Option->OptionType == OPTION_PAD ||
Option->OptionType == OPTION_END ) {
nextOption = (LPOPTION)( (LPBYTE)(Option) + 1);
} else {
nextOption = (LPOPTION)( (LPBYTE)(Option) + Option->OptionLength + 2);
}
//
// Make sure that we don't walk off the edge of the message, due
// to a forgotten OPTION_END option.
//
if ((PCHAR)nextOption - (PCHAR)start > (long)MessageSize ) {
return;
}
switch ( Option->OptionType ) {
case OPTION_MESSAGE_TYPE:
DhcpAssert( Option->OptionLength == sizeof(BYTE) );
DhcpOptions->MessageType =
(BYTE UNALIGNED *)&Option->OptionValue;
break;
case OPTION_SUBNET_MASK:
DhcpAssert( Option->OptionLength == sizeof(DWORD) );
DhcpOptions->SubnetMask =
(DHCP_IP_ADDRESS UNALIGNED *)&Option->OptionValue;
break;
case OPTION_LEASE_TIME:
DhcpAssert( Option->OptionLength == sizeof(DWORD) );
DhcpOptions->LeaseTime =
(DWORD UNALIGNED *)&Option->OptionValue;
break;
case OPTION_SERVER_IDENTIFIER:
DhcpAssert( Option->OptionLength == sizeof(DWORD) );
DhcpOptions->ServerIdentifier =
(DHCP_IP_ADDRESS UNALIGNED *)&Option->OptionValue;
break;
case OPTION_RENEWAL_TIME:
DhcpAssert( Option->OptionLength == sizeof(DWORD) );
DhcpOptions->T1Time =
(DWORD UNALIGNED *)&Option->OptionValue;
break;
case OPTION_REBIND_TIME:
DhcpAssert( Option->OptionLength == sizeof(DWORD) );
DhcpOptions->T2Time =
(DWORD UNALIGNED *)&Option->OptionValue;
break;
default:
break;
}
Option = nextOption;
}
return;
}
DWORD
SendDiscovery(
VOID
)
{
DWORD Error;
BYTE MessageBuffer[DHCP_SEND_MESSAGE_SIZE];
PDHCP_MESSAGE dhcpMessage = (PDHCP_MESSAGE)MessageBuffer;
LPOPTION option;
LPBYTE OptionEnd;
BYTE value;
BYTE *HardwareAddress = "123456";
BYTE HardwareAddressLength = 6;
LPSTR HostName = "ROGUE";
SOCKET Sock;
struct sockaddr_in socketName;
DWORD i;
//
// prepare message.
//
RtlZeroMemory( dhcpMessage, DHCP_SEND_MESSAGE_SIZE );
dhcpMessage->Operation = BOOT_REQUEST;
dhcpMessage->HardwareAddressType = 1;
//
// Transaction ID is filled in during send
//
dhcpMessage->SecondsSinceBoot = 60; // random value ??
dhcpMessage->Reserved = htons(DHCP_BROADCAST);
memcpy(
dhcpMessage->HardwareAddress,
HardwareAddress,
HardwareAddressLength
);
dhcpMessage->HardwareAddressLength = (BYTE)HardwareAddressLength;
option = &dhcpMessage->Option;
OptionEnd = (LPBYTE)dhcpMessage + DHCP_SEND_MESSAGE_SIZE;
//
// always add magic cookie first
//
option = (LPOPTION) DhcpAppendMagicCookie( (LPBYTE) option, OptionEnd );
value = DHCP_DISCOVER_MESSAGE;
option = DhcpAppendOption(
option,
OPTION_MESSAGE_TYPE,
&value,
1,
OptionEnd );
//
// Add client ID Option.
//
option = DhcpAppendClientIDOption(
option,
1,
HardwareAddress,
HardwareAddressLength,
OptionEnd );
//
// add Host name and comment options.
//
option = DhcpAppendOption(
option,
OPTION_HOST_NAME,
(LPBYTE)HostName,
(BYTE)((strlen(HostName) + 1) * sizeof(CHAR)),
OptionEnd );
//
// Add END option.
//
option = DhcpAppendOption( option, OPTION_END, NULL, 0, OptionEnd );
//
// Send the message
//
//
// open socket.
//
Error = OpenSocket(
&Sock,
GlobalIpAddress,
DHCP_SERVR_PORT );
if( Error != ERROR_SUCCESS ) {
printf("OpenReceiveSocket failed %ld.", Error );
return( Error );
}
//
// Initialize the outgoing address.
//
socketName.sin_family = PF_INET;
socketName.sin_port = htons( DHCP_SERVR_PORT );
socketName.sin_addr.s_addr = (DHCP_IP_ADDRESS)(INADDR_BROADCAST);
for ( i = 0; i < 8 ; i++ ) {
socketName.sin_zero[i] = 0;
}
Error = sendto(
Sock,
(PCHAR)MessageBuffer,
DHCP_SEND_MESSAGE_SIZE,
0,
(struct sockaddr *)&socketName,
sizeof( struct sockaddr )
);
if ( Error == SOCKET_ERROR ) {
Error = WSAGetLastError();
printf("sendto failed %ld\n", Error );
return( Error );
}
return( ERROR_SUCCESS );
}
VOID
LogEvent(
LPSTR MsgTypeString,
LPSTR IpAddressString,
LPSTR ServerAddressString
)
{
HANDLE EventlogHandle;
LPSTR Strings[3];
//
// open event registry.
//
EventlogHandle = RegisterEventSourceA(
NULL,
"DhcpTools" );
if (EventlogHandle == NULL) {
printf("RegisterEventSourceA failed %ld.", GetLastError() );
return;
}
Strings[0] = MsgTypeString;
Strings[1] = ServerAddressString;
Strings[2] = IpAddressString;
if( !ReportEventA(
EventlogHandle,
(WORD)EVENTLOG_INFORMATION_TYPE,
0, // event category
DHCP_ROGUE_SERVER_MESSAGE,
NULL,
(WORD)3,
0,
Strings,
NULL
) ) {
printf("ReportEventA failed %ld.", GetLastError() );
}
DeregisterEventSource(EventlogHandle);
return;
}
VOID
RaiseAlert(
LPSTR MsgTypeString,
LPSTR IpAddressString,
LPSTR ServerAddressString
)
{
time_t TimeNow;
DWORD Error;
TimeNow = time( NULL );
if( TimeNow > (time_t)(GlobalLastAlertTime + GlobalAlertInterval) ) {
WCHAR uIpAddressString[SMALL_BUFFER_SIZE];
WCHAR uServerAddressString[SMALL_BUFFER_SIZE];
WCHAR uMsgTypeString[SMALL_BUFFER_SIZE];
DWORD i;
LPWSTR MessageParams[3];
WCHAR AlertMessage[ ALERT_MESSAGE_LENGTH ];
DWORD MsgLength;
MessageParams[0] =
DhcpOemToUnicode( MsgTypeString, uMsgTypeString );
MessageParams[1] =
DhcpOemToUnicode( ServerAddressString, uServerAddressString );
MessageParams[2] =
DhcpOemToUnicode( IpAddressString, uIpAddressString );
MsgLength = FormatMessage(
FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_ARGUMENT_ARRAY,
NULL,
DHCP_ROGUE_SERVER_MESSAGE,
0, // language id.
AlertMessage, // return buffer place holder.
ALERT_MESSAGE_LENGTH, // minimum buffer size (in characters) to allocate.
(va_list *)MessageParams // insert strings.
);
if( MsgLength == 0 ) {
printf("FormatMessage failed %ld.", GetLastError() );
}
else {
//
// send alert message.
//
for( i = 0; i < GlobalAlertNamesCount; i++) {
Error = NetMessageBufferSend(
NULL,
GlobalAlertNames[i],
NULL,
(LPBYTE)AlertMessage,
MsgLength * sizeof(WCHAR) );
if( Error != ERROR_SUCCESS ) {
printf("NetMessageBufferSend failed %ld.", Error );
break;
}
}
}
GlobalLastAlertTime = TimeNow;
}
}
VOID
DisplayMessage(
LPSTR MessageBuffer,
DWORD BufferLength,
struct sockaddr_in *source
)
{
DHCP_OPTIONS DhcpOptions;
PDHCP_MESSAGE DhcpMessage;
SYSTEMTIME SystemTime;
DWORD MessageType;
LPSTR MessageTypeString;
BOOL AuthServer = FALSE;
CHAR IpAddressString[SMALL_BUFFER_SIZE];
CHAR ServerAddressString[SMALL_BUFFER_SIZE];
//
// check to see this is valid DHCP packet.
//
if( BufferLength < DHCP_MESSAGE_FIXED_PART_SIZE ) {
return;
}
DhcpMessage = (LPDHCP_MESSAGE) MessageBuffer;
if( (DhcpMessage->Operation != BOOT_REQUEST) &&
(DhcpMessage->Operation != BOOT_REPLY) ) {
return;
}
//
// extract options.
//
ExtractOptions1(
&DhcpMessage->Option,
&DhcpOptions,
BufferLength - DHCP_MESSAGE_FIXED_PART_SIZE );
if( DhcpOptions.MessageType == NULL ) {
return;
}
MessageType = *DhcpOptions.MessageType;
if( (MessageType < DHCP_DISCOVER_MESSAGE ) ||
(MessageType > DHCP_RELEASE_MESSAGE ) ) {
return;
}
//
// packet is valid dhcp packet, print info.
//
//
// if this packet is from one of the auth server and we are asked
// not to print auth servers packet, so so.
//
if( DhcpOptions.ServerIdentifier != NULL ) {
AuthServer = IsAuthServer(*DhcpOptions.ServerIdentifier);
}
if( GlobalNoAuthPrint && AuthServer ) {
return;
}
GetLocalTime( &SystemTime );
printf("%02u:%02u:%02u ",
SystemTime.wHour,
SystemTime.wMinute,
SystemTime.wSecond );
switch ( MessageType ) {
case DHCP_DISCOVER_MESSAGE:
MessageTypeString = "DISCOVER";
case DHCP_OFFER_MESSAGE:
MessageTypeString = "OFFER";
break;
case DHCP_REQUEST_MESSAGE:
MessageTypeString = "REQUEST";
break;
case DHCP_DECLINE_MESSAGE:
MessageTypeString = "DECLINE";
break;
case DHCP_ACK_MESSAGE:
MessageTypeString = "ACK";
break;
case DHCP_NACK_MESSAGE:
MessageTypeString = "NACK";
break;
case DHCP_RELEASE_MESSAGE:
MessageTypeString = "RELEASE";
break;
default:
MessageTypeString = "UNKNOWN";
break;
}
printf("%8s ", MessageTypeString);
strcpy(
IpAddressString,
inet_ntoa(*(struct in_addr *)&DhcpMessage->YourIpAddress) );
printf("(IP)%-15s ", IpAddressString );
if(DhcpOptions.ServerIdentifier != NULL ) {
DWORD ServerId;
ServerId = *DhcpOptions.ServerIdentifier;
strcpy( ServerAddressString, inet_ntoa(*(struct in_addr *)&ServerId) );
printf("(S)%-15s ", ServerAddressString );
if( source->sin_addr.s_addr != ServerId ) {
printf("(S1)%-15s ",
inet_ntoa(*(struct in_addr *)&source->sin_addr.s_addr) );
}
}
//
// beep if this it is a non-auth server.
//
if( AuthServer == FALSE ) {
printf("***");
MessageBeep( MB_ICONASTERISK );
//
// log an event.
//
LogEvent(
MessageTypeString,
IpAddressString,
ServerAddressString );
RaiseAlert(
MessageTypeString,
IpAddressString,
ServerAddressString );
}
printf("\n");
}
DWORD
ReceiveDatagram(
VOID
)
{
DWORD Error;
SOCKET Sock;
BOOL SocketOpened = FALSE;
fd_set readSocketSet;
struct timeval timeout;
struct sockaddr socketName;
int socketNameSize = sizeof( socketName );
CHAR MessageBuffer[DHCP_MESSAGE_SIZE];
Error = OpenSocket(
&Sock,
GlobalIpAddress,
DHCP_CLIENT_PORT );
if( Error != ERROR_SUCCESS ) {
printf("OpenReceiveSocket failed %ld.", Error );
goto Cleanup;
}
SocketOpened = TRUE;
//
// receive message.
//
while( GlobalTerminate != TRUE ) {
FD_ZERO( &readSocketSet );
FD_SET( Sock, &readSocketSet );
timeout.tv_sec = RECEIVE_TIMEOUT;
timeout.tv_usec = 0;
Error = select( 0, &readSocketSet, NULL, NULL, &timeout);
if ( Error == 0 ) {
//
// Timeout before read data is available.
//
// printf("Receive timeout.\n");
}
else {
//
// receive available message.
//
Error = recvfrom(
Sock,
MessageBuffer,
sizeof(MessageBuffer),
0,
&socketName,
&socketNameSize
);
if ( Error == SOCKET_ERROR ) {
Error = WSAGetLastError();
printf("recvfrom failed %ld\n", Error );
goto Cleanup;
}
if( GlobalTerminate == TRUE ) {
break;
}
DisplayMessage(
MessageBuffer,
Error, // buffer length returned.
(struct sockaddr_in *)&socketName );
}
}
Cleanup:
if( SocketOpened == TRUE ) {
//
// close socket.
//
closesocket( Sock );
}
GlobalTerminate = TRUE;
return( Error );
}
DWORD __cdecl
main(
int argc,
char **argv
)
{
DWORD Error;
LPSTR AppName = NULL;
WSADATA wsaData;
DWORD ThreadId;
//
// parse input parameters.
//
if( argc < 1 ) {
goto Usage;
}
AppName = argv[0];
argv++;
argc--;
if( argc < 1 ) {
goto Usage;
}
//
// parse flag parameter.
//
while( (argv[0][0] == '-') || (argv[0][0] == '/') ) {
switch (argv[0][1] ) {
case 'p':
GlobalNoAuthPrint = TRUE;
break;
case 'i':
GlobalAlertInterval = atoi( &argv[0][3] ) * 60;
break;
case 'a': {
LPSTR NextName;
LPSTR Ptr;
Ptr = &argv[0][3];
//
// skip blanks.
//
while( *Ptr == ' ' ) {
Ptr++;
}
NextName = Ptr;
while( *Ptr != '\0' ) {
if( *Ptr == ' ' ) {
//
// found another name.
//
*Ptr++ = '\0';
GlobalAlertNames[GlobalAlertNamesCount] =
DhcpOemToUnicode( NextName, NULL );
GlobalAlertNamesCount++;
if( GlobalAlertNamesCount >= MAX_ALERT_NAMES ) {
break;
}
//
// skip blanks.
//
while( *Ptr == ' ' ) {
Ptr++;
}
NextName = Ptr;
}
else {
Ptr++;
}
}
if( GlobalAlertNamesCount < MAX_ALERT_NAMES ) {
if( NextName != Ptr ) {
GlobalAlertNames[GlobalAlertNamesCount] =
DhcpOemToUnicode( NextName, NULL );
GlobalAlertNamesCount++;
}
}
break;
}
//
// (a-martih) - Bug Fix
//
default:
if ((_stricmp(argv[0], "/?")) &&
(_stricmp(argv[0], "-?")) &&
(_stricmp(argv[0], "/h")) &&
(_stricmp(argv[0], "-h")) ) {
printf( "\nunknown flag, %s \n", argv[0] );
}
goto Usage;
break;
}
argv++;
argc--;
}
if( argc < 1 ) {
goto Usage;
}
//
// read ipaddress parameter.
//
GlobalIpAddress = inet_addr( argv[0] );
argv++;
argc--;
//
// now read auth dhcp servers ipaddresses.
//
while( (argc > 0) && (GlobalAuthServersCount < AUTH_SERVERS_MAX) ) {
GlobalAuthServers[GlobalAuthServersCount++] =
inet_addr( argv[0] );
argv++;
argc--;
}
//
// init socket.
//
Error = WSAStartup( WS_VERSION_REQUIRED, &wsaData);
if( Error != ERROR_SUCCESS ) {
printf( "WSAStartup failed %ld.\n", Error );
return(1);
}
//
// create receive datagrams thread.
//
GlobalRecvThreadHandle =
CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)ReceiveDatagram,
NULL,
0,
&ThreadId );
if( GlobalRecvThreadHandle == NULL ) {
printf("CreateThread failed %ld.\n", GetLastError() );
return(1);
}
//
// read input.
//
while ( GlobalTerminate != TRUE ) {
CHAR ch;
ch = (CHAR)_getch();
switch( ch ) {
case 'q':
case 'Q':
// case '\c':
GlobalTerminate = TRUE;
break;
case 'd':
case 'D':
//
// send out discover message.
//
Error = SendDiscovery();
if(Error != ERROR_SUCCESS ) {
printf("SendDiscover failed %ld.\n", Error );
}
break;
case 'h':
case 'H':
default:
printf("Type d - to discover; q - to quit; h - for help.\n");
//
// print out help message.
//
break;
}
}
//
// terminate receive thread.
//
WaitForSingleObject(
GlobalRecvThreadHandle,
THREAD_TERMINATION_TIMEOUT );
CloseHandle( GlobalRecvThreadHandle );
// Cleanup:
return(0);
Usage:
printf("\nUSAGE:\n\n");
printf("%s [-p] [-a:\"list-of-alertnames\"] [-i:alertinterval] "
"machine-ip-address "
"[list of valid dhcp servers ip addresses]", AppName );
return(1);
}