q931.c: Tighten mandatory ie checks.

Libpri was lax in checking if a missing channel identification ie is
mandatory for the SETUP ACKNOWLEDGE, PROCEEDING, ALERTING, and CONNECT
messages.  That ie is mandatory when those messages are the first response
to a SETUP message sent by the CPE side.

* Made those messages check if a missing channel identification ie is
mandatory and send a STATUS with cause 96 "Mandatory information element
is missing" in response.

Libpri did not care if a mandatory ie had a coding error.

* Made coding errors in mandatory ie's send a STATUS with cause 100
"Invalid information element contents" in response.

* Fixed detection of coding errors in channel identification ie.

SWP-8721
SWP-8722


git-svn-id: https://origsvn.digium.com/svn/libpri/branches/1.4@2337 2fbb986a-6c06-0410-b554-c9c1f0a7f128
This commit is contained in:
Richard Mudgett 2016-02-17 20:21:18 +00:00
parent 6b2cc87b30
commit a9722804c1
2 changed files with 201 additions and 64 deletions

View File

@ -647,10 +647,13 @@ struct q931_call {
unsigned char char_set;
} display;
/* AOC charge requesting on Setup */
/*! AOC charge requesting on Setup */
int aoc_charging_request;
unsigned int slotmap_size:1;/* TRUE if the slotmap is E1 (32 bits). */
/*! TRUE if the slotmap is E1 (32 bits). */
unsigned int slotmap_size:1;
/*! TRUE if need to see the channel id ie in first response to SETUP. */
unsigned int channel_id_ie_mandatory:1;
/*! Control the RESTART reception to the upper layer. */
struct {

250
q931.c
View File

@ -40,6 +40,12 @@
#include <stdio.h>
#include <limits.h>
enum mandatory_ie_status {
MAND_STATUS_OK,
MAND_STATUS_CONTENT_ERROR,
MAND_STATUS_MISSING,
};
#define MAX_MAND_IES 10
struct msgtype {
@ -50,13 +56,27 @@ struct msgtype {
static struct msgtype msgs[] = {
/* Call establishment messages */
{ Q931_ALERTING, "ALERTING" },
{ Q931_CALL_PROCEEDING, "CALL PROCEEDING" },
{ Q931_CONNECT, "CONNECT" },
{ Q931_ALERTING, "ALERTING", { Q931_CHANNEL_IDENT } },
{ Q931_CALL_PROCEEDING, "CALL PROCEEDING", { Q931_CHANNEL_IDENT } },
{ Q931_CONNECT, "CONNECT", { Q931_CHANNEL_IDENT } },
{ Q931_CONNECT_ACKNOWLEDGE, "CONNECT ACKNOWLEDGE" },
/*
* Very early in libpri SVN history (SVN -r154) a change was explicitly
* made to not care about a missing Progress Indicator ie. There is no
* explanation in the commit message why this was done even though
* Q.931 and ECMA-143 clearly show that the ie is mandatory for a
* PROGRESS message.
*
* Hazzarding a guess, I think it was because of the
* ALERTING_NO_PROGRESS support for Mitel switches. (SVN -r44)
*/
#ifdef ALERTING_NO_PROGRESS
{ Q931_PROGRESS, "PROGRESS" },
#else
{ Q931_PROGRESS, "PROGRESS", { Q931_PROGRESS_INDICATOR } },
#endif
{ Q931_SETUP, "SETUP", { Q931_BEARER_CAPABILITY, Q931_CHANNEL_IDENT } },
{ Q931_SETUP_ACKNOWLEDGE, "SETUP ACKNOWLEDGE" },
{ Q931_SETUP_ACKNOWLEDGE, "SETUP ACKNOWLEDGE", { Q931_CHANNEL_IDENT } },
/* Call disestablishment messages */
{ Q931_DISCONNECT, "DISCONNECT", { Q931_CAUSE } },
@ -93,7 +113,7 @@ static struct msgtype msgs[] = {
{ Q931_ANY_MESSAGE, "ANY MESSAGE" },
};
static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_call *c, int missingmand);
static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_call *c, enum mandatory_ie_status mand_status);
static void nt_ptmp_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_call *c, int *allow_event, int *allow_posthandle);
struct msgtype att_maintenance_msgs[] = {
@ -1258,6 +1278,14 @@ static int receive_channel_id(int full_ie, struct pri *ctrl, q931_call *call, in
int pos = 0;
int need_extended_channel_octets;/*!< TRUE if octets 3.2 and 3.3 need to be present. */
call->channel_id_ie_mandatory = 0;
if (ie->len < 1) {
/* Must have at least one payload octet. */
pri_error(ctrl, "!! Channel Identification ie too short\n");
return -1;
}
call->restart.count = 0;
if (ie->data[0] & 0x08) {
@ -1308,10 +1336,24 @@ static int receive_channel_id(int full_ie, struct pri *ctrl, q931_call *call, in
pos++;
if (ie->data[0] & 0x40) {
/* DS1 specified -- stop here */
call->ds1no = ie->data[1] & 0x7f;
/* Explicitly defined DS1 (One or more 3.1 octets expected) */
if (ctrl->switchtype == PRI_SWITCH_QSIG) {
pri_error(ctrl, "!! Q.SIG does not support Channel Identification octet 3.1\n");
return -1;
}
if (ie->len <= pos) {
pri_error(ctrl, "!! Missing expected Channel Identification octet 3.1\n");
return -1;
}
/* Extract explicit DS1 reference. */
call->ds1no = ie->data[pos] & 0x7f;
call->ds1explicit = 1;
pos++;
/* Advance to 3.2 octet if present. */
do {
++pos;
} while (pos < ie->len && !(ie->data[pos - 1] & 0x80));
} else {
call->ds1explicit = 0;
}
@ -1324,8 +1366,12 @@ static int receive_channel_id(int full_ie, struct pri *ctrl, q931_call *call, in
return 0;
}
if (need_extended_channel_octets && pos + 2 < len) {
/* More coming */
if (need_extended_channel_octets && pos + 2 <= ie->len) {
/*
* Octet 3.2 and at least one octet 3.3 are present.
*
* Process octet 3.2
*/
if ((ie->data[pos] & 0x0f) != 3) {
/* Channel type/mapping is not for B channel units. */
pri_error(ctrl, "!! Unexpected Channel Type %d\n", ie->data[pos] & 0x0f);
@ -1335,14 +1381,25 @@ static int receive_channel_id(int full_ie, struct pri *ctrl, q931_call *call, in
pri_error(ctrl, "!! Invalid CCITT coding %d\n", (ie->data[pos] & 0x60) >> 5);
return -1;
}
if (ie->data[pos] & 0x10) {
/* Expect Slot Map */
if (ie->data[pos++] & 0x10) {
/* Process 3.3 octets as a Slot Map */
call->slotmap = 0;
pos++;
call->slotmap_size = (ie->len - pos > 3) ? 1 : 0;
for (x = 0; x < (call->slotmap_size ? 4 : 3); ++x) {
/*
* We will be tolerant of partial slot maps that do not
* specify the upper channels. We will assume they
* are not requested.
*
* We will only read up to 24 or 32 bit channel maps.
*/
for (x = call->slotmap_size ? 4 : 3; x--;) {
call->slotmap <<= 8;
call->slotmap |= ie->data[x + pos];
call->slotmap |= ie->data[pos++];
if (ie->len <= pos) {
/* No more ie contents. */
break;
}
}
if (msgtype == Q931_RESTART) {
@ -1357,8 +1414,11 @@ static int receive_channel_id(int full_ie, struct pri *ctrl, q931_call *call, in
}
}
} else {
pos++;
/* Only expect a particular channel */
/*
* Process 3.3 octets as either a single channel or a channel list
*
* Get single channel
*/
call->channelno = ie->data[pos] & 0x7f;
if (ctrl->chan_mapping_logical && call->channelno > 15) {
call->channelno++;
@ -1385,6 +1445,9 @@ static int receive_channel_id(int full_ie, struct pri *ctrl, q931_call *call, in
}
}
}
} else if (need_extended_channel_octets) {
pri_error(ctrl, "!! Missing expected Channel Identification octets 3.2 or 3.3\n");
return -1;
}
return 0;
}
@ -1518,21 +1581,29 @@ static void dump_channel_id(int full_ie, struct pri *ctrl, q931_ie *ie, int len,
(ie->data[0] & 0x08) ? "Exclusive" : "Preferred",
(ie->data[0] & 0x04) ? 1 : 0);
pri_message(ctrl, "%c ChanSel: %s\n",
prefix, msg_chan_sel[(ie->data[0] & 0x03) | ((ie->data[0] >> 3) & 0x04)]);
prefix, msg_chan_sel[(ie->data[0] & 0x03) | ((ie->data[0] & 0x20) >> 3)]);
pos = 1;
len -= 2;
if (ie->data[0] & 0x40) {
/* Explicitly defined DS1 */
/* Explicitly defined DS1 (One or more 3.1 octets expected) */
if (ctrl->switchtype == PRI_SWITCH_QSIG) {
pri_message(ctrl, "%c Octet 3.1 specified when Q.SIG!\n",
prefix);
}
if (pos < len) {
do {
pri_message(ctrl, "%c Ext: %d DS1 Identifier: %d \n",
pri_message(ctrl, "%c Ext: %d DS1 Identifier: %d\n",
prefix, (ie->data[pos] & 0x80) >> 7, ie->data[pos] & 0x7f);
++pos;
} while (!(ie->data[pos - 1] & 0x80) && pos < len);
} else {
pri_message(ctrl, "%c Octet 3.1 is missing!\n", prefix);
}
} else {
/* Implicitly defined DS1 */
}
if (pos < len) {
/* Still more information here */
/* Octet 3.2 present */
pri_message(ctrl,
"%c Ext: %d Coding: %d %s Specified Channel Type: %d\n",
prefix, (ie->data[pos] & 0x80) >> 7, (ie->data[pos] & 60) >> 5,
@ -1540,6 +1611,7 @@ static void dump_channel_id(int full_ie, struct pri *ctrl, q931_ie *ie, int len,
++pos;
}
if (pos < len) {
/* One or more 3.3 octets present */
if (!(ie->data[pos - 1] & 0x10)) {
/* Number specified */
do {
@ -2864,7 +2936,7 @@ static int receive_progress_indicator(int full_ie, struct pri *ctrl, q931_call *
break;
default:
pri_error(ctrl, "XXX Invalid Progress indicator value received: %02x\n",(ie->data[1] & 0x7f));
break;
return -1;
}
return 0;
}
@ -6316,6 +6388,9 @@ int q931_setup(struct pri *ctrl, q931_call *c, struct pri_sr *req)
c->chanflags = FLAG_PREFERRED;
}
}
if (ctrl->localtype == PRI_CPE) {
c->channel_id_ie_mandatory = 1;
}
c->slotmap = -1;
c->nonisdn = req->nonisdn;
@ -7510,12 +7585,13 @@ int q931_receive(struct q921_link *link, q931_h *h, int len)
int res;
int r;
int mandies[MAX_MAND_IES];
int missingmand;
int is_mandatory;
int codeset, cur_codeset;
int last_ie[8];
int cref;
int allow_event;
int allow_posthandle;
enum mandatory_ie_status mand_status;
ctrl = link->ctrl;
memset(last_ie, 0, sizeof(last_ie));
@ -7595,7 +7671,7 @@ int q931_receive(struct q921_link *link, q931_h *h, int len)
q931_clr_subcommands(ctrl);
q931_display_clear(c);
/* Handle IEs */
/* Determine which ies are mandatory for this message. */
memset(mandies, 0, sizeof(mandies));
for (x = 0; x < ARRAY_LEN(msgs); ++x) {
if (msgs[x].msgnum == mh->msg) {
@ -7603,6 +7679,25 @@ int q931_receive(struct q921_link *link, q931_h *h, int len)
break;
}
}
for (x = 0; x < ARRAY_LEN(mandies); ++x) {
if (mandies[x]) {
/* Check mandatory channel identification ie exceptions */
if (mandies[x] != Q931_CHANNEL_IDENT
/* Always mandatory for RESUME_ACKNOWLEDGE */
|| mh->msg == Q931_RESUME_ACKNOWLEDGE
/* Mandatory in Net -> CPE direction for SETUP */
|| (mh->msg == Q931_SETUP && ctrl->localtype == PRI_CPE)
/* Mandatory for first SETUP response message in Net -> CPE direction. */
|| c->channel_id_ie_mandatory) {
/* ie is mandatory for this message */
continue;
}
/* ie is not mandatory for this message */
mandies[x] = 0;
}
}
mand_status = MAND_STATUS_OK;
/* Do real IE processing */
len -= (h->crlen + 3);
codeset = cur_codeset = 0;
@ -7619,10 +7714,16 @@ int q931_receive(struct q921_link *link, q931_h *h, int len)
q931_display_clear(c);
return -1;
}
/* Check if processing a mandatory ie. */
is_mandatory = 0;
for (y = 0; y < ARRAY_LEN(mandies); ++y) {
if (mandies[y] == Q931_FULL_IE(cur_codeset, ie->ie))
if (mandies[y] == Q931_FULL_IE(cur_codeset, ie->ie)) {
mandies[y] = 0;
is_mandatory = 1;
}
}
/* Special processing for codeset shifts */
switch (ie->ie & 0xf8) {
case Q931_LOCKING_SHIFT:
@ -7665,6 +7766,15 @@ int q931_receive(struct q921_link *link, q931_h *h, int len)
/* Fall through */
default:
y = q931_handle_ie(cur_codeset, ctrl, c, mh->msg, ie);
if (y < 0 && is_mandatory) {
/* Error processing mandatory ie. */
mand_status = MAND_STATUS_CONTENT_ERROR;
pri_error(ctrl, "%s: Error in mandatory IE %d (cs%d, %s)\n",
msg2str(mh->msg),
ie->ie,
cur_codeset,
ie2str(Q931_FULL_IE(codeset, ie->ie)));
}
/* XXX Applicable to codeset 0 only? XXX */
if (!cur_codeset && !(ie->ie & 0xf0) && (y < 0)) {
/*
@ -7681,19 +7791,21 @@ int q931_receive(struct q921_link *link, q931_h *h, int len)
break;
}
}
missingmand = 0;
/* Check for missing mandatory ies. */
for (x = 0; x < ARRAY_LEN(mandies); ++x) {
if (mandies[x]) {
/* check if there is no channel identification when we're configured as network -> that's not an error */
if (((ctrl->localtype != PRI_NETWORK) || (mh->msg != Q931_SETUP) || (mandies[x] != Q931_CHANNEL_IDENT)) &&
((mh->msg != Q931_PROGRESS) || (mandies[x] != Q931_PROGRESS_INDICATOR))) {
pri_error(ctrl, "XXX Missing handling for mandatory IE %d (cs%d, %s) XXX\n", Q931_IE_IE(mandies[x]), Q931_IE_CODESET(mandies[x]), ie2str(mandies[x]));
missingmand++;
}
/* This mandatory ie was not processed. */
mand_status = MAND_STATUS_MISSING;
pri_error(ctrl, "%s: Missing mandatory IE %d (cs%d, %s)\n",
msg2str(mh->msg),
Q931_IE_IE(mandies[x]),
Q931_IE_CODESET(mandies[x]),
ie2str(mandies[x]));
}
}
if (!missingmand) {
if (mand_status == MAND_STATUS_OK) {
switch (mh->msg) {
case Q931_SETUP:
case Q931_CONNECT:
@ -7729,7 +7841,7 @@ int q931_receive(struct q921_link *link, q931_h *h, int len)
}
if (allow_posthandle) {
res = post_handle_q931_message(ctrl, mh, c, missingmand);
res = post_handle_q931_message(ctrl, mh, c, mand_status);
if (res == Q931_RES_HAVEEVENT && !allow_event) {
res = 0;
}
@ -8599,7 +8711,7 @@ static void q931_restart_notify(struct q931_call *call)
* \param ctrl D channel controller.
* \param mh Q.931 message header.
* \param c Q.931 call leg.
* \param missingmand Number of missing mandatory ie's.
* \param mand_status Mandatory ie status
*
* \note
* When this function returns c may be destroyed so you can no
@ -8609,19 +8721,32 @@ static void q931_restart_notify(struct q931_call *call)
* \retval Q931_RES_HAVEEVENT if have an event.
* \retval -1 on error.
*/
static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_call *c, int missingmand)
static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct q931_call *c, enum mandatory_ie_status mand_status)
{
int res;
int changed;
int mand_cause;
struct apdu_event *cur = NULL;
struct pri_subcommand *subcmd;
struct q931_call *master_call;
mand_cause = 0;
switch (mand_status) {
case MAND_STATUS_OK:
break;
case MAND_STATUS_CONTENT_ERROR:
mand_cause = PRI_CAUSE_INVALID_IE_CONTENTS;
break;
case MAND_STATUS_MISSING:
mand_cause = PRI_CAUSE_MANDATORY_IE_MISSING;
break;
}
switch(mh->msg) {
case Q931_RESTART:
q931_display_subcmd(ctrl, c);
if (missingmand) {
q931_status(ctrl, c, PRI_CAUSE_MANDATORY_IE_MISSING);
if (mand_status != MAND_STATUS_OK) {
q931_status(ctrl, c, mand_cause);
pri_destroycall(ctrl, c);
break;
}
@ -8679,9 +8804,8 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct
return Q931_RES_HAVEEVENT;
case Q931_SETUP:
q931_display_subcmd(ctrl, c);
if (missingmand) {
q931_release_complete(ctrl, c, PRI_CAUSE_MANDATORY_IE_MISSING);
if (mand_status != MAND_STATUS_OK) {
q931_release_complete(ctrl, c, mand_cause);
break;
}
/* Must be new call */
@ -8724,6 +8848,10 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct
return Q931_RES_HAVEEVENT;
case Q931_ALERTING:
q931_display_subcmd(ctrl, c);
if (mand_status != MAND_STATUS_OK) {
q931_status(ctrl, c, mand_cause);
break;
}
stop_t303(c->master_call);
if (c->newcall) {
q931_release_complete(ctrl, c, newcall_rel_comp_cause(c));
@ -8769,6 +8897,10 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct
return Q931_RES_HAVEEVENT;
case Q931_CONNECT:
q931_display_subcmd(ctrl, c);
if (mand_status != MAND_STATUS_OK) {
q931_status(ctrl, c, mand_cause);
break;
}
stop_t303(c->master_call);
if (c->newcall) {
q931_release_complete(ctrl, c, newcall_rel_comp_cause(c));
@ -8847,16 +8979,15 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct
}
break;
case Q931_PROGRESS:
if (missingmand) {
q931_status(ctrl, c, PRI_CAUSE_MANDATORY_IE_MISSING);
pri_destroycall(ctrl, c);
break;
}
ctrl->ev.e = PRI_EVENT_PROGRESS;
ctrl->ev.proceeding.cause = c->cause;
/* Fall through */
case Q931_CALL_PROCEEDING:
q931_display_subcmd(ctrl, c);
if (mand_status != MAND_STATUS_OK) {
q931_status(ctrl, c, mand_cause);
break;
}
stop_t303(c->master_call);
ctrl->ev.proceeding.subcmds = &ctrl->subcmds;
if (c->newcall) {
@ -8926,9 +9057,8 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct
break;
case Q931_STATUS:
q931_display_subcmd(ctrl, c);
if (missingmand) {
q931_status(ctrl, c, PRI_CAUSE_MANDATORY_IE_MISSING);
pri_destroycall(ctrl, c);
if (mand_status != MAND_STATUS_OK) {
q931_status(ctrl, c, mand_cause);
break;
}
if (c->newcall) {
@ -9034,9 +9164,9 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct
case Q931_RELEASE:
q931_display_subcmd(ctrl, c);
c->hangupinitiated = 1;
if (missingmand) {
/* Force cause to be mandatory IE missing */
c->cause = PRI_CAUSE_MANDATORY_IE_MISSING;
if (mand_status != MAND_STATUS_OK) {
/* Force mandatory ie cause */
c->cause = mand_cause;
}
/*
@ -9080,9 +9210,9 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct
case Q931_DISCONNECT:
q931_display_subcmd(ctrl, c);
c->hangupinitiated = 1;
if (missingmand) {
if (mand_status != MAND_STATUS_OK) {
/* Still let user call release */
c->cause = PRI_CAUSE_MANDATORY_IE_MISSING;
c->cause = mand_cause;
}
if (c->newcall) {
q931_release_complete(ctrl, c, newcall_rel_comp_cause(c));
@ -9242,6 +9372,10 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct
break;
case Q931_SETUP_ACKNOWLEDGE:
q931_display_subcmd(ctrl, c);
if (mand_status != MAND_STATUS_OK) {
q931_status(ctrl, c, mand_cause);
break;
}
stop_t303(c->master_call);
if (c->newcall) {
q931_release_complete(ctrl, c, newcall_rel_comp_cause(c));
@ -9467,9 +9601,9 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct
master_call = c->master_call;
switch (master_call->hold_state) {
case Q931_HOLD_STATE_HOLD_REQ:
if (missingmand) {
if (mand_status != MAND_STATUS_OK) {
/* Still, let hold rejection continue. */
c->cause = PRI_CAUSE_MANDATORY_IE_MISSING;
c->cause = mand_cause;
}
ctrl->ev.e = PRI_EVENT_HOLD_REJ;
ctrl->ev.hold_rej.channel = q931_encode_channel(c);
@ -9591,9 +9725,9 @@ static int post_handle_q931_message(struct pri *ctrl, struct q931_mh *mh, struct
pri_schedule_del(ctrl, master_call->hold_timer);
master_call->hold_timer = 0;
if (missingmand) {
if (mand_status != MAND_STATUS_OK) {
/* Still, let retrive rejection continue. */
c->cause = PRI_CAUSE_MANDATORY_IE_MISSING;
c->cause = mand_cause;
}
ctrl->ev.e = PRI_EVENT_RETRIEVE_REJ;
ctrl->ev.retrieve_rej.channel = q931_encode_channel(c);