diff options
Diffstat (limited to 'src/sh_sig.c')
-rw-r--r-- | src/sh_sig.c | 1789 |
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 + + + + + + + + |