SNMPv3 USM decryption/authentication phase 1

svn path=/trunk/; revision=20353
This commit is contained in:
Luis Ontanon 2007-01-09 18:38:55 +00:00
parent d9465f6b12
commit f7a79f43e7
10 changed files with 1453 additions and 153 deletions

View File

@ -12,6 +12,8 @@
* Updated to use the asn2wrs compiler made by Tomas Kukosa
* Copyright (C) 2005 - 2006 Anders Broman [AT] ericsson.com
*
* See RFC 3414 for User-based Security Model for SNMPv3
* Copyright (C) 2007 Luis E. Garcia Ontanon <luis.ontanon@gmail.com>
*
* $Id$
*
@ -58,8 +60,13 @@
#include <epan/sminmpec.h>
#include <epan/emem.h>
#include <epan/next_tvb.h>
#include <epan/crypt/hmac.h>
#include <epan/expert.h>
#include <epan/report_err.h>
#include "packet-ipx.h"
#include "packet-hpext.h"
#include <epan/crypt/airpdcap_sha1.h>
#include <epan/crypt/crypt-md5.h>
#include "packet-ber.h"
@ -90,10 +97,14 @@
# define VALTYPE_COUNTER64 ASN_COUNTER64
#endif /* HAVE_NET_SNMP */
#include "packet-snmp.h"
#include "format-oid.h"
#ifdef HAVE_LIBGCRYPT
#include <gcrypt.h>
#endif
/* Take a pointer that may be null and return a pointer that's not null
by turning null pointers into pointers to the above null string,
and, if the argument pointer wasn't null, make sure we handle
@ -131,6 +142,11 @@ static const gchar *mib_modules = DEF_MIB_MODULES;
static gboolean display_oid = TRUE;
static gboolean snmp_var_in_tree = TRUE;
static const gchar* ue_assocs_filename = "";
static const gchar* ue_assocs_filename_loaded = "";
static snmp_ue_assoc_t* ue_assocs = NULL;
static snmp_usm_params_t usm_p = {FALSE,FALSE,0,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
/* Subdissector tables */
static dissector_table_t variable_oid_dissector_table;
@ -166,6 +182,8 @@ static int hf_snmp_engineid_text = -1;
static int hf_snmp_engineid_time = -1;
static int hf_snmp_engineid_data = -1;
static int hf_snmp_counter64 = -1;
static int hf_snmp_decryptedPDU = -1;
static int hf_snmp_msgAuthentication = -1;
#include "packet-snmp-hf.c"
@ -177,9 +195,18 @@ static gint ett_smux = -1;
static gint ett_snmp = -1;
static gint ett_engineid = -1;
static gint ett_msgFlags = -1;
static gint ett_encryptedPDU = -1;
static gint ett_decrypted = -1;
static gint ett_authParameters = -1;
#include "packet-snmp-ett.c"
static const true_false_string auth_flags = {
"OK",
"Failed"
};
/* defined in net-SNMP; include/net-snmp/library/snmp.h */
#undef SNMP_MSG_GET
#undef SNMP_MSG_SET
@ -822,7 +849,7 @@ snmp_variable_decode(tvbuff_t *tvb, proto_tree *snmp_tree, packet_info *pinfo,tv
switch (vb_type) {
case SNMP_INTEGER:
offset = dissect_ber_integer(FALSE, pinfo, NULL, tvb, start, -1, &vb_integer_value);
offset = dissect_ber_integer(FALSE, pinfo, NULL, tvb, start, -1, &(vb_integer_value));
length = offset - vb_value_start;
if (snmp_tree) {
#ifdef HAVE_NET_SNMP
@ -1020,8 +1047,219 @@ snmp_variable_decode(tvbuff_t *tvb, proto_tree *snmp_tree, packet_info *pinfo,tv
return;
}
static snmp_ue_assoc_t* get_user_assoc(tvbuff_t* engine_tvb, tvbuff_t* user_tvb) {
static snmp_ue_assoc_t* a;
guint given_username_len;
guint8* given_username;
guint given_engine_len;
guint8* given_engine;
if ( ! ue_assocs ) return NULL;
if (! ( user_tvb && engine_tvb ) ) return NULL;
given_username_len = tvb_length_remaining(user_tvb,0);
given_username = ep_tvb_memdup(user_tvb,0,-1);
given_engine_len = tvb_length_remaining(engine_tvb,0);
given_engine = ep_tvb_memdup(engine_tvb,0,-1);
for(a = ue_assocs; a->user.userName.data; a++) {
guint username_len = a->user.userName.len;
if ( given_username_len == username_len
&& memcmp(a->user.userName.data, given_username, given_username_len) == 0 ) {
const guint8* engine_data = a->engine.data;
guint engine_len = a->engine.len;
if ( engine_data ) {
if ( engine_len == given_engine_len
&& memcmp(given_engine,engine_data,engine_len) == 0 ) {
return a;
}
} else {
return a;
}
}
}
return NULL;
}
#ifdef DEBUG_USM
#define DUMP_DATA(n,b,l) { unsigned i; printf("%s: ",n); for (i=0;i<(unsigned)l;i++) printf("%.2x",b[i]); printf("\n"); }
#define D(p) printf p
#else
#define DUMP_DATA(n,b,l)
#define D(p)
#endif
gboolean snmp_usm_auth_md5(snmp_usm_params_t* p, gchar** error) {
guint msg_len;
guint8* msg;
guint auth_len;
guint8* auth;
guint8* key;
guint key_len;
guint8 calc_auth[16];
guint start;
guint end;
guint i;
if (!p->auth_tvb) {
*error = "No Authenticator";
return FALSE;
}
key = p->user_assoc->user.authKey.data;
key_len = p->user_assoc->user.authKey.len;
if (! key ) {
*error = "User has no authKey";
return FALSE;
}
auth_len = tvb_length_remaining(p->auth_tvb,0);
if (auth_len != 12) {
*error = "Authenticator length wrong";
return FALSE;
}
msg_len = tvb_length_remaining(p->msg_tvb,0);
msg = ep_tvb_memdup(p->msg_tvb,0,msg_len);
auth = ep_tvb_memdup(p->auth_tvb,0,auth_len);
start = p->auth_offset - p->start_offset;
end = start + auth_len;
/* fill the authenticator with zeros */
for ( i = start ; i < end ; i++ ) {
msg[i] = '\0';
}
md5_hmac(msg, msg_len, key, key_len, calc_auth);
return ( memcmp(auth,calc_auth,12) != 0 ) ? FALSE : TRUE;
}
gboolean snmp_usm_auth_sha1(snmp_usm_params_t* p _U_, gchar** error _U_) {
*error = "SHA1 authentication Not Yet Implemented";
return FALSE;
}
tvbuff_t* snmp_usm_priv_des(snmp_usm_params_t* p _U_, tvbuff_t* encryptedData _U_, gchar** error _U_) {
#ifdef HAVE_LIBGCRYPT
gcry_error_t err;
gcry_cipher_hd_t hd = NULL;
guint8* cleartext;
guint8* des_key = p->user_assoc->user.privKey.data; /* first 8 bytes */
guint8* pre_iv = &(p->user_assoc->user.privKey.data[8]); /* last 8 bytes */
guint8* salt;
gint salt_len;
gint cryptgrm_len;
guint8* cryptgrm;
tvbuff_t* clear_tvb;
guint8 iv[8];;
guint i;
salt_len = tvb_length_remaining(p->priv_tvb,0);
D(("salt_len=%d\n",salt_len));
if (salt_len != 8) {
*error = "msgPrivacyParameters lenght != 8";
return NULL;
}
salt = ep_tvb_memdup(p->priv_tvb,0,salt_len);
DUMP_DATA("salt",salt,salt_len);
DUMP_DATA("pre_iv",iv,8);
/*
The resulting "salt" is XOR-ed with the pre-IV to obtain the IV.
*/
for (i=0; i<8; i++) {
iv[i] = pre_iv[i] ^ salt[i];
}
DUMP_DATA("iv",iv,8);
DUMP_DATA("des_key",des_key,8);
cryptgrm_len = tvb_length_remaining(encryptedData,0);
D(("cryptgrm_len=%d\n",cryptgrm_len));
if (cryptgrm_len % 8) {
*error = "the length of the encrypted data is noty a mutiple of 8";
return NULL;
}
cryptgrm = ep_tvb_memdup(encryptedData,0,-1);
DUMP_DATA("cryptgrm",cryptgrm,cryptgrm_len);
cleartext = ep_alloc(cryptgrm_len);
err = gcry_cipher_open(&hd, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_CBC, 0);
if (err != GPG_ERR_NO_ERROR) goto on_gcry_error;
err = gcry_cipher_setiv(hd, iv, 8);
if (err != GPG_ERR_NO_ERROR) goto on_gcry_error;
err = gcry_cipher_setkey(hd,des_key,8);
if (err != GPG_ERR_NO_ERROR) goto on_gcry_error;
err = gcry_cipher_decrypt(hd, cleartext, cryptgrm_len, cryptgrm, cryptgrm_len);
if (err != GPG_ERR_NO_ERROR) goto on_gcry_error;
gcry_cipher_close(hd);
DUMP_DATA("cleartext",cryptgrm,cryptgrm_len);
/*
???
The first ciphertext block is decrypted, the decryption output is
XOR-ed with the Initialization Vector, and the result is the first
plaintext block.
For each subsequent block, the ciphertext block is decrypted, the
decryption output is XOR-ed with the previous ciphertext block and
the result is the plaintext block.
*/
clear_tvb = tvb_new_real_data(cleartext, cryptgrm_len, cryptgrm_len);
return clear_tvb;
on_gcry_error:
*error = (void*)gpg_strerror(err);
if (hd) gcry_cipher_close(hd);
return NULL;
#else
*error = "libgcrypt not present, cannot decrypt";
return NULL;
#endif
}
tvbuff_t* snmp_usm_priv_aes(snmp_usm_params_t* p _U_, tvbuff_t* encryptedData _U_, gchar** error _U_) {
#ifdef HAVE_LIBGCRYPT
*error = "AES decryption Not Yet Implemented";
return NULL;
#else
*error = "libgcrypt not present, cannot decrypt";
return NULL;
#endif
}
#include "packet-snmp-fn.c"
guint
dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
proto_tree *tree, int proto, gint ett, gboolean is_tcp)
@ -1039,6 +1277,18 @@ dissect_snmp_pdu(tvbuff_t *tvb, int offset, packet_info *pinfo,
proto_tree *snmp_tree = NULL;
proto_item *item = NULL;
usm_p.msg_tvb = tvb;
usm_p.start_offset = offset_from_real_beginning(tvb,0) ;
usm_p.engine_tvb = NULL;
usm_p.user_tvb = NULL;
usm_p.auth_item = NULL;
usm_p.auth_tvb = NULL;
usm_p.auth_offset = 0;
usm_p.priv_tvb = NULL;
usm_p.user_assoc = NULL;
usm_p.authenticated = FALSE;
usm_p.encrypted = FALSE;
/*
* This will throw an exception if we don't have any data left.
* That's what we want. (See "tcp_dissect_pdus()", which is
@ -1268,6 +1518,132 @@ dissect_smux(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
dissect_SMUX_PDUs_PDU(tvb, pinfo, tree);
}
/*
MD5 Password to Key Algorithm
from RFC 3414 A.2.1
*/
void snmp_usm_password_to_key_md5(
guint8 *password, /* IN */
guint passwordlen, /* IN */
guint8 *engineID, /* IN - pointer to snmpEngineID */
guint engineLength,/* IN - length of snmpEngineID */
guint8 *key) /* OUT - pointer to caller 16-octet buffer */
{
md5_state_t MD;
guint8 *cp, password_buf[64];
guint32 password_index = 0;
guint32 count = 0, i;
guint8 key1[16];
md5_init(&MD); /* initialize MD5 */
/**********************************************/
/* Use while loop until we've done 1 Megabyte */
/**********************************************/
while (count < 1048576) {
cp = password_buf;
for (i = 0; i < 64; i++) {
/*************************************************/
/* Take the next octet of the password, wrapping */
/* to the beginning of the password as necessary.*/
/*************************************************/
*cp++ = password[password_index++ % passwordlen];
}
md5_append(&MD, password_buf, 64);
count += 64;
}
md5_finish(&MD, key1); /* tell MD5 we're done */
/*****************************************************/
/* Now localize the key with the engineID and pass */
/* through MD5 to produce final key */
/* May want to ensure that engineLength <= 32, */
/* otherwise need to use a buffer larger than 64 */
/*****************************************************/
md5_init(&MD);
md5_append(&MD, key1, 16);
md5_append(&MD, engineID, engineLength);
md5_append(&MD, key1, 16);
md5_finish(&MD, key);
return;
}
/*
SHA1 Password to Key Algorithm COPIED from RFC 3414 A.2.2
*/
#define SHAUpdate(c,b,l) sha1_loop((c),(b),(l))
#define SHAFinal(d,c) sha1_result((c),(d))
void snmp_usm_password_to_key_sha1(
guint8 *password, /* IN */
guint passwordlen, /* IN */
guint8 *engineID, /* IN - pointer to snmpEngineID */
guint engineLength,/* IN - length of snmpEngineID */
guint8 *key) /* OUT - pointer to caller 20-octet buffer */
{
SHA1_CONTEXT SH;
guint8 *cp, password_buf[72];
guint32 password_index = 0;
guint32 count = 0, i;
sha1_init (&SH); /* initialize SHA */
/**********************************************/
/* Use while loop until we've done 1 Megabyte */
/**********************************************/
while (count < 1048576) {
cp = password_buf;
for (i = 0; i < 64; i++) {
/*************************************************/
/* Take the next octet of the password, wrapping */
/* to the beginning of the password as necessary.*/
/*************************************************/
*cp++ = password[password_index++ % passwordlen];
}
SHAUpdate (&SH, password_buf, 64);
count += 64;
}
SHAFinal (key, &SH); /* tell SHA we're done */
/*****************************************************/
/* Now localize the key with the engineID and pass */
/* through SHA to produce final key */
/* May want to ensure that engineLength <= 32, */
/* otherwise need to use a buffer larger than 72 */
/*****************************************************/
memcpy(password_buf, key, 20);
memcpy(password_buf+20, engineID, engineLength);
memcpy(password_buf+20+engineLength, key, 20);
sha1_init(&SH);
SHAUpdate(&SH, password_buf, 40+engineLength);
SHAFinal(key, &SH);
return;
}
static void destroy_ue_assocs(snmp_ue_assoc_t* assocs) {
if (assocs) {
snmp_ue_assoc_t* a;
for(a = assocs; a->user.userName.data; a++) {
g_free(a->user.userName.data);
if (a->user.authKey.data) g_free(a->user.authKey.data);
if (a->user.privKey.data) g_free(a->user.privKey.data);
if (a->engine.data) g_free(a->engine.data);
}
g_free(ue_assocs);
}
}
static void
process_prefs(void)
{
@ -1322,17 +1698,55 @@ process_prefs(void)
read_configs();
mibs_loaded = TRUE;
#endif /* HAVE_NET_SNMP */
if ( g_str_equal(ue_assocs_filename_loaded,ue_assocs_filename) ) return;
ue_assocs_filename_loaded = ue_assocs_filename;
if (ue_assocs) destroy_ue_assocs(ue_assocs);
if ( *ue_assocs_filename ) {
gchar* err = load_snmp_users_file(ue_assocs_filename,&ue_assocs);
if (err) report_failure("Error while loading SNMP's users file:\n%s",err);
}
#ifdef DUMP_USMDATA
{
GString* s = g_string_new(">>");
g_string_sprintfa(s,"File: %s\n",ue_assocs_filename);
#define DUMP(s,n,b,l) { unsigned i; g_string_sprintfa(s,"%s: ",n); for (i=0;i<l;i++) g_string_sprintfa(s,"%.2x",b[i]); g_string_sprintfa(s,"\n"); }
if (ue_assocs) {
snmp_ue_assoc_t* a;
for(a = ue_assocs; a->user.userName.data; a++) {
if (a->engine.data) DUMP(s,"engine",a->engine.data,a->engine.len);
DUMP(s,"userName",a->user.userName.data,a->user.userName.len);
DUMP(s,"authPassword",a->user.authPassword.data,a->user.authPassword.len);
if (a->user.authKey.data) DUMP(s,"authKey",a->user.authKey.data,a->user.authKey.len);
DUMP(s,"privPassword",a->user.privPassword.data,a->user.privPassword.len);
if (a->user.privKey.data) DUMP(s,"privKey",a->user.privKey.data,a->user.privKey.len);
g_string_sprintfa(s,"\n");
}
}
report_failure("%s",s->str);
g_string_free(s,TRUE);
}
#endif
}
/*--- proto_register_snmp -------------------------------------------*/
void proto_register_snmp(void) {
/*--- proto_register_snmp -------------------------------------------*/
void proto_register_snmp(void) {
#if defined(_WIN32) && defined(HAVE_NET_SNMP)
char *mib_path;
int mib_path_len;
#define MIB_PATH_APPEND "snmp\\mibs"
#endif
gchar *tmp_mib_modules;
/* List of fields */
static hf_register_info hf[] = {
{ &hf_snmp_v3_flags_auth,
@ -1374,7 +1788,13 @@ void proto_register_snmp(void) {
{ &hf_snmp_counter64, {
"Value", "snmp.counter64", FT_INT64, BASE_DEC,
NULL, 0, "A counter64 value", HFILL }},
{ &hf_snmp_msgAuthentication,
{ "Authentication OK", "snmp.v3.authOK", FT_BOOLEAN, 8,
TFS(&auth_flags), 0, "", HFILL }},
{ &hf_snmp_decryptedPDU, {
"Decrypted PDU", "snmp.decryptedPdu", FT_BYTES, BASE_HEX,
NULL, 0, "Decrypted PDU", HFILL }},
#include "packet-snmp-hfarr.c"
};
@ -1383,7 +1803,10 @@ void proto_register_snmp(void) {
&ett_snmp,
&ett_engineid,
&ett_msgFlags,
&ett_encryptedPDU,
&ett_decrypted,
&ett_authParameters,
#include "packet-snmp-ettarr.c"
};
module_t *snmp_module;
@ -1459,6 +1882,11 @@ void proto_register_snmp(void) {
"ON - display dissected variables inside SNMP tree, OFF - display dissected variables in root tree after SNMP",
&snmp_var_in_tree);
prefs_register_string_preference(snmp_module, "users_file",
"USMuserTable",
"The filename of the user table used for authentication/decryption",
&ue_assocs_filename);
variable_oid_dissector_table =
register_dissector_table("snmp.variable_oid",
"SNMP Variable OID", FT_STRING, BASE_NONE);

View File

@ -25,6 +25,61 @@
#ifndef PACKET_SNMP_H
#define PACKET_SNMP_H
typedef struct _snmp_usm_key {
guint8* data;
guint len;
} snmp_usm_key_t;
typedef struct _snmp_ue_assoc_t snmp_ue_assoc_t;
typedef struct _snmp_usm_params_t snmp_usm_params_t;
typedef gboolean (*snmp_usm_authenticator_t)(snmp_usm_params_t*, gchar** error);
typedef tvbuff_t* (*snmp_usm_decoder_t)(snmp_usm_params_t*, tvbuff_t* encryptedData, gchar** error);
typedef void (*snmp_usm_password_to_key_t)(guint8 *password, guint passwordlen, guint8 *engineID, guint engineLength, guint8 *key);
typedef struct _snmp_usm_auth_model_t {
snmp_usm_password_to_key_t pass2key;
snmp_usm_authenticator_t authenticate;
guint key_size;
} snmp_usm_auth_model_t;
typedef struct _snmp_user_t {
snmp_usm_key_t userName;
snmp_usm_auth_model_t* authModel;
snmp_usm_key_t authPassword;
snmp_usm_key_t authKey;
snmp_usm_decoder_t privProtocol;
snmp_usm_key_t privPassword;
snmp_usm_key_t privKey;
} snmp_user_t;
typedef struct {
guint8* data;
guint len;
} snmp_engine_id_t;
struct _snmp_ue_assoc_t {
snmp_user_t user;
snmp_engine_id_t engine;
};
struct _snmp_usm_params_t {
gboolean authenticated;
gboolean encrypted;
guint start_offset;
guint auth_offset;
tvbuff_t* engine_tvb;
tvbuff_t* user_tvb;
proto_item* auth_item;
tvbuff_t* auth_tvb;
tvbuff_t* priv_tvb;
tvbuff_t* msg_tvb;
snmp_ue_assoc_t* user_assoc;
};
/*
* Guts of the SNMP dissector - exported for use by protocols such as
* ILMI.
@ -33,6 +88,24 @@ extern guint dissect_snmp_pdu(tvbuff_t *, int, packet_info *, proto_tree *tree,
int, gint, gboolean);
extern int dissect_snmp_engineid(proto_tree *, tvbuff_t *, int, int);
/* SNMPv3 USM authentication functions */
gboolean snmp_usm_auth_md5(snmp_usm_params_t* p, gchar**);
gboolean snmp_usm_auth_sha1(snmp_usm_params_t* p, gchar**);
/* SNMPv3 USM privacy functions */
tvbuff_t* snmp_usm_priv_des(snmp_usm_params_t*, tvbuff_t*, gchar**);
tvbuff_t* snmp_usm_priv_aes(snmp_usm_params_t*, tvbuff_t*, gchar**);
void snmp_usm_password_to_key_md5(guint8 *password, guint passwordlen, guint8 *engineID, guint engineLength, guint8 *key);
void snmp_usm_password_to_key_sha1(guint8 *password, guint passwordlen, guint8 *engineID, guint engineLength, guint8 *key);
/* defined in load_snmp_users_file.l */
/* returns NULL when OK or else the error string */
extern gchar* load_snmp_users_file(const char* filename, snmp_ue_assoc_t** assocs);
/*#include "packet-snmp-exp.h"*/
#endif /* PACKET_SNMP_H */

View File

@ -8,6 +8,13 @@
SMUX-PDUs
#.NO_EMIT
GetNextRequest-PDU
GetResponse-PDU
SetRequest-PDU
GetRequest-PDU
Gauge32
NotificationName
SnmpEngineID
#.TYPE_RENAME
@ -20,7 +27,6 @@ BulkPDU/request-id bulkPDU_request-id
VAL_PTR = &pdu_type
#.FN_BODY PDUs
gint pdu_type;
%(DEFAULT_BODY)s
@ -167,32 +173,105 @@ gint pdu_type;
VAL_PTR = &MsgSecurityModel
#.FN_BODY SNMPv3Message/msgSecurityParameters
#.FN_BODY UsmSecurityParameters/msgAuthoritativeEngineID
tvbuff_t *parameter_tvb = NULL;
switch(MsgSecurityModel){
case SNMP_SEC_USM: /* 3 */
offset = dissect_snmp_UsmSecurityParameters(FALSE, tvb, offset+2, pinfo, tree, -1);
break;
case SNMP_SEC_ANY: /* 0 */
case SNMP_SEC_V1: /* 1 */
case SNMP_SEC_V2C: /* 2 */
default:
%(DEFAULT_BODY)s
break;
offset = dissect_ber_octet_string(implicit_tag, pinfo, tree, tvb, offset, hf_index,
&usm_p.engine_tvb);
if (parameter_tvb) {
proto_tree* engine_tree = proto_item_add_subtree(get_ber_last_created_item(),ett_engineid);
dissect_snmp_engineid(engine_tree, usm_p.engine_tvb, 0, tvb_length_remaining(usm_p.engine_tvb,0));
}
#.FN_PARS SnmpEngineID
#.FN_PARS UsmSecurityParameters/msgUserName
VAL_PTR = &usm_p.user_tvb
#.FN_BODY UsmSecurityParameters/msgAuthenticationParameters
offset = dissect_ber_octet_string(FALSE, pinfo, tree, tvb, offset, hf_index, &usm_p.auth_tvb);
if (usm_p.auth_tvb) {
usm_p.auth_item = get_ber_last_created_item();
usm_p.auth_offset = offset_from_real_beginning(usm_p.auth_tvb,0);
}
#.FN_PARS UsmSecurityParameters/msgPrivacyParameters
VAL_PTR = &usm_p.priv_tvb
VAL_PTR = &parameter_tvb
#.FN_BODY ScopedPduData/encryptedPDU
tvbuff_t* crypt_tvb;
offset = dissect_ber_octet_string(FALSE, pinfo, tree, tvb, offset, hf_snmp_encryptedPDU, &crypt_tvb);
#.FN_BODY SnmpEngineID
tvbuff_t *parameter_tvb = NULL;
proto_tree *engineid_tree = NULL;
proto_item *item = NULL;
if( usm_p.encrypted && crypt_tvb
&& usm_p.user_assoc
&& usm_p.user_assoc->user.privProtocol ) {
gchar* error = NULL;
%(DEFAULT_BODY)s
if (parameter_tvb)
dissect_snmp_engineid(tree, parameter_tvb, 0, tvb_length_remaining(parameter_tvb,0));
tvbuff_t* cleartext_tvb = usm_p.user_assoc->user.privProtocol(&usm_p,
crypt_tvb,
&error );
if (! cleartext_tvb) {
proto_item* item = get_ber_last_created_item();
expert_add_info_format( pinfo, item, PI_UNDECODED, PI_WARN, "Failed to decrypt encryptedPDU: %%s", error );
} else {
proto_tree* decrypted_tree = proto_item_add_subtree(get_ber_last_created_item(),ett_decrypted);
add_new_data_source(pinfo, cleartext_tvb, "Decrypted ScopedPDU");
tvb_set_child_real_data_tvbuff(tvb, cleartext_tvb);
dissect_snmp_ScopedPDU(FALSE, cleartext_tvb, 0, pinfo, decrypted_tree, -1);
}
}
#.FN_BODY SNMPv3Message/msgSecurityParameters
// printf(">msgSecurityParameters\n");
switch(MsgSecurityModel){
case SNMP_SEC_USM: /* 3 */
offset = dissect_snmp_UsmSecurityParameters(FALSE, tvb, offset+2, pinfo, tree, -1);
usm_p.user_assoc = get_user_assoc(usm_p.engine_tvb, usm_p.user_tvb);
break;
case SNMP_SEC_ANY: /* 0 */
case SNMP_SEC_V1: /* 1 */
case SNMP_SEC_V2C: /* 2 */
default:
%(DEFAULT_BODY)s
break;
}
#.FN_FTR SNMPv3Message
if( usm_p.authenticated
&& usm_p.user_assoc
&& usm_p.user_assoc->user.authModel ) {
gchar* error = NULL;
proto_item* authen_item;
proto_tree* authen_tree = proto_item_add_subtree(usm_p.auth_item,ett_authParameters);
gboolean authen_ok = usm_p.user_assoc->user.authModel->authenticate(
&usm_p,
&error );
if (error) {
authen_item = proto_tree_add_text(authen_tree,tvb,0,0,"Error while verifying Messsage authenticity: %s", error);
PROTO_ITEM_SET_GENERATED(authen_item);
expert_add_info_format( pinfo, authen_item, PI_MALFORMED, PI_ERROR, "Error while verifying Messsage authenticity: %s", error );
} else {
int severity;
gchar* fmt;
authen_item = proto_tree_add_boolean(authen_tree, hf_snmp_msgAuthentication, tvb, 0, 0, authen_ok);
PROTO_ITEM_SET_GENERATED(authen_item);
if (authen_ok) {
fmt = "SNMP Authentication OK";
severity = PI_CHAT;
} else {
fmt = "SNMP Authentication Error";
severity = PI_WARN;
}
expert_add_info_format( pinfo, authen_item, PI_CHECKSUM, severity, fmt );
}
}
#.FN_PARS HeaderData/msgFlags
@ -203,10 +282,14 @@ gint pdu_type;
%(DEFAULT_BODY)s
if (parameter_tvb){
guint8 v3_flags = tvb_get_guint8(parameter_tvb, 0);
proto_tree_add_item(tree, hf_snmp_v3_flags_report, parameter_tvb, 0, 1, FALSE);
proto_tree_add_item(tree, hf_snmp_v3_flags_crypt, parameter_tvb, 0, 1, FALSE);
proto_tree_add_item(tree, hf_snmp_v3_flags_auth, parameter_tvb, 0, 1, FALSE);
usm_p.encrypted = v3_flags & TH_CRYPT ? TRUE : FALSE;
usm_p.authenticated = v3_flags & TH_AUTH ? TRUE : FALSE;
}
#.FN_BODY VarBind

View File

@ -68,6 +68,7 @@ EXTRA_DIST = \
dtd_grammar.lemon \
dtd_parse.l \
dtd_preparse.l \
load_snmp_users_file.l \
enterprise-numbers \
libwireshark.def \
Makefile.common \
@ -89,6 +90,7 @@ DISTCLEANFILES = \
dtd_grammar.h \
dtd_parse.c \
dtd_preparse.c \
load_snmp_users_file.c \
radius_dict.c
@ -111,6 +113,9 @@ exntest: exntest.o except.o
radius_dict.c: radius_dict.l
$(LEX) $^
load_snmp_users_file.c: load_snmp_users_file.l
$(LEX) -oload_snmp_users_file.c $(srcdir)/load_snmp_users_file.l
dtd_parse.c : dtd_parse.l
$(LEX) -odtd_parse.c $(srcdir)/dtd_parse.l

View File

@ -61,6 +61,7 @@ LIBWIRESHARK_SRC = \
in_cksum.c \
ipproto.c \
ipv4.c \
load_snmp_users_file.c \
next_tvb.c \
nstime.c \
oid_resolv.c \

View File

@ -31,7 +31,8 @@ LIBAIRPDCAP_SRC = \
airpdcap_sha1.c \
airpdcap_tkip.c \
airpdcap_wep.c \
crypt-md5.c
crypt-md5.c \
hmac.c
LIBAIRPDCAP_INCLUDES = \
airpdcap_debug.h \
@ -43,4 +44,5 @@ LIBAIRPDCAP_INCLUDES = \
airpdcap_user.h \
airpdcap_ws.h \
crypt-md5.h \
hmac.h \
wep-wpadefs.h

120
epan/crypt/hmac.c Normal file
View File

@ -0,0 +1,120 @@
/*
* hmac.c
*
* HMAC: Keyed-Hashing for Message Authentication
*
* code copied from RFC 2104
*
* $Id:$
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <glib.h>
#include <epan/emem.h>
#include <epan/tvbuff.h>
#include <epan/crypt/airpdcap_sha1.h>
#include <epan/crypt/crypt-md5.h>
#include "hmac.h"
/*
** Function: hmac_sha1
*/
void hmac_sha1(const guint8* text,gint text_len, const guint8* key, gint key_len, guint8 digest[20]) {
SHA1_CONTEXT context;
guint8 k_ipad[65]; /* inner padding -
* key XORd with ipad
*/
guint8 k_opad[65]; /* outer padding -
* key XORd with opad
*/
guint8 tk[20];
int i;
/* if key is longer than 64 bytes reset it to key=MD5(key) */
if (key_len > 64) {
SHA1_CONTEXT tctx;
sha1_init(&tctx);
sha1_loop(&tctx, key, key_len);
sha1_result(&tctx, tk);
key = tk;
key_len = 20;
}
/*
* the HMAC_MD5 transform looks like:
*
* MD5(K XOR opad, MD5(K XOR ipad, text))
*
* where K is an n byte key
* ipad is the byte 0x36 repeated 64 times
Krawczyk, et. al. Informational [Page 8]
RFC 2104 HMAC February 1997
* opad is the byte 0x5c repeated 64 times
* and text is the data being protected
*/
/* start out by storing key in pads */
bzero( k_ipad, sizeof k_ipad);
bzero( k_opad, sizeof k_opad);
bcopy( key, k_ipad, key_len);
bcopy( key, k_opad, key_len);
/* XOR key with ipad and opad values */
for (i=0; i<64; i++) {
k_ipad[i] ^= 0x36;
k_opad[i] ^= 0x5c;
}
/*
* perform inner SHA1
*/
sha1_init(&context); /* init context for 1st
* pass */
sha1_loop(&context, k_ipad, 64); /* start with inner pad */
sha1_loop(&context, text, text_len); /* then text of datagram */
sha1_result(&context, digest); /* finish up 1st pass */
/*
* perform outer SHA1
*/
sha1_init(&context); /* init context for 2nd
* pass */
sha1_loop(&context, k_opad, 64); /* start with outer pad */
sha1_loop(&context, digest, 16); /* then results of 1st
* hash */
sha1_result(&context, digest); /* finish up 2nd pass */
}

34
epan/crypt/hmac.h Normal file
View File

@ -0,0 +1,34 @@
/* hmac.h
*
*
* HMAC: Keyed-Hashing for Message Authentication
*
* See RFC 2104
*
* Copyright 2007 Luis E. Garcia Ontanon <luis.ontanon@gmail.com>
*
* $Id:$
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
void hmac_md5(const guint8* text,gint text_len, const guint8* key, gint key_len, guint8 digest[16]);
void hmac_sha1(const guint8* text,gint text_len, const guint8* key, gint key_len, guint8 digest[20]);

File diff suppressed because it is too large Load Diff

View File

@ -33,6 +33,61 @@
#ifndef PACKET_SNMP_H
#define PACKET_SNMP_H
typedef struct _snmp_usm_key {
guint8* data;
guint len;
} snmp_usm_key_t;
typedef struct _snmp_ue_assoc_t snmp_ue_assoc_t;
typedef struct _snmp_usm_params_t snmp_usm_params_t;
typedef gboolean (*snmp_usm_authenticator_t)(snmp_usm_params_t*, gchar** error);
typedef tvbuff_t* (*snmp_usm_decoder_t)(snmp_usm_params_t*, tvbuff_t* encryptedData, gchar** error);
typedef void (*snmp_usm_password_to_key_t)(guint8 *password, guint passwordlen, guint8 *engineID, guint engineLength, guint8 *key);
typedef struct _snmp_usm_auth_model_t {
snmp_usm_password_to_key_t pass2key;
snmp_usm_authenticator_t authenticate;
guint key_size;
} snmp_usm_auth_model_t;
typedef struct _snmp_user_t {
snmp_usm_key_t userName;
snmp_usm_auth_model_t* authModel;
snmp_usm_key_t authPassword;
snmp_usm_key_t authKey;
snmp_usm_decoder_t privProtocol;
snmp_usm_key_t privPassword;
snmp_usm_key_t privKey;
} snmp_user_t;
typedef struct {
guint8* data;
guint len;
} snmp_engine_id_t;
struct _snmp_ue_assoc_t {
snmp_user_t user;
snmp_engine_id_t engine;
};
struct _snmp_usm_params_t {
gboolean authenticated;
gboolean encrypted;
guint start_offset;
guint auth_offset;
tvbuff_t* engine_tvb;
tvbuff_t* user_tvb;
proto_item* auth_item;
tvbuff_t* auth_tvb;
tvbuff_t* priv_tvb;
tvbuff_t* msg_tvb;
snmp_ue_assoc_t* user_assoc;
};
/*
* Guts of the SNMP dissector - exported for use by protocols such as
* ILMI.
@ -41,6 +96,24 @@ extern guint dissect_snmp_pdu(tvbuff_t *, int, packet_info *, proto_tree *tree,
int, gint, gboolean);
extern int dissect_snmp_engineid(proto_tree *, tvbuff_t *, int, int);
/* SNMPv3 USM authentication functions */
gboolean snmp_usm_auth_md5(snmp_usm_params_t* p, gchar**);
gboolean snmp_usm_auth_sha1(snmp_usm_params_t* p, gchar**);
/* SNMPv3 USM privacy functions */
tvbuff_t* snmp_usm_priv_des(snmp_usm_params_t*, tvbuff_t*, gchar**);
tvbuff_t* snmp_usm_priv_aes(snmp_usm_params_t*, tvbuff_t*, gchar**);
void snmp_usm_password_to_key_md5(guint8 *password, guint passwordlen, guint8 *engineID, guint engineLength, guint8 *key);
void snmp_usm_password_to_key_sha1(guint8 *password, guint passwordlen, guint8 *engineID, guint engineLength, guint8 *key);
/* defined in load_snmp_users_file.l */
/* returns NULL when OK or else the error string */
extern gchar* load_snmp_users_file(const char* filename, snmp_ue_assoc_t** assocs);
/*#include "packet-snmp-exp.h"*/
#endif /* PACKET_SNMP_H */