/* SAMHAIN file system integrity testing                                   */
/* Copyright (C) 1999, 2000 Rainer Wichmann                                */
/*                                                                         */
/*  This program is free software; you can redistribute it                 */
/*  and/or modify                                                          */
/*  it under the terms of the GNU General Public License as                */
/*  published by                                                           */
/*  the Free Software Foundation; either version 2 of the License, or      */
/*  (at your option) any later version.                                    */
/*                                                                         */
/*  This program is distributed in the hope that it will be useful,        */
/*  but WITHOUT ANY WARRANTY; without even the implied warranty of         */
/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
/*  GNU General Public License for more details.                           */
/*                                                                         */
/*  You should have received a copy of the GNU General Public License      */
/*  along with this program; if not, write to the Free Software            */
/*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */

#include "config_xor.h"

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


#if defined(WITH_SIG)

#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#if defined(SH_WITH_SERVER)
#include <pwd.h>
#endif
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/wait.h>

#include <string.h>
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif


#if !defined(O_NONBLOCK)
#if defined(O_NDELAY)
#define O_NONBLOCK  O_NDELAY
#else
#define O_NONBLOCK  0
#endif
#endif


#include "samhain.h"
#include "sh_utils.h"
#include "sh_error.h"
#include "sh_tiger.h"
#if defined(SH_WITH_SERVER)
#define SH_NEED_PWD_GRP 1
#include "sh_static.h"
#endif
#include "sh_sig.h"

int get_the_fd(SL_TICKET file_1);

#if defined(WITH_GPG)
static struct {
  char     conf_id[SH_MINIBUF+1];
  char     conf_fp[SH_MINIBUF+1];
  char     data_id[SH_MINIBUF+1];
  char     data_fp[SH_MINIBUF+1];
} gp;
#endif

typedef struct {
  pid_t    pid;
  FILE   * pipe;
} sh_gpg_popen_t;

#define SH_SIG_OK      0
#define SH_SIG_BAD     1
#define SH_SIG_BADSIGN 2

/* replace #if 0 by #if 1 and set an appropriate path in front of '/pdbg.'
 * for debugging
 */
#if 0
#define PDGBFILE "/pdbg."
#endif

#if defined(PDGBFILE)
FILE * pdbg;
FILE * pdbgc;
#define PDBG_OPEN    pdbg = fopen(PDGBFILE"main",  "a")  
#define PDBG_CLOSE   sl_fclose (FIL__, __LINE__, pdbg)
#define PDBG(arg)    fprintf(pdbg,  "PDBG: step %d\n", arg); fflush(pdbg)
#define PDBG_D(arg)  fprintf(pdbg,  "PDBG: %d\n", arg); fflush(pdbg)
#define PDBG_S(arg)  fprintf(pdbg,  "PDBG: %s\n", arg); fflush(pdbg)

#define PDBGC_OPEN   pdbgc = fopen(PDGBFILE"child", "a")  
#define PDBGC_CLOSE  sl_fclose (FIL__, __LINE__, pdbgc)
#define PDBGC(arg)   fprintf(pdbgc, "PDBG: step %d\n", arg); fflush(pdbgc)
#define PDBGC_D(arg) fprintf(pdbgc, "PDBG: %d\n", arg); fflush(pdbgc)
#define PDBGC_S(arg) fprintf(pdbgc, "PDBG: %s\n", arg); fflush(pdbgc)
#else
#define PDBG_OPEN    
#define PDBG_CLOSE   
#define PDBG(arg)    
#define PDBG_D(arg)  
#define PDBG_S(arg)  
#define PDBGC_OPEN    
#define PDBGC_CLOSE   
#define PDBGC(arg)    
#define PDBGC_D(arg)  
#define PDBGC_S(arg)  
#endif

#undef  FIL__
#define FIL__  _("sh_sig.c")

#if defined(SIG_HASH) || defined(SIG_KEY_HASH)

typedef enum { SIG_HASH_REPORT, SIG_HASH_REPORTFULL, SIG_HASH_OTHER } checksum_flag;

static int sh_sig_checksum (SL_TICKET checkfd, checksum_flag flag, const char * expected_in, const char * path)
{
  char * test_sig;
  char * expected = NULL;
  char * test_ptr1 = NULL;
  char * test_ptr2 = NULL;
  char   wstrip1[128];
  char   wstrip2[128];
  int    i, k;
#include "sh_sig_chksum.h"

  SL_ENTER(_("sh_sig_checksum"));

  
  if (flag == SIG_HASH_OTHER)
    expected = sh_util_strdup(expected_in);
  
  if (flag == SIG_HASH_OTHER)
    test_sig = sh_tiger_hash_gpg (path, checkfd, TIGER_NOLIM);
  else
    test_sig = sh_tiger_hash_gpg (DEFAULT_SIG_PATH, checkfd, TIGER_NOLIM);
  
  test_ptr1 = (flag == SIG_HASH_OTHER) ? strchr(expected, ':') : strchr(SIG_HASH, ':');
  if (test_ptr1 != NULL)
    test_ptr1 += 2;
  else
    test_ptr1 = (flag == SIG_HASH_OTHER) ? expected : SIG_HASH;

  if (test_sig != NULL)
    test_ptr2 = strchr(test_sig, ':');
  if (test_ptr2 != NULL)
    test_ptr2 += 2;
  else
    test_ptr2 = test_sig;

  /* Tue Jun 24 23:11:54 CEST 2003 (1.7.9) -- strip whitespace
   */
  k = 0;
  for (i = 0; i < 127; ++i)
    {
      if (test_ptr1[i] == '\0')
	break;
      if (test_ptr1[i] != ' ')
	{
	  wstrip1[k] = test_ptr1[i];
	  ++k;
	}
    }
  wstrip1[k] = '\0';

  if (flag != SIG_HASH_OTHER)
    {
      for(i = 0; i < KEY_LEN; ++i)
	{
	  if (sigchk[i] != wstrip1[i]) 
	    {
	      sh_error_handle(SH_ERR_SEVERE, FIL__, __LINE__, 0, MSG_E_GPG_CHK, 
			      sigchk, wstrip1);
	      break;
	    }
	}
    }

  k = 0;
  if (test_ptr2)
    {
      for (i = 0; i < 127; ++i)
	{
	  if (test_ptr2[i] == '\0')
	    break;
	  if (test_ptr2[i] != ' ')
	    {
	      wstrip2[k] = test_ptr2[i];
	      ++k;
	    }
	}
    }
  wstrip2[k] = '\0';

  if (0 != sl_strncmp(wstrip1, wstrip2, 127))
    {
      TPT(((0), FIL__, __LINE__, _("msg=<sig checksum: %s>\n"), test_sig));
      TPT(((0), FIL__, __LINE__, _("msg=<compiled in : %s>\n"), (flag == SIG_HASH_OTHER) ? expected : SIG_HASH));
      TPT(((0), FIL__, __LINE__, _("msg=<wstrip1     : %s>\n"), wstrip1));
      TPT(((0), FIL__, __LINE__, _("msg=<wstrip2     : %s>\n"), wstrip2));
      if (flag == SIG_HASH_REPORTFULL)
	sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_GPG, 
			SIG_HASH, test_sig);
      if (flag == SIG_HASH_OTHER)
	dlog(1, FIL__, __LINE__, _("The compiled-in checksum of the public key %s\n(%s)\ndoes not match the actual checksum\n(%s).\nYou need to recompile with the correct checksum."), path, wstrip1, wstrip2);
      else
	dlog(1, FIL__, __LINE__, _("The compiled-in checksum of the signature checking binary %s\n(%s)\ndoes not match the actual checksum\n(%s).\nYou need to recompile with the correct checksum."), DEFAULT_SIG_PATH, wstrip1, wstrip2);
      SH_FREE(test_sig);
      if (expected)
	SH_FREE(expected);
      SL_RETURN((-1), _("sh_sig_checksum"));
    }
  SH_FREE(test_sig);
  if (expected)
    SH_FREE(expected);
  SL_RETURN( (0), _("sh_sig_checksum"));
}
#endif

