/** * 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 #include #include #include #include #include /* openssl */ #include #include /* apr/apr-util */ #define QS_USEAPR 1 #include #include #include #include #include #include #include #define PCRE2_CODE_UNIT_WIDTH 8 #include #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); /* ' ' '#' */ 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); /* ' ' '#' */ 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); /* ' ' '#' */ 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); /* ' ' '#' */ 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); /* ' ' '#' */ 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); /* ' ' '#' */ 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); /* ' ' '#' */ 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 [-e] [-v] [-u ] [-f ] [-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 \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 \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 \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 \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; }