
#include "lclient.h"

#ifdef _AIX
#define _POSIX_SOURCE
#endif

#include <stdio.h>
#include <errno.h>
#include <string.h>	/* strerror() */
#include <fcntl.h>
#include <signal.h>

extern char *tmpnam(char *);

extern void takehup();

/* 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(char *progname, char *argv[], int infile,
		 unsigned char *init_key)
{
  int fd, pid;
  char tempfilename[L_tmpnam];

  char tmpchar;
  int p[2];
  struct sigaction sigact;

  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_crypt_file(progname, infile, fd, init_key, 0))
    {
      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;
    }

  /* Unlike the usual convention in doing execs, the _parent_ does this
     exec, so that it will be the job controlled by the shell. The child
     will be the wrapper process keeping an eye on its parent via kill. */

  if (pid == 0)
    {
      /*
       * Now that we have a process to watch, we want to watch it
       * and not be affected by user actions any more. We also have
       * reason to take HUPs. We also don't want that process to get
       * this signal behavior.
       */
      sigact.sa_flags = 0;
      sigemptyset(&sigact.sa_mask);
      sigact.sa_handler = (void (*)())takehup;
      sigaction(SIGHUP, &sigact, NULL);
      sigaction(SIGTERM, &sigact, NULL);
      sigact.sa_handler = (void (*)())SIG_IGN;
      sigaction(SIGINT, &sigact, NULL);
      sigaction(SIGTSTP, &sigact, NULL);

      /* Need to close this stuff; some shells (Solaris jsh, for
	 example) when creating pipes, wait for the pipe to close
	 rather for the children to exit before doing anything,
	 including reaping the children. Thus, the wrapper waits
	 for the app to exit (via kill 0) while the shell waits
	 for the pipe to close. Thus we have a deadlock, for kill 0
	 returns the child is still alive while it has exited but
	 not been reaped, and so doesn't exit, closing the pipe,
	 so the child is not reaped... */
#ifndef DEBUG
      fclose(stdin);
      fclose(stdout);
      fclose(stderr);
#endif

#ifdef SOLARIS
      (void)setpgrp();
      (void)setsid();
#else
      (void)setpgrp(0, getpid());
#endif

      close(p[1]);
      /* Allow at most 3 seconds to exec the program.  */
      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);

      unlink(tempfilename);
      return getppid();
    }

  close(p[0]);
#if DEBUG > 1
  link(tempfilename, "/tmp/jfc0");
#endif

  execv(tempfilename, argv);
  perror("exec");
  _exit(2);
}
