#define XID_CACHESIZE 32 #include #include #include #include "main.h" #include "ip.h" #include "udp.h" #include "rpc.h" #include "xdr.h" #include "util.h" #include "vars.h" #include "unused-arg.h" #include "rpccall.h" #include "rpc-portmap.h" #include "rpc-nfs.h" #include "rpc-mount.h" #include "rpc-bootparam.h" typedef struct rpcprog RPCPROG; typedef struct portcache PORTCACHE; typedef struct xidcache XIDCACHE; struct rpcprog { unsigned int number; const char *name; void *(*dump)(RPCDUMP_PROTO); } ; struct portcache { PORTCACHE *link; unsigned long int host; unsigned short int port; const RPCPROG *prog; } ; struct xidcache { unsigned int lastuse; unsigned long int clienthost; unsigned short int clientport; unsigned long int xid; const RPCPROG *prog; unsigned int vers; unsigned int proc; void *token; } ; static const RPCPROG progs[] = { { 100000, "portmap", dumprpc_portmap }, { 100003, "nfs", dumprpc_nfs }, { 100005, "mount", dumprpc_mount }, { 100026, "bootparam", dumprpc_bootparam }, { 0, 0, 0 } }; static XIDCACHE xidcache[XID_CACHESIZE]; static unsigned int xid_lastuse; static PORTCACHE *portcache; static int didinit = 0; static void maybe_init(void) { int i; XIDCACHE *c; if (didinit) return; portcache = 0; xid_lastuse = 1; for (i=0;ilastuse = 0; c->clienthost = 0; c->clientport = 0; c->xid = 0; c->prog = 0; } didinit = 1; } void rpc_portcache_set(unsigned long int host, unsigned short int port, unsigned long int prog) { PORTCACHE *pc; const RPCPROG *p; for (p=&progs[0];p->number;p++) if (p->number == prog) break; for (pc=portcache;pc;pc=pc->link) { if ((pc->host == host) && (pc->port == port)) { pc->prog = p; return; } } pc = malloc(sizeof(PORTCACHE)); pc->host = host; pc->port = port; pc->prog = p; pc->link = portcache; portcache = pc; } void rpc_portcache_unset(unsigned long int host, unsigned long int prog) { PORTCACHE *pc; PORTCACHE **pcp; pcp = &portcache; while (1) { pc = *pcp; if ((pc->host == host) && pc->prog && (pc->prog->number == prog)) { *pcp = pc->link; free(pc); } else { pcp = &pc->link; } } } static XIDCACHE *xid_setcache(unsigned long int host, unsigned short int port, unsigned long int xid, const RPCPROG *p, unsigned int vers, unsigned int proc) { int i; XIDCACHE *o; o = &xidcache[0]; for (i=1;ilastuse) o = &xidcache[i]; } if (o->prog) (*o->prog->dump)(DO_FREE,0,0,0,o->token); o->lastuse = xid_lastuse ++; o->clienthost = host; o->clientport = port; o->xid = xid; o->prog = p; o->vers = vers; o->proc = proc; o->token = 0; return(o); } static XIDCACHE *xid_findcache(unsigned long int host, unsigned short int port, unsigned long int xid) { int i; XIDCACHE *c; for (i=0;iclienthost == host) && (c->clientport == port) && (c->xid == xid) && c->prog ) { c->lastuse = xid_lastuse ++; return(c); } } return(0); } static void dump_rpc_auth(UNUSED_ARG(int off), const char *tag) { unsigned int type; unsigned int len; unsigned int plen; need(8,"authorization sizes"); type = get32(0); len = get32(4); plen = ROUNDUP(len,4); need(8+plen,"Authorization"); printx(pkt,4); printf("%stype %u",tag,type); switch (type) { case 0: printf(" [AUTH_NULL]\n"); break; case 1: printf(" [AUTH_UNIX]\n"); break; case 2: printf(" [AUTH_SHORT]\n"); break; default: printf(" [unknown]\n"); break; } consume(4); printx(pkt,4); printf("%slength %u\n",tag,len); consume(4); plen -= len; if (len > 0) { switch (type) { case 1: { unsigned int stamp; unsigned int uid; unsigned int gid; unsigned int gidlen; unsigned int gids[10]; int i; need(20,"Authorization"); stamp = get32(0); printx(pkt,4); printf("%sstamp 0x%08x\n",tag,stamp); consume(4); { char *t; t = malloc(strlen(tag)+13+1); sprintf(t,"%smachine name ",tag); dump_xdr_string(t); free(t); } uid = get32(0); printx(pkt,4); printf("UID %u\n",uid); consume(4); gid = get32(0); printx(pkt,4); printf("GID %u\n",gid); consume(4); gidlen = get32(0); printx(pkt,4); printf("Group count %u\n",gidlen); if (gidlen > 10) { printf("*** Group count out of range\n"); return; } consume(4); need(gidlen*4,"Authorization - group IDs"); for (i=0;i 0) { printx(pkt,plen); printf("%spadding\n",tag); consume(plen); } } } static void dump_rpc(const RPCPROG *expected_prog) { int rpc_xid; int rpc_pkt_type; printx(pkt,0); printf("RPC packet\n"); need(8,"rudimentary RPC header"); rpc_xid = get32(0); printx(pkt,4); consume(4); printf("xid 0x%08x\n",rpc_xid); rpc_pkt_type = get32(0); printx(pkt,4); consume(4); printf("type %d",rpc_pkt_type); switch (rpc_pkt_type) { case 0: printf(" [CALL]\n"); { unsigned int rpcvers; unsigned int prog; unsigned int vers; unsigned int proc; const RPCPROG *p; need(32,"minimum RPC call header"); rpcvers = get32(0); prog = get32(4); vers = get32(8); proc = get32(12); printx(pkt,4); printf("RPC version %u\n",rpcvers); consume(4); if (rpcvers != 2) { printf("*** Unknown RPC version\n"); break; } printx(pkt,4); printf("Program %u",prog); for (p=&progs[0];p->number;p++) { if (p->number == prog) break; } if (p) printf(" [%s]",p->name); if (expected_prog && (expected_prog->number != prog)) { printf(" (expected %u for this port)",expected_prog->number); } printf("\n"); consume(4); printx(pkt,4); printf("Program version %u\n",vers); consume(4); printx(pkt,4); printf("Procedure %u\n",proc); consume(4); dump_rpc_auth(0,"Credentials "); dump_rpc_auth(0,"Verification "); if (p && (p->number == prog)) { XIDCACHE *c; c = xid_setcache(ip_src,uh_sport,rpc_xid,p,vers,proc); c->token = (*p->dump)(DO_CALL,prog,vers,proc,0); } } break; case 1: printf(" [REPLY]\n"); { unsigned int status; need(4,"minimal RPC reply"); status = get32(0); printx(pkt,4); printf("Status %u",status); consume(4); switch (status) { case 0: printf(" [ACCEPTED]\n"); { unsigned int accstat; need(12,"minimal ACCEPTED packet"); dump_rpc_auth(0,"Verification "); accstat = get32(0); printx(pkt,4); printf("Accept status %u",accstat); consume(4); switch (accstat) { case 0: printf(" [SUCCESS]\n"); { XIDCACHE *c; c = xid_findcache(ip_dst,uh_dport,rpc_xid); if (c) (*c->prog->dump)(DO_REPLY,c->prog->number,c->vers,c->proc,c->token); } break; case 1: printf(" [PROG_UNAVAIL]\n"); break; case 2: printf(" [PROG_MISMATCH]\n"); { unsigned int low; unsigned int high; need(8,"PROG_MISMATCH range"); low = get32(0); high = get32(4); printx(pkt,8); printf("Range %u..%u\n",low,high); consume(8); } break; case 3: printf(" [PROC_UNAVAIL]\n"); break; case 4: printf(" [GARBAGE_ARGS]\n"); break; default: printf(" [unknown]\n"); break; } } break; case 1: printf(" [DENIED]\n"); { unsigned int denstat; need(4,"DENIED type"); denstat = get32(0); printx(pkt,4); printf("Denial status %u",denstat); consume(4); switch (denstat) { case 0: printf(" [RPC_MISMATCH]\n"); { unsigned int low; unsigned int high; need(8,"RPC_MISMATCH range"); low = get32(0); high = get32(4); printx(pkt,8); printf("Range %u..%u\n",low,high); consume(8); } break; case 1: printf(" [AUTH_ERROR]\n"); { unsigned int err; need(4,"AUTH_ERROR reason"); err = get32(0); printx(pkt,4); printf("Error %u",err); consume(4); switch (err) { case 0: printf(" [AUTH_BADCRED]\n"); break; case 2: printf(" [AUTH_REJECTEDCRED]\n"); break; case 3: printf(" [AUTH_BADVERF]\n"); break; case 4: printf(" [AUTH_REJECTEDVERF]\n"); break; case 5: printf(" [AUTH_TOOWEAK]\n"); break; } } break; default: printf(" [unknown]\n"); break; } } break; default: printf(" [unknown]\n"); break; } } break; default: printf(" [unknown]\n"); break; } } void dump_rpc_pkt(void) { ip_src = 1; ip_dst = 1; uh_sport = 1; uh_dport = 1; dump_rpc(0); } const char *rpc_prog_name(unsigned long int prog) { const RPCPROG *p; for (p=&progs[0];p->number;p++) { if (p->number == prog) return(p->name); } return(0); } void maybe_dump_rpc(void) { PORTCACHE *pc; maybe_init(); if ((uh_dport == 111) || (uh_sport == 111)) { dump_rpc(0); return; } if ((uh_dport == 2049) || (uh_sport == 2049)) { dump_rpc(0); return; } for (pc=portcache;pc;pc=pc->link) { if ( ((pc->host == ip_src) && (pc->port == uh_sport)) || ((pc->host == ip_dst) && (pc->port == uh_dport)) ) { dump_rpc(pc->prog); return; } } } #if 0 case 0: printf(" [RPCERR_NONE]\n"); break; case 1: printf(" [RPCERR_RPCMISMATCH]\n"); break; case 2: printf(" [RPCERR_BADCRED]\n"); break; case 3: printf(" [RPCERR_REJECTEDCRED]\n"); break; case 4: printf(" [RPCERR_BADVERF]\n"); break; case 5: printf(" [RPCERR_REJECTEDVERF]\n"); break; case 6: printf(" [RPCERR_TOOWEAK]\n"); break; case 7: printf(" [RPCERR_PROGMISMATCH]\n"); break; case 8: printf(" [RPCERR_PROGUNAVAIL]\n"); break; case 9: printf(" [RPCERR_PROCUNAVAIL]\n"); break; case 10: printf(" [RPCERR_GARBAGEARGS]\n"); break; case 11: printf(" [RPCERR_GARBAGEREPLY]\n"); break; case 12: printf(" [RPCERR_TIMEDOUT]\n"); break; default: printf(" [unknown]\n"); break; #endif