struct startup_info {
  long   line;
  char * program;
  long   uid;
  char * path;
  char * key_uid;
  char * key_id;
};

static struct startup_info startInfo = { 0, NULL, 0, NULL, NULL, NULL };

static void sh_sig_fill_startup (long line, char * program, long uid, char * path, 
				 char * key_uid, char * key_id)
{
  startInfo.line    = line;
  startInfo.program = sh_util_strdup(program);
  startInfo.uid     = uid;
  startInfo.path    = sh_util_strdup(path);
  if (key_uid)
    startInfo.key_uid = sh_util_strdup(key_uid);
  else
    startInfo.key_uid = sh_util_strdup(_("(not given)"));
  if (key_id)
    startInfo.key_id  = sh_util_strdup(key_id);
  else
    startInfo.key_id  = sh_util_strdup(_("(not given)"));
  return;
}

typedef enum { SIG_DATASIG, SIG_DATAONLY } extractlevel;


static FILE * sh_sig_popen (char *const argv[], sh_gpg_popen_t  *source, int fd);


static FILE * sh_sig_popen (char *const arg[], sh_gpg_popen_t  *source, int fd)
{
  size_t len;
  extern int flag_err_debug;
  int pipedes[2];
  FILE * outf = NULL;
  char * envp[2];

#if defined(HAVE_SIG_CHECKSUM)
  SL_TICKET   checkfd = -1;
  int         myrand;
  int         i;
#if defined(__linux__)
  int         get_the_fd(SL_TICKET);
  char        pname[128];
  int         pfd;
  int         val_return;
#endif
#endif

  SL_ENTER(_("sh_sig_popen"));

  /* use homedir of effective user
   */
  len = sl_strlen(sh.effective.home) + 6;
  envp[0] = calloc(1, len); /* free() ok   */
  if (envp[0] != NULL)
	sl_snprintf (envp[0], len, _("HOME=%s"), sh.effective.home); 
  envp[1] = NULL;

  /* Create the pipe 
   */
  if (aud_pipe(FIL__, __LINE__, pipedes) < 0) 
    {
      if (envp[0] != NULL) 
	free(envp[0]);
      SL_RETURN( (NULL), _("sh_gpg_popen"));
    }

  fflush (NULL);
  
  source->pid = aud_fork(FIL__, __LINE__);
  
  /* Failure
   */
  if (source->pid == (pid_t) - 1) 
    {
      sl_close_fd(FIL__, __LINE__, pipedes[0]);
      sl_close_fd(FIL__, __LINE__, pipedes[1]);
      if (envp[0] != NULL) 
	free(envp[0]);
      SL_RETURN( (NULL), _("sh_sig_popen"));
    }

  if (source->pid == (pid_t) 0) 
    {

      /* child - make read side of the pipe stdout 
       */
      if (retry_aud_dup2(FIL__, __LINE__,
			pipedes[STDOUT_FILENO], STDOUT_FILENO) < 0)
	{
	  TPT(((0), FIL__, __LINE__, _("msg=<dup2 on pipe failed>\n")));
	  dlog(1, FIL__, __LINE__, _("Internal error: dup2 failed\n"));
	  aud__exit(FIL__, __LINE__, EXIT_FAILURE);
	}
      
      /* close the pipe descriptors 
       */
      sl_close_fd (FIL__, __LINE__, pipedes[STDIN_FILENO]);
      sl_close_fd (FIL__, __LINE__, pipedes[STDOUT_FILENO]);
      
      if (retry_aud_dup2(FIL__, __LINE__, fd, STDIN_FILENO) < 0)
	{
	  TPT(((0), FIL__, __LINE__, _("msg=<dup2 on fd failed>\n")));
	  dlog(1, FIL__, __LINE__, _("Internal error: dup2 failed\n"));
	  aud__exit(FIL__, __LINE__, EXIT_FAILURE);
	}
 
      /* don't leak file descriptors
       */
      sh_unix_closeall (3, -1, S_TRUE); /* in child process */

      if (flag_err_debug != S_TRUE)
	{
	  if (NULL == freopen(_("/dev/null"), "r+", stderr))
	    {
	      dlog(1, FIL__, __LINE__, _("Internal error: freopen failed\n"));
	      aud__exit(FIL__, __LINE__, EXIT_FAILURE);
	    }
	}


      /* We should become privileged if SUID,
       * to be able to read the keyring.
       * We have checked that gpg is OK,
       * AND that only a trusted user could overwrite
       * gpg.
       */
      memset (skey, 0, sizeof(sh_key_t));
      aud_setuid(FIL__, __LINE__, geteuid());
      
      PDBGC_OPEN;
      PDBGC_D((int)getuid());
      PDBGC_D((int)geteuid());

      {
	int i = 0;
	while (arg[i] != NULL)
	  {
	    PDBGC_S(arg[i]);
	    ++i;
	  }
      }
      PDBGC_CLOSE;

      /* exec the program */

#if defined(__linux__) && defined(HAVE_SIG_CHECKSUM)
      /* 
       * --  emulate an fexecve with checksum testing
       */
      checkfd = sl_open_read(FIL__, __LINE__, DEFAULT_SIG_PATH, SL_NOPRIV);

      if (0 != sh_sig_checksum(checkfd, SIG_HASH_REPORT, NULL, NULL))
	{
	  sl_close(checkfd);
	  aud__exit(FIL__, __LINE__, EXIT_FAILURE);
	}

      pfd = get_the_fd(checkfd);
      do {
	val_return = dup (pfd);
      } while (val_return < 0 && errno == EINTR);
      pfd = val_return;
      sl_close(checkfd);
      /* checkfd = -1; *//* never read */

      sl_snprintf(pname, sizeof(pname), _("/proc/self/fd/%d"), pfd);
      if (0 == access(pname, R_OK|X_OK))               /* flawfinder: ignore */

	{
	  fcntl  (pfd, F_SETFD, FD_CLOEXEC);
	  retry_aud_execve (FIL__, __LINE__,  pname, arg, envp);
	      
	  dlog(1, FIL__, __LINE__, _("Unexpected error: execve %s failed\n"),
	       pname);
	  /* failed 
	   */
	  aud__exit(FIL__, __LINE__, EXIT_FAILURE);
	}
	  
      /* procfs not working, go ahead 
       */
#endif

#if defined(HAVE_SIG_CHECKSUM)
      /* This is an incredibly ugly kludge to prevent an attacker
       * from knowing when it is safe to slip in a fake executable
       * between the integrity check and the execve
       */
      myrand = (int) taus_get ();

      myrand = (myrand < 0) ? (-myrand) : myrand;
      myrand = (myrand % 32) + 2;

      for (i = 0; i < myrand; ++i)
	{
	  checkfd = sl_open_fastread(FIL__, __LINE__, 
				     DEFAULT_SIG_PATH, SL_NOPRIV);

	  if (0 != sh_sig_checksum(checkfd, SIG_HASH_REPORT, NULL, NULL)) {
	    aud__exit(FIL__, __LINE__, EXIT_FAILURE);
	  }
	  sl_close(checkfd);
	}
#endif

      retry_aud_execve (FIL__, __LINE__, DEFAULT_SIG_PATH, arg, envp);
      dlog(1, FIL__, __LINE__, _("Unexpected error: execve %s failed\n"),
	   DEFAULT_SIG_PATH);
      
      /* failed 
       */
      TPT(((0), FIL__, __LINE__, _("msg=<execve failed>\n")));
      dlog(1, FIL__, __LINE__, _("Unexpected error: execve failed\n"));
      aud__exit(FIL__, __LINE__, EXIT_FAILURE);
    }

  /* parent
   */

  if (envp[0] != NULL) 
    free(envp[0]);

  sl_close_fd (FIL__, __LINE__, pipedes[STDOUT_FILENO]);
  retry_fcntl (FIL__, __LINE__, pipedes[STDIN_FILENO], F_SETFD, FD_CLOEXEC);
  retry_fcntl (FIL__, __LINE__, pipedes[STDIN_FILENO], F_SETFL,  O_NONBLOCK);

  outf = fdopen (pipedes[STDIN_FILENO], "r");
  
  if (outf == NULL) 
    {
      aud_kill (FIL__, __LINE__, source->pid, SIGKILL);
      sl_close_fd (FIL__, __LINE__, pipedes[STDOUT_FILENO]);
      waitpid (source->pid, NULL, 0);
      source->pid = 0;
      SL_RETURN( (NULL), _("sh_sig_popen"));
    }
  
  SL_RETURN( (outf), _("sh_sig_popen"));
}


