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 |
|==============================================================================|
| Copyright (c)1999-2010, Lukas Gebauer |
| Copyright (c)1999-2011, Lukas Gebauer |
| All rights reserved. |
| |
| Redistribution and use in source and binary forms, with or without |
@ -33,7 +33,7 @@
| DAMAGE. |
|==============================================================================|
| 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. |
|==============================================================================|
| Contributor(s): |
@ -45,9 +45,12 @@
{:@abstract(SNMP client)
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}
@ -67,7 +70,7 @@ interface
uses
Classes, SysUtils,
blcksock, synautil, asn1util, synaip, synacode;
blcksock, synautil, asn1util, synaip, synacode, synacrypt;
const
cSnmpProtocol = '161';
@ -125,6 +128,12 @@ type
AuthMD5,
AuthSHA1);
{:@abstract(Type of SNMPv3 privacy)}
TV3Priv = (
PrivDES,
Priv3DES,
PrivAES);
{:@abstract(Data object with one record of MIB OID and corresponding values.)}
TSNMPMib = class(TObject)
protected
@ -175,19 +184,25 @@ type
FUserName: AnsiString;
FPassword: AnsiString;
FAuthKey: AnsiString;
FPrivMode: TV3Priv;
FPrivPassword: AnsiString;
FPrivKey: AnsiString;
FPrivSalt: AnsiString;
FPrivSaltCounter: integer;
FOldTrapEnterprise: AnsiString;
FOldTrapHost: AnsiString;
FOldTrapGen: Integer;
FOldTrapSpec: Integer;
FOldTrapTimeTicks: Integer;
function Pass2Key(const Value: AnsiString): AnsiString;
function EncryptPDU(const value: AnsiString): AnsiString;
function DecryptPDU(const value: AnsiString): AnsiString;
public
constructor Create;
destructor Destroy; override;
{: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.}
function EncodeBuf: AnsiString;
@ -246,8 +261,7 @@ type
{:Maximum message size in bytes for SNMPv3. For sending is default 1472 bytes.}
property MaxSize: Integer read FMaxSize write FMaxSize;
{:Specify if message is authorised or encrypted. Used only in SNMPv3, and
encryption is not yet supported!}
{:Specify if message is authorised or encrypted. Used only in SNMPv3.}
property Flags: TV3Flags read FFlags write FFlags;
{:For SNMPv3.... If is @true, SNMP agent must send reply (at least with some
@ -264,6 +278,9 @@ type
authorization)}
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.}
property AuthEngineID: AnsiString read FAuthEngineID write FAuthEngineID;
@ -285,7 +302,10 @@ type
{:For SNMPv3. Computed Athorization key from @link(password).}
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;
{:MIB value to identify the object that sent the TRAPv1.}
@ -417,6 +437,10 @@ begin
inherited Create;
FSNMPMibList := TList.Create;
Clear;
FAuthMode := AuthMD5;
FPassword := '';
FPrivMode := PrivDES;
FPrivPassword := '';
FID := 1;
FMaxSize := 1472;
end;
@ -452,8 +476,66 @@ begin
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
Pos: Integer;
EndPos: Integer;
@ -508,14 +590,24 @@ begin
FAuthEngineTimeStamp := GetTick;
FUserName := ASNItem(SPos, s, Svt);
FAuthKey := ASNItem(SPos, s, Svt);
FPrivKey := ASNItem(SPos, s, Svt);
FPrivSalt := ASNItem(SPos, s, Svt);
end;
//scopedPDU
s := ASNItem(Pos, Buffer, Svt);
if Svt = ASN1_OCTSTR then
if FFlags = AuthPriv then
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;
ASNItem(Pos, Buffer, Svt); //skip sequence mark
FContextEngineID := ASNItem(Pos, Buffer, Svt);
FContextName := ASNItem(Pos, Buffer, Svt);
end
@ -547,11 +639,88 @@ begin
ASNItem(Pos, Buffer, Svt);
Sm := ASNItem(Pos, Buffer, Svt);
Sv := ASNItem(Pos, Buffer, Svt);
Self.MIBAdd(sm, sv, Svt);
if sm <> '' then
Self.MIBAdd(sm, sv, Svt);
end;
Result := True;
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;
var
s: AnsiString;
@ -610,8 +779,9 @@ begin
pdu := ASNObject(FContextEngineID, ASN1_OCTSTR)
+ ASNObject(FContextName, ASN1_OCTSTR)
+ pdu;
//maybe encrypt pdu... in future
pdu := ASNObject(pdu, ASN1_SEQ);
//encrypt PDU if Priv mode is enabled
pdu := EncryptPDU(pdu);
//prepare flags
case FFlags of
@ -646,7 +816,7 @@ begin
AuthPriv:
begin
s := authbeg + ASNObject(StringOfChar(#0, 12), ASN1_OCTSTR)
+ ASNObject(FPrivKey, ASN1_OCTSTR);
+ ASNObject(FPrivSalt, ASN1_OCTSTR);
s := ASNObject(s, ASN1_SEQ);
s := head + ASNObject(s, ASN1_OCTSTR);
s := ASNObject(s + pdu, ASN1_SEQ);
@ -672,7 +842,7 @@ begin
end;
auth := authbeg + ASNObject(FAuthKey, ASN1_OCTSTR)
+ ASNObject(FPrivKey, ASN1_OCTSTR);
+ ASNObject(FPrivSalt, ASN1_OCTSTR);
auth := ASNObject(auth, ASN1_SEQ);
head := head + ASNObject(auth, ASN1_OCTSTR);
@ -694,7 +864,6 @@ begin
FVersion := SNMP_V1;
FCommunity := 'public';
FUserName := '';
FPassword := '';
FPDUType := 0;
FErrorStatus := 0;
FErrorIndex := 0;
@ -710,13 +879,14 @@ begin
FFlagReportable := false;
FContextEngineID := '';
FContextName := '';
FAuthMode := AuthMD5;
FAuthEngineID := '';
FAuthEngineBoots := 0;
FAuthEngineTime := 0;
FAuthEngineTimeStamp := 0;
FAuthKey := '';
FPrivKey := '';
FPrivSalt := '';
FPrivSaltCounter := random(maxint);
end;
procedure TSNMPRec.MIBAdd(const MIB, Value: AnsiString; ValueType: Integer);
@ -813,6 +983,10 @@ end;
function TSNMPSend.InternalSendRequest(const QValue, RValue: TSNMPRec): Boolean;
begin
Result := False;
RValue.AuthMode := QValue.AuthMode;
RValue.Password := QValue.Password;
RValue.PrivMode := QValue.PrivMode;
RValue.PrivPassword := QValue.PrivPassword;
FSock.Bind(FIPInterface, cAnyPort);
FSock.Connect(FTargetHost, FTargetPort);
if InternalSendSnmp(QValue) then
@ -832,10 +1006,7 @@ begin
FQuery.AuthEngineTimeStamp := Sync.EngineStamp;
FQuery.AuthEngineID := Sync.EngineID;
end;
FSock.Bind(FIPInterface, cAnyPort);
FSock.Connect(FTargetHost, FTargetPort);
if InternalSendSnmp(FQuery) then
Result := InternalRecvSnmp(FReply);
Result := InternalSendRequest(FQuery, FReply);
end;
function TSNMPSend.SendTrap: Boolean;
@ -892,6 +1063,9 @@ begin
SyncQuery.Password := FQuery.Password;
SyncQuery.FlagReportable := True;
SyncQuery.Flags := FQuery.Flags;
SyncQuery.AuthMode := FQuery.AuthMode;
SyncQuery.PrivMode := FQuery.PrivMode;
SyncQuery.PrivPassword := FQuery.PrivPassword;
SyncQuery.PDUType := PDUGetRequest;
SyncQuery.AuthEngineID := FReply.FAuthEngineID;
if InternalSendRequest(SyncQuery, FReply) then

View File

@ -1356,16 +1356,6 @@ const
type
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);
begin
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 |
|==============================================================================|
@ -324,6 +324,9 @@ function GetTempFile(const Dir, prefix: AnsiString): AnsiString;
smaller, string is padded by Pad character.}
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
is Splitted into multiple lines, then this procedure de-split it into one line.}
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;
var
s, t: string;