From: Alan T. DeKok Date: Fri, 13 Jan 2023 18:37:32 +0000 (-0500) Subject: add and use fr_tacacs_packet_log_hex() X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5b6e49204eeb055226bea6577e643ba7abef096a;p=thirdparty%2Ffreeradius-server.git add and use fr_tacacs_packet_log_hex() 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. --- diff --git a/src/protocols/tacacs/base.c b/src/protocols/tacacs/base.c index bb6328362ea..d71985c594b 100644 --- a/src/protocols/tacacs/base.c +++ b/src/protocols/tacacs/base.c @@ -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; + } +} diff --git a/src/protocols/tacacs/decode.c b/src/protocols/tacacs/decode.c index 75d263f94bd..7ad536a3f34 100644 --- a/src/protocols/tacacs/decode.c +++ b/src/protocols/tacacs/decode.c @@ -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)) { diff --git a/src/protocols/tacacs/encode.c b/src/protocols/tacacs/encode.c index 6c735fcb559..0de44500531 100644 --- a/src/protocols/tacacs/encode.c +++ b/src/protocols/tacacs/encode.c @@ -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 * diff --git a/src/protocols/tacacs/tacacs.h b/src/protocols/tacacs/tacacs.h index 7c881ef5e04..8a4cfb75f32 100644 --- a/src/protocols/tacacs/tacacs.h +++ b/src/protocols/tacacs/tacacs.h @@ -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);