static int sh_sig_pclose (sh_gpg_popen_t *source)
{
  int status = 0;
  
  SL_ENTER(_("sh_sig_pclose"));

  status = sl_fclose(FIL__, __LINE__, source->pipe);
  if (status)
    SL_RETURN( (-1), _("sh_sig_pclose"));
  
  if (waitpid(source->pid, NULL, 0) != source->pid)
    status = -1;
  
  source->pipe = NULL;
  source->pid = 0;
  SL_RETURN( (status), _("sh_sig_pclose"));
}

/* This is signify specific stuff 
 */
#if defined(WITH_SIGNIFY)

#include <ctype.h>

static
int sh_signify_comp_comm(const char * line, size_t * commlen)
{
  /* check for a valid comment line: not exceeding 1023 chars and 
   * starting with 'untrusted comment: ' */
  static char   cmp[SH_MINIBUF];
  static size_t cmp_len = 0;

  size_t len = sl_strlen(line);
  
  if (cmp_len == 0) {
    sl_strlcpy(cmp, _("untrusted comment: "), sizeof(cmp));
    cmp_len = strlen(cmp);
  }

  if (line[len-1] == '\n') {
    /* signify will replace the '\n' with '\0', so 1024 -> 1023, which fits */
    if (len > 1024) return S_FALSE;
    else            *commlen = len;
  } else {
    if (len > 1023) return S_FALSE;
    else            *commlen = (len+1);
  }

  if (len >= cmp_len && 0 == strncmp(cmp, line, cmp_len))
    return S_TRUE;
  return S_FALSE;
}

static const char bto64_0[] = N_("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
static char bto64[65] = { '\0' };

static
int sh_signify_comp_sig(const char * line, size_t commlen)
{
  char   cmp[128];
  char   out[128];
  size_t len = sl_strlen(line);
  size_t i, j = 0;
  int padf = 0;

  if (bto64[0] == '\0')
    memcpy(bto64, _(bto64_0), 65);
  
  if (line[len-1] == '\n') {
    if ((len+commlen) > 2047) return S_FALSE;
  } else {
    if ((len+commlen) > 2046) return S_FALSE;
  }

  for (i = 0; i < len; ++i)
    {
      if (isspace(line[i])) {
	/* signify will skip arbitrary space, using isspace() */
	continue;
      }
      if (line[i] == '=') {
	if (padf > 1) /* more than two padding '=' */
	  return S_FALSE;
	else
	  ++padf;
      } else if (!strchr(bto64, line[i]) || (line[i] == '=' && padf > 0)) {
	return S_FALSE;
      }
      if (j < sizeof(cmp)) {
	  cmp[j] = line[i]; ++j;
      }
    }

  /* signature is 'Ed' + 8 byte random + 64 bytes = 74 bytes
   * => 1 pad byte => 75 bytes => 100 b64 bytes */
  if (j != 100 || padf != 1)
    return S_FALSE;
  
  cmp[j] = '\0'; /* j == 100 */
  sh_util_base64_dec((unsigned char *) out, (unsigned char *) cmp, j);
  if(out[0] == 'E' && out[1] == 'd')
    return S_TRUE;
  
  return S_FALSE;
}
static
int sh_signify_msg_start(const char * line)
{
  static int  step = 0;
  static size_t commlen = 0;

  if (step == 0) {
    if (S_TRUE == sh_signify_comp_comm(line, &commlen))
      ++step;
  }
  else if (step == 1) {
    if (S_TRUE == sh_signify_comp_sig(line, commlen)) {
      ++step;
    }
    else {
      step = 0; commlen = 0;
    }
  }
  else if (step == 2) {
    step = 0; commlen = 0;
    return S_TRUE;
  }
  return S_FALSE;
}

static
int sh_signify_msg_startdata(const char * line)
{
  (void) line;
  return S_TRUE;
}
 
static
int sh_signify_msg_end(const char * line)
{
  if (line[0] != '\0')
    return S_FALSE;
  return S_TRUE;
}

static
int sh_signify_data_end(const char * line)
{
  if (line[0] == '[' && line[1] == 'E' && line[2] == 'O' &&
      line[3] == 'F' && line[4] == ']')
    return S_TRUE;
  else if (line[0] != '\0')
    return S_FALSE;
  return S_TRUE;
}

static
SL_TICKET sh_signify_extract_signed(SL_TICKET fd, extractlevel extract_level)
{
  const  int fgets_buf_size = 16384;
  FILE * fin_cp = NULL;
  char * buf    = NULL;
  int    bufc;
  char * comment = NULL;
  size_t commlen = 0;
  
  int    flag_comm = S_FALSE;
  int    flag_sig  = S_FALSE;
  SL_TICKET fdTmp  = (-1);
  SL_TICKET open_tmp  (void);
  int       close_tmp ( SL_TICKET);

  /* extract the data and copy to temporary file
   */
  fdTmp = open_tmp();
  if (SL_ISERROR(fdTmp))
    {
      dlog(1, FIL__, __LINE__, _("Error opening temporary file.\n")); 
      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, 
		      _("Error opening temporary file."), 
		      _("sh_signify_extract_signed"));
      return -1;
    }

  fin_cp = fdopen(dup(get_the_fd(fd)), "rb");
  if (!fin_cp) { close_tmp(fdTmp); return -1; }
  buf = SH_ALLOC(fgets_buf_size);
  
  while (NULL != fgets(buf, fgets_buf_size, fin_cp))
    {
      
      bufc = 0; 
      while (bufc < fgets_buf_size) { 
	if (buf[bufc] == '\n') { ++bufc; break; }
	++bufc;
      }

      if (flag_comm == S_FALSE)
	{
	  if (sh_signify_comp_comm(buf, &commlen) == S_TRUE)
	    {
	      flag_comm = S_TRUE;
	      if (extract_level == SIG_DATASIG)
		{
		  comment = sh_util_strdup(buf);
		  commlen = bufc;
		}
	    }
	  continue;
	}
      else if (flag_comm == S_TRUE && flag_sig == S_FALSE)
	{
	  if (sh_signify_comp_sig(buf, commlen) == S_TRUE)
	    {
	      flag_sig = S_TRUE;
	      if (extract_level == SIG_DATASIG)
		{
		  sl_write(fdTmp, comment, commlen);
		  sl_write(fdTmp, buf, bufc);
		}
	      if (comment != NULL)
		SH_FREE(comment);
	      comment = NULL;
	    }
	  else
	    {
	      if (comment != NULL)
		SH_FREE(comment);
	      comment = NULL; commlen = 0; flag_comm = 0;
	    }
	  continue;
	}
      
      if (flag_sig == S_TRUE)
	{
	  sl_write(fdTmp, buf, bufc); 
	}
    }
  if (comment != NULL)
    SH_FREE(comment);
  sl_fclose(FIL__, __LINE__, fin_cp);
  sl_rewind (fdTmp);

#if defined(SH_DEBUG_SIGNIFY)
  fin_cp = fdopen(dup(get_the_fd(fdTmp)), "rb");
  FILE * fout = fopen("xxx.out", "w+");
  if (!fout) { close_tmp(fdTmp); return -1; }
  if (!fin_cp) { close_tmp(fdTmp); fclose(fout); return -1; }
  while (NULL != fgets(buf, fgets_buf_size, fin_cp))
    {
      fputs(buf, fout);
    }
  fclose(fout);
  sl_rewind(fdTmp);
#endif
  
  SH_FREE(buf);
  return fdTmp;
}


