summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--epan/dissectors/packet-sip.c263
1 files changed, 254 insertions, 9 deletions
diff --git a/epan/dissectors/packet-sip.c b/epan/dissectors/packet-sip.c
index eca88dcb22..db4406310d 100644
--- a/epan/dissectors/packet-sip.c
+++ b/epan/dissectors/packet-sip.c
@@ -44,9 +44,12 @@
#include <epan/tap.h>
#include <epan/proto_data.h>
#include <epan/uat.h>
+#include <epan/strutil.h>
+#include <epan/to_str.h>
#include <wsutil/str_util.h>
#include <wsutil/strtoi.h>
+#include <wsutil/wsgcrypt.h>
#include "packet-ssl.h"
@@ -272,6 +275,7 @@ static expert_field ei_sip_sipsec_malformed = EI_INIT;
static expert_field ei_sip_via_sent_by_port = EI_INIT;
static expert_field ei_sip_content_length_invalid = EI_INIT;
static expert_field ei_sip_Status_Code_invalid = EI_INIT;
+static expert_field ei_sip_authorization_invalid = EI_INIT;
/* patterns used for tvb_ws_mempbrk_pattern_guint8 */
static ws_mempbrk_pattern pbrk_comma_semi;
@@ -968,12 +972,8 @@ header_fields_free_cb(void*r)
{
header_field_t* rec = (header_field_t*)r;
- if (rec->header_name) {
- g_free(rec->header_name);
- }
- if (rec->header_desc) {
- g_free(rec->header_desc);
- }
+ g_free(rec->header_name);
+ g_free(rec->header_desc);
}
static void
@@ -1028,6 +1028,89 @@ header_fields_initialize_cb(void)
UAT_CSTRING_CB_DEF(sip_custom_header_fields, header_name, header_field_t)
UAT_CSTRING_CB_DEF(sip_custom_header_fields, header_desc, header_field_t)
+/* SIP authorization parameters */
+static gboolean global_sip_validate_authorization = FALSE;
+
+typedef struct _authorization_user_t {
+ gchar* username;
+ gchar* realm;
+ gchar* password;
+} authorization_user_t;
+
+static authorization_user_t* sip_authorization_users = NULL;
+static guint sip_authorization_num_users = 0;
+
+static gboolean
+authorization_users_update_cb(void *r, char **err)
+{
+ authorization_user_t *rec = (authorization_user_t *)r;
+ char c;
+
+ if (rec->username == NULL) {
+ *err = g_strdup("Username can't be empty");
+ return FALSE;
+ }
+
+ g_strstrip(rec->username);
+ if (rec->username[0] == 0) {
+ *err = g_strdup("Username can't be empty");
+ return FALSE;
+ }
+
+ /* Check for invalid characters (to avoid asserting out when
+ * registering the field).
+ */
+ c = proto_check_field_name(rec->username);
+ if (c) {
+ *err = g_strdup_printf("Username can't contain '%c'", c);
+ return FALSE;
+ }
+
+ *err = NULL;
+ return TRUE;
+}
+
+static void *
+authorization_users_copy_cb(void* n, const void* o, size_t siz _U_)
+{
+ authorization_user_t* new_rec = (authorization_user_t*)n;
+ const authorization_user_t* old_rec = (const authorization_user_t*)o;
+
+ if (old_rec->username) {
+ new_rec->username = g_strdup(old_rec->username);
+ } else {
+ new_rec->username = NULL;
+ }
+
+ if (old_rec->realm) {
+ new_rec->realm = g_strdup(old_rec->realm);
+ } else {
+ new_rec->realm = NULL;
+ }
+
+ if (old_rec->password) {
+ new_rec->password = g_strdup(old_rec->password);
+ } else {
+ new_rec->password = NULL;
+ }
+
+ return new_rec;
+}
+
+static void
+authorization_users_free_cb(void*r)
+{
+ authorization_user_t* rec = (authorization_user_t*)r;
+
+ g_free(rec->username);
+ g_free(rec->realm);
+ g_free(rec->password);
+}
+
+UAT_CSTRING_CB_DEF(sip_authorization_users, username, authorization_user_t)
+UAT_CSTRING_CB_DEF(sip_authorization_users, realm, authorization_user_t)
+UAT_CSTRING_CB_DEF(sip_authorization_users, password, authorization_user_t)
+
/* Forward declaration we need below */
void proto_reg_handoff_sip(void);
static gboolean dissect_sip_common(tvbuff_t *tvb, int offset, int remaining_length, packet_info *pinfo,
@@ -1059,6 +1142,35 @@ static guint sip_find_invite(packet_info *pinfo,
guchar cseq_number_set, guint32 cseq_number,
guint32 *response_time);
+typedef struct
+{
+ gchar * username;
+ gchar * realm;
+ gchar * uri;
+ gchar * nonce;
+ gchar * cnonce;
+ gchar * nonce_count;
+ gchar * response;
+ gchar * qop;
+ gchar * algorithm;
+ gchar * method;
+} sip_authorization_t;
+
+static authorization_user_t * sip_get_authorization(sip_authorization_t *authorization_info);
+static gboolean sip_validate_authorization(sip_authorization_t *authorization_info, gchar *password);
+
+static authorization_user_t * sip_get_authorization(sip_authorization_t *authorization_info)
+{
+ guint i;
+ for (i = 0; i < sip_authorization_num_users; i++) {
+ if ((!strcmp(sip_authorization_users[i].username, authorization_info->username)) &&
+ (!strcmp(sip_authorization_users[i].realm, authorization_info->realm))) {
+ return &sip_authorization_users[i];
+ }
+ }
+ return NULL;
+}
+
/* SIP content type and internet media type used by other dissectors
* are the same. List of media types from IANA at:
* http://www.iana.org/assignments/media-types/index.html */
@@ -1946,7 +2058,7 @@ dissect_sip_contact_item(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gi
* Returns offset at end of parsing, or -1 for unsuccessful parsing
*/
static gint
-dissect_sip_authorization_item(tvbuff_t *tvb, proto_tree *tree, gint start_offset, gint line_end_offset)
+dissect_sip_authorization_item(tvbuff_t *tvb, proto_tree *tree, gint start_offset, gint line_end_offset, sip_authorization_t *authorization_info)
{
gint current_offset, par_name_end_offset, queried_offset, value_offset, value_search_offset;
gint equals_offset = 0;
@@ -2011,6 +2123,33 @@ dissect_sip_authorization_item(tvbuff_t *tvb, proto_tree *tree, gint start_offse
proto_tree_add_item(tree, *(auth_parameter->hf_item), tvb,
value_offset, current_offset - value_offset,
ENC_UTF_8|ENC_NA);
+ if (global_sip_validate_authorization) {
+ gint real_value_offset = value_offset;
+ gint real_value_length = current_offset - value_offset;
+ if ((tvb_get_guint8(tvb, value_offset) == '\"') && (tvb_get_guint8(tvb, current_offset - 1) == '\"') && (real_value_length > 1)) {
+ real_value_offset++;
+ real_value_length -= 2;
+ }
+ if (g_ascii_strcasecmp(name, "response") == 0) {
+ authorization_info->response = tvb_get_string_enc(wmem_packet_scope(), tvb, real_value_offset, real_value_length, ENC_ASCII);
+ } else if (g_ascii_strcasecmp(name, "nc") == 0) {
+ authorization_info->nonce_count = tvb_get_string_enc(wmem_packet_scope(), tvb, real_value_offset, real_value_length, ENC_ASCII);
+ } else if (g_ascii_strcasecmp(name, "username") == 0) {
+ authorization_info->username = tvb_get_string_enc(wmem_packet_scope(), tvb, real_value_offset, real_value_length, ENC_ASCII);
+ } else if (g_ascii_strcasecmp(name, "realm") == 0) {
+ authorization_info->realm = tvb_get_string_enc(wmem_packet_scope(), tvb, real_value_offset, real_value_length, ENC_ASCII);
+ } else if (g_ascii_strcasecmp(name, "algorithm") == 0) {
+ authorization_info->algorithm = tvb_get_string_enc(wmem_packet_scope(), tvb, real_value_offset, real_value_length, ENC_ASCII);
+ } else if (g_ascii_strcasecmp(name, "nonce") == 0) {
+ authorization_info->nonce = tvb_get_string_enc(wmem_packet_scope(), tvb, real_value_offset, real_value_length, ENC_ASCII);
+ } else if (g_ascii_strcasecmp(name, "qop") == 0) {
+ authorization_info->qop = tvb_get_string_enc(wmem_packet_scope(), tvb, real_value_offset, real_value_length, ENC_ASCII);
+ } else if (g_ascii_strcasecmp(name, "cnonce") == 0) {
+ authorization_info->cnonce = tvb_get_string_enc(wmem_packet_scope(), tvb, real_value_offset, real_value_length, ENC_ASCII);
+ } else if (g_ascii_strcasecmp(name, "uri") == 0) {
+ authorization_info->uri = tvb_get_string_enc(wmem_packet_scope(), tvb, real_value_offset, real_value_length, ENC_ASCII);
+ }
+ }
break;
}
}
@@ -3882,6 +4021,8 @@ dissect_sip_common(tvbuff_t *tvb, int offset, int remaining_length, packet_info
/* Add tree using whole text of line */
if (hdr_tree) {
proto_item *ti_c;
+ sip_authorization_t authorization_info = { 0 };
+ authorization_user_t * authorization_user = NULL;
/* Add whole line as header tree */
sip_element_item = sip_proto_tree_add_string(hdr_tree,
hf_header_array[hf_index], tvb,
@@ -3912,7 +4053,7 @@ dissect_sip_common(tvbuff_t *tvb, int offset, int remaining_length, packet_info
}
/* Parse each individual parameter in the line */
- while ((comma_offset = dissect_sip_authorization_item(tvb, sip_element_tree, comma_offset, line_end_offset)) != -1)
+ while ((comma_offset = dissect_sip_authorization_item(tvb, sip_element_tree, comma_offset, line_end_offset, &authorization_info)) != -1)
{
if(comma_offset == line_end_offset)
{
@@ -3927,6 +4068,15 @@ dissect_sip_common(tvbuff_t *tvb, int offset, int remaining_length, packet_info
}
comma_offset++; /* skip comma */
}
+ if ((authorization_info.response != NULL) && (global_sip_validate_authorization)) { /* If there is a response, check for valid credentials */
+ authorization_user = sip_get_authorization(&authorization_info);
+ if (authorization_user) {
+ authorization_info.method = wmem_strdup(wmem_packet_scope(), stat_info->request_method);
+ if (!sip_validate_authorization(&authorization_info, authorization_user->password)) {
+ proto_tree_add_expert_format(tree, pinfo, &ei_sip_authorization_invalid, tvb, offset, line_end_offset - offset, "SIP digest does not match known password %s", authorization_user->password);
+ }
+ }
+ }
}/*hdr_tree*/
break;
@@ -5136,6 +5286,65 @@ guint sip_find_invite(packet_info *pinfo,
return result;
}
+static gboolean sip_validate_authorization(sip_authorization_t *authorization_info, gchar *password) {
+ gchar ha1[33] = {0};
+ gchar ha2[33] = {0};
+ gchar response[33] = {0};
+ gcry_md_hd_t md5_handle;
+ if ( (authorization_info->qop == NULL) ||
+ (authorization_info->username == NULL) ||
+ (authorization_info->realm == NULL) ||
+ (authorization_info->method == NULL) ||
+ (authorization_info->uri == NULL) ||
+ (authorization_info->nonce == NULL) ) {
+ return TRUE; /* If no qop, discard */
+ }
+ if (strcmp(authorization_info->qop, "auth") ||
+ (authorization_info->nonce_count == NULL) ||
+ (authorization_info->cnonce == NULL) ||
+ (authorization_info->response == NULL) ||
+ (password == NULL)) {
+ return TRUE; /* Obsolete or not enough information, discard */
+ }
+
+ if (gcry_md_open(&md5_handle, GCRY_MD_MD5, 0)) {
+ return FALSE;
+ }
+
+ gcry_md_write(md5_handle, authorization_info->username, strlen(authorization_info->username));
+ gcry_md_putc(md5_handle, ':');
+ gcry_md_write(md5_handle, authorization_info->realm, strlen(authorization_info->realm));
+ gcry_md_putc(md5_handle, ':');
+ gcry_md_write(md5_handle, password, strlen(password));
+ /* Array is zeroed out so there is always a \0 at index 32 for string termination */
+ bytes_to_hexstr(ha1, gcry_md_read(md5_handle, 0), HASH_MD5_LENGTH);
+ gcry_md_reset(md5_handle);
+ gcry_md_write(md5_handle, authorization_info->method, strlen(authorization_info->method));
+ gcry_md_putc(md5_handle, ':');
+ gcry_md_write(md5_handle, authorization_info->uri, strlen(authorization_info->uri));
+ /* Array is zeroed out so there is always a \0 at index 32 for string termination */
+ bytes_to_hexstr(ha2, gcry_md_read(md5_handle, 0), HASH_MD5_LENGTH);
+ gcry_md_reset(md5_handle);
+ gcry_md_write(md5_handle, ha1, strlen(ha1));
+ gcry_md_putc(md5_handle, ':');
+ gcry_md_write(md5_handle, authorization_info->nonce, strlen(authorization_info->nonce));
+ gcry_md_putc(md5_handle, ':');
+ gcry_md_write(md5_handle, authorization_info->nonce_count, strlen(authorization_info->nonce_count));
+ gcry_md_putc(md5_handle, ':');
+ gcry_md_write(md5_handle, authorization_info->cnonce, strlen(authorization_info->cnonce));
+ gcry_md_putc(md5_handle, ':');
+ gcry_md_write(md5_handle, authorization_info->qop, strlen(authorization_info->qop));
+ gcry_md_putc(md5_handle, ':');
+ gcry_md_write(md5_handle, ha2, strlen(ha2));
+ /* Array is zeroed out so there is always a \0 at index 32 for string termination */
+ bytes_to_hexstr(response, gcry_md_read(md5_handle, 0), HASH_MD5_LENGTH);
+ gcry_md_close(md5_handle);
+ if (!strncmp(response, authorization_info->response, 32)) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
/* TAP STAT INFO */
/*
@@ -6762,12 +6971,14 @@ void proto_register_sip(void)
{ &ei_sip_sipsec_malformed, { "sip.sec_mechanism.malformed", PI_MALFORMED, PI_WARN, "SIP Security-mechanism header malformed", EXPFILL }},
{ &ei_sip_via_sent_by_port, { "sip.Via.sent-by.port.invalid", PI_MALFORMED, PI_NOTE, "Invalid SIP Via sent-by-port", EXPFILL }},
{ &ei_sip_content_length_invalid, { "sip.content_length.invalid", PI_MALFORMED, PI_NOTE, "Invalid content_length", EXPFILL }},
- { &ei_sip_Status_Code_invalid, { "sip.Status-Code.invalid", PI_MALFORMED, PI_NOTE, "Invalid Status-Code", EXPFILL }}
+ { &ei_sip_Status_Code_invalid, { "sip.Status-Code.invalid", PI_MALFORMED, PI_NOTE, "Invalid Status-Code", EXPFILL }},
+ { &ei_sip_authorization_invalid, { "sip.authorization.invalid", PI_PROTOCOL, PI_WARN, "Invalid authorization reponse for known credentials", EXPFILL }}
};
module_t *sip_module;
expert_module_t* expert_sip;
uat_t* sip_custom_headers_uat;
+ uat_t* sip_authorization_users_uat;
static tap_param sip_stat_params[] = {
{ PARAM_FILTER, "filter", "Filter", NULL, TRUE }
@@ -6796,6 +7007,13 @@ void proto_register_sip(void)
UAT_END_FIELDS
};
+ static uat_field_t sip_authorization_users_uat_fields[] = {
+ UAT_FLD_CSTRING(sip_authorization_users, username, "Username", "SIP authorization username"),
+ UAT_FLD_CSTRING(sip_authorization_users, realm, "Realm", "SIP authorization realm"),
+ UAT_FLD_CSTRING(sip_authorization_users, password, "Password", "SIP authorization password"),
+ UAT_END_FIELDS
+ };
+
/* Register the protocol name and description */
proto_sip = proto_register_protocol("Session Initiation Protocol", "SIP", "sip");
proto_raw_sip = proto_register_protocol("Session Initiation Protocol (SIP as raw text)",
@@ -6895,6 +7113,33 @@ void proto_register_sip(void)
"A table to define custom SIP header for which fields can be setup and used for filtering/data extraction etc.",
sip_custom_headers_uat);
+ prefs_register_bool_preference(sip_module, "validate_authorization",
+ "Validate SIP authorization",
+ "Validate SIP authorizations with known credentials",
+ &global_sip_validate_authorization);
+
+ sip_authorization_users_uat = uat_new("SIP authorization users",
+ sizeof(authorization_user_t),
+ "authorization_users_sip",
+ TRUE,
+ &sip_authorization_users,
+ &sip_authorization_num_users,
+ /* specifies named fields, so affects dissection
+ and the set of named fields */
+ UAT_AFFECTS_DISSECTION|UAT_AFFECTS_FIELDS,
+ NULL,
+ authorization_users_copy_cb,
+ authorization_users_update_cb,
+ authorization_users_free_cb,
+ NULL,
+ NULL,
+ sip_authorization_users_uat_fields
+ );
+
+ prefs_register_uat_preference(sip_module, "authorization_users_sip", "SIP authorization users",
+ "A table to define user credentials used for validating authorization attempts",
+ sip_authorization_users_uat);
+
register_init_routine(&sip_init_protocol);
register_cleanup_routine(&sip_cleanup_protocol);
heur_subdissector_list = register_heur_dissector_list("sip", proto_sip);