/* 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 #include #if defined(WITH_SIG) #include #include #include #if defined(SH_WITH_SERVER) #include #endif #include #include #include #include #include #ifdef HAVE_MEMORY_H #include #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=\n"), test_sig)); TPT(((0), FIL__, __LINE__, _("msg=\n"), (flag == SIG_HASH_OTHER) ? expected : SIG_HASH)); TPT(((0), FIL__, __LINE__, _("msg=\n"), wstrip1)); TPT(((0), FIL__, __LINE__, _("msg=\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=\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=\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=\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 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=\n"))); TPT(((0), FIL__, __LINE__, _("msg=\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=\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=\n"))); xagain: errno = 0; while (NULL != fgets(line, sizeof(line), source.pipe)) { TPT(((0), FIL__, __LINE__, _("msg=\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=\n"))); if (n_goodsig == 1 && n_lines == 1) { TPT(((0), FIL__, __LINE__, _("msg=\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=\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=\n"), fd)); status = sh_signify_check_file_sign(fd, homedir); if (status != SH_SIG_OK) { TPT(((0), FIL__, __LINE__, _("msg=\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=\n"))); TPT(((0), FIL__, __LINE__, _("msg=\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=\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=\n"))); xagain: errno = 0; while (NULL != fgets(line, sizeof(line), source.pipe)) { TPT(((0), FIL__, __LINE__, _("msg=\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=\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=\n"))); } if (have_fp == GOOD) { TPT(((0), FIL__, __LINE__, _("msg=\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=\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=\n"), fd)); status = sh_gpg_check_file_sign(fd, gp.conf_id, gp.conf_fp, homedir, SIG_CONF); TPT(((0), FIL__, __LINE__, _("msg=\n"), gp.conf_id)); TPT(((0), FIL__, __LINE__, _("msg=\n"), gp.conf_fp)); sig_id = gp.conf_id; sig_fp = gp.conf_fp; } if (what == SIG_DATA) { TPT(((0), FIL__, __LINE__, _("msg=\n"), fd)); status = sh_gpg_check_file_sign(fd, gp.data_id, gp.data_fp, homedir, SIG_DATA); TPT(((0), FIL__, __LINE__, _("msg=\n"), gp.data_id)); TPT(((0), FIL__, __LINE__, _("msg=\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=\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