]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
add and use fr_tacacs_packet_log_hex()
authorAlan T. DeKok <aland@freeradius.org>
Fri, 13 Jan 2023 18:37:32 +0000 (13:37 -0500)
committerAlan T. DeKok <aland@freeradius.org>
Fri, 13 Jan 2023 18:37:32 +0000 (13:37 -0500)
Which makes my head hurt, because the TACACS+ packet format
was created by drunken orangutans throwing darts at a pumpkin.
The resulting Jackson Pollock eyesore was the published as
a work of utter genius.

src/protocols/tacacs/base.c
src/protocols/tacacs/decode.c
src/protocols/tacacs/encode.c
src/protocols/tacacs/tacacs.h

index bb6328362ead53c94859f45d8d15fcec43988788..d71985c594bdb13ae0fe7983398ce0bbc907d967 100644 (file)
@@ -286,3 +286,378 @@ ssize_t fr_tacacs_length(uint8_t const *buffer, size_t buffer_len)
 
        return length;
 }
+
+static void print_hex(fr_log_t const *log, char const *file, int line, char const *prefix, uint8_t const *data, size_t datalen, uint8_t const *end)
+{
+       size_t i, j, left;
+       char buffer[16*3+1];
+
+       if ((data + datalen) > end) {
+               fr_assert(data <= end);
+
+               if (data > end) return;
+
+               fr_log(log, L_DBG, file, line, "%s TRUNCATED field says %zu, but only %zu left in the packet",
+                      prefix, datalen, end - data);
+
+               datalen = end - data;
+       }
+
+       if (!datalen) return;
+
+       for (i = 0; i < datalen; i += 16) {
+               char *q = buffer;
+
+               left = 16;
+               if ((i + left) > datalen) left = datalen - i;
+
+               for (j = 0; j < left; j++) {
+                       sprintf(q, "%02x ", data[i + j]);
+                       q += 3;
+               }
+
+               q--;
+               *q = '\0';
+
+               fr_log(log, L_DBG, file, line, "%s %s", prefix, buffer);
+       }
+}
+
+static void print_ascii(fr_log_t const *log, char const *file, int line, char const *prefix, uint8_t const *data, size_t datalen, uint8_t const *end)
+{
+       uint8_t const *p;
+
+       fr_assert((data + datalen) <= end);
+
+       if (!datalen) return;
+
+       if (datalen > 80) {
+       hex:
+               print_hex(log, file, line, prefix, data, datalen, end);
+               return;
+       }
+
+       for (p = data; p < (data + datalen); p++) {
+               if ((*p < 0x20) || (*p > 0x80)) goto hex;
+       }
+
+       fr_log(log, L_DBG, file, line, "%s %.*s", prefix, (int) datalen, (char const *) data);
+}
+
+static void print_args(fr_log_t const *log, char const *file, int line, uint8_t const *start, size_t arg_cnt, uint8_t const *end)
+{
+       size_t i;
+       uint8_t const *p;
+       char prefix[64];
+
+       if (start + arg_cnt > end) {
+               fr_log(log, L_DBG, file, line, "      ARG cnt overflows packet");
+               return;
+       }
+
+       p = start + arg_cnt;
+       for (i = 0; i < arg_cnt; i++) {
+               if (p == end) {
+                       fr_log(log, L_DBG, file, line, "      ARG[%zu] is at EOF", i);
+                       return;
+               }
+
+               if ((p + start[i]) > end) {
+                       fr_log(log, L_DBG, file, line, "      ARG[%zu] overflows packet", i);
+                       print_hex(log, file, line, "                     ", p, end - p, end);
+                       return;
+               }
+
+               snprintf(prefix, sizeof(prefix), "      arg[%zu]            ", i);
+               prefix[21] = '\0';
+
+               print_ascii(log, file, line, prefix, p, start[i], end);
+
+               p += start[i];
+       }
+}
+
+void _fr_tacacs_packet_log_hex(fr_log_t const *log, fr_tacacs_packet_t const *packet, char const *file, int line)
+{
+       size_t length;
+       uint8_t const *p = (uint8_t const *) packet;
+       uint8_t const *hdr, *end;
+
+       /*
+        *      It has to be at least 12 bytes long.
+        */
+       fr_log(log, L_DBG, file, line, "  major  %u", (p[0] & 0xf0) >> 4);
+       fr_log(log, L_DBG, file, line, "  minor  %u", (p[0] & 0x0f));
+
+       fr_log(log, L_DBG, file, line, "  type   %02x", p[1]);
+       fr_log(log, L_DBG, file, line, "  seq_no %02x", p[2]);
+       fr_log(log, L_DBG, file, line, "  flags  %02x", p[3]);
+
+       fr_log(log, L_DBG, file, line, "  sessid %08x", fr_nbo_to_uint32(p + 4));
+       fr_log(log, L_DBG, file, line, "  length %08x", fr_nbo_to_uint32(p + 8));
+
+       fr_log(log, L_DBG, file, line, "  body");
+       length = fr_nbo_to_uint32(p + 8);
+
+       if ((p[3] & 0x01) == 0) {
+               fr_log(log, L_DBG, file, line, "  ... encrypted ...");
+               return;
+       }
+
+       if (length > 65535) {
+               fr_log(log, L_DBG, file, line, "      TOO LARGE");
+               return;
+       }
+
+       p += 12;
+       hdr = p;
+       end = hdr + length;
+
+#define OVERFLOW8(_field, _name) do { \
+       if ((p + _field) > end) { \
+               fr_log(log, L_DBG, file, line, "      " STRINGIFY(_name) " overflows packet!"); \
+               return; \
+       } \
+       p += _field; \
+    } while (0)
+
+#define OVERFLOW16(_field, _name) do { \
+       if ((p + fr_nbo_to_uint16(_field)) > end) { \
+               fr_log(log, L_DBG, file, line, "      " STRINGIFY(_name) " overflows packet!"); \
+               return; \
+       } \
+       p += fr_nbo_to_uint16(_field); \
+    } while (0)
+
+#define REQUIRE(_length) do { \
+       if ((end - hdr) < _length) { \
+               fr_log(log, L_DBG, file, line, "      TRUNCATED     ", hdr, end - hdr, end); \
+               return; \
+       } \
+    } while (0)
+
+       switch (packet->hdr.type) {
+               default:
+                       print_hex(log, file, line, "      data   ", p, length, end);
+                       return;
+
+       case FR_TAC_PLUS_AUTHEN:
+               if (packet_is_authen_start_request(packet)) {
+                       fr_log(log, L_DBG, file, line, "      authentication-start");
+
+                       REQUIRE(8);
+
+                       fr_log(log, L_DBG, file, line, "      action          %02x", hdr[0]);
+                       fr_log(log, L_DBG, file, line, "      priv_lvl        %02x", hdr[1]);
+                       fr_log(log, L_DBG, file, line, "      authen_type     %02x", hdr[2]);
+                       fr_log(log, L_DBG, file, line, "      authen_service  %02x", hdr[3]);
+                       fr_log(log, L_DBG, file, line, "      user_len        %02x", hdr[4]);
+                       fr_log(log, L_DBG, file, line, "      port_len        %02x", hdr[5]);
+                       fr_log(log, L_DBG, file, line, "      rem_addr_len    %02x", hdr[6]);
+                       fr_log(log, L_DBG, file, line, "      data_len        %02x", hdr[7]);
+                       p = hdr + 8;
+
+                       /*
+                        *      Do some sanity checks on the lengths.
+                        */
+                       OVERFLOW8(hdr[4], user_len);
+                       OVERFLOW8(hdr[5], port_len);
+                       OVERFLOW8(hdr[6], rem_addr_len);
+                       OVERFLOW8(hdr[7], data_len);
+
+                       p = hdr + 8;
+                       if (p >= end) return;
+
+                       print_ascii(log, file, line, "      user           ", p, hdr[4], end);
+                       p += hdr[4];
+
+                       print_ascii(log, file, line, "      port           ", p, hdr[5], end);
+                       p += hdr[5];
+
+                       print_ascii(log, file, line, "      rem_addr       ", p, hdr[6], end);
+                       p += hdr[6];
+
+                       print_hex(log, file, line, "      data           ", p, hdr[7], end); /* common auth flows */
+
+               } else if (packet_is_authen_continue(packet)) {
+                       fr_log(log, L_DBG, file, line, "      authentication-continue");
+
+                       REQUIRE(5);
+
+                       fr_log(log, L_DBG, file, line, "      user_msg_len    %04x", fr_nbo_to_uint16(hdr));
+                       fr_log(log, L_DBG, file, line, "      data_len        %04x", fr_nbo_to_uint16(hdr + 2));
+                       fr_log(log, L_DBG, file, line, "      flags           %02x", hdr[4]);
+                       p = hdr + 5;
+
+                       /*
+                        *      Do some sanity checks on the lengths.
+                        */
+                       OVERFLOW16(hdr, user_msg_len);
+                       OVERFLOW16(hdr + 2, data_len);
+
+                       p = hdr + 5;
+                       if (p >= end) return;
+
+                       print_ascii(log, file, line, "      user_msg       ", p, fr_nbo_to_uint16(hdr), end);
+                       p += fr_nbo_to_uint16(hdr + 2);
+
+                       print_hex(log, file, line, "      data           ", p, fr_nbo_to_uint16(hdr + 2), end);
+
+               } else {
+                       fr_assert(packet_is_authen_reply(packet));
+
+                       fr_log(log, L_DBG, file, line, "      authentication-reply");
+
+                       REQUIRE(6);
+
+                       fr_log(log, L_DBG, file, line, "      status          %02x", hdr[0]);
+                       fr_log(log, L_DBG, file, line, "      flags           %02x", hdr[1]);
+                       fr_log(log, L_DBG, file, line, "      server_msg_len  %04x", fr_nbo_to_uint16(hdr + 2));
+                       fr_log(log, L_DBG, file, line, "      data_len        %04x", fr_nbo_to_uint16(hdr + 4));
+                       p = hdr + 6;
+
+                       /*
+                        *      Do some sanity checks on the lengths.
+                        */
+                       OVERFLOW16(hdr + 2, server_msg_len);
+                       OVERFLOW16(hdr + 4, data_len);
+
+                       p = hdr + 6;
+                       if (p >= end) return;
+
+                       print_ascii(log, file, line, "      server_msg     ", p, fr_nbo_to_uint16(hdr + 2), end);
+                       p += fr_nbo_to_uint16(hdr + 2);
+
+                       print_hex(log, file, line, "      data           ", p, fr_nbo_to_uint16(hdr + 4), end);
+               }
+               break;
+
+       case FR_TAC_PLUS_AUTHOR:
+               if (packet_is_author_request(packet)) {
+                       fr_log(log, L_DBG, file, line, "      authorization-request");
+                       REQUIRE(8);
+
+                       fr_log(log, L_DBG, file, line, "      auth_method     %02x", hdr[0]);
+                       fr_log(log, L_DBG, file, line, "      priv_lvl        %02x", hdr[1]);
+                       fr_log(log, L_DBG, file, line, "      authen_type     %02x", hdr[2]);
+                       fr_log(log, L_DBG, file, line, "      authen_service  %02x", hdr[3]);
+                       fr_log(log, L_DBG, file, line, "      user_len        %02x", hdr[4]);
+                       fr_log(log, L_DBG, file, line, "      port_len        %02x", hdr[5]);
+                       fr_log(log, L_DBG, file, line, "      rem_addr_len    %02x", hdr[6]);
+                       fr_log(log, L_DBG, file, line, "      arg_cnt         %02x", hdr[7]);
+                       p = hdr + 8;
+
+                       OVERFLOW8(hdr[4], user_len);
+                       OVERFLOW8(hdr[5], port_len);
+                       OVERFLOW8(hdr[6], rem_addr_len);
+                       OVERFLOW8(hdr[7], arg_cnt);
+
+                       print_hex(log, file, line, "      argc           ", p, hdr[7], end);
+
+                       p = hdr + 8 + hdr[7];
+                       print_ascii(log, file, line, "      user           ", p, hdr[4], end);
+                       p += hdr[4];
+
+                       print_ascii(log, file, line, "      port           ", p, hdr[5], end);
+                       p += hdr[5];
+
+                       print_ascii(log, file, line, "      rem_addr       ", p, hdr[6], end);
+                       p += hdr[6];
+
+                       print_args(log, file, line, p, hdr[7], end);
+
+               } else {
+                       fr_log(log, L_DBG, file, line, "      authorization-response");
+
+                       fr_assert(packet_is_author_response(packet));
+
+                       REQUIRE(6);
+
+                       fr_log(log, L_DBG, file, line, "      status          %02x", hdr[0]);
+                       fr_log(log, L_DBG, file, line, "      arg_cnt         %02x", hdr[1]);
+                       fr_log(log, L_DBG, file, line, "      server_msg_len  %04x", fr_nbo_to_uint16(hdr + 2));
+                       fr_log(log, L_DBG, file, line, "      data_len        %04x", fr_nbo_to_uint16(hdr + 4));
+                       p = hdr + 6;
+
+                       OVERFLOW8(hdr[1], arg_cnt);
+                       OVERFLOW16(hdr + 2, server_msg_len);
+                       OVERFLOW16(hdr + 4, data_len);
+
+                       print_hex(log, file, line, "      argc           ", p, hdr[1], end);
+
+                       p = hdr + 6 + hdr[7];
+                       print_ascii(log, file, line, "      server_msg     ", p, fr_nbo_to_uint16(hdr + 2), end);
+                       p += fr_nbo_to_uint16(hdr + 2);
+
+                       print_ascii(log, file, line, "      data           ", p, fr_nbo_to_uint16(hdr + 4), end);
+                       p += fr_nbo_to_uint16(hdr + 4);
+
+                       print_args(log, file, line, p, hdr[1], end);
+               }
+               break;
+
+       case FR_TAC_PLUS_ACCT:
+               if (packet_is_acct_request(packet)) {
+                       fr_log(log, L_DBG, file, line, "      accounting-response");
+
+                       REQUIRE(9);
+
+                       fr_log(log, L_DBG, file, line, "      flags           %02x", hdr[0]);
+                       fr_log(log, L_DBG, file, line, "      auth_method     %02x", hdr[1]);
+                       fr_log(log, L_DBG, file, line, "      priv_lvl        %02x", hdr[2]);
+                       fr_log(log, L_DBG, file, line, "      authen_type     %02x", hdr[3]);
+                       fr_log(log, L_DBG, file, line, "      authen_service  %02x", hdr[4]);
+                       fr_log(log, L_DBG, file, line, "      user_len        %02x", hdr[5]);
+                       fr_log(log, L_DBG, file, line, "      port_len        %02x", hdr[6]);
+                       fr_log(log, L_DBG, file, line, "      rem_addr_len    %02x", hdr[7]);
+                       fr_log(log, L_DBG, file, line, "      arg_cnt         %02x", hdr[8]);
+                       p = hdr + 8;
+
+                       OVERFLOW8(hdr[8], arg_cnt);
+                       OVERFLOW8(hdr[5], user_len);
+                       OVERFLOW8(hdr[6], port_len);
+                       OVERFLOW8(hdr[7], rem_addr_len);
+
+                       print_hex(log, file, line, "      argc           ", p, hdr[8], end);
+
+                       p = hdr + 8 + hdr[7];
+                       print_ascii(log, file, line, "      user           ", p, hdr[4], end);
+                       p += hdr[5];
+
+                       print_ascii(log, file, line, "      port           ", p, hdr[5], end);
+                       p += hdr[6];
+
+                       print_ascii(log, file, line, "      rem_addr       ", p, hdr[6], end);
+                       p += hdr[7];
+
+                       print_args(log, file, line, p, hdr[8], end);
+               } else {
+                       fr_log(log, L_DBG, file, line, "      accounting-reply");
+                       fr_assert(packet_is_acct_reply(packet));
+
+                       fr_log(log, L_DBG, file, line, "      authentication-reply");
+
+                       REQUIRE(5);
+
+                       fr_log(log, L_DBG, file, line, "      server_msg_len  %04x", fr_nbo_to_uint16(hdr));
+                       fr_log(log, L_DBG, file, line, "      data_len        %04x", fr_nbo_to_uint16(hdr + 2));
+                       fr_log(log, L_DBG, file, line, "      status          %02x", hdr[0]);
+                       p = hdr + 5;
+
+                       /*
+                        *      Do some sanity checks on the lengths.
+                        */
+                       OVERFLOW16(hdr, server_msg_len);
+                       OVERFLOW16(hdr + 2, data_len);
+
+                       p = hdr + 5;
+                       if (p >= end) return;
+
+                       print_ascii(log, file, line, "      server_msg     ", p, fr_nbo_to_uint16(hdr), end);
+                       p += fr_nbo_to_uint16(hdr);
+
+                       print_hex(log, file, line, "      data           ", p, fr_nbo_to_uint16(hdr + 2), end);
+               }
+               break;
+       }
+}
index 75d263f94bdddb1f4b37355b3d9f00d134bc2834..7ad536a3f34c569627e79c2c8eaeb55419e34e9b 100644 (file)
@@ -361,9 +361,15 @@ ssize_t fr_tacacs_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *bu
                        return -1;
                }
 
