diff options
Diffstat (limited to 'tools/src/qssign.c')
-rw-r--r-- | tools/src/qssign.c | 799 |
1 files changed, 799 insertions, 0 deletions
diff --git a/tools/src/qssign.c b/tools/src/qssign.c new file mode 100644 index 0000000..6ea7021 --- /dev/null +++ b/tools/src/qssign.c @@ -0,0 +1,799 @@ +/** + * Utilities for the quality of service module mod_qos. + * + * qssign.c: Log data signing tool to ensure data integrity. + * + * See http://mod-qos.sourceforge.net/ for further + * details. + * + * Copyright (C) 2023 Pascal Buchbinder + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +static const char revision[] = "$Id: qssign.c 2654 2022-05-13 09:12:42Z pbuchbinder $"; + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <regex.h> +#include <signal.h> + +/* openssl */ +#include <openssl/evp.h> +#include <openssl/hmac.h> + +/* apr/apr-util */ +#define QS_USEAPR 1 +#include <apr.h> +#include <apr_base64.h> +#include <apr_pools.h> +#include <apr_strings.h> +#include <apr_thread_proc.h> +#include <apr_file_io.h> +#include <apr_time.h> + +#define PCRE2_CODE_UNIT_WIDTH 8 +#include <pcre2.h> + +#include "qs_util.h" +#include "qs_apo.h" + +#define SEQDIG "12" +#define QS_END "qssign---end-of-data" +#define QS_START "qssign---------start" + +static const char *m_start_fmt = ""; +static const char *m_end_fmt = ""; +static long m_nr = 1; +static int m_logend = 0; +static void (*m_end)(const char *, int) = NULL; +static int m_end_pos = 0; +static const char *m_sec = NULL; +static const EVP_MD *m_evp; +static qs_regex_t *m_filter = NULL; + +typedef struct { + const char* start_fmt; + const char* end_fmt; + const char* pattern; + const char* test; +} qos_p_t; + +#define severity "[A-Z]+" + +static const qos_p_t pattern[] = { + { + "%s | INFO | "QS_START, + "%s | INFO | "QS_END, + "^[0-9]{4}[-][0-9]{2}[-][0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}[ ]+[|][ ]+"severity"[ ]+[|][ ]+[a-zA-Z0-9]+", + "2010-04-14 20:18:37,464 | INFO | org.hibernate.cfg.Configuration" + }, + { + "%s INFO "QS_START, + "%s INFO "QS_END, + "^[0-9]{4}[-][0-9]{2}[-][0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}[ ]+"severity"[ ]+", + "2011-08-30 07:27:22,738 INFO loginId='test'" + }, + { + "%s qssign start INFO "QS_START, + "%s qssign end INFO "QS_END, + "^[0-9]{4}[-][0-9]{2}[-][0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}[ ]+[a-zA-Z0-9\\.-]+[ ]+[a-zA-Z0-9\\.-]+[ ]+"severity"[ ]+", + "2011-09-01 07:37:17,275 main org.apache.catalina.startup.Catalina INFO Server" + }, + { + "%s INFO "QS_START, + "%s INFO "QS_END, + "^[0-9]{4}[-][0-9]{2}[-][0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}[ ]+", + "2011-08-30 07:27:22,738 " + }, + { NULL, NULL, NULL } +}; + +/** + * Writes the signed log line to stdout. + * + * @param line Data to sign + * @param line_size Length of the data + * @param sec Secret + * @param sec_len Length of the secret + */ +static void qs_write(char *line, int line_size, const char *sec, int sec_len) { +#if OPENSSL_VERSION_NUMBER < 0x10100000L + HMAC_CTX hmac; + HMAC_CTX *hmac_p = &hmac; +#else + HMAC_CTX *hmac_p; +#endif + unsigned char data[HMAC_MAX_MD_CBLOCK]; + unsigned int len; + char *m; + int data_len; + sprintf(&line[strlen(line)], " %."SEQDIG"ld", m_nr); +#if OPENSSL_VERSION_NUMBER < 0x10100000L + HMAC_CTX_init(hmac_p); +#else + hmac_p = HMAC_CTX_new(); +#endif + HMAC_Init_ex(hmac_p, sec, sec_len, m_evp, NULL); + HMAC_Update(hmac_p, (const unsigned char *)line, strlen(line)); + HMAC_Final(hmac_p, data, &len); +#if OPENSSL_VERSION_NUMBER < 0x10100000L + HMAC_CTX_cleanup(hmac_p); +#else + HMAC_CTX_free(hmac_p); +#endif + m = calloc(1, apr_base64_encode_len(len) + 1); + data_len = apr_base64_encode(m, (char *)data, len); + m[data_len] = '\0'; + printf("%s#%s\n", line, m); + fflush(stdout); + free(m); + m_nr++; + return; +} + +/* + * [Fri Dec 03 07:37:40 2010] [notice] ......... + */ +static void qs_end_apache_err(const char *sec, int start) { + int sec_len = strlen(sec); + char line[MAX_LINE]; + int dig = atoi(SEQDIG); + /* <data> ' ' <sequence number> '#' <hmac>*/ + int line_size = sizeof(line) - 1 - dig - 1 - (2*HMAC_MAX_MD_CBLOCK) - 1; + char time_string[1024]; + time_t tm = time(NULL); + struct tm *ptr = localtime(&tm); + strftime(time_string, sizeof(time_string), "%a %b %d %H:%M:%S %Y", ptr); + if(start) { + sprintf(line, "[%s] [notice] "QS_START, time_string); + } else { + sprintf(line, "[%s] [notice] "QS_END, time_string); + } + qs_write(line, line_size, sec, sec_len); + return; +} + +/* + * 12.12.12.12 - - [03/Dec/2010:07:36:51 +0100] ............... + */ +static void qs_end_apache_acc(const char *sec, int start) { + int sec_len = strlen(sec); + char line[MAX_LINE]; + int dig = atoi(SEQDIG); + /* <data> ' ' <sequence number> '#' <hmac>*/ + int line_size = sizeof(line) - 1 - dig - 1 - (2*HMAC_MAX_MD_CBLOCK) - 1; + char time_string[1024]; + time_t tm = time(NULL); + struct tm *ptr = localtime(&tm); + char sign; + int timz; + apr_time_exp_t xt; + apr_time_exp_lt(&xt, apr_time_now()); + timz = xt.tm_gmtoff; + if(timz < 0) { + timz = -timz; + sign = '-'; + } else { + sign = '+'; + } + strftime(time_string, sizeof(time_string), "%d/%b/%Y:%H:%M:%S", ptr); + if(start) { + sprintf(line, "0.0.0.0 - - [%s %c%.2d%.2d] "QS_START, time_string, sign, timz / (60*60), (timz % (60*60)) / 60); + } else { + sprintf(line, "0.0.0.0 - - [%s %c%.2d%.2d] "QS_END, time_string, sign, timz / (60*60), (timz % (60*60)) / 60); + } + qs_write(line, line_size, sec, sec_len); + return; +} + +/* + * 2010 12 03 17:00:30.425 qssign end 0.0 5-NOTICE: .............. + */ +static void qs_end_nj(const char *sec, int start) { + int sec_len = strlen(sec); + char line[MAX_LINE]; + int dig = atoi(SEQDIG); + /* <data> ' ' <sequence number> '#' <hmac>*/ + int line_size = sizeof(line) - 1 - dig - 1 - (2*HMAC_MAX_MD_CBLOCK) - 1; + char time_string[1024]; + time_t tm = time(NULL); + struct tm *ptr = localtime(&tm); + char buf[1024]; + int i; + for(i = 0; i < m_end_pos; i++) { + buf[i] = ' '; + } + buf[i] = '\0'; + strftime(time_string, sizeof(time_string), "%Y %m %d %H:%M:%S.000", ptr); + if(start) { + sprintf(line, "%s qssign start 0.0%s 5-NOTICE: "QS_START, time_string, buf); + } else { + sprintf(line, "%s qssign end 0.0%s 5-NOTICE: "QS_END, time_string, buf); + } + qs_write(line, line_size, sec, sec_len); + return; +} + +/* + * 2010-04-14 20:18:37,464 ... (using m_fmt) + */ +static void qs_end_lj(const char *sec, int start) { + int sec_len = strlen(sec); + char line[MAX_LINE]; + int dig = atoi(SEQDIG); + /* <data> ' ' <sequence number> '#' <hmac>*/ + int line_size = sizeof(line) - 1 - dig - 1 - (2*HMAC_MAX_MD_CBLOCK) - 1; + char time_string[1024]; + time_t tm = time(NULL); + struct tm *ptr = localtime(&tm); + strftime(time_string, sizeof(time_string), "%Y-%m-%d %H:%M:%S,000", ptr); + if(start) { + sprintf(line, m_start_fmt, time_string); + } else { + sprintf(line, m_end_fmt, time_string); + } + qs_write(line, line_size, sec, sec_len); + return; +} + +/* + * Dec 6 04:00:06 localhost kernel: + */ +static void qs_end_lx(const char *sec, int start) { + char hostname[1024]; + int len = sizeof(hostname); + int sec_len = strlen(sec); + char line[MAX_LINE]; + int dig = atoi(SEQDIG); + /* <data> ' ' <sequence number> '#' <hmac>*/ + int line_size = sizeof(line) - 1 - dig - 1 - (2*HMAC_MAX_MD_CBLOCK) - 1; + char time_string[1024]; + time_t tm = time(NULL); + struct tm *ptr = localtime(&tm); + strftime(time_string, sizeof(time_string), "%b %e %H:%M:%S", ptr); + if(gethostname(hostname, len) != 0) { + hostname[0] = '-'; + hostname[1] = '\0'; + } + if(start) { + sprintf(line, "%s %s qssign: "QS_START, time_string, hostname); + } else { + sprintf(line, "%s %s qssign: "QS_END, time_string, hostname); + } + qs_write(line, line_size, sec, sec_len); + return; +} + +/* + * 2013/11/13 17:38:41 [error] 6577#0: *1 open() + */ +static void qs_end_ngx(const char *sec, int start) { + int sec_len = strlen(sec); + char line[MAX_LINE]; + int dig = atoi(SEQDIG); + /* <data> ' ' <sequence number> '#' <hmac>*/ + int line_size = sizeof(line) - 1 - dig - 1 - (2*HMAC_MAX_MD_CBLOCK) - 1; + char time_string[1024]; + time_t tm = time(NULL); + struct tm *ptr = localtime(&tm); + strftime(time_string, sizeof(time_string), "%Y/%m/%d %H:%M:%S", ptr); + if(start) { + sprintf(line, "%s [notice] 0#0: "QS_END, time_string); + } else { + sprintf(line, "%s [notice] 0#0: "QS_END, time_string); + } + qs_write(line, line_size, sec, sec_len); + return; +} + +void qs_signal_exit(int e) { + if(m_logend && (m_end != NULL)) { + m_end(m_sec, 0); + } + exit(0); +} + +/** + * Tries to find out a suitable log line format which is used + * to log sign end messages (so let the verifier known, that the + * data ends nothing has been cut off). + * + * Sets the format to global variables. + * + * known pattern + * - [Fri Dec 03 07:37:40 2010] [notice] ......... + * - 12.12.12.12 - - [03/Dec/2010:07:36:51 +0100] ............... + * - 2010 12 03 17:00:30.425 qssign end 0.0 5-NOTICE: .............. + * 46 <- var -> 63 71 + * - Dec 6 04:00:06 localhost kernel: + * - some 2010-12-03 17:00:30,425 ... + * + * @param s + */ +static void qs_set_format(char *s) { + regex_t r_apache_err; + regex_t r_apache_acc; + regex_t r_nj; + regex_t r_lx; + regex_t r_ngx; + if(regcomp(&r_apache_err, + "^\\[[a-zA-Z]{3} [a-zA-Z]{3} [0-9]+ [0-9]+:[0-9]+:[0-9]+ [0-9]+\\] \\[[a-zA-Z]+\\] ", + REG_EXTENDED) != 0) { + fprintf(stderr, "failed to compile regex (err)\n"); + exit(1); + } + if(regcomp(&r_apache_acc, + "^[0-9.]+ [a-zA-Z0-9\\@_\\.\\-]+ [a-zA-Z0-9\\@_\\.\\-]+ \\[[0-9]+/[a-zA-Z]{3}/[0-9:]+[0-9\\+ ]+\\] ", + REG_EXTENDED) != 0) { + fprintf(stderr, "failed to compile regex (acc)\n"); + exit(1); + } + if(regcomp(&r_nj, + "^[0-9]{4} [0-9]{2} [0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3} [a-zA-Z0-9]+[ ]+.*[A-Z]+[ ]*:", + REG_EXTENDED) != 0) { + fprintf(stderr, "failed to compile regex (nj)\n"); + exit(1); + } + if(regcomp(&r_lx, + "^[a-zA-Z]{3}[ ]+[0-9]+[ ]+[0-9]{2}:[0-9]{2}:[0-9]{2}[ ]+[a-zA-Z0-9_\\.\\-]+[ ]+[a-zA-Z0-9_\\.\\-]+:", + REG_EXTENDED) != 0) { + fprintf(stderr, "failed to compile regex (lx)\n"); + exit(1); + } + if(regcomp(&r_ngx, + "^[0-9]{4}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \\[[a-z]+\\] [0-9]+#[0-9]+: ", + REG_EXTENDED) != 0) { + fprintf(stderr, "failed to compile regex (ngx)\n"); + exit(1); + } + + + if(regexec(&r_apache_err, s, 0, NULL, 0) == 0) { + m_end = &qs_end_apache_err; + } else if(regexec(&r_apache_acc, s, 0, NULL, 0) == 0) { + m_end = &qs_end_apache_acc; + } else if(regexec(&r_nj, s, 0, NULL, 0) == 0) { + char *dp = strstr(s, ": "); + if(dp) { + /* calculate the "var" size, see comment above */ + m_end_pos = dp - s - 47 - 8 - 3; + if((m_end_pos < 0) || (m_end_pos > 1000)) { + m_end_pos = 0; + } + } + m_end = &qs_end_nj; + } else if(regexec(&r_lx, s, 0, NULL, 0) == 0) { + m_end = &qs_end_lx; + } else if(regexec(&r_ngx, s, 0, NULL, 0) == 0) { + m_end = &qs_end_ngx; + } + // search within the generic yyyy-mm-dd hh-mm-ss,mmm patterns + if(!m_end) { + const qos_p_t *p = pattern; + while(p->end_fmt) { + regex_t r_j; + if(regcomp(&r_j, p->pattern, REG_EXTENDED) != 0) { + fprintf(stderr, "failed to compile regex (%s)\n", p->pattern); + exit(1); + } + if(regexec(&r_j, s, 0, NULL, 0) == 0) { + m_start_fmt = p->start_fmt; + m_end_fmt = p->end_fmt; + m_end = &qs_end_lj; + break; + } + p++; + } + } + /* default (apache error log format) */ + if(m_end == NULL) { + m_end = &qs_end_apache_err; + } + return; +} + +/** + * Process the data from stdin. + * + * @param sec Passphrase + */ +static void qs_sign(const char *sec) { + int sec_len = strlen(sec); + char *line = calloc(1, MAX_LINE_BUFFER+1); + int dig = atoi(SEQDIG); + /* <data> ' ' <sequence number> '#' <hmac>*/ + int line_size = MAX_LINE_BUFFER - 1 - dig - 1 - (2*HMAC_MAX_MD_CBLOCK) - 1; + int line_len; + while(fgets(line, MAX_LINE_BUFFER, stdin) != NULL) { + line_len = strlen(line) - 1; + while(line_len > 0) { // cut tailing CR/LF + if(line[line_len] >= ' ') { + break; + } + line[line_len] = '\0'; + line_len--; + } + if(m_logend && (m_end == NULL)) { + qs_set_format(line); + m_end(m_sec, 1); + } + if(m_filter != NULL && qs_regexec_len(m_filter, line, line_len, 0, NULL, 0) >= 0) { + printf("%s\n", line); + fflush(stdout); + } else { + qs_write(line, line_size, sec, sec_len); + } + } + return; +} + +static int isSpecialLine(const char *line, const char *marker) { + char *se_marker = strstr(line, marker); + if(se_marker != NULL) { + /* QS_END/START + " " + SEQDIG */ + int sz = strlen(marker) + 1 + atoi(SEQDIG); + if(sz == (strlen(line) - (se_marker - line))) { + return 1; + } + } + return 0; +} + +static long qs_verify(const char *sec) { + int end_seen = 0; + int sec_len = strlen(sec); + long err = 0; // errors + long lineNumber = 0; // line number of the file / input data + char *line = calloc(1, MAX_LINE_BUFFER+1); + int line_size = MAX_LINE_BUFFER; + int line_len; + m_nr = -1; // expected sequence number (start with any) + long nr_alt = -1; // alternatively expected sequence number (if a line was injected) + long nr_alt_lineNumber = -1; + long nr_usr1_lineNumber = -1; // we may have lines written by a prev. qssign binary (while graceful restart) + while(fgets(line, line_size, stdin) != NULL) { + int valid = 0; + long msgSeqNr = 0; + int isOldProcess = 0; +#if OPENSSL_VERSION_NUMBER < 0x10100000L + HMAC_CTX hmac; + HMAC_CTX *hmac_p = &hmac; +#else + HMAC_CTX *hmac_p; +#endif + unsigned char data[HMAC_MAX_MD_CBLOCK]; + unsigned int len; + char *m; + int data_len; + char *sig; + char *seq; + line_len = strlen(line) - 1; + while(line_len > 0) { // cut tailing CR/LF + if(line[line_len] >= ' ') { + break; + } + line[line_len] = '\0'; + line_len--; + } + sig = strrchr(line, '#'); + seq = strrchr(line, ' '); + lineNumber++; + if(seq && sig) { + sig[0] = '\0'; + sig++; + /* verify hmac */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L + HMAC_CTX_init(hmac_p); +#else + hmac_p = HMAC_CTX_new(); +#endif + HMAC_Init_ex(hmac_p, sec, sec_len, m_evp, NULL); + HMAC_Update(hmac_p, (const unsigned char *)line, strlen(line)); + HMAC_Final(hmac_p, data, &len); +#if OPENSSL_VERSION_NUMBER < 0x10100000L + HMAC_CTX_cleanup(hmac_p); +#else + HMAC_CTX_free(hmac_p); +#endif + m = calloc(1, apr_base64_encode_len(len) + 1); + data_len = apr_base64_encode(m, (char *)data, len); + m[data_len] = '\0'; + if(strcmp(m, sig) != 0) { + err++; + fprintf(stderr, "ERROR on line %ld: invalid signature\n", lineNumber); + /* message may be modified/corrupt or inserted: next line may have + the next sequence number (modified) or the same (inserted) */ + nr_alt = m_nr + 1; + nr_alt_lineNumber = lineNumber + 1; + } else { + valid = 1; + } + free(m); + /* verify sequence */ + seq++; + msgSeqNr = atol(seq); + if(msgSeqNr == 0) { + err++; + fprintf(stderr, "ERROR on line %ld: invalid sequence\n", lineNumber); + } else { + if(m_nr != -1) { + if(lineNumber == nr_alt_lineNumber) { + // last line was modified + if(m_nr != msgSeqNr) { + // and therefore, we also accept the next sequence number + m_nr = nr_alt; + } + nr_alt = -1; + nr_alt_lineNumber = -1; + } + if(valid && isSpecialLine(line, QS_START)) { + // new start line (graceful restart) + // we expect now msg number 1 + // but still acept the old until we get the end marker + nr_usr1_lineNumber = m_nr; + m_nr = 1; + } + if(valid && nr_usr1_lineNumber == msgSeqNr) { + // msg from old process is okay... + nr_usr1_lineNumber++; + isOldProcess = 1; + } else { + if(m_nr != msgSeqNr) { + if(msgSeqNr == 1) { + if(!end_seen) { + err++; + fprintf(stderr, "ERROR on line %ld: wrong sequence, server restart? (expect %."SEQDIG"ld)\n", + lineNumber, m_nr); + } + } else { + err++; + fprintf(stderr, "ERROR on line %ld: wrong sequence (expect %."SEQDIG"ld)\n", lineNumber, m_nr); + } + } else { + // well done - this is the sequence number we expect + } + } + } else if(m_logend) { + // log should (if not rotated) start with message 1 + if(msgSeqNr != 1) { + fprintf(stderr, "NOTICE: log starts with sequence %."SEQDIG"ld, log rotation?" + " (expect %."SEQDIG"d)\n", msgSeqNr, 1); + } + } + if(valid && !isOldProcess) { + // adjust + m_nr = msgSeqNr; + } + } + } else { + err++; + fprintf(stderr, "ERROR on line %ld: missing signature/sequence\n", lineNumber); + } + end_seen = 0; + if(valid) { + if(!isOldProcess) { + m_nr++; + } + if(isSpecialLine(line, QS_END)) { + if(nr_usr1_lineNumber == -1) { + end_seen = 1; + } else { + nr_usr1_lineNumber = -1; // no more messages from an old process + } + } + } + } + if(m_logend && !end_seen) { + fprintf(stderr, "NOTICE: no end marker seen, log rotation? (expect %."SEQDIG"ld)\n", m_nr); + } + return err; +} + +static void usage(char *cmd, int man) { + if(man) { + //.TH [name of program] [section number] [center footer] [left footer] [center header] + printf(".TH %s 1 \"%s\" \"mod_qos utilities %s\" \"%s man page\"\n", qs_CMD(cmd), man_date, + man_version, cmd); + } + printf("\n"); + if(man) { + printf(".SH NAME\n"); + } + qs_man_print(man, "%s - an utility to sign and verify the integrity of log data.\n", cmd); + printf("\n"); + if(man) { + printf(".SH SYNOPSIS\n"); + } + qs_man_print(man, "%s%s -s|S <secret> [-e] [-v] [-u <name>] [-f <regex>] [-a 'sha1'|'sha256']\n", man ? "" : "Usage: ", cmd); + printf("\n"); + if(man) { + printf(".SH DESCRIPTION\n"); + } else { + printf("Summary\n"); + } + qs_man_print(man, "%s is a log data integrity check tool. It reads log data\n", cmd); + qs_man_print(man, "from stdin (pipe) and writes the data to stdout adding a sequence\n"); + qs_man_print(man, "number and signature to ever log line.\n"); + printf("\n"); + if(man) { + printf(".SH OPTIONS\n"); + } else { + printf("Options\n"); + } + if(man) printf(".TP\n"); + qs_man_print(man, " -s <secret>\n"); + if(man) printf("\n"); + qs_man_print(man, " Passphrase used to calculate signature.\n"); + if(man) printf("\n.TP\n"); + qs_man_print(man, " -S <program>\n"); + if(man) printf("\n"); + qs_man_print(man, " Specifies a program which writes the passphrase to stdout.\n"); + if(man) printf("\n.TP\n"); + qs_man_print(man, " -e\n"); + if(man) printf("\n"); + qs_man_print(man, " Writes start/end marker when starting/stopping data signing.\n"); + if(man) printf("\n.TP\n"); + qs_man_print(man, " -v\n"); + if(man) printf("\n"); + qs_man_print(man, " Verification mode checking the integrity of signed data.\n"); + if(man) printf("\n.TP\n"); + qs_man_print(man, " -u <name>\n"); + if(man) printf("\n"); + qs_man_print(man, " Becomes another user, e.g. www-data.\n"); + if(man) printf("\n.TP\n"); + qs_man_print(man, " -f <regex>\n"); + if(man) printf("\n"); + qs_man_print(man, " Filter pattern (case sensitive regular expression) for messages\n"); + qs_man_print(man, " which do not need to be signed.\n"); + if(man) printf("\n.TP\n"); + qs_man_print(man, " -a 'sha1'|'sha256'\n"); + if(man) printf("\n"); + qs_man_print(man, " Specifies the algorithm to use. Default is sha1.\n"); + printf("\n"); + if(man) { + printf(".SH EXAMPLE\n"); + printf("Sign:\n"); + printf("\n"); + } else { + printf("Example (sign):\n"); + } + qs_man_println(man, " TransferLog \"|/usr/bin/%s -s password -e |/usr/bin/qsrotate -o /var/log/apache/access.log\"\n", cmd); + printf("\n"); + if(man) { + printf("\n"); + printf("Verify:\n"); + printf("\n"); + } else { + qs_man_print(man, "Example (verify):\n"); + } + qs_man_println(man, " cat access.log | %s -s password -v\n", cmd); + printf("\n"); + if(man) { + printf(".SH SEE ALSO\n"); + printf("qsdt(1), qsexec(1), qsfilter2(1), qsgeo(1), qsgrep(1), qshead(1), qslog(1), qslogger(1), qspng(1), qsre(1), qsrespeed(1), qsrotate(1), qstail(1)\n"); + printf(".SH AUTHOR\n"); + printf("Pascal Buchbinder, http://mod-qos.sourceforge.net/\n"); + } else { + printf("See http://mod-qos.sourceforge.net/ for further details.\n"); + } + if(man) { + exit(0); + } else { + exit(1); + } +} + +int main(int argc, const char * const argv[]) { + apr_pool_t *pool; + int verify = 0; + char *cmd = strrchr(argv[0], '/'); + const char *username = NULL; + const char *filter = NULL; + if(cmd == NULL) { + cmd = (char *)argv[0]; + } else { + cmd++; + } + apr_app_initialize(&argc, &argv, NULL); + apr_pool_create(&pool, NULL); + m_evp = EVP_sha1(); + argc--; + argv++; + while(argc >= 1) { + if(strcmp(*argv,"-s") == 0) { + if (--argc >= 1) { + m_sec = *(++argv); + } + } else if(strcmp(*argv,"-S") == 0) { + if (--argc >= 1) { + m_sec = qs_readpwd(pool, *(++argv)); + } + } else if(strcmp(*argv,"-v") == 0) { + verify = 1; + } else if(strcmp(*argv,"-e") == 0) { + m_logend = 1; + } else if(strcmp(*argv,"-u") == 0) { /* switch user id */ + if (--argc >= 1) { + username = *(++argv); + } + } else if(strcmp(*argv,"-f") == 0) { /* filter */ + if (--argc >= 1) { + filter = *(++argv); + } + } else if(strcmp(*argv,"-a") == 0) { /* set alg */ + if (--argc >= 1) { + const char *alg = *(++argv); + if(strcasecmp(alg, "SHA256") == 0) { + m_evp = EVP_sha256(); + } else if(strcasecmp(alg, "SHA1") != 0) { + m_evp = NULL; + } + } else { + m_evp = NULL; + } + } else if(strcmp(*argv,"-?") == 0) { + usage(cmd, 0); + } else if(strcmp(*argv,"-help") == 0) { + usage(cmd, 0); + } else if(strcmp(*argv,"--help") == 0) { + usage(cmd, 0); + } else if(strcmp(*argv,"--man") == 0) { + usage(cmd, 1); + } + argc--; + argv++; + } + + if(filter != NULL) { + m_filter = apr_palloc(pool, sizeof(qs_regex_t)); + if(qs_regcomp(m_filter, filter, 0) != 0) { + fprintf(stderr, "failed to compile filter pattern <%s>\n", filter); + exit(1); + } + apr_pool_pre_cleanup_register(pool, m_filter, qs_pregfree); + } + + if(m_evp == NULL) { + usage(cmd, 0); + } + + if(m_sec == NULL) { + usage(cmd, 0); + } + + qs_setuid(username, cmd); + + if(verify) { + long err = qs_verify(m_sec); + if(err != 0) { + return 1; + } + } else { + if(m_logend) { + signal(SIGTERM, qs_signal_exit); + } + qs_sign(m_sec); + if(m_logend && (m_end != NULL)) { + m_end(m_sec, 0); + } + } + + apr_pool_destroy(pool); + return 0; +} |