tacacs-F4.0.4.28/dump.c (453 lines of code) (raw):
/*
* $Id: dump.c,v 1.12 2009-03-18 21:09:26 heas Exp $
*
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
* Copyright (c) 1995-1998 by Cisco systems, Inc.
*
* Permission to use, copy, modify, and distribute this software for
* any purpose and without fee is hereby granted, provided that this
* copyright and permission notice appear on all copies of the
* software and supporting documentation, the name of Cisco Systems,
* Inc. not be used in advertising or publicity pertaining to
* distribution of the program without specific prior permission, and
* notice be given in supporting documentation that modification,
* copying and distribution is by permission of Cisco Systems, Inc.
*
* Cisco Systems, Inc. makes no representations about the suitability
* of this software for any purpose. THIS SOFTWARE IS PROVIDED ``AS
* IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
* WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE.
*/
#include "tac_plus.h"
/* Routines for dumping packets to stderr */
char *
summarise_outgoing_packet_type(u_char *pak)
{
HDR *hdr;
struct authen_reply *authen;
struct author_reply *author;
char *p;
hdr = (HDR *)pak;
switch (hdr->type) {
case TAC_PLUS_AUTHEN:
authen = (struct authen_reply *) (pak + TAC_PLUS_HDR_SIZE);
switch (authen->status) {
case TAC_PLUS_AUTHEN_STATUS_PASS:
p = "AUTHEN/SUCCEED";
break;
case TAC_PLUS_AUTHEN_STATUS_FAIL:
p = "AUTHEN/FAIL";
break;
case TAC_PLUS_AUTHEN_STATUS_GETDATA:
p = "AUTHEN/GETDATA";
break;
case TAC_PLUS_AUTHEN_STATUS_GETUSER:
p = "AUTHEN/GETUSER";
break;
case TAC_PLUS_AUTHEN_STATUS_GETPASS:
p = "AUTHEN/GETPASS";
break;
case TAC_PLUS_AUTHEN_STATUS_ERROR:
p = "AUTHEN/ERROR";
break;
default:
p = "AUTHEN/UNKNOWN";
break;
}
break;
case TAC_PLUS_AUTHOR:
author = (struct author_reply *) (pak + TAC_PLUS_HDR_SIZE);
switch (author->status) {
case AUTHOR_STATUS_PASS_ADD:
p = "AUTHOR/PASS_ADD";
break;
case AUTHOR_STATUS_FAIL:
p = "AUTHOR/FAIL";
break;
case AUTHOR_STATUS_PASS_REPL:
p = "AUTHOR/PASS_REPL";
break;
case AUTHOR_STATUS_ERROR:
p = "AUTHOR/ERROR";
break;
default:
p = "AUTHOR/UNKNOWN";
break;
}
break;
case TAC_PLUS_ACCT:
p = "ACCT";
break;
default:
p = "UNKNOWN";
break;
}
return(p);
}
void
dump_header(u_char *pak)
{
HDR *hdr;
u_char *data;
hdr = (HDR *)pak;
report(LOG_DEBUG, "PACKET: key=%s", session.key ? session.key : "<NULL>");
report(LOG_DEBUG, "version %d (0x%x), type %d, seq no %d, flags 0x%x",
hdr->version, hdr->version, hdr->type, hdr->seq_no, hdr->flags);
report(LOG_DEBUG, "session_id %u (0x%x), Data length %d (0x%x)",
ntohl(hdr->session_id), ntohl(hdr->session_id),
ntohl(hdr->datalength), ntohl(hdr->datalength));
report(LOG_DEBUG, "End header");
if (debug & DEBUG_HEX_FLAG) {
report(LOG_DEBUG, "Packet body hex dump:");
data = (u_char *)(pak + TAC_PLUS_HDR_SIZE);
report_hex(LOG_DEBUG, data, ntohl(hdr->datalength));
}
}
/* Dump packets originated by a NAS */
void
dump_nas_pak(u_char *pak)
{
struct authen_start *start;
struct authen_cont *cont;
struct author *author;
struct acct *acct;
int i, resid;
HDR *hdr;
u_char *p, *argsizep;
int seq;
dump_header(pak);
hdr = (HDR *)pak;
seq = hdr->seq_no;
if (seq % 2 != 1) {
report(LOG_DEBUG, "nas packets should be odd numbered seq=%d", seq);
exit(1);
}
resid = ntohl(hdr->datalength);
switch (hdr->type) {
case TAC_PLUS_AUTHEN:
start = (struct authen_start *) (pak + TAC_PLUS_HDR_SIZE);
switch (hdr->seq_no) {
case 1:
report(LOG_DEBUG, "type=AUTHEN/START, priv_lvl = %d",
start->priv_lvl);
if (resid < TAC_AUTHEN_START_FIXED_FIELDS_SIZE) {
report(LOG_DEBUG, "Bad AUTHEN/START packet length %d", resid);
return;
}
resid -= TAC_AUTHEN_START_FIXED_FIELDS_SIZE;
switch (start->action) {
case TAC_PLUS_AUTHEN_LOGIN:
report(LOG_DEBUG, "action=login");
break;
case TAC_PLUS_AUTHEN_CHPASS:
report(LOG_DEBUG, "action=chpass");
break;
case TAC_PLUS_AUTHEN_SENDPASS:
report(LOG_DEBUG, "action=sendpass");
break;
case TAC_PLUS_AUTHEN_SENDAUTH:
report(LOG_DEBUG, "action=sendauth");
break;
default:
report(LOG_DEBUG, "action=UNKNOWN %d", start->action);
break;
}
switch(start->authen_type) {
case TAC_PLUS_AUTHEN_TYPE_ASCII:
report(LOG_DEBUG, "authen_type=ascii");
break;
case TAC_PLUS_AUTHEN_TYPE_PAP:
report(LOG_DEBUG, "authen_type=pap");
break;
case TAC_PLUS_AUTHEN_TYPE_CHAP:
report(LOG_DEBUG, "authen_type=chap");
break;
case TAC_PLUS_AUTHEN_TYPE_ARAP:
report(LOG_DEBUG, "authen_type=arap");
break;
default:
report(LOG_DEBUG, "authen_type=unknown %d", start->authen_type);
break;
}
switch(start->service) {
case TAC_PLUS_AUTHEN_SVC_LOGIN:
report(LOG_DEBUG, "service=login");
break;
case TAC_PLUS_AUTHEN_SVC_ENABLE:
report(LOG_DEBUG, "service=enable");
break;
case TAC_PLUS_AUTHEN_SVC_PPP:
report(LOG_DEBUG, "service=ppp");
break;
case TAC_PLUS_AUTHEN_SVC_ARAP:
report(LOG_DEBUG, "service=arap");
break;
case TAC_PLUS_AUTHEN_SVC_PT:
report(LOG_DEBUG, "service=pt");
break;
case TAC_PLUS_AUTHEN_SVC_RCMD:
report(LOG_DEBUG, "service=rcmd");
break;
case TAC_PLUS_AUTHEN_SVC_X25:
report(LOG_DEBUG, "service=x25");
break;
case TAC_PLUS_AUTHEN_SVC_NASI:
report(LOG_DEBUG, "service=nasi");
break;
default:
report(LOG_DEBUG, "service=unknown %d", start->service);
break;
}
report(LOG_DEBUG, "user_len=%d port_len=%d (0x%x), rem_addr_len=%d"
" (0x%x)", start->user_len, start->port_len, start->port_len,
start->rem_addr_len, start->rem_addr_len);
report(LOG_DEBUG, "data_len=%d", start->data_len);
if (resid < (start->user_len + start->port_len +
start->rem_addr_len + start->data_len)) {
report(LOG_DEBUG, "AUTHEN/START data length (%d) exceeds "
"packet length length %d", (start->user_len +
start->port_len + start->rem_addr_len + start->data_len),
resid);
return;
}
/* start of variable length data is here */
p = pak + TAC_PLUS_HDR_SIZE + TAC_AUTHEN_START_FIXED_FIELDS_SIZE;
report(LOG_DEBUG, "User: ");
report_string(LOG_DEBUG, p, start->user_len);
p += start->user_len;
report(LOG_DEBUG, "port: ");
report_string(LOG_DEBUG, p, start->port_len);
p += start->port_len;
report(LOG_DEBUG, "rem_addr: ");
report_string(LOG_DEBUG, p, start->rem_addr_len);
p += start->rem_addr_len;
report(LOG_DEBUG, "data: ");
report_string(LOG_DEBUG, p, start->data_len);
report(LOG_DEBUG, "End packet");
return;
default:
cont = (struct authen_cont *) (pak + TAC_PLUS_HDR_SIZE);
report(LOG_DEBUG, "type=AUTHEN/CONT");
if (resid < TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE) {
report(LOG_DEBUG, "Bad AUTHEN/CONT packet length %d", resid);
return;
}
resid -= TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE;
report(LOG_DEBUG, "user_msg_len %d (0x%x), user_data_len %d (0x%x)",
cont->user_msg_len, cont->user_msg_len,
cont->user_data_len, cont->user_data_len);
report(LOG_DEBUG, "flags=0x%x", cont->flags);
if (resid < (cont->user_msg_len + cont->user_data_len)) {
report(LOG_DEBUG, "AUTHEN/CONT data length (%d) exceeds "
"packet length length %d", (cont->user_msg_len +
cont->user_data_len), resid);
return;
}
/* start of variable length data is here */
p = pak + TAC_PLUS_HDR_SIZE +
TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE;
report(LOG_DEBUG, "User msg: ");
report_string(LOG_DEBUG, p, cont->user_msg_len);
p += cont->user_msg_len;
report(LOG_DEBUG, "User data: ");
report_string(LOG_DEBUG, p, cont->user_data_len);
report(LOG_DEBUG, "End packet");
return;
}
case TAC_PLUS_AUTHOR:
author = (struct author *) (pak + TAC_PLUS_HDR_SIZE);
report(LOG_DEBUG, "type=AUTHOR, priv_lvl=%d, authen=%d",
author->priv_lvl,
author->authen_type);
if (resid < TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE) {
report(LOG_DEBUG, "Bad AUTHOR packet length %d", resid);
return;
}
resid -= TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE;
switch(author->authen_method) {
case AUTHEN_METH_NONE:
report(LOG_DEBUG, "method=none");
break;
case AUTHEN_METH_KRB5:
report(LOG_DEBUG, "method=krb5");
break;
case AUTHEN_METH_LINE:
report(LOG_DEBUG, "method=line");
break;
case AUTHEN_METH_ENABLE:
report(LOG_DEBUG, "method=enable");
break;
case AUTHEN_METH_LOCAL:
report(LOG_DEBUG, "method=local");
break;
case AUTHEN_METH_TACACSPLUS:
report(LOG_DEBUG, "method=tacacs+");
break;
case AUTHEN_METH_RCMD:
report(LOG_DEBUG, "method=rcmd");
break;
default:
report(LOG_DEBUG, "method=unknown %d", author->authen_method);
break;
}
report(LOG_DEBUG, "svc=%d user_len=%d port_len=%d rem_addr_len=%d",
author->service, author->user_len,
author->port_len, author->rem_addr_len);
report(LOG_DEBUG, "arg_cnt=%d", author->arg_cnt);
if (resid < (author->service + author->user_len + author->port_len +
author->rem_addr_len)) {
report(LOG_DEBUG, "AUTHOR data length (%d) exceeds packet length "
"length %d", (author->service + author->user_len +
author->port_len + author->rem_addr_len), resid);
return;
}
/* variable length data start here */
p = pak + TAC_PLUS_HDR_SIZE + TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE;
argsizep = p;
p += author->arg_cnt;
report(LOG_DEBUG, "User: ");
report_string(LOG_DEBUG, p, author->user_len);
p += author->user_len;
report(LOG_DEBUG, "port: ");
report_string(LOG_DEBUG, p, author->port_len);
p += author->port_len;
report(LOG_DEBUG, "rem_addr: ");
report_string(LOG_DEBUG, p, author->rem_addr_len);
p += author->rem_addr_len;
for (i = 0; i < (int)author->arg_cnt; i++) {
report(LOG_DEBUG, "arg[%d]: size=%d ", i, *argsizep);
report_string(LOG_DEBUG, p, *argsizep);
p += *argsizep;
argsizep++;
}
break;
case TAC_PLUS_ACCT:
acct = (struct acct *) (pak + TAC_PLUS_HDR_SIZE);
report(LOG_DEBUG, "ACCT, flags=0x%x method=%d priv_lvl=%d",
acct->flags, acct->authen_method, acct->priv_lvl);
report(LOG_DEBUG, "type=%d svc=%d",
acct->authen_type, acct->authen_service);
if (resid < TAC_ACCT_REQ_FIXED_FIELDS_SIZE) {
report(LOG_DEBUG, "Bad ACCT packet length %d", resid);
return;
}
resid -= TAC_ACCT_REQ_FIXED_FIELDS_SIZE;
if (resid < (acct->user_len + acct->port_len + acct->rem_addr_len)) {
report(LOG_DEBUG, "AUTHOR data length (%d) exceeds packet length "
"%d", (acct->user_len + acct->port_len + acct->rem_addr_len),
resid);
return;
}
resid -= acct->user_len + acct->port_len + acct->rem_addr_len;
report(LOG_DEBUG, "user_len=%d port_len=%d rem_addr_len=%d",
acct->user_len, acct->port_len, acct->rem_addr_len);
report(LOG_DEBUG, "arg_cnt=%d", acct->arg_cnt);
p = pak + TAC_PLUS_HDR_SIZE + TAC_ACCT_REQ_FIXED_FIELDS_SIZE;
argsizep = p;
p += acct->arg_cnt;
report(LOG_DEBUG, "User: ");
report_string(LOG_DEBUG, p, acct->user_len);
p += acct->user_len;
report(LOG_DEBUG, "port: ");
report_string(LOG_DEBUG, p, acct->port_len);
p += acct->port_len;
report(LOG_DEBUG, "rem_addr: ");
report_string(LOG_DEBUG, p, acct->rem_addr_len);
p += acct->rem_addr_len;
for (i = 0; i < (int)acct->arg_cnt; i++) {
report(LOG_DEBUG, "arg[%d]: size=%d ", i, *argsizep);
if (resid < 1 + *argsizep) {
report(LOG_DEBUG, "ACCT arg %d data length (%d) exceeds packet "
"length %d", i, (1 + *argsizep), resid);
return;
}
resid -= 1 + *argsizep;
report_string(LOG_DEBUG, p, *argsizep);
p += *argsizep;
argsizep++;
}
break;
default:
report(LOG_DEBUG, "dump_nas_pak: unrecognized header type %d",
hdr->type);
}
report(LOG_DEBUG, "End packet");
}
/* Dump packets originated by Tacacsd */
void
dump_tacacs_pak(u_char *pak)
{
struct authen_reply *authen;
struct author_reply *author;
struct acct_reply *acct;
HDR *hdr;
u_char *p, *argsizep;
int i;
int seq;
dump_header(pak);
hdr = (HDR *)pak;
seq = hdr->seq_no;
if (seq % 2 != 0) {
report(LOG_ERR, "%s: Bad sequence number %d should be even",
session.peer, seq);
tac_exit(1);
}
switch (hdr->type) {
case TAC_PLUS_AUTHEN:
authen = (struct authen_reply *) (pak + TAC_PLUS_HDR_SIZE);
report(LOG_DEBUG, "type=AUTHEN status=%d (%s) flags=0x%x",
authen->status, summarise_outgoing_packet_type(pak),
authen->flags);
report(LOG_DEBUG, "msg_len=%d, data_len=%d",
authen->msg_len, authen->data_len);
/* start of variable length data is here */
p = pak + TAC_PLUS_HDR_SIZE + TAC_AUTHEN_REPLY_FIXED_FIELDS_SIZE;
report(LOG_DEBUG, "msg: ");
report_string(LOG_DEBUG, p, authen->msg_len);
p += authen->msg_len;
report(LOG_DEBUG, "data: ");
report_string(LOG_DEBUG, p, authen->data_len);
report(LOG_DEBUG, "End packet");
return;
case TAC_PLUS_AUTHOR:
author = (struct author_reply *) (pak + TAC_PLUS_HDR_SIZE);
report(LOG_DEBUG, "type=AUTHOR/REPLY status=%d (%s) ",
author->status, summarise_outgoing_packet_type(pak));
report(LOG_DEBUG, "msg_len=%d, data_len=%d arg_cnt=%d",
author->msg_len, author->data_len, author->arg_cnt);
/* start of variable length data is here */
p = pak + TAC_PLUS_HDR_SIZE + TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE;
/* arg sizes come next */
argsizep = p;
p += author->arg_cnt;
report(LOG_DEBUG, "msg: ");
report_string(LOG_DEBUG, p, author->msg_len);
p += author->msg_len;
report(LOG_DEBUG, "data: ");
report_string(LOG_DEBUG, p, author->data_len);
p += author->data_len;
/* args follow */
for (i = 0; i < (int)author->arg_cnt; i++) {
int size = argsizep[i];
report(LOG_DEBUG, "arg[%d] size=%d ", i, size);
report_string(LOG_DEBUG, p, size);
p += size;
}
break;
case TAC_PLUS_ACCT:
acct = (struct acct_reply *) (pak + TAC_PLUS_HDR_SIZE);
report(LOG_DEBUG, "ACCT/REPLY status=%d", acct->status);
report(LOG_DEBUG, "msg_len=%d data_len=%d", acct->msg_len,
acct->data_len);
p = pak + TAC_PLUS_HDR_SIZE + TAC_ACCT_REPLY_FIXED_FIELDS_SIZE;
report(LOG_DEBUG, "msg: ");
report_string(LOG_DEBUG, p, acct->msg_len);
p += acct->msg_len;
report(LOG_DEBUG, "data: ");
report_string(LOG_DEBUG, p, acct->data_len);
break;
default:
report(LOG_DEBUG, "dump_tacacs_pak: unrecognized header type %d",
hdr->type);
}
report(LOG_DEBUG, "End packet");
}
/* summarise packet types for logging routines. */
char *
summarise_incoming_packet_type(u_char *pak)
{
HDR *hdr;
char *p;
hdr = (HDR *)pak;
switch (hdr->type) {
case TAC_PLUS_AUTHEN:
switch (hdr->seq_no) {
case 1:
p = "AUTHEN/START";
break;
default:
p = "AUTHEN/CONT";
break;
}
return(p);
case TAC_PLUS_AUTHOR:
p = "AUTHOR";
break;
case TAC_PLUS_ACCT:
p = "ACCT";
break;
default:
p = "UNKNOWN";
break;
}
return(p);
}