Beispiel #1
0
int
check_ipc (uid_t uid, gid_t gid)
{
  FILE *fd;
  char *ret_char;
  char readbuf[80];
  char ipcscommand[80];
  char ipc_buf[10];
  int ipc_id;
  char ipc_obj[3] = { 'm', 's', 'q' };        /* m => SHM, s => SEM, Q => MSQ */
  int opt;

  setids (0, 0);
  for (opt = 0; opt < 3; opt++)
    {
      /* Store the resulto of ipcscommand in tmpfile and read each 
       * line to test IPC objects
       */
      memset (ipcscommand, '\0', sizeof (ipcscommand));
      sprintf (ipcscommand, "ipcs -%c >%s", ipc_obj[opt], tmpfile);
      system (ipcscommand);
      fd = fopen (tmpfile, "r");
      /* Skip the header */
      fgets (readbuf, sizeof (readbuf), fd);
      fgets (readbuf, sizeof (readbuf), fd);
      fgets (readbuf, sizeof (readbuf), fd);
      memset (readbuf, '\0', sizeof (readbuf));
      ret_char = fgets (readbuf, sizeof (readbuf), fd);
      while ((ret_char != NULL) && (*readbuf != '\n'))
        {
          sscanf (readbuf, "%s %d", ipc_buf, &ipc_id);
          /* ipc_id stores the id of ipc object */
          testall_ipc (ipc_id, ipc_obj[opt], uid, gid);
          memset (readbuf, '\0', sizeof (readbuf));
          ret_char = fgets (readbuf, sizeof (readbuf), fd);
        }
      fclose (fd);
      unlink (tmpfile);
    }
  return (0);
}
Beispiel #2
0
int main(int argc,char *argv[])
{
    register int m,n;
    register char *p;
    struct stat statx;
    int mode;
    uid_t effuid;
    gid_t effgid;
    NOT_USED(argc);
    arglist = argv;
    if((command = argv[1]) == 0)
        error_exit(badexec);
    ruserid = getuid();
    euserid = geteuid();
    rgroupid = getgid();
    egroupid = getegid();
    p = argv[0];
#ifndef _lib_setreuid
    maketemp(tmpname);
    if(strcmp(p,tmpname)==0)
    {
        /* At this point, the presumption is that we are the
         * version of THISPROG copied into /tmp, with the owner,
         * group, and setuid/gid bits correctly set.  This copy of
         * the program is executable by anyone, so we must be careful
         * not to allow just any invocation of it to succeed, since
         * it is setuid/gid.  Validate the proper execution by
         * examining the FDVERIFY file descriptor -- if it is owned
         * by root and is mode SPECIAL, then this is proof that it was
         * passed by a program with superuser privileges -- hence we
         * can presume legitimacy.  Otherwise, bail out, as we suspect
         * an impostor.
         */
        if(fstat(FDVERIFY,&statb) < 0 || statb.st_uid != 0 ||
                (statb.st_mode & ~S_IFMT) != SPECIAL || close(FDVERIFY)<0)
            error_exit(badexec);
        /* This enables the grandchild to clean up /tmp file */
        close(FDSYNC);
        /* Make sure that this is a valid invocation of the clone.
         * Perhaps unnecessary, given FDVERIFY, but what the heck...
         */
        if(stat(tmpname,&statb) < 0 || statb.st_nlink != 1 ||
                !S_ISREG(statb.st_mode))
            error_exit(badexec);
        if(ruserid != euserid &&
                ((statb.st_mode & S_ISUID) == 0 || statb.st_uid != euserid))
            error_exit(badexec);
        goto exec;
    }
    /* Make sure that this is the real setuid program, not the clone.
     * It is possible by clever hacking to get past this point in the
     * clone, but it doesn't do the hacker any good that I can see.
     */
    if(euserid)
        error_exit(badexec);
#endif /* _lib_setreuid */
    /* Open the script for reading first and then validate it.  This
     * prevents someone from pulling a switcheroo while we are validating.
     */
    n = open(p,0);
    if(n == FDIN)
    {
        n = dup(n);
        close(FDIN);
    }
    if(n < 0)
        error_exit(badopen);
    /* validate execution rights to this script */
    if(fstat(FDIN,&statb) < 0 || (statb.st_mode & ~S_IFMT) != SPECIAL)
        euserid = ruserid;
    else
        euserid = statb.st_uid;
    /* do it the easy way if you can */
    if(euserid == ruserid && egroupid == rgroupid)
    {
        if(access(p,X_OK) < 0)
            error_exit(badexec);
    }
    else
    {
        /* have to check access on each component */
        while(*p++)
        {
            if(*p == '/' || *p == 0)
            {
                m = *p;
                *p = 0;
                if(eaccess(argv[0],X_OK) < 0)
                    error_exit(badexec);
                *p = m;
            }
        }
        p = argv[0];
    }
    if(fstat(n, &statb) < 0 || !S_ISREG(statb.st_mode))
        error_exit(badopen);
    if(stat(p, &statx) < 0 ||
            statb.st_ino != statx.st_ino || statb.st_dev != statx.st_dev)
        error_exit(badexec);
    if(stat(THISPROG, &statx) < 0 ||
            (statb.st_ino == statx.st_ino && statb.st_dev == statx.st_dev))
        error_exit(badexec);
    close(FDIN);
    if(fcntl(n,F_DUPFD,FDIN) != FDIN)
        error_exit(badexec);
    close(n);

    /* compute the desired new effective user and group id */
    effuid = euserid;
    effgid = egroupid;
    mode = 0;
    if(statb.st_mode & S_ISUID)
        effuid = statb.st_uid;
    if(statb.st_mode & S_ISGID)
        effgid = statb.st_gid;

    /* see if group needs setting */
    if(effgid != egroupid)
        if(effgid != rgroupid || setgid(rgroupid) < 0)
            mode = S_ISGID;

    /* now see if the uid needs setting */
    if(mode)
    {
        if(effuid != ruserid)
            mode |= S_ISUID;
    }
    else if(effuid)
    {
        if(effuid != ruserid || setuid(ruserid) < 0)
            mode = S_ISUID;
    }

    if(mode)
        setids(mode, effuid, effgid);
#ifndef _lib_setreuid
exec:
#endif /* _lib_setreuid */
    /* only use SHELL if file is in trusted directory and ends in sh */
    shell = getenv("SHELL");
    if(shell == 0 || !endsh(shell) || (
                !in_dir("/bin",shell) &&
                !in_dir("/usr/bin",shell) &&
                !in_dir("/usr/lbin",shell) &&
                !in_dir("/usr/local/bin",shell)))
        shell = DEFSHELL;
    argv[0] = command;
    argv[1] = (char*)devfd;
    execv(shell,argv);
    error_exit(badexec);
}
Beispiel #3
0
/*
 * Test access for owner, group, other
 */
