Garbage on the end of Q.931 messages causing calls to fail to connect.

The DAHDI driver had a bug where an extra byte appeared on the end of
Q.931 messages.  This garbage byte caused the message to be discarded with
the diagnostic "XXX Message longer than it should be??  XXX".  The Q.931
message will no longer be discarded if there were earlier ie's in the
message.

This patch also addresses the potential problem of reading beyond the
buffer when trying to parse the garbage data.

Thanks to roeften for the base patch.

(closes issue #14378)
Reported by: timking


git-svn-id: https://origsvn.digium.com/svn/libpri/branches/1.4@1674 2fbb986a-6c06-0410-b554-c9c1f0a7f128
This commit is contained in:
Richard Mudgett 2010-04-26 19:39:28 +00:00
parent ca0fc1a99d
commit bfcab2eabe

71
q931.c
View File

@ -3348,6 +3348,25 @@ static inline unsigned int ielen(q931_ie *ie)
return 2 + ie->len; return 2 + ie->len;
} }
static inline int ielen_checked(q931_ie *ie, int len_remaining)
{
int len;
if (ie->ie & 0x80) {
len = 1;
} else if (len_remaining < 2) {
/* There is no length octet when there should be. */
len = -1;
} else {
len = 2 + ie->len;
if (len_remaining < len) {
/* There is not enough room left in the packet for this ie. */
len = -1;
}
}
return len;
}
const char *msg2str(int msg) const char *msg2str(int msg)
{ {
unsigned int x; unsigned int x;
@ -3881,13 +3900,20 @@ void q931_dump(struct pri *ctrl, int tei, q931_h *h, int len, int txrx)
{ {
q931_mh *mh; q931_mh *mh;
char c; char c;
int x=0, r; int x;
int r;
int cur_codeset; int cur_codeset;
int codeset; int codeset;
int cref; int cref;
c = txrx ? '>' : '<'; c = txrx ? '>' : '<';
pri_message(ctrl, "%c Protocol Discriminator: %s (%d) len=%d\n", c, disc2str(h->pd), h->pd, len); pri_message(ctrl, "%c Protocol Discriminator: %s (%d) len=%d\n", c, disc2str(h->pd), h->pd, len);
if (len < 2 || len < 2 + h->crlen) {
pri_message(ctrl, "%c Message too short for call reference. len=%d\n",
c, len);
return;
}
cref = q931_cr(h); cref = q931_cr(h);
pri_message(ctrl, "%c TEI=%d Call Ref: len=%2d (reference %d/0x%X) (%s)\n", pri_message(ctrl, "%c TEI=%d Call Ref: len=%2d (reference %d/0x%X) (%s)\n",
c, tei, h->crlen, cref & ~Q931_CALL_REFERENCE_FLAG, c, tei, h->crlen, cref & ~Q931_CALL_REFERENCE_FLAG,
@ -3896,6 +3922,12 @@ void q931_dump(struct pri *ctrl, int tei, q931_h *h, int len, int txrx)
: (cref & Q931_CALL_REFERENCE_FLAG) : (cref & Q931_CALL_REFERENCE_FLAG)
? "Sent to originator" : "Sent from originator"); ? "Sent to originator" : "Sent from originator");
if (len < 3 + h->crlen) {
pri_message(ctrl, "%c Message too short for supported protocols. len=%d\n",
c, len);
return;
}
/* Message header begins at the end of the call reference number */ /* Message header begins at the end of the call reference number */
mh = (q931_mh *)(h->contents + h->crlen); mh = (q931_mh *)(h->contents + h->crlen);
if ((h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) || (h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) { if ((h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) || (h->pd == MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) {
@ -3906,8 +3938,14 @@ void q931_dump(struct pri *ctrl, int tei, q931_h *h, int len, int txrx)
/* Drop length of header, including call reference */ /* Drop length of header, including call reference */
len -= (h->crlen + 3); len -= (h->crlen + 3);
codeset = cur_codeset = 0; codeset = cur_codeset = 0;
while(x < len) { for (x = 0; x < len; x += r) {
r = ielen((q931_ie *)(mh->data + x)); r = ielen_checked((q931_ie *) (mh->data + x), len - x);
if (r < 0) {
/* We have garbage on the end of the packet. */
pri_message(ctrl, "Not enough room for codeset:%d ie:%d(%02x)\n", cur_codeset,
mh->data[x], mh->data[x]);
break;
}
q931_dumpie(ctrl, cur_codeset, (q931_ie *)(mh->data + x), c); q931_dumpie(ctrl, cur_codeset, (q931_ie *)(mh->data + x), c);
switch (mh->data[x] & 0xf8) { switch (mh->data[x] & 0xf8) {
case Q931_LOCKING_SHIFT: case Q931_LOCKING_SHIFT:
@ -3921,10 +3959,7 @@ void q931_dump(struct pri *ctrl, int tei, q931_h *h, int len, int txrx)
/* Reset temporary codeset change */ /* Reset temporary codeset change */
cur_codeset = codeset; cur_codeset = codeset;
} }
x += r;
} }
if (x > len)
pri_error(ctrl, "XXX Message longer than it should be?? XXX\n");
} }
static int q931_handle_ie(int codeset, struct pri *ctrl, q931_call *c, int msg, q931_ie *ie) static int q931_handle_ie(int codeset, struct pri *ctrl, q931_call *c, int msg, q931_ie *ie)
@ -5854,6 +5889,10 @@ int q931_receive(struct pri *ctrl, int tei, q931_h *h, int len)
#ifdef LIBPRI_COUNTERS #ifdef LIBPRI_COUNTERS
ctrl->q931_rxcount++; ctrl->q931_rxcount++;
#endif #endif
if (len < 3 || len < 3 + h->crlen) {
/* Message too short for supported protocols. */
return -1;
}
mh = (q931_mh *)(h->contents + h->crlen); mh = (q931_mh *)(h->contents + h->crlen);
if ((h->pd != ctrl->protodisc) && (h->pd != MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) && (h->pd != MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) { if ((h->pd != ctrl->protodisc) && (h->pd != MAINTENANCE_PROTOCOL_DISCRIMINATOR_1) && (h->pd != MAINTENANCE_PROTOCOL_DISCRIMINATOR_2)) {
pri_error(ctrl, "Warning: unknown/inappropriate protocol discriminator received (%02x/%d)\n", h->pd, h->pd); pri_error(ctrl, "Warning: unknown/inappropriate protocol discriminator received (%02x/%d)\n", h->pd, h->pd);
@ -5912,21 +5951,25 @@ int q931_receive(struct pri *ctrl, int tei, q931_h *h, int len)
memcpy(mandies, msgs[x].mandies, sizeof(mandies)); memcpy(mandies, msgs[x].mandies, sizeof(mandies));
} }
} }
x = 0;
/* Do real IE processing */ /* Do real IE processing */
len -= (h->crlen + 3); len -= (h->crlen + 3);
codeset = cur_codeset = 0; codeset = cur_codeset = 0;
while(len) { for (x = 0; x < len; x += r) {
ie = (q931_ie *)(mh->data + x); ie = (q931_ie *)(mh->data + x);
r = ielen_checked(ie, len - x);
if (r < 0) {
/* We have garbage on the end of the packet. */
pri_error(ctrl, "XXX Message longer than it should be?? XXX\n");
if (x) {
/* Allow the message anyway. We have already processed an ie. */
break;
}
return -1;
}
for (y=0;y<MAX_MAND_IES;y++) { for (y=0;y<MAX_MAND_IES;y++) {
if (mandies[y] == Q931_FULL_IE(cur_codeset, ie->ie)) if (mandies[y] == Q931_FULL_IE(cur_codeset, ie->ie))
mandies[y] = 0; mandies[y] = 0;
} }
r = ielen(ie);
if (r > len) {
pri_error(ctrl, "XXX Message longer than it should be?? XXX\n");
return -1;
}
/* Special processing for codeset shifts */ /* Special processing for codeset shifts */
switch (ie->ie & 0xf8) { switch (ie->ie & 0xf8) {
case Q931_LOCKING_SHIFT: case Q931_LOCKING_SHIFT:
@ -5976,8 +6019,6 @@ int q931_receive(struct pri *ctrl, int tei, q931_h *h, int len)
/* Reset current codeset */ /* Reset current codeset */
cur_codeset = codeset; cur_codeset = codeset;
} }
x += r;
len -= r;
} }
missingmand = 0; missingmand = 0;
for (x=0;x<MAX_MAND_IES;x++) { for (x=0;x<MAX_MAND_IES;x++) {