static FILE * sh_signify_popen (sh_gpg_popen_t  *source, int fd, char * homedir)
{
  char   path[256];
  char   cc1[32];
  char   cc2[32];
  char   cc3[32];
  char   cc4[SH_PATHBUF+32];
  char   cc5[32];
  char   cc6[32];
  char * argv[9];
  FILE * retval = NULL;

  struct stat lbuf;
  int         status_stat = 0;

#ifdef HAVE_SIG_KEY_HASH
  SL_TICKET checkfd;
#endif


  SL_ENTER(_("sh_signify_popen"));

  sl_strlcpy (path,  DEFAULT_SIG_PATH,  256);

  sl_strlcpy (cc1,   _("-Vem"),         32);
  sl_strlcpy (cc2,   _("/dev/null"),    32);

  sl_strlcpy (cc3,   _("-p"),           32);
  sl_strlcpy (cc4,   homedir,           SH_PATHBUF+32);
  sl_strlcat (cc4,   _("/.signify/"),   SH_PATHBUF+32);
  sl_strlcat (cc4,   SH_INSTALL_NAME,   SH_PATHBUF+32);
  sl_strlcat (cc4,   _(".pub"),         SH_PATHBUF+32);

  /* read signed message from stdin */
  sl_strlcpy (cc5,   _("-x"),           32);
  sl_strlcpy (cc6,   _("-"),            32);

  status_stat =  retry_lstat(FIL__, __LINE__, cc4, &lbuf);
  if (status_stat == -1)
    {
      dlog(1, FIL__, __LINE__, 
	   _("Signify public key %s\ndoes not exist or is not accessible.\nPlease add the directory and put the key there\nto allow signature verification.\n"),
	   cc4);
      sh_error_handle((-1), FIL__, __LINE__, status_stat, MSG_EXIT_ABORT1, 
		      sh.prg_name);
      aud_exit (FIL__, __LINE__, EXIT_FAILURE);
    }
#ifdef HAVE_SIG_KEY_HASH
  checkfd = sl_open_read(FIL__, __LINE__, cc4, SL_YESPRIV);

  if (0 != sh_sig_checksum(checkfd, SIG_HASH_OTHER, SIG_KEY_HASH, cc4))
    {
      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, 
		      _("Checksum mismatch for signify public key"), 
		      _("signify_popen"));
      sl_close(checkfd);
      sh_error_handle((-1), FIL__, __LINE__, status_stat, MSG_EXIT_ABORT1, 
		      sh.prg_name);
      aud_exit (FIL__, __LINE__, EXIT_FAILURE);
    }
  sl_close(checkfd);
#endif
 
  argv[0] = path; 
  argv[1] = cc1;
  argv[2] = cc2;
  argv[3] = cc3;
  argv[4] = cc4;
  argv[5] = cc5;
  argv[6] = cc6;
  argv[7] = NULL;

  retval = sh_sig_popen(argv, source, fd);
  SL_RETURN((retval), _("sh_signify_popen")); 
}

static
int sh_signify_check_file_sign(int fd, char * homedir)
{
  struct stat buf;
  char line[256];
  sh_gpg_popen_t  source;
  int status = 0;
  unsigned int n_goodsig  = 0;
  unsigned int n_lines    = 0;
  
#ifdef HAVE_SIG_CHECKSUM
  SL_TICKET checkfd;
#endif

  SL_ENTER(_("sh_signify_check_file_sign"));

  /* check whether signify exists and has the correct checksum
   */
  TPT(((0), FIL__, __LINE__, _("msg=<Check signature>\n")));
  TPT(((0), FIL__, __LINE__, _("msg=<signify is %s>\n"), DEFAULT_SIG_PATH));

  if (0 != retry_lstat(FIL__, __LINE__, DEFAULT_SIG_PATH, &buf))
    {
      char errbuf[SH_ERRBUF_SIZE];

      status = errno;
      sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, status, MSG_ERR_LSTAT,
		      sh_error_message(status, errbuf, sizeof(errbuf)), DEFAULT_SIG_PATH);
      SL_RETURN( SH_SIG_BAD, _("sh_signify_check_file_sign"));
    }

  if (0 != tf_trust_check (DEFAULT_SIG_PATH, SL_YESPRIV))
    SL_RETURN( SH_SIG_BAD, _("sh_signify_check_file_sign"));

#ifdef HAVE_SIG_CHECKSUM
  checkfd = sl_open_read(FIL__, __LINE__, DEFAULT_SIG_PATH, SL_YESPRIV);

  if (0 != sh_sig_checksum(checkfd, SIG_HASH_REPORTFULL, NULL, NULL))
    {
      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, 
		      _("Checksum mismatch"), 
		      _("signify_check_file_sign"));
      sl_close(checkfd);
      SL_RETURN( SH_SIG_BAD, _("sh_signify_check_file_sign"));
    }
  sl_close(checkfd);
#endif

  TPT(((0), FIL__, __LINE__, _("msg=<Open pipe to check signature>\n")));

  fflush(NULL);
 
  source.pipe   = sh_signify_popen  ( &source, fd, homedir );

  if (NULL == source.pipe)
    {
      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, 
		      _("Could not open pipe"), 
		      _("signify_check_file_sign"));
      SL_RETURN( SH_SIG_BAD, _("sh_signify_check_file_sign"));
    }

  TPT(((0), FIL__, __LINE__, _("msg=<Open pipe success>\n")));

 xagain:

  errno = 0;

  while (NULL != fgets(line, sizeof(line), source.pipe))
    {
      TPT(((0), FIL__, __LINE__, _("msg=<signify out: %s>\n"), line));
      if (line[strlen(line)-1] == '\n')
	line[strlen(line)-1] = ' ';
      sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, 0, MSG_E_SUBGEN, 
		      line, 
		      _("signify_check_file_sign"));

      ++n_lines;

      /* the '\n' has been replaced with ' ' for logging */ 
      if (0 == sl_strcmp(_("Signature Verified "), line))
	{
	  ++n_goodsig;
	} 
    }

  if (ferror(source.pipe) && errno == EAGAIN) 
    {
      /* sleep 10 ms to avoid starving the gpg child writing to the pipe */
      retry_msleep(0,10); 
      clearerr(source.pipe);
      goto xagain;
    }
 
  if (0 != sh_sig_pclose (&source))
    {
	sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, 
			_("Error on closing process pipe"), 
			_("signify_check_file_sign"));
	n_goodsig = 0;
    }

  TPT(((0), FIL__, __LINE__, _("msg=<Close pipe>\n")));

  if (n_goodsig == 1 && n_lines == 1)
    {
      TPT(((0), FIL__, __LINE__, _("msg=<Signature Verified>\n")));
      SL_RETURN( SH_SIG_OK, _("sh_signature_check_file_sign"));
    }
  else
    {
      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, 
		      _("Error verifying file signature"), 
		      _("signify_check_file_sign"));
    }
  SL_RETURN( SH_SIG_BADSIGN, _("sh_signature_check_file_sign"));
}


