
#include "lclient.h"

#ifdef _AIX
#define _POSIX_SOURCE
#endif

#include <stdio.h>
#include <errno.h>
#ifdef _AIX
#include <fcntl.h>
#else
#include <sys/file.h>
#endif

#ifndef L_tmpnam
#define L_tmpnam 32
#endif

static void alarm_handler(int sig)
{
  return;
}

extern char *tmpnam(char *);

/* Copy an encrypted executable to a temporary file, decrypting with
   the supplied key.  Remove the temporary file as soon as possible.
   Return the process ID of the child, or 0 on failure.  */

int decrypt_exec(const char *progname, char *argv[], int infile,
		 unsigned char *init_key)
{
  int fd, pid;
  char tempfilename[L_tmpnam];

  char tmpchar;
  int p[2];

  strcpy(tempfilename, "/tmp/decrXXXXXX");
  tmpnam(tempfilename);
  unlink(tempfilename);

  /* user execute, no read, no write */
  fd = open(tempfilename, O_WRONLY | O_CREAT | O_EXCL, 0100);
  if (fd == -1)
    {
      fprintf(stderr, "%s: unable to open temporary file: %s\n",
	      progname, strerror(errno));
      return 0;
    }
  if (copy_xor_file(progname, infile, fd, init_key))
    {
      unlink(tempfilename);
      close(fd);
      return 0;
    }

  /* must close before exec */
  close(fd);

  if (pipe(p) == -1)
    return 0;
  if (fcntl(p[1], F_SETFD, 1) == -1)
    return 0;
  pid = fork();

  if (pid == -1)
    {
      fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
      unlink(tempfilename);
      close(fd);
      return 0;
    }

  /* Parent execs program, child monitors parent.  If the parent can not
     exec the program, it kills the child and returns an error code.  */

  if (pid != 0)
    {
      close(p[0]);
      execv(tempfilename, argv);
      perror("exec");
      /* no reason to use anything but SIGKILL here */
      kill(pid, SIGKILL);
      unlink(tempfilename);
      return -1;
    }

  close(p[1]);

  /* Allow at most 3 seconds to exec the program.  */
  signal(SIGALRM, alarm_handler);
  alarm(3);

  /* When the child execs the program, the pipe is closed and this read
     returns EOF.  On BSD the close happens after the kernel has a reference
     to the executable.  I think any other UNIX will also do the right thing.
     --jfc  */
  read(p[0], &tmpchar, 1);
  alarm(0);
  signal(SIGALRM, SIG_IGN);

  unlink(tempfilename);

  return getppid();
}
