snmpsend.pas - added Privacy encryption support. Supported are DES, 3DES and AES.

git-svn-id: https://svn.code.sf.net/p/synalist/code/trunk@142 7c85be65-684b-0410-a082-b2ed4fbef004
This commit is contained in:
geby
2011-05-05 08:14:14 +00:00
parent 66ac5c3977
commit 7bfac2f4b1
3 changed files with 227 additions and 48 deletions

View File

@ -1,9 +1,9 @@
{==============================================================================| {==============================================================================|
| Project : Ararat Synapse | 003.000.010 | | Project : Ararat Synapse | 004.000.000 |
|==============================================================================| |==============================================================================|
| Content: SNMP client | | Content: SNMP client |
|==============================================================================| |==============================================================================|
| Copyright (c)1999-2010, Lukas Gebauer | | Copyright (c)1999-2011, Lukas Gebauer |
| All rights reserved. | | All rights reserved. |
| | | |
| Redistribution and use in source and binary forms, with or without | | Redistribution and use in source and binary forms, with or without |
@ -33,7 +33,7 @@
| DAMAGE. | | DAMAGE. |
|==============================================================================| |==============================================================================|
| The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).|
| Portions created by Lukas Gebauer are Copyright (c)2000-2010. | | Portions created by Lukas Gebauer are Copyright (c)2000-2011. |
| All Rights Reserved. | | All Rights Reserved. |
|==============================================================================| |==============================================================================|
| Contributor(s): | | Contributor(s): |
@ -45,9 +45,12 @@
{:@abstract(SNMP client) {:@abstract(SNMP client)
Supports SNMPv1 include traps, SNMPv2c and SNMPv3 include authorization Supports SNMPv1 include traps, SNMPv2c and SNMPv3 include authorization
(encryption not yet supported!) and privacy encryption.
Used RFC: RFC-1157, RFC-1901, RFC-3412, RFC-3414, RFC-3416 Used RFC: RFC-1157, RFC-1901, RFC-3412, RFC-3414, RFC-3416, RFC-3826
Supported Authorization hashes: MD5, SHA1
Supported Privacy encryptions: DES, 3DES, AES
} }
{$IFDEF FPC} {$IFDEF FPC}
@ -67,7 +70,7 @@ interface
uses uses
Classes, SysUtils, Classes, SysUtils,
blcksock, synautil, asn1util, synaip, synacode; blcksock, synautil, asn1util, synaip, synacode, synacrypt;
const const
cSnmpProtocol = '161'; cSnmpProtocol = '161';
@ -125,6 +128,12 @@ type
AuthMD5, AuthMD5,
AuthSHA1); AuthSHA1);
{:@abstract(Type of SNMPv3 privacy)}
TV3Priv = (
PrivDES,
Priv3DES,
PrivAES);
{:@abstract(Data object with one record of MIB OID and corresponding values.)} {:@abstract(Data object with one record of MIB OID and corresponding values.)}
TSNMPMib = class(TObject) TSNMPMib = class(TObject)
protected protected
@ -175,19 +184,25 @@ type
FUserName: AnsiString; FUserName: AnsiString;
FPassword: AnsiString; FPassword: AnsiString;
FAuthKey: AnsiString; FAuthKey: AnsiString;
FPrivMode: TV3Priv;
FPrivPassword: AnsiString;
FPrivKey: AnsiString; FPrivKey: AnsiString;
FPrivSalt: AnsiString;
FPrivSaltCounter: integer;
FOldTrapEnterprise: AnsiString; FOldTrapEnterprise: AnsiString;
FOldTrapHost: AnsiString; FOldTrapHost: AnsiString;
FOldTrapGen: Integer; FOldTrapGen: Integer;
FOldTrapSpec: Integer; FOldTrapSpec: Integer;
FOldTrapTimeTicks: Integer; FOldTrapTimeTicks: Integer;
function Pass2Key(const Value: AnsiString): AnsiString; function Pass2Key(const Value: AnsiString): AnsiString;
function EncryptPDU(const value: AnsiString): AnsiString;
function DecryptPDU(const value: AnsiString): AnsiString;
public public
constructor Create; constructor Create;
destructor Destroy; override; destructor Destroy; override;
{:Decode SNMP packet in buffer to object properties.} {:Decode SNMP packet in buffer to object properties.}
function DecodeBuf(const Buffer: AnsiString): Boolean; function DecodeBuf(Buffer: AnsiString): Boolean;
{:Encode obeject properties to SNMP packet.} {:Encode obeject properties to SNMP packet.}
function EncodeBuf: AnsiString; function EncodeBuf: AnsiString;
@ -246,8 +261,7 @@ type
{:Maximum message size in bytes for SNMPv3. For sending is default 1472 bytes.} {:Maximum message size in bytes for SNMPv3. For sending is default 1472 bytes.}
property MaxSize: Integer read FMaxSize write FMaxSize; property MaxSize: Integer read FMaxSize write FMaxSize;
{:Specify if message is authorised or encrypted. Used only in SNMPv3, and {:Specify if message is authorised or encrypted. Used only in SNMPv3.}
encryption is not yet supported!}
property Flags: TV3Flags read FFlags write FFlags; property Flags: TV3Flags read FFlags write FFlags;
{:For SNMPv3.... If is @true, SNMP agent must send reply (at least with some {:For SNMPv3.... If is @true, SNMP agent must send reply (at least with some
@ -264,6 +278,9 @@ type
authorization)} authorization)}
property AuthMode: TV3Auth read FAuthMode write FAuthMode; property AuthMode: TV3Auth read FAuthMode write FAuthMode;
{:For SNMPv3. Specify Privacy mode.}
property PrivMode: TV3Priv read FPrivMode write FPrivMode;
{:value used by SNMPv3 authorisation for synchronization with SNMP agent.} {:value used by SNMPv3 authorisation for synchronization with SNMP agent.}
property AuthEngineID: AnsiString read FAuthEngineID write FAuthEngineID; property AuthEngineID: AnsiString read FAuthEngineID write FAuthEngineID;
@ -285,7 +302,10 @@ type
{:For SNMPv3. Computed Athorization key from @link(password).} {:For SNMPv3. Computed Athorization key from @link(password).}
property AuthKey: AnsiString read FAuthKey write FAuthKey; property AuthKey: AnsiString read FAuthKey write FAuthKey;
{:For SNMPv3. Encryption key for message encryption. Not yet used!} {:SNMPv3 privacy password}
property PrivPassword: AnsiString read FPrivPassword write FPrivPassword;
{:For SNMPv3. Computed Privacy key from @link(PrivPassword).}
property PrivKey: AnsiString read FPrivKey write FPrivKey; property PrivKey: AnsiString read FPrivKey write FPrivKey;
{:MIB value to identify the object that sent the TRAPv1.} {:MIB value to identify the object that sent the TRAPv1.}
@ -417,6 +437,10 @@ begin
inherited Create; inherited Create;
FSNMPMibList := TList.Create; FSNMPMibList := TList.Create;
Clear; Clear;
FAuthMode := AuthMD5;
FPassword := '';
FPrivMode := PrivDES;
FPrivPassword := '';
FID := 1; FID := 1;
FMaxSize := 1472; FMaxSize := 1472;
end; end;
@ -452,8 +476,66 @@ begin
end; end;
end; end;
function TSNMPRec.DecryptPDU(const value: AnsiString): AnsiString;
var
des: TSynaDes;
des3: TSyna3Des;
aes: TSynaAes;
s: string;
begin
FPrivKey := '';
if FFlags <> AuthPriv then
Result := value
else
begin
case FPrivMode of
Priv3DES:
begin
FPrivKey := Pass2Key(FPrivPassword);
FPrivKey := FPrivKey + Pass2Key(FPrivKey);
des3 := TSyna3Des.Create(PadString(FPrivKey, 24, #0));
try
s := PadString(FPrivKey, 32, #0);
delete(s, 1, 24);
des3.SetIV(xorstring(s, FPrivSalt));
s := des3.DecryptCBC(value);
Result := s;
finally
des3.free;
end;
end;
PrivAES:
begin
FPrivKey := Pass2Key(FPrivPassword);
aes := TSynaAes.Create(PadString(FPrivKey, 16, #0));
try
s := CodeLongInt(FAuthEngineBoots) + CodeLongInt(FAuthEngineTime) + FPrivSalt;
aes.SetIV(s);
s := aes.DecryptCFBblock(value);
Result := s;
finally
aes.free;
end;
end;
else //PrivDES as default
begin
FPrivKey := Pass2Key(FPrivPassword);
des := TSynaDes.Create(PadString(FPrivKey, 8, #0));
try
s := PadString(FPrivKey, 16, #0);
delete(s, 1, 8);
des.SetIV(xorstring(s, FPrivSalt));
s := des.DecryptCBC(value);
Result := s;
finally
des.free;
end;
end;
end;
end;
end;
function TSNMPRec.DecodeBuf(const Buffer: AnsiString): Boolean; function TSNMPRec.DecodeBuf(Buffer: AnsiString): Boolean;
var var
Pos: Integer; Pos: Integer;
EndPos: Integer; EndPos: Integer;
@ -508,14 +590,24 @@ begin
FAuthEngineTimeStamp := GetTick; FAuthEngineTimeStamp := GetTick;
FUserName := ASNItem(SPos, s, Svt); FUserName := ASNItem(SPos, s, Svt);
FAuthKey := ASNItem(SPos, s, Svt); FAuthKey := ASNItem(SPos, s, Svt);
FPrivKey := ASNItem(SPos, s, Svt); FPrivSalt := ASNItem(SPos, s, Svt);
end; end;
//scopedPDU //scopedPDU
s := ASNItem(Pos, Buffer, Svt); if FFlags = AuthPriv then
if Svt = ASN1_OCTSTR then
begin begin
//decrypt! x := Pos;
s := ASNItem(Pos, Buffer, Svt);
if Svt <> ASN1_OCTSTR then
exit;
s := DecryptPDU(s);
//replace encoded content by decoded version and continue
Buffer := copy(Buffer, 1, x - 1);
Buffer := Buffer + s;
Pos := x;
if length(Buffer) < EndPos then
EndPos := length(buffer);
end; end;
ASNItem(Pos, Buffer, Svt); //skip sequence mark
FContextEngineID := ASNItem(Pos, Buffer, Svt); FContextEngineID := ASNItem(Pos, Buffer, Svt);
FContextName := ASNItem(Pos, Buffer, Svt); FContextName := ASNItem(Pos, Buffer, Svt);
end end
@ -547,11 +639,88 @@ begin
ASNItem(Pos, Buffer, Svt); ASNItem(Pos, Buffer, Svt);
Sm := ASNItem(Pos, Buffer, Svt); Sm := ASNItem(Pos, Buffer, Svt);
Sv := ASNItem(Pos, Buffer, Svt); Sv := ASNItem(Pos, Buffer, Svt);
if sm <> '' then
Self.MIBAdd(sm, sv, Svt); Self.MIBAdd(sm, sv, Svt);
end; end;
Result := True; Result := True;
end; end;
function TSNMPRec.EncryptPDU(const value: AnsiString): AnsiString;
var
des: TSynaDes;
des3: TSyna3Des;
aes: TSynaAes;
s: string;
x: integer;
begin
FPrivKey := '';
if FFlags <> AuthPriv then
Result := Value
else
begin
case FPrivMode of
Priv3DES:
begin
FPrivKey := Pass2Key(FPrivPassword);
FPrivKey := FPrivKey + Pass2Key(FPrivKey);
des3 := TSyna3Des.Create(PadString(FPrivKey, 24, #0));
try
s := PadString(FPrivKey, 32, #0);
delete(s, 1, 24);
FPrivSalt := CodeLongInt(FAuthEngineBoots) + CodeLongInt(FPrivSaltCounter);
inc(FPrivSaltCounter);
s := xorstring(s, FPrivSalt);
des3.SetIV(s);
x := length(value) mod 8;
x := 8 - x;
if x = 8 then
x := 0;
s := des3.EncryptCBC(value + Stringofchar(#0, x));
Result := ASNObject(s, ASN1_OCTSTR);
finally
des3.free;
end;
end;
PrivAES:
begin
FPrivKey := Pass2Key(FPrivPassword);
aes := TSynaAes.Create(PadString(FPrivKey, 16, #0));
try
FPrivSalt := CodeLongInt(0) + CodeLongInt(FPrivSaltCounter);
inc(FPrivSaltCounter);
s := CodeLongInt(FAuthEngineBoots) + CodeLongInt(FAuthEngineTime) + FPrivSalt;
aes.SetIV(s);
s := aes.EncryptCFBblock(value);
Result := ASNObject(s, ASN1_OCTSTR);
finally
aes.free;
end;
end;
else //PrivDES as default
begin
FPrivKey := Pass2Key(FPrivPassword);
des := TSynaDes.Create(PadString(FPrivKey, 8, #0));
try
s := PadString(FPrivKey, 16, #0);
delete(s, 1, 8);
FPrivSalt := CodeLongInt(FAuthEngineBoots) + CodeLongInt(FPrivSaltCounter);
inc(FPrivSaltCounter);
s := xorstring(s, FPrivSalt);
des.SetIV(s);
x := length(value) mod 8;
x := 8 - x;
if x = 8 then
x := 0;
s := des.EncryptCBC(value + Stringofchar(#0, x));
Result := ASNObject(s, ASN1_OCTSTR);
finally
des.free;
end;
end;
end;
end;
end;
function TSNMPRec.EncodeBuf: AnsiString; function TSNMPRec.EncodeBuf: AnsiString;
var var
s: AnsiString; s: AnsiString;
@ -610,8 +779,9 @@ begin
pdu := ASNObject(FContextEngineID, ASN1_OCTSTR) pdu := ASNObject(FContextEngineID, ASN1_OCTSTR)
+ ASNObject(FContextName, ASN1_OCTSTR) + ASNObject(FContextName, ASN1_OCTSTR)
+ pdu; + pdu;
//maybe encrypt pdu... in future
pdu := ASNObject(pdu, ASN1_SEQ); pdu := ASNObject(pdu, ASN1_SEQ);
//encrypt PDU if Priv mode is enabled
pdu := EncryptPDU(pdu);
//prepare flags //prepare flags
case FFlags of case FFlags of
@ -646,7 +816,7 @@ begin
AuthPriv: AuthPriv:
begin begin
s := authbeg + ASNObject(StringOfChar(#0, 12), ASN1_OCTSTR) s := authbeg + ASNObject(StringOfChar(#0, 12), ASN1_OCTSTR)
+ ASNObject(FPrivKey, ASN1_OCTSTR); + ASNObject(FPrivSalt, ASN1_OCTSTR);
s := ASNObject(s, ASN1_SEQ); s := ASNObject(s, ASN1_SEQ);
s := head + ASNObject(s, ASN1_OCTSTR); s := head + ASNObject(s, ASN1_OCTSTR);
s := ASNObject(s + pdu, ASN1_SEQ); s := ASNObject(s + pdu, ASN1_SEQ);
@ -672,7 +842,7 @@ begin
end; end;
auth := authbeg + ASNObject(FAuthKey, ASN1_OCTSTR) auth := authbeg + ASNObject(FAuthKey, ASN1_OCTSTR)
+ ASNObject(FPrivKey, ASN1_OCTSTR); + ASNObject(FPrivSalt, ASN1_OCTSTR);
auth := ASNObject(auth, ASN1_SEQ); auth := ASNObject(auth, ASN1_SEQ);
head := head + ASNObject(auth, ASN1_OCTSTR); head := head + ASNObject(auth, ASN1_OCTSTR);
@ -694,7 +864,6 @@ begin
FVersion := SNMP_V1; FVersion := SNMP_V1;
FCommunity := 'public'; FCommunity := 'public';
FUserName := ''; FUserName := '';
FPassword := '';
FPDUType := 0; FPDUType := 0;
FErrorStatus := 0; FErrorStatus := 0;
FErrorIndex := 0; FErrorIndex := 0;
@ -710,13 +879,14 @@ begin
FFlagReportable := false; FFlagReportable := false;
FContextEngineID := ''; FContextEngineID := '';
FContextName := ''; FContextName := '';
FAuthMode := AuthMD5;
FAuthEngineID := ''; FAuthEngineID := '';
FAuthEngineBoots := 0; FAuthEngineBoots := 0;
FAuthEngineTime := 0; FAuthEngineTime := 0;
FAuthEngineTimeStamp := 0; FAuthEngineTimeStamp := 0;
FAuthKey := ''; FAuthKey := '';
FPrivKey := ''; FPrivKey := '';
FPrivSalt := '';
FPrivSaltCounter := random(maxint);
end; end;
procedure TSNMPRec.MIBAdd(const MIB, Value: AnsiString; ValueType: Integer); procedure TSNMPRec.MIBAdd(const MIB, Value: AnsiString; ValueType: Integer);
@ -813,6 +983,10 @@ end;
function TSNMPSend.InternalSendRequest(const QValue, RValue: TSNMPRec): Boolean; function TSNMPSend.InternalSendRequest(const QValue, RValue: TSNMPRec): Boolean;
begin begin
Result := False; Result := False;
RValue.AuthMode := QValue.AuthMode;
RValue.Password := QValue.Password;
RValue.PrivMode := QValue.PrivMode;
RValue.PrivPassword := QValue.PrivPassword;
FSock.Bind(FIPInterface, cAnyPort); FSock.Bind(FIPInterface, cAnyPort);
FSock.Connect(FTargetHost, FTargetPort); FSock.Connect(FTargetHost, FTargetPort);
if InternalSendSnmp(QValue) then if InternalSendSnmp(QValue) then
@ -832,10 +1006,7 @@ begin
FQuery.AuthEngineTimeStamp := Sync.EngineStamp; FQuery.AuthEngineTimeStamp := Sync.EngineStamp;
FQuery.AuthEngineID := Sync.EngineID; FQuery.AuthEngineID := Sync.EngineID;
end; end;
FSock.Bind(FIPInterface, cAnyPort); Result := InternalSendRequest(FQuery, FReply);
FSock.Connect(FTargetHost, FTargetPort);
if InternalSendSnmp(FQuery) then
Result := InternalRecvSnmp(FReply);
end; end;
function TSNMPSend.SendTrap: Boolean; function TSNMPSend.SendTrap: Boolean;
@ -892,6 +1063,9 @@ begin
SyncQuery.Password := FQuery.Password; SyncQuery.Password := FQuery.Password;
SyncQuery.FlagReportable := True; SyncQuery.FlagReportable := True;
SyncQuery.Flags := FQuery.Flags; SyncQuery.Flags := FQuery.Flags;
SyncQuery.AuthMode := FQuery.AuthMode;
SyncQuery.PrivMode := FQuery.PrivMode;
SyncQuery.PrivPassword := FQuery.PrivPassword;
SyncQuery.PDUType := PDUGetRequest; SyncQuery.PDUType := PDUGetRequest;
SyncQuery.AuthEngineID := FReply.FAuthEngineID; SyncQuery.AuthEngineID := FReply.FAuthEngineID;
if InternalSendRequest(SyncQuery, FReply) then if InternalSendRequest(SyncQuery, FReply) then

View File

@ -1356,16 +1356,6 @@ const
type type
PDWord = ^LongWord; PDWord = ^LongWord;
function XorString(Indata1, Indata2: AnsiString): AnsiString;
var
i: integer;
begin
Indata2 := PadString(Indata2, length(Indata1), #0);
Result := '';
for i := 1 to length(Indata1) do
Result := Result + AnsiChar(ord(Indata1[i]) xor ord(Indata2[i]));
end;
procedure hperm_op(var a, t: integer; n, m: integer); procedure hperm_op(var a, t: integer; n, m: integer);
begin begin
t:= ((a shl (16 - n)) xor a) and m; t:= ((a shl (16 - n)) xor a) and m;

View File

@ -1,5 +1,5 @@
{==============================================================================| {==============================================================================|
| Project : Ararat Synapse | 004.014.000 | | Project : Ararat Synapse | 004.014.001 |
|==============================================================================| |==============================================================================|
| Content: support procedures and functions | | Content: support procedures and functions |
|==============================================================================| |==============================================================================|
@ -324,6 +324,9 @@ function GetTempFile(const Dir, prefix: AnsiString): AnsiString;
smaller, string is padded by Pad character.} smaller, string is padded by Pad character.}
function PadString(const Value: AnsiString; len: integer; Pad: AnsiChar): AnsiString; function PadString(const Value: AnsiString; len: integer; Pad: AnsiChar): AnsiString;
{:XOR each byte in the strings}
function XorString(Indata1, Indata2: AnsiString): AnsiString;
{:Read header from "Value" stringlist beginning at "Index" position. If header {:Read header from "Value" stringlist beginning at "Index" position. If header
is Splitted into multiple lines, then this procedure de-split it into one line.} is Splitted into multiple lines, then this procedure de-split it into one line.}
function NormalizeHeader(Value: TStrings; var Index: Integer): string; function NormalizeHeader(Value: TStrings; var Index: Integer): string;
@ -1781,6 +1784,18 @@ end;
{==============================================================================} {==============================================================================}
function XorString(Indata1, Indata2: AnsiString): AnsiString;
var
i: integer;
begin
Indata2 := PadString(Indata2, length(Indata1), #0);
Result := '';
for i := 1 to length(Indata1) do
Result := Result + AnsiChar(ord(Indata1[i]) xor ord(Indata2[i]));
end;
{==============================================================================}
function NormalizeHeader(Value: TStrings; var Index: Integer): string; function NormalizeHeader(Value: TStrings; var Index: Integer): string;
var var
s, t: string; s, t: string;