From: bodea Date: Thu, 10 Mar 2005 03:08:07 +0000 (+0000) Subject: simplify AVP unhiding code X-Git-Tag: release_2_1_0~70 X-Git-Url: http://git.sameswireless.fr/l2tpns.git/commitdiff_plain/466ffcdaac51211102276e824d02ed9bc77dd82d simplify AVP unhiding code --- diff --git a/Changes b/Changes index 308c552..b9b96e2 100644 --- a/Changes +++ b/Changes @@ -1,4 +1,4 @@ -* Mon Feb 14 2005 Brendan O'Dea 2.1.0 +* Thu Mar 10 2005 Brendan O'Dea 2.1.0 - Add IPv6 support from Jonathan McDowell. - Add CHAP support from Jordan Hrycaj (work in progress). - Sanity check that cluster_send_session is not called from a child @@ -32,6 +32,7 @@ address. - Avoid a possible buffer overflow in processpap. - Kill session if authentication was rejected. +- Simplify AVP unhiding code. * Fri Dec 17 2004 Brendan O'Dea 2.0.13 - Better cluster master collision resolution: keep a counter of state diff --git a/l2tpns.c b/l2tpns.c index e6cdff7..a28cd5f 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -4,7 +4,7 @@ // Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced // vim: sw=8 ts=8 -char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.84 2005/02/14 06:58:39 bodea Exp $"; +char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.85 2005/03/10 03:08:08 bodea Exp $"; #include #include @@ -187,7 +187,7 @@ static int remove_plugin(char *plugin_name); static void plugins_done(void); static void processcontrol(uint8_t *buf, int len, struct sockaddr_in *addr, int alen); static tunnelidt new_tunnel(void); -static int unhide_avp(uint8_t *avp, tunnelidt t, sessionidt s, uint16_t length); +static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vector, size_t vec_len); // return internal time (10ths since process startup) static clockt now(void) @@ -1761,7 +1761,7 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) { // control uint16_t message = 0xFFFF; // message type uint8_t fatal = 0; - uint8_t mandatorymessage = 0; + uint8_t mandatory = 0; uint8_t chap = 0; // if CHAP being used uint16_t asession = 0; // assigned session uint32_t amagic = 0; // magic number @@ -1777,7 +1777,10 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) return; } - if ((*buf & 0xCA) != 0xC8) + // control messages must have bits 0x80|0x40|0x08 + // (type, length and sequence) set, and bits 0x02|0x01 + // (offset and priority) clear + if ((*buf & 0xCB) != 0xC8) { LOG(1, s, t, "Bad control header %02X\n", *buf); STAT(tunnel_rx_errors); @@ -1888,23 +1891,42 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) if (l) { // if not a null message // process AVPs - while (l && !(fatal & 0x80)) + while (l && !(fatal & 0x80)) // 0x80 = mandatory AVP { uint16_t n = (ntohs(*(uint16_t *) p) & 0x3FF); uint8_t *b = p; uint8_t flags = *p; uint16_t mtype; - p += n; // next - if (l < n) + if (n > l) { LOG(1, s, t, "Invalid length in AVP\n"); STAT(tunnel_rx_errors); - fatal = flags; return; } + p += n; // next l -= n; + if (flags & 0x3C) // reserved bits, should be clear + { + LOG(1, s, t, "Unrecognised AVP flags %02X\n", *b); + fatal = flags; + continue; // next + } + b += 2; + if (*(uint16_t *) (b)) + { + LOG(2, s, t, "Unknown AVP vendor %d\n", ntohs(*(uint16_t *) (b))); + fatal = flags; + continue; // next + } + b += 2; + mtype = ntohs(*(uint16_t *) (b)); + b += 2; + n -= 6; + if (flags & 0x40) { + uint16_t orig_len; + // handle hidden AVPs if (!*config->l2tpsecret) { @@ -1918,40 +1940,36 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) fatal = flags; continue; } + if (n < 8) + { + LOG(2, s, t, "Short hidden AVP.\n"); + fatal = flags; + continue; + } + LOG(4, s, t, "Hidden AVP\n"); + // Unhide the AVP - n = unhide_avp(b, t, s, n); - if (n == 0) + unhide_value(b, n, mtype, session[s].random_vector, session[s].random_vector_length); + + orig_len = ntohs(*(uint16_t *) b); + if (orig_len > n + 2) { fatal = flags; continue; } + + b += 2; + n = orig_len; } - if (*b & 0x3C) - { - LOG(1, s, t, "Unrecognised AVP flags %02X\n", *b); - fatal = flags; - continue; // next - } - b += 2; - if (*(uint16_t *) (b)) - { - LOG(2, s, t, "Unknown AVP vendor %d\n", ntohs(*(uint16_t *) (b))); - fatal = flags; - continue; // next - } - b += 2; - mtype = ntohs(*(uint16_t *) (b)); - b += 2; - n -= 6; LOG(4, s, t, " AVP %d (%s) len %d\n", mtype, avp_name(mtype), n); switch (mtype) { case 0: // message type message = ntohs(*(uint16_t *) b); + mandatory = flags & 0x80; LOG(4, s, t, " Message type = %d (%s)\n", *b, l2tp_message_type(message)); - mandatorymessage = flags; break; case 1: // result code { @@ -2276,8 +2294,8 @@ void processudp(uint8_t * buf, int len, struct sockaddr_in *addr) break; default: STAT(tunnel_rx_errors); - if (mandatorymessage & 0x80) - tunnelshutdown(t, "Unknown message"); + if (mandatory) + tunnelshutdown(t, "Unknown message type"); else LOG(1, s, t, "Unknown message type %d\n", message); break; @@ -4727,75 +4745,45 @@ int cmd_show_hist_open(struct cli_def *cli, char *command, char **argv, int argc /* Unhide an avp. * - * This unencodes the AVP using the L2TP CHAP secret and the - * previously stored random vector. It replaces the hidden data with - * the cleartext data and returns the length of the cleartext data - * (including the AVP "header" of 6 bytes). - * - * Based on code from rp-l2tpd by Roaring Penguin Software Inc. + * This unencodes the AVP using the L2TP secret and the previously + * stored random vector. It overwrites the hidden data with the + * unhidden AVP subformat. */ -static int unhide_avp(uint8_t *avp, tunnelidt t, sessionidt s, uint16_t length) +static void unhide_value(uint8_t *value, size_t len, uint16_t type, uint8_t *vector, size_t vec_len) { MD5_CTX ctx; - uint8_t *cursor; uint8_t digest[16]; - uint8_t working_vector[16]; - uint16_t hidden_length; - uint8_t type[2]; - size_t done, todo; - uint8_t *output; - - // Find the AVP type. - type[0] = *(avp + 4); - type[1] = *(avp + 5); - - // Line up with the hidden data - cursor = output = avp + 6; + uint8_t *last; + size_t d = 0; // Compute initial pad MD5Init(&ctx); - MD5Update(&ctx, type, 2); + MD5Update(&ctx, (uint8_t) (type >> 8) & 0xff, 1); + MD5Update(&ctx, (uint8_t) type & 0xff, 1); MD5Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret)); - MD5Update(&ctx, session[s].random_vector, session[s].random_vector_length); + MD5Update(&ctx, vector, vec_len); MD5Final(digest, &ctx); - // Get hidden length - hidden_length = ((uint16_t) (digest[0] ^ cursor[0])) * 256 + (uint16_t) (digest[1] ^ cursor[1]); - - // Keep these for later use - working_vector[0] = *cursor; - working_vector[1] = *(cursor + 1); - cursor += 2; - - if (hidden_length > length - 8) - { - LOG(1, s, t, "Hidden length %d too long in AVP of length %d\n", (int) hidden_length, (int) length); - return 0; - } + // pointer to last decoded 16 octets + last = value; - /* Decrypt remainder */ - done = 2; - todo = hidden_length; - while (todo) + while (len > 0) { - working_vector[done] = *cursor; - *output = digest[done] ^ *cursor; - ++output; - ++cursor; - --todo; - ++done; - if (done == 16 && todo) + // calculate a new pad based on the last decoded block + if (d >= sizeof(digest)) { - // Compute new digest - done = 0; MD5Init(&ctx); MD5Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret)); - MD5Update(&ctx, &working_vector, 16); + MD5Update(&ctx, last, sizeof(digest)); MD5Final(digest, &ctx); + + d = 0; + last = value; } - } - return hidden_length + 6; + *value++ ^= digest[d++]; + len--; + } } static int ip_filter_port(ip_filter_portt *p, uint16_t port) diff --git a/l2tpns.spec b/l2tpns.spec index 37f7995..cb1c718 100644 --- a/l2tpns.spec +++ b/l2tpns.spec @@ -43,5 +43,5 @@ rm -rf %{buildroot} %attr(644,root,root) /usr/share/man/man[58]/* %changelog -* Mon Feb 14 2005 Brendan O'Dea 2.1.0-1 +* Thu Mar 10 2005 Brendan O'Dea 2.1.0-1 - 2.1.0 release, see /usr/share/doc/l2tpns-2.1.0/Changes