int sh_signify_check_signature (SL_TICKET file, ShSigFile what)
{
  int status = SH_SIG_BAD;
  int fd = 0;

  static int smsg = S_FALSE;

  char  * homedir = sh.effective.home;
  char  * home_alloc = NULL;
#if defined(SH_WITH_SERVER)
  struct passwd * tempres;
#if defined(USE_GETPWNAM_R)
  struct passwd    pwd;
  char           * buffer = SH_ALLOC(SH_PWBUF_SIZE);
#endif
#endif

  SL_ENTER(_("sh_signify_check_sign"));

  (void) what;
  
  fd = get_the_fd(file);

  if (fd < 0)
    {
      TPT(((0), FIL__, __LINE__, _("msg=<GPG_CHECK: FD = %d>\n"), fd));
      dlog(1, FIL__, __LINE__, 
	   _("This looks like an unexpected internal error.\n"));
#if defined(SH_WITH_SERVER) && defined(USE_GETPWNAM_R)
      SH_FREE(buffer);
#endif
      SL_RETURN( (-1), _("sh_signify_check_sign"));
    }
  
#if defined(SH_WITH_SERVER)
#if defined(USE_GETPWNAM_R)
  sh_getpwnam_r(DEFAULT_IDENT, &pwd, buffer, SH_PWBUF_SIZE, &tempres);
#else
  tempres = sh_getpwnam(DEFAULT_IDENT);
#endif
  if ((tempres != NULL) && (0 == sl_ret_euid()))
    {
      /* privileges not dropped yet*/
      homedir = tempres->pw_dir;
    }
#endif

  home_alloc = sh_util_strdup(homedir);
  
  TPT(((0), FIL__, __LINE__, _("msg=<SIGNIFY_CHECK: FD = %d>\n"), fd));
  status = sh_signify_check_file_sign(fd, homedir);
  
  if (status != SH_SIG_OK) 
    {
      TPT(((0), FIL__, __LINE__, _("msg=<Status = %d>\n"), status));
      dlog(1, FIL__, __LINE__, 
	   _("The signature of the configuration file or the file signature database\ncould not be verified. Possible reasons are:\n - signify binary (%s) not found\n - invalid signature\n - there is no keyfile in %s/.signify/%s.pub, or\n - the file is not signed - did you move /filename.sig to /filename ?\nTo create a signed file, use (remove old signatures before):\n   signify|signify-openbsd -Se -s KEYNAME.sec -m FILE\n   mv FILE.sig FILE\n"),
	   DEFAULT_SIG_PATH, home_alloc, SH_INSTALL_NAME);
      SH_FREE(home_alloc);
      SL_RETURN( (-1), _("sh_signify_check_sign"));
    }

  if (smsg == S_FALSE)
    {
      sh_sig_fill_startup (__LINE__,
			   sh.prg_name, sh.real.uid,
			   (sh.flag.hidefile == S_TRUE) ? 
			   _("(hidden)") : file_path('C', 'R'), 
			   NULL,  NULL);
    }
  smsg = S_TRUE;

  SH_FREE(home_alloc);
  SL_RETURN(0, _("sh_signify_check_sign"));
}  

/* This is GPG specific stuff 
 */
#elif defined(WITH_GPG)
static FILE * sh_gpg_popen (sh_gpg_popen_t  *source, int fd, char * homedir)
{
  char   path[256];
  char   cc1[32];
  char   cc2[32];

  char   cc0[2] = "-";
  char   cc3[32];
  char   cc4[SH_PATHBUF+32];
  char   cc5[32];
  char * argv[9];
  FILE * retval = NULL;


  SL_ENTER(_("sh_gpg_popen"));

  /* -- GnuPG -- */
  sl_strlcpy (path,  DEFAULT_SIG_PATH,  256);
  sl_strlcpy (cc1,   _("--status-fd"),  32);
  sl_strlcpy (cc2,   _("--verify"),     32);
  sl_strlcpy (cc3,   _("--homedir"),    32);
  /* sl_strlcpy (cc4,   sh.effective.home, SH_PATHBUF+32); */
  sl_strlcpy (cc4,   homedir,           SH_PATHBUF+32);
  sl_strlcat (cc4,   _("/.gnupg"),      SH_PATHBUF+32);
  sl_strlcpy (cc5,   _("--no-tty"),     32);

#if defined(SH_WITH_SERVER)
  if (0 == sl_ret_euid())   /* privileges not dropped yet */
    {
      struct stat lbuf;
      int         status_stat = 0;
#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETPWNAM_R)
      struct passwd    pwd;
      char          *  buffer = SH_ALLOC(SH_PWBUF_SIZE);
      struct passwd *  tempres;
      sh_getpwnam_r(DEFAULT_IDENT, &pwd, buffer, SH_PWBUF_SIZE, &tempres);
#else
      struct passwd * tempres = sh_getpwnam(DEFAULT_IDENT);
#endif

      if (!tempres)
	{
	  dlog(1, FIL__, __LINE__, 
	       _("User %s does not exist. Please add the user to your system.\n"), 
	       DEFAULT_IDENT);
	  status_stat = -1;
	}
      if (!tempres->pw_dir || tempres->pw_dir[0] == '\0')
	{
	  dlog(1, FIL__, __LINE__, 
	       _("User %s does not have a home directory.\nPlease add the home directory for this user to your system.\n"), 
	       DEFAULT_IDENT);
	  status_stat = -2;
	}
      if (status_stat == 0)
	{
	  sl_strlcpy (cc4, tempres->pw_dir, SH_PATHBUF+32); 
	  sl_strlcat (cc4,   _("/.gnupg"),      SH_PATHBUF+32); 
	  status_stat =  retry_lstat(FIL__, __LINE__, cc4, &lbuf);
	  if (status_stat == -1)
	    {
	      dlog(1, FIL__, __LINE__, 
		   _("Gnupg directory %s for user %s\ndoes not exist or is not accessible.\nPlease add the directory and put the keyring (pubring.gpg or pubring.kbx) there\nto verify the configuration file.\n"),
		   cc4, DEFAULT_IDENT);
	      status_stat = -3;
	    }
	}
      if (status_stat == 0 && lbuf.st_uid != tempres->pw_uid)
	{
	  dlog(1, FIL__, __LINE__, 
	       _("Gnupg directory %s\nis not owned by user %s.\n"), 
	       cc4, DEFAULT_IDENT);
	  status_stat = -4;
	}
      if (status_stat == 0)
	{
	  char   cc4_test[SH_PATHBUF+32];

	  sl_strlcpy(cc4_test, cc4, SH_PATHBUF+32);
	  sl_strlcat (cc4_test,   _("/pubring.gpg"),      SH_PATHBUF+32); 

	  status_stat =  retry_lstat(FIL__, __LINE__, cc4_test, &lbuf);
	  if (status_stat == -1)
	    {
	      sl_strlcpy(cc4_test, cc4, SH_PATHBUF+32);
	      sl_strlcat (cc4_test,   _("/pubring.kbx"),      SH_PATHBUF+32);

	      status_stat =  retry_lstat(FIL__, __LINE__, cc4_test, &lbuf);
	      if (status_stat == -1)
		{
		  sl_strlcpy(cc4_test, cc4, SH_PATHBUF+32);
		  sl_strlcat (cc4_test,   _("/pubring.(gpg|kbx)"),      SH_PATHBUF+32);
		  
		  dlog(1, FIL__, __LINE__, 
		       _("Gnupg public keyring %s for user %s\ndoes not exist or is not accessible.\nPlease add the directory and put the keyring (pubring.gpg or pubring.kbx) there\nto verify the configuration file.\n"),
		       cc4_test, DEFAULT_IDENT);
		  status_stat = -5;
		}
	    }
	}
      if (status_stat == 0 && lbuf.st_uid != tempres->pw_uid)
	{
	  dlog(1, FIL__, __LINE__, 
	       _("Gnupg public keyring %s\nis not owned by user %s.\n"), 
	       cc4, DEFAULT_IDENT);
	  status_stat = -6;
	}
      if (status_stat != 0)
	{
	  sh_error_handle((-1), FIL__, __LINE__, status_stat, MSG_EXIT_ABORT1, 
			  sh.prg_name);
	  aud_exit (FIL__, __LINE__, EXIT_FAILURE);
	}
      sl_strlcpy (cc4, tempres->pw_dir, SH_PATHBUF+32); 
      sl_strlcat (cc4,   _("/.gnupg"),      SH_PATHBUF+32); 
#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETPWNAM_R)
      SH_FREE(buffer);
#endif
    }
#endif

  argv[0] = path; 
  argv[1] = cc1;
  argv[2] = "1";
  argv[3] = cc2;
  argv[4] = cc3;
  argv[5] = cc4;
  argv[6] = cc5;
  argv[7] = cc0;
  argv[8] = NULL;

  retval = sh_sig_popen(argv, source, fd);
  SL_RETURN((retval), _("sh_gpg_popen")); 
}