void
testall (struct stat *ostatbufp, char *pathname, uid_t uid, gid_t gid)
{
  int i;
  int rc = 0;
  char outbuf[512];
  struct passwd passwd;
  struct passwd *passwdp;
  struct group group;
  struct group *groupp;
  struct stat statbuf;
  struct stat *statbufp = &statbuf;
  char *pbuf;
  char *gbuf;

  passwdp = (struct passwd *) malloc (sizeof (passwd));
  groupp = (struct group *) malloc (sizeof (group));
  pbuf = (char *) malloc (4096);
  gbuf = (char *) malloc (4096);
  memset (pbuf, '\0', 4096);
  memset (gbuf, '\0', 4096);

  setids (0, 0);
  printf ("\n%s\n", pathname);

  /* For test 1 we chown the file owner/group */
  if (test == 1)
    {
      if ((rc = chown (pathname, uid, gid)) == -1)
{
  printf ("ERROR: unable to chown %s to %d:%d\n", pathname, uid, gid);
  goto EXIT;
}
    }

  /* Start with clean buffers */
  memset (&statbuf, '\0', sizeof (statbuf));

  /* Get file stat info to determine actual owner and group */
  stat (pathname, &statbuf);

  /*
   * If we successfully chown'd the file, but the owner hasn't changed
   * log it and skip.
   */
  if ((test == 1) && ((statbufp->st_uid != uid) || (statbufp->st_gid != gid)))
    {
      printf ("INFO: chown success, but file owner "
      "did not change: %s\n", pathname);
      goto EXIT;
    }

  memset (outbuf, '\0', sizeof (outbuf));
  strcat (outbuf, "MODE: ");
  for (i = 0; i < sizeof (perms) / sizeof (int); i++)
    {
      if (statbufp->st_mode & perms[i])
{
  strcat (outbuf, ptext[i]);
}
      else
{
  strcat (outbuf, "-");
}
    }
  getpwuid_r (statbufp->st_uid, &passwd, pbuf, 4096, &passwdp);
  getgrgid_r (statbufp->st_gid, &group, gbuf, 4096, &groupp);

  sprintf (&(outbuf[strlen (outbuf)]), "%s:%s\n",
   passwd.pw_name, group.gr_name);
  printf ("%s", outbuf);

  /* Check owner access for read/write */
  setids (statbufp->st_uid, gid_nobody);
  memset (outbuf, '\0', sizeof (outbuf));
  strcat (outbuf, "Owner read\t");
  /*
   * If we are root, we expect to succeed event
   * without explicit permission.
   */
  if ((statbufp->st_mode & S_IRUSR) || (statbufp->st_uid == 0))
    {
      testaccess (pathname, O_RDONLY, 0, outbuf);
    }
  else
    {
      testaccess (pathname, O_RDONLY, -1, outbuf);
    }
  memset (outbuf, '\0', sizeof (outbuf));
  strcat (outbuf, "Owner write\t");
  /*
   * If we are root, we expect to succeed event
   * without explicit permission.
   */
  if ((statbufp->st_mode & S_IWUSR) || (statbufp->st_uid == 0))
    {
      testaccess (pathname, O_WRONLY, 0, outbuf);
    }
  else
    {
      testaccess (pathname, O_WRONLY, -1, outbuf);
    }

  /* Check group access for read/write */
  setids (0, 0);
  setids (uid_nobody, statbufp->st_gid);
  memset (outbuf, '\0', sizeof (outbuf));
  strcat (outbuf, "Group read\t");
  if (statbufp->st_mode & S_IRGRP)
    {
      testaccess (pathname, O_RDONLY, 0, outbuf);
    }
  else
    {
      testaccess (pathname, O_RDONLY, -1, outbuf);
    }
  memset (outbuf, '\0', sizeof (outbuf));
  strcat (outbuf, "Group write\t");
  if (statbufp->st_mode & S_IWGRP)
    {
      testaccess (pathname, O_WRONLY, 0, outbuf);
    }
  else
    {
      testaccess (pathname, O_WRONLY, -1, outbuf);
    }

  /* Check other access for read/write */
  setids (0, 0);
  setids (uid_nobody, gid_nobody);
  memset (outbuf, '\0', sizeof (outbuf));
  strcat (outbuf, "Other read\t");
  if (statbufp->st_gid == gid_nobody)
    {
      /* special case! file's gid == our 'other' gid */
      if (statbufp->st_mode & S_IRGRP)
{
  testaccess (pathname, O_RDONLY, 0, outbuf);
}
      else
{
  testaccess (pathname, O_RDONLY, -1, outbuf);
}
    }
  else
    {
      if (statbufp->st_mode & S_IROTH)
{
  testaccess (pathname, O_RDONLY, 0, outbuf);
}
      else
{
  testaccess (pathname, O_RDONLY, -1, outbuf);
}
    }
  memset (outbuf, '\0', sizeof (outbuf));
  strcat (outbuf, "Other write\t");
  if (statbufp->st_gid == gid_nobody)
    {
      /* special case! ile's gid == our 'other' gid */
      if (statbufp->st_mode & S_IWGRP)
{
  testaccess (pathname, O_WRONLY, 0, outbuf);
}
      else
{
  testaccess (pathname, O_WRONLY, -1, outbuf);
}
    }
  else
    {
      if (statbufp->st_mode & S_IWOTH)
{
  testaccess (pathname, O_WRONLY, 0, outbuf);
}
      else
{
  testaccess (pathname, O_WRONLY, -1, outbuf);
}
    }
  setids (0, 0);

  if (test == 1)
    {
      chown (pathname, ostatbufp->st_uid, ostatbufp->st_gid);
    }

EXIT:
  return;
}
Beispiel #4
0
int
testall_ipc (int ipc_id, char opt, uid_t uid, gid_t gid)
{
  int rc = 0;
  char outbuf[256];
  struct passwd passwd;
  struct passwd *passwdp;
  struct group group;
  struct group *groupp;
  struct shmid_ds shbuf;
  struct semid_ds sembuf;
  struct msqid_ds msqbuf;
  struct ipc_perm *ipcperm;
  char *pbuf;
  char *gbuf;
  uid_t tmpuid;
  gid_t tmpgid;

  pbuf = (char *) malloc (4096);
  gbuf = (char *) malloc (4096);
  memset (pbuf, '\0', 4096);
  memset (gbuf, '\0', 4096);

  setids (0, 0);
  printf ("\nIPC ID: %d - %s\n", ipc_id,
  ((opt == 's') ? "Semaphore Sets" : (opt ==
      'm') ? "Shared Memory Segment" :
   "Message Queues"));

  switch (opt)
    {
      /* Shared Memory */
    case 'm':
      /* Get stat information of shared memory. */
      if ((rc = shmctl (ipc_id, IPC_STAT, &shbuf)) == -1)
{
  printf ("Error getting stat of Shared Memory: %d\n", errno);
  return (rc);
}
      else
{
  /* ipcperm get the shm_perm memory address to manipulate
   * permissions with the same struct (ipc_perm).
   */
  ipcperm = &(shbuf.shm_perm);
}
      break;
      /* Semaphores */
    case 's':
      /* Set permission mode for semaphore. */
      if ((rc = semctl (ipc_id, 0, IPC_STAT, &sembuf)) == -1)
{
  printf ("Error getting stat of Semaphores: %d\n", errno);
  return (rc);
}
      else
{
  /* ipcperm get the sem_perm memory address to manipulate
   * permissions with the same struct (ipc_perm).
   */
          ipcperm = &(sembuf.sem_perm);
        }
      break;
      /* Message Queues */
    case 'q':
      if ((rc = msgctl (ipc_id, IPC_STAT, &msqbuf)) == -1)
        {
          printf ("Error getting stat of Message Queue: %d\n", errno);
          return (rc);
        }
      else
        {
          /* ipcperm get the msg_perm memory address to manipulate
           * permissions with the same struct (ipc_perm).
           */
          ipcperm = &(msqbuf.msg_perm);
        }
      break;
    }
  /* For test 1, change owner and group */
  if (test == 1)
    {
      tmpuid = ipcperm->uid;
      tmpgid = ipcperm->gid;
      ipcperm->uid = uid;
      ipcperm->gid = gid;
      switch (opt)
        {
          /* Shared Memory */
        case 'm':
          if ((rc = shmctl (ipc_id, IPC_SET, &shbuf)) == -1)
            {
              printf ("Error setting stat of Shared Memory: " "%d\n", errno);
              return (rc);
            }
          break;
          /* Semaphores */
        case 's':
          if ((rc = semctl (ipc_id, 0, IPC_SET, &sembuf)) == -1)
            {
              printf ("Error setting stat of Semaphores: " "%d\n", errno);
              return (rc);
            }
          break;
          /* Message Queues */
        case 'q':
          if ((rc = msgctl (ipc_id, IPC_SET, &msqbuf)) == -1)
            {
              printf ("Error setting stat of Message Queue: " "%d\n", errno);
              return (rc);
            }
          break;
        }
    }

  memset (outbuf, '\0', sizeof (outbuf));
  strcat (outbuf, "MODE: ");

  strcat (outbuf, (((ipcperm->mode & S_IRUSR) != 0) ? "r" : "-"));
  strcat (outbuf, (((ipcperm->mode & S_IWUSR) != 0) ? "w" : "-"));
  strcat (outbuf, "-");
  strcat (outbuf, (((ipcperm->mode & S_IRGRP) != 0) ? "r" : "-"));
  strcat (outbuf, (((ipcperm->mode & S_IWGRP) != 0) ? "w" : "-"));
  strcat (outbuf, "-");
  strcat (outbuf, (((ipcperm->mode & S_IROTH) != 0) ? "r" : "-"));
  strcat (outbuf, (((ipcperm->mode & S_IWOTH) != 0) ? "w" : "-"));
  strcat (outbuf, "-");

  getpwuid_r (ipcperm->uid, &passwd, pbuf, 4096, &passwdp);
  getgrgid_r (ipcperm->gid, &group, gbuf, 4096, &groupp);

  /* Print owner:group */
  sprintf (&(outbuf[strlen (outbuf)]), " %s:%s\n",
           passwd.pw_name, group.gr_name);
  /* Change current owner of process to access IPC function */
  setids (ipcperm->uid, gid_nobody);
  strcat (outbuf, "Owner read\t");
  /*
   * If we are root, we expect to succeed event
   * without explicit permission.
   */
  if ((ipcperm->mode & S_IRUSR) || (ipcperm->uid == 0))
    {
      testaccess_ipc (ipc_id, opt, O_RDONLY, 0, outbuf);
    }
  else
    {
      testaccess_ipc (ipc_id, opt, O_RDONLY, -1, outbuf);
    }
  memset (outbuf, '\0', sizeof (outbuf));
  strcat (outbuf, "Owner write\t");
  /*
   * If we are root, we expect to succeed event
   * without explicit permission.
   */
  if ((ipcperm->mode & S_IWUSR) || (ipcperm->uid == 0))
    {
      testaccess_ipc (ipc_id, opt, O_WRONLY, 0, outbuf);
    }
  else
    {
      testaccess_ipc (ipc_id, opt, O_WRONLY, -1, outbuf);
    }

  /* Check group access for read/write */
  setids (0, 0);
  setids (uid_nobody, ipcperm->gid);
  memset (outbuf, '\0', sizeof (outbuf));
  strcat (outbuf, "Group read\t");
  if (ipcperm->mode & S_IRGRP)
    {
      testaccess_ipc (ipc_id, opt, O_RDONLY, 0, outbuf);
    }
  else
    {
      testaccess_ipc (ipc_id, opt, O_RDONLY, -1, outbuf);
    }
  memset (outbuf, '\0', sizeof (outbuf));
  strcat (outbuf, "Group write\t");
  if (ipcperm->mode & S_IWGRP)
    {
      testaccess_ipc (ipc_id, opt, O_WRONLY, 0, outbuf);
    }
  else
    {
      testaccess_ipc (ipc_id, opt, O_WRONLY, -1, outbuf);
    }

  /* Check other access for read/write */
  setids (0, 0);
  setids (uid_nobody, gid_nobody);
  memset (outbuf, '\0', sizeof (outbuf));
  strcat (outbuf, "Other read\t");
  if (ipcperm->mode & S_IROTH)
    {
      testaccess_ipc (ipc_id, opt, O_RDONLY, 0, outbuf);
    }
  else
    {
      testaccess_ipc (ipc_id, opt, O_RDONLY, -1, outbuf);
    }
  memset (outbuf, '\0', sizeof (outbuf));
  strcat (outbuf, "Other write\t");
  if (ipcperm->mode & S_IWOTH)
    {
      testaccess_ipc (ipc_id, opt, O_WRONLY, 0, outbuf);
    }
  else
    {
      testaccess_ipc (ipc_id, opt, O_WRONLY, -1, outbuf);
    }
  setids (0, 0);
  /* Back to the original owner */
  if (test == 1)
    {
      ipcperm->uid = tmpuid;
      ipcperm->gid = tmpgid;
      switch (opt)
        {
          /* Shared Memory */
        case 'm':
          if ((rc = shmctl (ipc_id, IPC_SET, &shbuf)) == -1)
            {
              printf ("Error setting stat back of Shared "
                      "Memory: %d\n", errno);
              return (rc);
            }
          break;
          /* Semaphores */
        case 's':
          if ((rc = semctl (ipc_id, 0, IPC_SET, &sembuf)) == -1)
            {
              printf ("Error setting stat back of Semaphores: "
                      "%d\n", errno);
              return (rc);
            }
          break;
          /* Message Queues */
        case 'q':
          if ((rc = msgctl (ipc_id, IPC_SET, &msqbuf)) == -1)
            {
              printf ("Error setting stat back of Message "
      "Queue: %d\n", errno);
              return (rc);
            }
          break;
        }
    }
  return (0);
}
Beispiel #5
0
void
testaccess_ipc (int ipc_id, char opt, int mode, int expected, char *outbuf)
{
  int actual, semval, rc;
  int myerror = 0;
  char *chPtr;
  struct sembuf sop;
  uid_t tmpuid;
  gid_t tmpgid;
  struct msqbuf
  {
    long mtype;
    char mtext[80];
  } s_message, r_message;

  /* If we are root, we expect to succeed event
   * without explicit permission.
   */
  strcat (outbuf, (expected == -1) ? "expected: fail  " : "expected: pass  ");

  switch (opt)
    {
      /* Shared Memory */
    case 'm':
      /* Try to get (mode) access
       * There is no notion of a write-only shared memory
       * segment. We are testing READ ONLY and READWRITE access.
       */
      chPtr = shmat (ipc_id, NULL, (mode == O_RDONLY) ? SHM_RDONLY : 0);
      if (chPtr != (void *) -1)
{
  strcat (outbuf, "actual: pass ");
  actual = 0;
  if (shmdt (chPtr) == -1)
    {
      perror ("Warning: Could not dettach memory segment");
    }
}
      else
{
  myerror = errno;
  strcat (outbuf, "actual: fail ");
  actual = -1;
}
      break;
      /* Semaphores */
    case 's':
      tmpuid = geteuid ();
      tmpgid = getegid ();
      semval = semctl (ipc_id, 0, GETVAL);
      /* Need semaphore value == 0 to execute read permission test */
      if ((mode == O_RDONLY) && (semval > 0))
{
  setids (0, 0);
  if ((semctl (ipc_id, 0, SETVAL, 0)) == -1)
    {
      printf ("Unable to set semaphore value: %d\n", errno);
    }
  setids (tmpuid, tmpgid);
}
      /* Try to get mode access */
      sop.sem_num = 0;
      sop.sem_op = mode;
      sop.sem_flg = SEM_UNDO;
      actual = semop (ipc_id, &sop, 1);
      myerror = errno;
      if (actual != -1)
{
  strcat (outbuf, "actual: pass ");
  /* back to semaphore original value */
  if (mode != O_RDONLY)
    {
      sop.sem_op = -1;/* decrement semaphore */
      rc = semop (ipc_id, &sop, 1);
    }
}
      else
{
  /* Back to semaphore original value */
  if ((mode == O_RDONLY) && (semval > 0))
    {
      setids (0, 0);
      if ((semctl (ipc_id, 0, SETVAL, semval)) == -1)
{
  printf ("Unable to set semaphore " "value: %d\n", errno);
}
      setids (tmpuid, tmpgid);
    }
  strcat (outbuf, "actual: fail ");
}
      break;
      /* Message Queues */
    case 'q':
      tmpuid = geteuid ();
      tmpgid = getegid ();
      if (mode == O_RDONLY)
{
  setids (0, 0);
  /* Send a message to test msgrcv function */
  s_message.mtype = 1;
  memset (s_message.mtext, '\0', sizeof (s_message.mtext));
  strcpy (s_message.mtext, "First Message\0");
  if ((rc = msgsnd (ipc_id, &s_message,
    strlen (s_message.mtext), 0)) == -1)
    {
      printf ("Error sending first message: %d\n", errno);
    }
  setids (tmpuid, tmpgid);
}
      s_message.mtype = 1;
      memset (s_message.mtext, '\0', sizeof (s_message.mtext));
      strcpy (s_message.mtext, "Write Test\0");

      /* Try to get WRITE access */
      if (mode == O_WRONLY)
{
  actual = msgsnd (ipc_id, &s_message, strlen (s_message.mtext), 0);
}
      else
{
  /* Try to get READ access */
  actual = msgrcv (ipc_id, &r_message,
   sizeof (r_message.mtext), 0, IPC_NOWAIT);
}
      myerror = errno;
      if (actual != -1)
{
  strcat (outbuf, "actual: pass ");
}
      else
{
  strcat (outbuf, "actual: fail ");
}
      if (((mode == O_RDONLY) && (actual == -1)) ||
  ((mode == O_WRONLY) && (actual != -1)))
{
  setids (0, 0);
  /* discard the message send */
  rc = msgrcv (ipc_id, &r_message,
       sizeof (r_message.mtext), 0, IPC_NOWAIT);
  setids (tmpuid, tmpgid);
}
      break;
    }

  if ((actual == expected) || ((expected == 0) && (actual != -1)))
    {
      strcat (outbuf, "\tresult: PASS\n");
      totalpass++;
    }
  else
    {
      errno = myerror;// restore errno from correct error code
      sprintf (&(outbuf[strlen (outbuf)]), "\tresult: FAIL : "
       "errno = %d\n", errno);
      totalfail++;
    }
  printf ("%s", outbuf);
  return;
}