+               decrypted[3] |= 0x01; /* say it's decrypted */
+
                FR_PROTO_HEX_DUMP(decrypted, buffer_len, "fr_tacacs_packet_t (unencrypted)");
        }
 
+#ifndef NDEBUG
+       if (fr_debug_lvl >= L_DBG_LVL_4) fr_tacacs_packet_log_hex(&default_log, pkt);
+#endif
+
        switch (pkt->hdr.type) {
        case FR_TAC_PLUS_AUTHEN:
                if (packet_is_authen_start_request(pkt)) {
index 6c735fcb559048472377fa7803fc0b107d3872d6..0de445005310562f98cb1e86e418eb9dfec3fc89 100644 (file)
@@ -737,6 +737,16 @@ ssize_t fr_tacacs_encode(fr_dbuff_t *dbuff, uint8_t const *original_packet, char
         */
        if (!secret) packet->hdr.flags = packet->hdr.flags & ~FR_TAC_PLUS_UNENCRYPTED_FLAG;
 
+#ifndef NDEBUG
+       if (fr_debug_lvl >= L_DBG_LVL_4) {
+               uint8_t flags = packet->hdr.flags;
+
+               packet->hdr.flags |= FR_TAC_PLUS_UNENCRYPTED_FLAG;
+               fr_tacacs_packet_log_hex(&default_log, packet);
+               packet->hdr.flags = flags;
+       }
+#endif
+
        /*
         *      3.6. Encryption
         *
index 7c881ef5e049c0f71c608bb6ed37a7688f991673..8a4cfb75f329b19dc37d08a15eae6ccd3d797721 100644 (file)
@@ -311,3 +311,6 @@ int         fr_tacacs_init(void);
 void           fr_tacacs_free(void);
 
 int fr_tacacs_body_xor(fr_tacacs_packet_t const *pkt, uint8_t *body, size_t body_len, char const *secret, size_t secret_len);
+
+#define fr_tacacs_packet_log_hex(_log, _packet) _fr_tacacs_packet_log_hex(_log, _packet, __FILE__, __LINE__);
+void           _fr_tacacs_packet_log_hex(fr_log_t const *log, fr_tacacs_packet_t const *packet, char const *file, int line) CC_HINT(nonnull);