static
int sh_gpg_check_file_sign(int fd, char * sign_id, char * sign_fp, 
			   char * homedir, ShSigFile whichfile)
{
  struct stat buf;
  char line[256];
  sh_gpg_popen_t  source;
  int have_id = BAD, have_fp = BAD, status = 0;
  unsigned int n_newsig   = 0;
  unsigned int n_goodsig  = 0;
  unsigned int n_validsig = 0;
  
#ifdef HAVE_SIG_CHECKSUM
  SL_TICKET checkfd;
#endif

  SL_ENTER(_("sh_gpg_check_file_sign"));

  /* check whether GnuPG exists and has the correct checksum
   */
  TPT(((0), FIL__, __LINE__, _("msg=<Check signature>\n")));
  TPT(((0), FIL__, __LINE__, _("msg=<gpg is %s>\n"), DEFAULT_SIG_PATH));

  if (0 != retry_lstat(FIL__, __LINE__, DEFAULT_SIG_PATH, &buf))
    {
      char errbuf[SH_ERRBUF_SIZE];

      status = errno;
      sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, status, MSG_ERR_LSTAT,
		      sh_error_message(status, errbuf, sizeof(errbuf)), DEFAULT_SIG_PATH);
      SL_RETURN( SH_SIG_BAD, _("sh_gpg_check_file_sign"));
    }

  if (0 != tf_trust_check (DEFAULT_SIG_PATH, SL_YESPRIV))
    SL_RETURN( SH_SIG_BAD, _("sh_gpg_check_file_sign"));

#ifdef HAVE_SIG_CHECKSUM
  checkfd = sl_open_read(FIL__, __LINE__, DEFAULT_SIG_PATH, SL_YESPRIV);

  if (0 != sh_sig_checksum(checkfd, SIG_HASH_REPORTFULL, NULL, NULL))
    {
      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, 
		      _("Checksum mismatch"), 
		      _("gpg_check_file_sign"));
      sl_close(checkfd);
      SL_RETURN( SH_SIG_BAD, _("sh_gpg_check_file_sign"));
    }
  sl_close(checkfd);
#endif

  TPT(((0), FIL__, __LINE__, _("msg=<Open pipe to check signature>\n")));

  fflush(NULL);
 
  source.pipe   = sh_gpg_popen  ( &source, fd, homedir );

  if (NULL == source.pipe)
    {
      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, 
		      _("Could not open pipe"), 
		      _("gpg_check_file_sign"));
      SL_RETURN( SH_SIG_BAD, _("sh_gpg_check_file_sign"));
    }

  TPT(((0), FIL__, __LINE__, _("msg=<Open pipe success>\n")));

 xagain:

  errno = 0;

  while (NULL != fgets(line, sizeof(line), source.pipe))
    {

      TPT(((0), FIL__, __LINE__, _("msg=<gpg out: %s>\n"), line));
      if (line[strlen(line)-1] == '\n')
	line[strlen(line)-1] = ' ';
      sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, 0, MSG_E_SUBGEN, 
		      line, 
		      _("gpg_check_file_sign"));

      if (sl_strlen(line) < 12) 
	continue;

      /* Sun May 27 18:40:05 CEST 2001
       */
      if (0 == sl_strncmp(_("BADSIG"),     &line[9], 6) ||
	  0 == sl_strncmp(_("ERRSIG"),     &line[9], 6) ||
	  0 == sl_strncmp(_("NO_PUBKEY"),  &line[9], 6) ||
	  0 == sl_strncmp(_("NODATA"),     &line[9], 6) ||
	  0 == sl_strncmp(_("ERROR"),      &line[9], 5) ||
	  0 == sl_strncmp(_("SIGEXPIRED"), &line[9], 6))
	{
	  if      (0 == sl_strncmp(_("BADSIG"), &line[9], 6)) {
	    dlog(1, FIL__, __LINE__, 
		 _("%s file is signed, but the signature is invalid."),
		 ((whichfile == SIG_CONF) ? _("Configuration") : _("Database")));
	  } 
	  else if (0 == sl_strncmp(_("NO_PUBKEY"), &line[9], 6)) {
	    dlog(1, FIL__, __LINE__, 
		 _("%s file is signed, but the public key to verify the signature is not in my keyring %s/.gnupg/pubring.asc."), 
		 ((whichfile == SIG_CONF) ? _("Configuration") : _("Database")),
		 homedir);
	  }
	  else if (0 == sl_strncmp(_("ERRSIG"), &line[9], 6)) {
	    dlog(1, FIL__, __LINE__, 
		 _("%s file is signed, but the public key to verify the signature is not in my keyring %s/.gnupg/pubring.asc."), 
		 ((whichfile == SIG_CONF) ? _("Configuration") : _("Database")),
		 homedir);
	  }
	  else if (0 == sl_strncmp(_("SIGEXPIRED"), &line[9], 6)) {
	    dlog(1, FIL__, __LINE__, 
		 _("%s file is signed, but the public key to verify the signature has expired."), 
		 ((whichfile == SIG_CONF) ? _("Configuration") : _("Database")));
	  }
	  else if (0 == sl_strncmp(_("NODATA"), &line[9], 6)) {
	    dlog(1, FIL__, __LINE__, 
		 _("%s file is not signed."), 
		 ((whichfile == SIG_CONF) ? _("Configuration") : _("Database")));
	  }
	  else if (0 == sl_strncmp(_("ERROR"), &line[9], 5)) {
	    dlog(1, FIL__, __LINE__, 
		 _("%s file is not correctly signed. An error occured while verifying the signature."), 
		 ((whichfile == SIG_CONF) ? _("Configuration") : _("Database")));
	  }

	  have_fp = BAD; have_id = BAD;
	  break;
	}
      if (0 == sl_strncmp(_("GOODSIG"), &line[9], 7))
	{
	  ++n_goodsig;
	  sl_strlcpy (sign_id, &line[25], SH_MINIBUF+1);
	  if (sign_id)
	    sign_id[sl_strlen(sign_id)-1] = '\0';  /* remove trailing '"' */
	  have_id = GOOD;
	} 
      else if (0 == sl_strncmp(_("VALIDSIG"), &line[9], 8))
	{
	  ++n_validsig;
	  strncpy (sign_fp, &line[18], 40);
	  sign_fp[40] = '\0';
	  have_fp = GOOD;
	}
      else if (0 == sl_strncmp(_("NEWSIG"), &line[9], 6))
	{
	  ++n_newsig;
	}
      
    }

  if (ferror(source.pipe) && errno == EAGAIN) 
    {
      /* sleep 10 ms to avoid starving the gpg child writing to the pipe */
      retry_msleep(0,10); 
      clearerr(source.pipe);
      goto xagain;
    }
 
  if (0 != sh_sig_pclose (&source))
    {
	sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, 
			_("Error on closing process pipe"), 
			_("gpg_check_file_sign"));
	have_id = BAD;
    }

  TPT(((0), FIL__, __LINE__, _("msg=<Close pipe>\n")));

  if (n_goodsig != n_validsig || n_newsig > 1 || n_goodsig > 1)
    {
	sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, 
			_("Too many or invalid signatures"), 
			_("gpg_check_file_sign"));
	have_id = BAD;
    }
  
  if (have_id == GOOD)
    {
      TPT(((0), FIL__, __LINE__, _("msg=<Got signator ID>\n")));
    }
  if (have_fp == GOOD)
    {
      TPT(((0), FIL__, __LINE__, _("msg=<Got fingerprint>\n")));
    }

  if (have_id == GOOD && have_fp == GOOD)
    SL_RETURN( SH_SIG_OK, _("sh_gpg_check_file_sign"));
  else
    {
      if (have_id == BAD)
	sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, 
			_("No good signature"), 
			_("gpg_check_file_sign"));
      else
	sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, 
			_("No fingerprint for key"), 
			_("gpg_check_file_sign"));
      SL_RETURN( SH_SIG_BADSIGN, _("sh_gpg_check_file_sign"));
    }
}

