#include <sys/types.h>
#include <sys/errno.h>
#include <setjmp.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/tiuser.h>
#include <rpc/types.h>
#include <rpc/xdr.h>
#include <rpc/auth.h>
#include <rpc/auth_unix.h>
#include <rpc/auth_des.h>
#include <rpc/clnt.h>
#include <rpc/rpc_msg.h>
#include <rpc/pmap_clnt.h>
#include <rpc/svc.h>
#include <rpcsvc/yp_prot.h>
#include <rpc/pmap_prot.h>
#include "snoop.h"
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
int pos;
struct cache_struct *find_xid();
extern jmp_buf xdr_err;
void protoprint();
void print_rpcsec_gss_cred(int xid, int authlen);
char *nameof_prog();
char *nameof_astat();
char *nameof_why();
static void rpc_detail_call(int, int, int, int, int, int, char *, int);
static void rpc_detail_reply(int, int, struct cache_struct *, char *, int len);
static void print_creds(int);
static void print_verif(int);
static void stash_xid(ulong_t, int, int, int, int);
struct cache_struct xid_cache[XID_CACHE_SIZE];
#define LAST_FRAG ((ulong_t)1 << 31)
int
interpret_rpc(int flags, char *rpc, int fraglen, int type)
{
ulong_t xid;
int direction;
struct cache_struct *x;
int rpcvers, prog, vers, proc;
int status, astat, rstat, why;
char *lp;
unsigned recmark;
int markpos;
extern int pi_frame;
int lo, hi;
xdr_init(rpc, fraglen);
if (setjmp(xdr_err)) {
if (flags & F_DTAIL)
(void) sprintf(get_line(0, 0),
"---- short frame ---");
return (fraglen);
}
if (type == IPPROTO_TCP) {
markpos = getxdr_pos();
recmark = getxdr_long();
}
xid = getxdr_u_long();
direction = getxdr_long();
if (direction == CALL) {
rpcvers = getxdr_long();
pos = getxdr_pos();
prog = getxdr_long();
vers = getxdr_long();
proc = getxdr_long();
stash_xid(xid, pi_frame, prog, vers, proc);
if (!(flags & (F_SUM | F_DTAIL))) {
protoprint(flags, CALL, xid, prog, vers, proc,
rpc, fraglen);
}
} else {
x = find_xid(xid);
}
if (flags & F_SUM) {
switch (direction) {
case CALL:
(void) sprintf(get_sum_line(),
"RPC C XID=%lu PROG=%d (%s) VERS=%d PROC=%d",
xid,
prog, nameof_prog(prog),
vers, proc);
if (getxdr_long() == RPCSEC_GSS) {
extract_rpcsec_gss_cred_info(xid);
} else {
xdr_skip(getxdr_long());
}
xdr_skip(4);
xdr_skip(RNDUP(getxdr_long()));
protoprint(flags, CALL, xid, prog, vers, proc,
rpc, fraglen);
break;
case REPLY:
lp = get_sum_line();
if (x == NULL)
(void) sprintf(lp, "RPC R XID=%lu", xid);
else
(void) sprintf(lp, "RPC R (#%d) XID=%lu",
x->xid_frame, xid);
lp += strlen(lp);
status = getxdr_long();
switch (status) {
case MSG_ACCEPTED:
(void) getxdr_long();
xdr_skip(RNDUP(getxdr_long()));
astat = getxdr_long();
(void) sprintf(lp, " %s",
nameof_astat(astat));
lp += strlen(lp);
switch (astat) {
case SUCCESS:
if (x) {
protoprint(flags, REPLY,
xid,
x->xid_prog,
x->xid_vers,
x->xid_proc,
rpc, fraglen);
}
break;
case PROG_UNAVAIL :
case PROG_MISMATCH:
case PROC_UNAVAIL :
lo = getxdr_long();
hi = getxdr_long();
(void) sprintf(lp,
" (low=%d, high=%d)",
lo, hi);
break;
case GARBAGE_ARGS:
case SYSTEM_ERR:
default:
;
}
break;
case MSG_DENIED:
rstat = getxdr_long();
switch (rstat) {
case RPC_MISMATCH:
lo = getxdr_long();
hi = getxdr_long();
(void) sprintf(lp,
" Vers mismatch (low=%d, high=%d)",
lo, hi);
break;
case AUTH_ERROR:
why = getxdr_u_long();
(void) sprintf(lp,
" Can't authenticate (%s)",
nameof_why(why));
break;
}
}
break;
}
}
if (flags & F_DTAIL) {
show_header("RPC: ", "SUN RPC Header", fraglen);
show_space();
if (type == IPPROTO_TCP) {
(void) sprintf(get_line(markpos, markpos+4),
"Record Mark: %s fragment, length = %d",
recmark & LAST_FRAG ? "last" : "",
recmark & ~LAST_FRAG);
}
(void) sprintf(get_line(0, 0),
"Transaction id = %lu",
xid);
(void) sprintf(get_line(0, 0),
"Type = %d (%s)",
direction,
direction == CALL ? "Call":"Reply");
switch (direction) {
case CALL:
rpc_detail_call(flags, xid, rpcvers,
prog, vers, proc, rpc, fraglen);
break;
case REPLY:
rpc_detail_reply(flags, xid, x, rpc, fraglen);
break;
}
}
return (fraglen);
}
static void
rpc_detail_call(int flags, int xid, int rpcvers, int prog, int vers, int proc,
char *data, int len)
{
char *nameof_flavor();
char *nameof_prog();
(void) sprintf(get_line(pos, getxdr_pos()),
"RPC version = %d",
rpcvers);
(void) sprintf(get_line(pos, getxdr_pos()),
"Program = %d (%s), version = %d, procedure = %d",
prog, nameof_prog(prog), vers, proc);
print_creds(xid);
print_verif(CALL);
show_trailer();
protoprint(flags, CALL, xid, prog, vers, proc, data, len);
}
char *
nameof_flavor(int flavor)
{
switch (flavor) {
case AUTH_NONE : return ("None");
case AUTH_UNIX : return ("Unix");
case AUTH_SHORT: return ("Unix short");
case AUTH_DES : return ("DES");
case RPCSEC_GSS: return ("RPCSEC_GSS");
default: return ("unknown");
}
}
char *
tohex(char *p, int len)
{
int i, j;
static char hbuff[1024];
static char *hexstr = "0123456789ABCDEF";
char toobig = 0;
if (len * 2 > sizeof (hbuff)) {
toobig++;
len = sizeof (hbuff) / 2;
}
j = 0;
for (i = 0; i < len; i++) {
hbuff[j++] = hexstr[p[i] >> 4 & 0x0f];
hbuff[j++] = hexstr[p[i] & 0x0f];
}
if (toobig) {
hbuff[len * 2 - strlen("<Too Long>")] = '\0';
strcat(hbuff, "<Too Long>");
} else
hbuff[j] = '\0';
return (hbuff);
}
static void
print_creds(int xid)
{
int pos, flavor, authlen;
int uid, gid, len;
int tlen, idlen;
int i, namekind;
char *p, *line;
pos = getxdr_pos();
flavor = getxdr_long();
authlen = getxdr_long();
(void) sprintf(get_line(pos, getxdr_pos()),
"Credentials: Flavor = %d (%s), len = %d bytes",
flavor, nameof_flavor(flavor), authlen);
if (authlen <= 0)
return;
switch (flavor) {
case AUTH_UNIX:
(void) showxdr_time(" Time = %s");
(void) showxdr_string(MAX_MACHINE_NAME, " Hostname = %s");
pos = getxdr_pos();
uid = getxdr_u_long();
gid = getxdr_u_long();
(void) sprintf(get_line(pos, getxdr_pos()),
" Uid = %d, Gid = %d",
uid, gid);
len = getxdr_u_long();
line = get_line(pos, len * 4);
if (len == 0)
(void) sprintf(line, " Groups = (none)");
else {
(void) sprintf(line, " Groups = ");
line += strlen(line);
while (len--) {
gid = getxdr_u_long();
(void) sprintf(line, "%d ", gid);
line += strlen(line);
}
}
break;
case AUTH_DES:
namekind = getxdr_u_long();
(void) sprintf(get_line(pos, getxdr_pos()),
" Name kind = %d (%s)",
namekind,
namekind == ADN_FULLNAME ?
"fullname" : "nickname");
switch (namekind) {
case ADN_FULLNAME:
(void) showxdr_string(64,
" Network name = %s");
(void) showxdr_hex(8,
" Conversation key = 0x%s (DES encrypted)");
(void) showxdr_hex(4,
" Window = 0x%s (DES encrypted)");
break;
case ADN_NICKNAME:
(void) showxdr_hex(4, " Nickname = 0x%s");
break;
};
break;
case RPCSEC_GSS:
print_rpcsec_gss_cred(xid, authlen);
break;
default:
(void) showxdr_hex(authlen, "[%s]");
break;
}
}
static void
print_verif(int direction)
{
int pos, flavor, verlen;
pos = getxdr_pos();
flavor = getxdr_long();
verlen = getxdr_long();
(void) sprintf(get_line(pos, getxdr_pos()),
"Verifier : Flavor = %d (%s), len = %d bytes",
flavor, nameof_flavor(flavor), verlen);
if (verlen == 0)
return;
switch (flavor) {
case AUTH_DES:
(void) showxdr_hex(8, " Timestamp = 0x%s (DES encrypted)");
if (direction == CALL)
(void) showxdr_hex(4,
" Window = 0x%s (DES encrypted)");
else
(void) showxdr_hex(4, " Nickname = 0x%s");
break;
default:
(void) showxdr_hex(verlen, "[%s]");
break;
}
}
struct rpcnames {
int rp_prog;
char *rp_name;
} rpcnames[] = {
100000, "PMAP",
100001, "RSTAT",
100002, "RUSERS",
100003, "NFS",
100004, "NIS",
100005, "MOUNT",
100006, "DBX",
100007, "NISBIND",
100008, "WALL",
100009, "NISPASSWD",
100010, "ETHERSTAT",
100011, "RQUOTA",
100012, "SPRAY",
100013, "IBM3270",
100014, "IBMRJE",
100015, "SELNSVC",
100016, "RDATABASE",
100017, "REX",
100018, "ALICE",
100019, "SCHED",
100020, "LLM",
100021, "NLM",
100022, "X25INR",
100023, "STATMON1",
100024, "STATMON2",
100025, "SELNLIB",
100026, "BOOTPARAM",
100027, "MAZEPROG",
100028, "NISUPDATE",
100029, "KEYSERVE",
100030, "SECURECMD",
100031, "NETFWDI",
100032, "NETFWDT",
100033, "SUNLINKMAP",
100034, "NETMON",
100035, "DBASE",
100036, "PWDAUTH",
100037, "TFS",
100038, "NSE",
100039, "NSE_ACTIVATE",
100040, "SUNVIEW_HELP",
100041, "PNP",
100042, "IPADDR_ALLOC",
100043, "FILEHANDLE",
100044, "MVSNFS",
100045, "REM_FILEOP_USER",
100046, "BATCH_NISUPDATE",
100047, "NEM",
100048, "RAYTRACE_RD",
100049, "RAYTRACE_LD",
100050, "REM_FILEOP_GROUP",
100051, "REM_FILEOP_SYSTEM",
100052, "REM_SYSTEM_ROLE",
100055, "IOADMD",
100056, "FILEMERGE",
100057, "NAMEBIND",
100058, "NJE",
100059, "MVSATTR",
100060, "RMGR",
100061, "UIDALLOC",
100062, "LBSERVER",
100063, "LBBINDER",
100064, "GIDALLOC",
100065, "SUNISAM",
100066, "RDBSRV",
100067, "NETDIR",
100068, "CMSD",
100069, "NISXFR",
100070, "TIMED",
100071, "BUGTRAQ",
100072, "NeFS",
100073, "BILLBOARD",
100074, "BILLBOARD",
100075, "SCHEDROOM",
100076, "AUTHNEGOTIATE",
100077, "ATTRPROG",
100080, "AUTODUMP",
100081, "EVENT_SVC",
100085, "ARM_PSD",
100086, "ARMTOD",
100087, "NA.ADMIN",
100099, "PLD",
100101, "NA.EVENT",
100102, "NA.LOGGER",
100103, "NA.DISCOVER",
100104, "NA.SYNC",
100105, "NA.DISKINFO",
100106, "NA.IOSTAT",
100107, "NA.HOSTPERF",
100108, "NA.CONFIG",
100109, "NA.ACTIVITY",
100111, "NA.LPSTAT",
100112, "NA.HOSTMEM",
100113, "NA.SAMPLE",
100114, "NA.X25",
100115, "NA.PING",
100116, "NA.RPCNFS",
100117, "NA.HOSTIF",
100118, "NA.ETHERIF",
100119, "NA.IPPATH",
100120, "NA.IPROUTES",
100121, "NA.LAYERS",
100122, "NA.SNMP",
100123, "NA.TRAFFIC",
100124, "NA.DNI",
100125, "NA.CHAT",
100126, "NA.FDDI",
100127, "NA.FDDISMT",
100128, "NA.MHS",
100130, "SNM_GRAPHER",
100132, "NA.TR",
100134, "NA.TOKENRING",
100136, "NA.FRAMERELAY",
100175, "NA.SNMPTRAP",
100180, "NA.MIPROUTES",
100201, "MVSNFSSTAT",
100227, "NFS_ACL",
101002, "NSELINKTOOL",
101003, "NSELINKAPP",
110001, "GOLABEL",
110002, "PUC",
150001, "PCNFSD",
150002, "TOPS",
150003, "TOPS",
150004, "TOPS",
150005, "TOPS",
150006, "SOLARNET_FW",
160001, "CM",
300004, "FRAME 1",
300009, "FRAME 2",
390101, "RAP",
390102, "RAPRD",
500021, "ZNS",
};
int
compare(struct rpcnames *a, struct rpcnames *b)
{
return (a->rp_prog - b->rp_prog);
}
char *
nameof_prog(int prog)
{
struct rpcnames *r;
struct rpcnames *bsearch();
int elems = sizeof (rpcnames) / sizeof (*r);
r = bsearch(&prog, rpcnames, elems, sizeof (*r), compare);
if (r)
return (r->rp_name);
if (prog >= 0x40000000 && prog <= 0x5fffffff)
return ("transient");
return ("?");
}
char *
nameof_astat(int status)
{
switch (status) {
case SUCCESS : return ("Success");
case PROG_UNAVAIL : return ("Program unavailable");
case PROG_MISMATCH: return ("Program number mismatch");
case PROC_UNAVAIL : return ("Procedure unavailable");
case GARBAGE_ARGS : return ("Garbage arguments");
case SYSTEM_ERR : return ("System error");
default: return ("unknown");
}
}
char *
nameof_why(int why)
{
switch (why) {
case AUTH_BADCRED: return ("bogus credentials (seal broken)");
case AUTH_REJECTEDCRED: return ("client should begin new session");
case AUTH_BADVERF: return ("bogus verifier (seal broken)");
case AUTH_REJECTEDVERF: return ("verifier expired or was replayed");
case AUTH_TOOWEAK: return ("too weak");
case AUTH_INVALIDRESP: return ("bogus response verifier");
case AUTH_TIMEEXPIRE: return ("time of credential expired");
case AUTH_TKT_FILE: return ("something wrong with ticket file");
case AUTH_DECODE: return ("can't decode authenticator");
case AUTH_NET_ADDR: return ("net address in ticket wrong");
case RPCSEC_GSS_NOCRED: return ("no credentials for user");
case RPCSEC_GSS_FAILED: return ("GSS failure, credentials deleted");
case AUTH_FAILED:
default:
return ("unknown reason");
}
}
static void
rpc_detail_reply(int flags, int xid, struct cache_struct *x, char *data,
int len)
{
int status;
int astat, rstat, why;
int pos;
if (x) {
(void) sprintf(get_line(0, 0),
"This is a reply to frame %d",
x->xid_frame);
}
pos = getxdr_pos();
status = getxdr_long();
(void) sprintf(get_line(pos, getxdr_pos()),
"Status = %d (%s)",
status, status ? "Denied" : "Accepted");
switch (status) {
case MSG_ACCEPTED:
print_verif(REPLY);
pos = getxdr_pos();
astat = getxdr_long();
(void) sprintf(get_line(pos, getxdr_pos()),
"Accept status = %d (%s)",
astat, nameof_astat(astat));
switch (astat) {
case SUCCESS:
if (x) {
show_trailer();
protoprint(flags, REPLY, xid,
x->xid_prog, x->xid_vers, x->xid_proc,
data, len);
}
break;
case PROG_UNAVAIL :
break;
case PROG_MISMATCH:
case PROC_UNAVAIL :
showxdr_long(" Low = %d");
showxdr_long(" High = %d");
break;
case GARBAGE_ARGS:
case SYSTEM_ERR:
default:
;
}
break;
case MSG_DENIED:
pos = getxdr_pos();
rstat = getxdr_long();
(void) sprintf(get_line(pos, getxdr_pos()),
"Reject status = %d (%s)",
rstat,
rstat ? "can't authenticate"
: "version mismatch");
switch (rstat) {
case RPC_MISMATCH:
showxdr_long(" Low = %d");
showxdr_long(" High = %d");
break;
case AUTH_ERROR:
why = getxdr_u_long();
(void) sprintf(get_line(pos, getxdr_pos()),
" Why = %d (%s)",
why, nameof_why(why));
break;
}
break;
}
}
int
valid_rpc(char *rpc, int rpclen)
{
XDR xdrm;
struct rpc_msg msg;
if (rpclen < 12)
return (0);
xdrmem_create(&xdrm, rpc, rpclen, XDR_DECODE);
if (xdr_u_int(&xdrm, &msg.rm_xid) &&
xdr_u_int(&xdrm, (uint_t *)&msg.rm_direction)) {
switch (msg.rm_direction) {
case CALL:
if (xdr_rpcvers(&xdrm, &msg.rm_call.cb_rpcvers) &&
msg.rm_call.cb_rpcvers == 2)
return (1);
break;
case REPLY:
if (xdr_u_int(&xdrm,
(uint_t *)&msg.rm_reply.rp_stat) &&
(msg.rm_reply.rp_stat == MSG_ACCEPTED ||
msg.rm_reply.rp_stat == MSG_DENIED))
return (1);
break;
}
}
return (0);
}
struct cache_struct *xcpfirst = &xid_cache[0];
struct cache_struct *xcp = &xid_cache[0];
struct cache_struct *xcplast = &xid_cache[XID_CACHE_SIZE - 1];
struct cache_struct *
find_xid(ulong_t xid)
{
struct cache_struct *x;
for (x = xcp; x >= xcpfirst; x--)
if (x->xid_num == xid)
return (x);
for (x = xcplast; x > xcp; x--)
if (x->xid_num == xid)
return (x);
return (NULL);
}
static void
stash_xid(ulong_t xid, int frame, int prog, int vers, int proc)
{
struct cache_struct *x;
x = find_xid(xid);
if (x == NULL) {
x = xcp++;
if (xcp > xcplast)
xcp = xcpfirst;
x->xid_num = xid;
x->xid_frame = frame;
}
x->xid_prog = prog;
x->xid_vers = vers;
x->xid_proc = proc;
x->xid_gss_proc = RPCSEC_GSS_DATA;
x->xid_gss_service = rpc_gss_svc_default;
}
void
check_retransmit(char *line, ulong_t xid)
{
struct cache_struct *x;
extern int pi_frame;
x = find_xid(xid);
if (x && x->xid_frame != pi_frame)
(void) strcat(line, " (retransmit)");
}