summaryrefslogtreecommitdiffstats
path: root/src/sh_sig.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sh_sig.c')
-rw-r--r--src/sh_sig.c1789
1 files changed, 1789 insertions, 0 deletions
diff --git a/src/sh_sig.c b/src/sh_sig.c
new file mode 100644
index 0000000..ea8c3cb
--- /dev/null
+++ b/src/sh_sig.c
@@ -0,0 +1,1789 @@
+/* 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);
+
+ /* 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");
+ 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+");
+ 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);
+
+ /* 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");
+ 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
+
+
+
+
+
+
+
+