1
0
Fork 0
mod-qos/tools/src/qssign.c
Daniel Baumann 24ecce9d56
Adding upstream version 11.74.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-03-24 13:01:01 +01:00

799 lines
23 KiB
C

/**
* 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;
}