#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && \
  defined(HAVE_GETPWNAM_R)
#define USE_GETPWNAM_R 1
#endif

static
int sh_gpg_check_signature (SL_TICKET file, ShSigFile what)
{
  int status = SH_SIG_BAD;
  int fd = 0;

  static int smsg = S_FALSE;
  char  * tmp;

  char  * sig_id;
  char  * sig_fp;

  char  * homedir = sh.effective.home;
#if defined(SH_WITH_SERVER)
  struct passwd * tempres;
#if defined(USE_GETPWNAM_R)
  struct passwd    pwd;
  char           * buffer = SH_ALLOC(SH_PWBUF_SIZE);
#endif
#endif

#ifdef USE_FINGERPRINT
#include "sh_gpg_fp.h"
#endif

  SL_ENTER(_("sh_gpg_check_sign"));


  if (what == SIG_CONF)
    fd = get_the_fd(file);
  if (what == SIG_DATA)
    fd = get_the_fd(file);


  if (fd < 0)
    {
      TPT(((0), FIL__, __LINE__, _("msg=<GPG_CHECK: FD = %d>\n"), fd));
      dlog(1, FIL__, __LINE__, 
	   _("This looks like an unexpected internal error.\n"));
#if defined(SH_WITH_SERVER) && defined(USE_GETPWNAM_R)
      SH_FREE(buffer);
#endif
      SL_RETURN( (-1), _("sh_gpg_check_sign"));
    }
  
#if defined(SH_WITH_SERVER)
#if defined(USE_GETPWNAM_R)
      sh_getpwnam_r(DEFAULT_IDENT, &pwd, buffer, SH_PWBUF_SIZE, &tempres);
#else
      tempres = sh_getpwnam(DEFAULT_IDENT);
#endif
      if ((tempres != NULL) && (0 == sl_ret_euid()))
	{
	  /* privileges not dropped yet*/
	  homedir = tempres->pw_dir;
	}
#endif

  if (what == SIG_CONF)
    {
      TPT(((0), FIL__, __LINE__, _("msg=<GPG_CHECK: FD = %d>\n"), fd));
      status = sh_gpg_check_file_sign(fd, gp.conf_id, gp.conf_fp, homedir, SIG_CONF);
      TPT(((0), FIL__, __LINE__, _("msg=<CONF SIGUSR: |%s|>\n"), gp.conf_id));
      TPT(((0), FIL__, __LINE__, _("msg=<CONF SIGFP:  |%s|>\n"), gp.conf_fp));
      sig_id =  gp.conf_id; sig_fp = gp.conf_fp;
    }

  if (what == SIG_DATA)
    {
      TPT(((0), FIL__, __LINE__, _("msg=<GPG_CHECK: FD = %d>\n"), fd));
      status = sh_gpg_check_file_sign(fd, gp.data_id, gp.data_fp, homedir, SIG_DATA);
      TPT(((0), FIL__, __LINE__, _("msg=<DATA SIGUSR: |%s|>\n"), gp.data_id));
      TPT(((0), FIL__, __LINE__, _("msg=<DATA SIGFP:  |%s|>\n"), gp.data_fp));
      sig_id =  gp.data_id; sig_fp = gp.data_fp;
    }
  
  if (SH_SIG_OK == status)
    {
#ifdef USE_FINGERPRINT
      if ((sl_strcmp(SH_GPG_FP, sig_fp) == 0))
	{
	  int i;

	  for(i = 0; i < (int) sl_strlen(sig_fp); ++i) {
	      if (gpgfp[i] != sig_fp[i]) {
		sh_error_handle(SH_ERR_SEVERE, FIL__, __LINE__, 0, 
				MSG_E_GPG_FP, gpgfp, sig_fp);
		break; }
	  }

	  if (smsg == S_FALSE) {
	    tmp  = sh_util_safe_name(sig_id);
	    sh_sig_fill_startup (__LINE__, sh.prg_name, sh.real.uid,
				 (sh.flag.hidefile == S_TRUE) ? 
				 _("(hidden)") : file_path('C', 'R'), 
				 tmp, 
				 sig_fp);
	    SH_FREE(tmp); }
	  smsg = S_TRUE;

#if defined(SH_WITH_SERVER) && defined(USE_GETPWNAM_R)
	  SH_FREE(buffer);
#endif
	  SL_RETURN(0, _("sh_gpg_check_sign"));
	}
      else
	{
	  /* fp mismatch */
	  dlog(1, FIL__, __LINE__, 
	       _("The fingerprint of the signing key: %s\ndoes not match the compiled-in fingerprint: %s.\nTherefore the signature could not be verified.\n"), 
	       sig_fp, SH_GPG_FP);
	  sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, 
		      _("Fingerprint mismatch"), _("gpg_check_sign"));
	  status = SH_SIG_BADSIGN;
	}
#else /* ifdef USE_FINGERPRINT */
      if (smsg == S_FALSE)
	{
	  tmp = sh_util_safe_name(sig_id);
	  sh_sig_fill_startup (__LINE__,
			       sh.prg_name, sh.real.uid,
			       (sh.flag.hidefile == S_TRUE) ? 
			       _("(hidden)") : file_path('C', 'R'), 
			       tmp,  sig_fp);
	  SH_FREE(tmp);
	}
      smsg = S_TRUE;

#if defined(SH_WITH_SERVER) && defined(USE_GETPWNAM_R)
      SH_FREE(buffer);
#endif

      /* status == OK and no fp checking */
      SL_RETURN(0, _("sh_gpg_check_sign"));
#endif /* !ifdef USE_FINGERPRINT */
    }

  if (status != SH_SIG_OK) 
    {
      uid_t   e_uid  = sl_ret_euid();
      char  * e_home = sh.effective.home;

#if defined(SH_WITH_SERVER)
#if defined(USE_GETPWNAM_R)
      struct passwd    e_pwd;
      char          *  e_buffer = SH_ALLOC(SH_PWBUF_SIZE);
      struct passwd *  e_tempres;
      sh_getpwnam_r(DEFAULT_IDENT, &e_pwd, e_buffer, SH_PWBUF_SIZE, &e_tempres);
#else
      struct passwd * e_tempres = sh_getpwnam(DEFAULT_IDENT);
#endif

      if ((e_tempres != NULL) && (0 == sl_ret_euid()))   
	{
	  /* privileges not dropped yet */
	  e_uid  = e_tempres->pw_uid;
	  e_home = e_tempres->pw_dir;
	}
#endif
      dlog(1, FIL__, __LINE__, 
	   _("The signature of the configuration file or the file signature database\ncould not be verified. Possible reasons are:\n - gpg binary (%s) not found\n - invalid signature\n - the signature key is not in the private keyring of UID %d,\n - there is no keyring in %s/.gnupg, or\n - the file is not signed - did you move /filename.asc to /filename ?\nTo create a signed file, use (remove old signatures before):\n   gpg -a --clearsign --not-dash-escaped FILE\n   mv FILE.asc FILE\n"),
	   DEFAULT_SIG_PATH,
	   (int) e_uid, e_home);

#if defined(SH_WITH_SERVER) && defined(USE_GETPWNAM_R)
      SH_FREE(e_buffer);
#endif
    }

  TPT(((0), FIL__, __LINE__, _("msg=<Status = %d>\n"), status));

  SL_RETURN(-1, _("sh_gpg_check_sign")); /* make compiler happy */
}  

static int sh_gpg_comp(const char * line, const char * cmp)
{
  int retval = S_FALSE;

  if (line && line[0] == '-' && line[1] == '-')
    {
      char * dup = sh_util_strdup(line);
      char * tmp = dup + sl_strlen( dup );
      --tmp;
      if (*tmp == '\n') { *tmp = '\0'; --tmp; }
      while( (*tmp == '\t' || *tmp == ' ' || *tmp == '\r' ) && tmp >= dup ) *tmp-- = '\0';

      if (0 == sl_strcmp(dup, cmp))
	retval = S_TRUE;
      SH_FREE(dup);
    }
  return retval;
}

static
int sh_gpg_msg_start(const char * line)
{
  static char cmp[SH_MINIBUF];
  static int  initialized = 0;

  if (initialized == 0) {
    sl_strlcpy(cmp, _("-----BEGIN PGP SIGNED MESSAGE-----"), sizeof(cmp));
    initialized = 1;
  }
  return sh_gpg_comp(line, cmp);
}

static
int sh_gpg_msg_startdata(const char * line)
{
  if (line[0] == '\n')
    return S_TRUE;
  return S_FALSE;
}

static
int sh_gpg_msg_end(const char * line)
{
  static char cmp[SH_MINIBUF];
  static int  initialized = 0;

  if (initialized == 0) {
    sl_strlcpy(cmp, _("-----BEGIN PGP SIGNATURE-----"), sizeof(cmp));
    initialized = 1;
  }
  return sh_gpg_comp(line, cmp);
}

static
int sh_gpg_sig_end(const char * line)
{
  static char cmp[SH_MINIBUF];
  static int  initialized = 0;

  if (initialized == 0) {
    sl_strlcpy(cmp, _("-----END PGP SIGNATURE-----"), sizeof(cmp));
    initialized = 1;
  }
  return sh_gpg_comp(line, cmp);
}

static
SL_TICKET sh_gpg_extract_signed(SL_TICKET fd, extractlevel extract_level)
{
  const  int fgets_buf_size = 16384;
  FILE * fin_cp = NULL;
  char * buf    = NULL;
  int    bufc;
  int    flag_pgp    = S_FALSE;
  int    flag_nohead = S_FALSE;
  SL_TICKET fdTmp = (-1);
  SL_TICKET open_tmp  (void);
  int       close_tmp ( SL_TICKET);
  
  /* extract the data and copy to temporary file
   */
  fdTmp = open_tmp();
  if (SL_ISERROR(fdTmp))
    {
      dlog(1, FIL__, __LINE__, _("Error opening temporary file.\n")); 
      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, 
		      _("Error opening temporary file."), 
		      _("sh_gpg_extract_signed"));
      return -1;
    }

  fin_cp = fdopen(dup(get_the_fd(fd)), "rb");
  if (!fin_cp) { close_tmp(fdTmp); return -1; }
  buf = SH_ALLOC(fgets_buf_size);
  
  while (NULL != fgets(buf, fgets_buf_size, fin_cp))
    {
      bufc = 0; 
      while (bufc < fgets_buf_size) { 
	if (buf[bufc] == '\n') { ++bufc; break; }
	++bufc;
      }

      if (flag_pgp == S_FALSE && sh_gpg_msg_start(buf) == S_TRUE)
	{
	  flag_pgp = S_TRUE;
	  if (extract_level == SIG_DATASIG)
	    sl_write(fdTmp, buf, bufc);
	  continue;
	}
      
      if (flag_pgp == S_TRUE && flag_nohead == S_FALSE)
	{
	  /* Header finished */
	  if (buf[0] == '\n')
	    {
	      flag_nohead = S_TRUE;
	      if (extract_level == SIG_DATASIG)
		sl_write(fdTmp, buf, 1);
	      continue;
	    }
	  /* copy these headers */
	  else if (0 == sl_strncmp(buf, _("Hash:"), 5) ||
		   0 == sl_strncmp(buf, _("NotDashEscaped:"), 15))
	    {
	      if (extract_level == SIG_DATASIG)
		sl_write(fdTmp, buf, bufc);
	      continue;
	    }
	  /* ignore other headers */
	  else
	    continue;
	}
    
      if (flag_pgp == S_TRUE && buf[0] == '\n')
	{
	  sl_write(fdTmp, buf, 1);
	}
      else if (flag_pgp == S_TRUE)
	{
	  if (extract_level == SIG_DATASIG) {
	    sl_write(fdTmp, buf, bufc); 
	  }
	  else {
	    if (sh_gpg_msg_end(buf) == S_TRUE)
	      break;
	    else
	      sl_write(fdTmp, buf, bufc);
	  }
	}

      /* This is after the copy has been done. */
      if (flag_pgp == S_TRUE && sh_gpg_sig_end(buf) == S_TRUE)
	break;
    }
  SH_FREE(buf);
  sl_fclose(FIL__, __LINE__, fin_cp);
  sl_rewind (fdTmp);

  return fdTmp;
}
#endif

/*********************************************************************
 *
 * Exported functions
 *
 *********************************************************************/

int sh_sig_check_signature (SL_TICKET file, ShSigFile what)
{
#if defined(WITH_GPG)
  return sh_gpg_check_signature (file, what);
#elif defined(WITH_SIGNIFY)
  return sh_signify_check_signature (file, what);
#else
  return -1;
#endif
}

SL_TICKET sh_sig_extract_signed(SL_TICKET fd)
{
#if defined(WITH_GPG)
  return sh_gpg_extract_signed(fd, SIG_DATASIG);
#elif defined(WITH_SIGNIFY)
  return sh_signify_extract_signed(fd, SIG_DATASIG);
#else
  return -1;
#endif
}

SL_TICKET sh_sig_extract_signed_data(SL_TICKET fd)
{
#if defined(WITH_GPG)
  return sh_gpg_extract_signed(fd, SIG_DATAONLY);
#elif defined(WITH_SIGNIFY)
  return sh_signify_extract_signed(fd, SIG_DATAONLY);
#else
  return -1;
#endif
}

int sh_sig_msg_start(const char * line)
{
#if defined(WITH_GPG)
  return sh_gpg_msg_start(line);
#elif defined(WITH_SIGNIFY)
  return sh_signify_msg_start(line);
#else
  return -1;
#endif
}

int sh_sig_msg_startdata(const char * line)
{
#if defined(WITH_GPG)
  return sh_gpg_msg_startdata(line);
#elif defined(WITH_SIGNIFY)
  return sh_signify_msg_startdata(line);
#else
  return -1;
#endif
}

int sh_sig_msg_end(const char * line)
{
#if defined(WITH_GPG)
  return sh_gpg_msg_end(line);
#elif defined(WITH_SIGNIFY)
  return sh_signify_msg_end(line);
#else
  return -1;
#endif
}

int sh_sig_data_end(const char * line)
{
#if defined(WITH_GPG)
  return sh_gpg_sig_end(line);
#elif defined(WITH_SIGNIFY)
  return sh_signify_data_end(line);
#else
  return -1;
#endif
}

void sh_sig_log_startup (void)
{
  if (startInfo.program != NULL)
    {
      sh_error_handle ((-1), FIL__, startInfo.line, 0, MSG_START_GH,
		       startInfo.program, startInfo.uid,
		       startInfo.path,
		       startInfo.key_uid, startInfo.key_id);
    }
  return;
}

/* #ifdef WITH_SIG */
#endif








