"Fossies" - the Fresh Open Source Software Archive

Member "mod_qos-11.63/tools/src/qssign.c" (25 May 2019, 23213 Bytes) of package /linux/www/apache_httpd_modules/mod_qos-11.63.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "qssign.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 11.61_vs_11.62.

    1 /**
    2  * Utilities for the quality of service module mod_qos.
    3  *
    4  * qssign.c: Log data signing tool to ensure data integrity.
    5  *
    6  * See http://mod-qos.sourceforge.net/ for further
    7  * details.
    8  *
    9  * Copyright (C) 2019 Pascal Buchbinder
   10  *
   11  * Licensed to the Apache Software Foundation (ASF) under one or more
   12  * contributor license agreements.  See the NOTICE file distributed with
   13  * this work for additional information regarding copyright ownership.
   14  * The ASF licenses this file to You under the Apache License, Version 2.0
   15  * (the "License"); you may not use this file except in compliance with
   16  * the License.  You may obtain a copy of the License at
   17  *
   18  *     http://www.apache.org/licenses/LICENSE-2.0
   19  *
   20  * Unless required by applicable law or agreed to in writing, software
   21  * distributed under the License is distributed on an "AS IS" BASIS,
   22  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   23  * See the License for the specific language governing permissions and
   24  * limitations under the License.
   25  */
   26 
   27 static const char revision[] = "$Id: qssign.c 2542 2019-02-22 06:37:10Z pbuchbinder $";
   28 
   29 #include <stdio.h>
   30 #include <unistd.h>
   31 #include <string.h>
   32 #include <stdlib.h>
   33 #include <regex.h>
   34 #include <signal.h>
   35 
   36 /* openssl */
   37 #include <openssl/evp.h>
   38 #include <openssl/hmac.h>
   39 
   40 /* PCRE */
   41 #include <pcre.h>
   42 
   43 /* apr/apr-util */
   44 #define QS_USEAPR 1
   45 #include <apr.h>
   46 #include <apr_base64.h>
   47 #include <apr_pools.h>
   48 #include <apr_strings.h>
   49 #include <apr_thread_proc.h>
   50 #include <apr_file_io.h>
   51 #include <apr_time.h>
   52 
   53 #include "qs_util.h"
   54 #include "qs_apo.h"
   55 
   56 #define SEQDIG "12"
   57 #define QS_END   "qssign---end-of-data"
   58 #define QS_START "qssign---------start"
   59 
   60 static const char *m_start_fmt = "";
   61 static const char *m_end_fmt = "";
   62 static long m_nr = 1;
   63 static int  m_logend = 0;
   64 static void (*m_end)(const char *, int) = NULL;
   65 static int m_end_pos = 0;
   66 static const char *m_sec = NULL;
   67 static const EVP_MD *m_evp;
   68 static const pcre *m_filter = NULL;
   69 
   70 typedef struct {
   71   const char* start_fmt;
   72   const char* end_fmt;
   73   const char* pattern;
   74   const char* test;
   75 } qos_p_t;
   76 
   77 #define severity "[A-Z]+"
   78 
   79 static const qos_p_t pattern[] = {
   80   {
   81     "%s | INFO  | "QS_START,
   82     "%s | INFO  | "QS_END,
   83     "^[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]+",
   84     "2010-04-14 20:18:37,464 | INFO  | org.hibernate.cfg.Configuration"
   85   },
   86   {
   87     "%s INFO  "QS_START,
   88     "%s INFO  "QS_END,
   89     "^[0-9]{4}[-][0-9]{2}[-][0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}[ ]+"severity"[ ]+",
   90     "2011-08-30 07:27:22,738 INFO  loginId='test'"
   91   },
   92   {
   93     "%s qssign          start                                    INFO  "QS_START,
   94     "%s qssign          end                                      INFO  "QS_END,
   95     "^[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"[ ]+",
   96     "2011-09-01 07:37:17,275 main            org.apache.catalina.startup.Catalina     INFO  Server"
   97   },
   98   {
   99     "%s INFO  "QS_START,
  100     "%s INFO  "QS_END,
  101     "^[0-9]{4}[-][0-9]{2}[-][0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}[ ]+",
  102     "2011-08-30 07:27:22,738 "
  103   },
  104   { NULL, NULL, NULL }
  105 };
  106 
  107 /**
  108  * Writes the signed log line to stdout.
  109  *
  110  * @param line Data to sign
  111  * @param line_size Length of the data
  112  * @param sec Secret
  113  * @param sec_len Length of the secret
  114  */
  115 static void qs_write(char *line, int line_size, const char *sec, int sec_len) {
  116 #if OPENSSL_VERSION_NUMBER < 0x10100000L
  117   HMAC_CTX hmac;
  118   HMAC_CTX *hmac_p = &hmac;
  119 #else
  120   HMAC_CTX *hmac_p;
  121 #endif
  122   unsigned char data[HMAC_MAX_MD_CBLOCK];
  123   unsigned int len;
  124   char *m;
  125   int data_len;
  126   sprintf(&line[strlen(line)], " %."SEQDIG"ld", m_nr);
  127 #if OPENSSL_VERSION_NUMBER < 0x10100000L
  128   HMAC_CTX_init(hmac_p);
  129 #else
  130   hmac_p = HMAC_CTX_new();
  131 #endif
  132   HMAC_Init_ex(hmac_p, sec, sec_len, m_evp, NULL);
  133   HMAC_Update(hmac_p, (const unsigned char *)line, strlen(line));
  134   HMAC_Final(hmac_p, data, &len);
  135 #if OPENSSL_VERSION_NUMBER < 0x10100000L
  136   HMAC_CTX_cleanup(hmac_p);
  137 #else
  138   HMAC_CTX_free(hmac_p);
  139 #endif
  140   m = calloc(1, apr_base64_encode_len(len) + 1);
  141   data_len = apr_base64_encode(m, (char *)data, len);
  142   m[data_len] = '\0';
  143   printf("%s#%s\n", line, m);
  144   fflush(stdout);
  145   free(m);
  146   m_nr++;
  147   return;
  148 }
  149 
  150 /*
  151  * [Fri Dec 03 07:37:40 2010] [notice] .........
  152  */
  153 static void qs_end_apache_err(const char *sec, int start) {
  154   int sec_len = strlen(sec);
  155   char line[MAX_LINE];
  156   int dig = atoi(SEQDIG);
  157   /* <data> ' ' <sequence number> '#' <hmac>*/
  158   int line_size = sizeof(line) - 1 - dig - 1 - (2*HMAC_MAX_MD_CBLOCK) - 1;
  159   char time_string[1024];
  160   time_t tm = time(NULL);
  161   struct tm *ptr = localtime(&tm);
  162   strftime(time_string, sizeof(time_string), "%a %b %d %H:%M:%S %Y", ptr);
  163   if(start) {
  164     sprintf(line, "[%s] [notice] "QS_START, time_string);
  165   } else {
  166     sprintf(line, "[%s] [notice] "QS_END, time_string);
  167   }
  168   qs_write(line, line_size, sec, sec_len);
  169   return;
  170 }
  171 
  172 /*
  173  * 12.12.12.12 - - [03/Dec/2010:07:36:51 +0100] ...............
  174  */
  175 static void qs_end_apache_acc(const char *sec, int start) {
  176   int sec_len = strlen(sec);
  177   char line[MAX_LINE];
  178   int dig = atoi(SEQDIG);
  179   /* <data> ' ' <sequence number> '#' <hmac>*/
  180   int line_size = sizeof(line) - 1 - dig - 1 - (2*HMAC_MAX_MD_CBLOCK) - 1;
  181   char time_string[1024];
  182   time_t tm = time(NULL);
  183   struct tm *ptr = localtime(&tm);
  184   char sign;
  185   int timz;
  186   apr_time_exp_t xt;
  187   apr_time_exp_lt(&xt, apr_time_now());
  188   timz = xt.tm_gmtoff;
  189   if(timz < 0) {
  190     timz = -timz;
  191     sign = '-';
  192   } else {
  193     sign = '+';
  194   }
  195   strftime(time_string, sizeof(time_string), "%d/%b/%Y:%H:%M:%S", ptr);
  196   if(start) {
  197     sprintf(line, "0.0.0.0 - - [%s %c%.2d%.2d] "QS_START, time_string, sign, timz / (60*60), (timz % (60*60)) / 60);
  198   } else {
  199     sprintf(line, "0.0.0.0 - - [%s %c%.2d%.2d] "QS_END, time_string, sign, timz / (60*60), (timz % (60*60)) / 60);
  200   }
  201   qs_write(line, line_size, sec, sec_len);
  202   return;
  203 }
  204 
  205 /*
  206  * 2010 12 03 17:00:30.425 qssign     end        0.0              5-NOTICE:  ..............
  207  */
  208 static void qs_end_nj(const char *sec, int start) {
  209   int sec_len = strlen(sec);
  210   char line[MAX_LINE];
  211   int dig = atoi(SEQDIG);
  212   /* <data> ' ' <sequence number> '#' <hmac>*/
  213   int line_size = sizeof(line) - 1 - dig - 1 - (2*HMAC_MAX_MD_CBLOCK) - 1;
  214   char time_string[1024];
  215   time_t tm = time(NULL);
  216   struct tm *ptr = localtime(&tm);
  217   char buf[1024];
  218   int i;
  219   for(i = 0; i < m_end_pos; i++) {
  220     buf[i] = ' ';
  221   }
  222   buf[i] = '\0';
  223   strftime(time_string, sizeof(time_string), "%Y %m %d %H:%M:%S.000", ptr);
  224   if(start) {
  225     sprintf(line, "%s qssign     start      0.0%s 5-NOTICE:  "QS_START, time_string, buf);
  226   } else {
  227     sprintf(line, "%s qssign     end        0.0%s 5-NOTICE:  "QS_END, time_string, buf);
  228   }
  229   qs_write(line, line_size, sec, sec_len);
  230   return;
  231 }
  232 
  233 /*
  234  * 2010-04-14 20:18:37,464 ... (using m_fmt)
  235  */
  236 static void qs_end_lj(const char *sec, int start) {
  237   int sec_len = strlen(sec);
  238   char line[MAX_LINE];
  239   int dig = atoi(SEQDIG);
  240   /* <data> ' ' <sequence number> '#' <hmac>*/
  241   int line_size = sizeof(line) - 1 - dig - 1 - (2*HMAC_MAX_MD_CBLOCK) - 1;
  242   char time_string[1024];
  243   time_t tm = time(NULL);
  244   struct tm *ptr = localtime(&tm);
  245   strftime(time_string, sizeof(time_string), "%Y-%m-%d %H:%M:%S,000", ptr);
  246   if(start) {
  247     sprintf(line, m_start_fmt, time_string);
  248   } else {
  249     sprintf(line, m_end_fmt, time_string);
  250   }
  251   qs_write(line, line_size, sec, sec_len);
  252   return;
  253 }
  254 
  255 /*
  256  * Dec  6 04:00:06 localhost kernel:
  257  */
  258 static void qs_end_lx(const char *sec, int start) {
  259   char hostname[1024];
  260   int len = sizeof(hostname);
  261   int sec_len = strlen(sec);
  262   char line[MAX_LINE];
  263   int dig = atoi(SEQDIG);
  264   /* <data> ' ' <sequence number> '#' <hmac>*/
  265   int line_size = sizeof(line) - 1 - dig - 1 - (2*HMAC_MAX_MD_CBLOCK) - 1;
  266   char time_string[1024];
  267   time_t tm = time(NULL);
  268   struct tm *ptr = localtime(&tm);
  269   strftime(time_string, sizeof(time_string), "%b %e %H:%M:%S", ptr);
  270   if(gethostname(hostname, len) != 0) {
  271     hostname[0] = '-';
  272     hostname[1] = '\0';
  273   }
  274   if(start) {
  275     sprintf(line, "%s %s qssign: "QS_START, time_string, hostname);
  276   } else {
  277     sprintf(line, "%s %s qssign: "QS_END, time_string, hostname);
  278   }
  279   qs_write(line, line_size, sec, sec_len);
  280   return;
  281 }
  282 
  283 /*
  284  * 2013/11/13 17:38:41 [error] 6577#0: *1 open()
  285  */
  286 static void qs_end_ngx(const char *sec, int start) {
  287   int sec_len = strlen(sec);
  288   char line[MAX_LINE];
  289   int dig = atoi(SEQDIG);
  290   /* <data> ' ' <sequence number> '#' <hmac>*/
  291   int line_size = sizeof(line) - 1 - dig - 1 - (2*HMAC_MAX_MD_CBLOCK) - 1;
  292   char time_string[1024];
  293   time_t tm = time(NULL);
  294   struct tm *ptr = localtime(&tm);
  295   strftime(time_string, sizeof(time_string), "%Y/%m/%d %H:%M:%S", ptr);
  296   if(start) {
  297     sprintf(line, "%s [notice] 0#0: "QS_END, time_string);
  298   } else {
  299     sprintf(line, "%s [notice] 0#0: "QS_END, time_string);
  300   }
  301   qs_write(line, line_size, sec, sec_len);
  302   return;
  303 }
  304 
  305 void qs_signal_exit(int e) {
  306   if(m_logend && (m_end != NULL)) {
  307     m_end(m_sec, 0);
  308   }
  309   exit(0);
  310 }
  311 
  312 /**
  313  * Tries to find out a suiteable log line format which is used
  314  * to log sign end messages (so let the verifier known, that the
  315  * data ends nothing has been cut off).
  316  *
  317  * Sets the format to global variables.
  318  *
  319  * known pattern
  320  * - [Fri Dec 03 07:37:40 2010] [notice] .........
  321  * - 12.12.12.12 - - [03/Dec/2010:07:36:51 +0100] ...............
  322  * - 2010 12 03 17:00:30.425 qssign     end        0.0              5-NOTICE:  ..............
  323  *                                                 46  <- var ->    63      71
  324  * - Dec  6 04:00:06 localhost kernel:
  325  * - some 2010-12-03 17:00:30,425 ...
  326  *
  327  * @param s
  328  */
  329 static void qs_set_format(char *s) {
  330   regex_t r_apache_err; 
  331   regex_t r_apache_acc;
  332   regex_t r_nj;
  333   regex_t r_lx;
  334   regex_t r_ngx;
  335   if(regcomp(&r_apache_err,
  336          "^\\[[a-zA-Z]{3} [a-zA-Z]{3} [0-9]+ [0-9]+:[0-9]+:[0-9]+ [0-9]+\\] \\[[a-zA-Z]+\\] ",
  337          REG_EXTENDED) != 0) {
  338     fprintf(stderr, "failed to compile regex (err)\n");
  339     exit(1);
  340   }
  341   if(regcomp(&r_apache_acc,
  342          "^[0-9.]+ [a-zA-Z0-9\\@_\\.\\-]+ [a-zA-Z0-9\\@_\\.\\-]+ \\[[0-9]+/[a-zA-Z]{3}/[0-9:]+[0-9\\+ ]+\\] ",
  343          REG_EXTENDED) != 0) {
  344     fprintf(stderr, "failed to compile regex (acc)\n");
  345     exit(1);
  346   }
  347   if(regcomp(&r_nj,
  348          "^[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]+[ ]*:",
  349          REG_EXTENDED) != 0) {
  350     fprintf(stderr, "failed to compile regex (nj)\n");
  351     exit(1);
  352   }
  353   if(regcomp(&r_lx,
  354          "^[a-zA-Z]{3}[ ]+[0-9]+[ ]+[0-9]{2}:[0-9]{2}:[0-9]{2}[ ]+[a-zA-Z0-9_\\.\\-]+[ ]+[a-zA-Z0-9_\\.\\-]+:",
  355          REG_EXTENDED) != 0) {
  356     fprintf(stderr, "failed to compile regex (lx)\n");
  357     exit(1);
  358   }
  359   if(regcomp(&r_ngx,
  360          "^[0-9]{4}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \\[[a-z]+\\] [0-9]+#[0-9]+: ",
  361          REG_EXTENDED) != 0) {
  362     fprintf(stderr, "failed to compile regex (ngx)\n");
  363     exit(1);
  364   }
  365     
  366 
  367   if(regexec(&r_apache_err, s, 0, NULL, 0) == 0) {
  368     m_end = &qs_end_apache_err;
  369   } else if(regexec(&r_apache_acc, s, 0, NULL, 0) == 0) {
  370     m_end = &qs_end_apache_acc;
  371   } else if(regexec(&r_nj, s, 0, NULL, 0) == 0) {
  372     char *dp = strstr(s, ": ");
  373     if(dp) {
  374       /* calculate the "var" size, see comment above */
  375       m_end_pos = dp - s - 47 - 8 - 3;
  376       if((m_end_pos < 0) || (m_end_pos > 1000)) {
  377     m_end_pos = 0;
  378       }
  379     }
  380     m_end = &qs_end_nj;
  381   } else if(regexec(&r_lx, s, 0, NULL, 0) == 0) {
  382     m_end = &qs_end_lx;    
  383   } else if(regexec(&r_ngx, s, 0, NULL, 0) == 0) {
  384     m_end = &qs_end_ngx;    
  385   }
  386   // search within the generic yyyy-mm-dd hh-mm-ss,mmm patterns
  387   if(!m_end) {
  388     const qos_p_t *p = pattern;
  389     while(p->end_fmt) {
  390       regex_t r_j;
  391       if(regcomp(&r_j, p->pattern, REG_EXTENDED) != 0) {
  392     fprintf(stderr, "failed to compile regex (%s)\n", p->pattern);
  393     exit(1);
  394       }
  395       if(regexec(&r_j, s, 0, NULL, 0) == 0) {
  396     m_start_fmt = p->start_fmt;
  397     m_end_fmt = p->end_fmt;
  398     m_end = &qs_end_lj;      
  399     break;
  400       }
  401       p++;
  402     }
  403   }
  404   /* default (apache error log format) */
  405   if(m_end == NULL) {
  406     m_end = &qs_end_apache_err;
  407   }
  408   return;
  409 }
  410 
  411 /**
  412  * Process the data from stdin.
  413  *
  414  * @param sec Passphrase
  415  */
  416 static void qs_sign(const char *sec) {
  417   int sec_len = strlen(sec);
  418   char *line = calloc(1, MAX_LINE_BUFFER+1);
  419   int dig = atoi(SEQDIG);
  420   /* <data> ' ' <sequence number> '#' <hmac>*/
  421   int line_size = MAX_LINE_BUFFER - 1 - dig - 1 - (2*HMAC_MAX_MD_CBLOCK) - 1;
  422   int line_len;
  423   while(fgets(line, MAX_LINE_BUFFER, stdin) != NULL) {
  424     line_len = strlen(line) - 1;
  425     while(line_len > 0) { // cut tailing CR/LF
  426       if(line[line_len] >= ' ') {
  427     break;
  428       }
  429       line[line_len] = '\0';
  430       line_len--;
  431     }
  432     if(m_logend && (m_end == NULL)) {
  433       qs_set_format(line);
  434       m_end(m_sec, 1);
  435     }
  436     if(pcre_exec(m_filter, NULL, line, line_size, 0, 0, NULL, 0) >= 0) {
  437       printf("%s\n", line);
  438       fflush(stdout);
  439     } else {
  440       qs_write(line, line_size, sec, sec_len);
  441     }
  442   }
  443   return;
  444 }
  445 
  446 static int isSpecialLine(const char *line, const char *marker) {
  447   char *se_marker = strstr(line, marker);
  448   if(se_marker != NULL) {
  449     /* QS_END/START + " " + SEQDIG */
  450     int sz = strlen(marker) + 1 + atoi(SEQDIG);
  451     if(sz == (strlen(line) - (se_marker - line))) {
  452       return 1;
  453     }
  454   }
  455   return 0;
  456 }
  457 
  458 static long qs_verify(const char *sec) {
  459   int end_seen = 0;
  460   int sec_len = strlen(sec);
  461   long err = 0; // errors
  462   long lineNumber = 0; // line number of the file / input data
  463   char *line = calloc(1, MAX_LINE_BUFFER+1);
  464   int line_size = MAX_LINE_BUFFER;
  465   int line_len;
  466   m_nr = -1;        // expected sequence number (start with any)
  467   long nr_alt = -1; // alternatively expected sequence number (if a line was injected)
  468   long nr_alt_lineNumber = -1;
  469   long nr_usr1_lineNumber = -1; // we may have lines written by a prev. qssign binary (while graceful restart)
  470   while(fgets(line, line_size, stdin) != NULL) {
  471     int valid = 0;
  472     long msgSeqNr = 0;
  473     int isOldProcess = 0;
  474 #if OPENSSL_VERSION_NUMBER < 0x10100000L
  475     HMAC_CTX hmac;
  476     HMAC_CTX *hmac_p = &hmac;
  477 #else
  478     HMAC_CTX *hmac_p;
  479 #endif
  480     unsigned char data[HMAC_MAX_MD_CBLOCK];
  481     unsigned int len;
  482     char *m;
  483     int data_len;
  484     char *sig;
  485     char *seq;
  486     line_len = strlen(line) - 1;
  487     while(line_len > 0) { // cut tailing CR/LF
  488       if(line[line_len] >= ' ') {
  489     break;
  490       }
  491       line[line_len] = '\0';
  492       line_len--;
  493     }
  494     sig = strrchr(line, '#');
  495     seq = strrchr(line, ' ');
  496     lineNumber++;
  497     if(seq && sig) {
  498       sig[0] = '\0';
  499       sig++;
  500       /* verify hmac */
  501 #if OPENSSL_VERSION_NUMBER < 0x10100000L
  502       HMAC_CTX_init(hmac_p);
  503 #else
  504       hmac_p = HMAC_CTX_new();
  505 #endif
  506       HMAC_Init_ex(hmac_p, sec, sec_len, m_evp, NULL);
  507       HMAC_Update(hmac_p, (const unsigned char *)line, strlen(line));
  508       HMAC_Final(hmac_p, data, &len);
  509 #if OPENSSL_VERSION_NUMBER < 0x10100000L
  510       HMAC_CTX_cleanup(hmac_p);
  511 #else
  512       HMAC_CTX_free(hmac_p);
  513 #endif
  514       m = calloc(1, apr_base64_encode_len(len) + 1);
  515       data_len = apr_base64_encode(m, (char *)data, len);
  516       m[data_len] = '\0';
  517       if(strcmp(m, sig) != 0) {
  518     err++;
  519     fprintf(stderr, "ERROR on line %ld: invalid signature\n", lineNumber);
  520     /* message may be modified/currupt or inserted: next line may have
  521        the next sequence number (modified) or the same (inserted) */
  522     nr_alt = m_nr + 1;
  523     nr_alt_lineNumber = lineNumber + 1;
  524       } else {
  525     valid = 1;
  526       }
  527       free(m);
  528       /* verify sequence */
  529       seq++;
  530       msgSeqNr = atol(seq);
  531       if(msgSeqNr == 0) {
  532     err++;
  533     fprintf(stderr, "ERROR on line %ld: invalid sequence\n", lineNumber);
  534       } else {
  535     if(m_nr != -1) {
  536       if(lineNumber == nr_alt_lineNumber) {
  537         // last line was modfied
  538         if(m_nr != msgSeqNr) {
  539           // and therefore, we also accept the next seqence number
  540           m_nr = nr_alt;
  541         }
  542         nr_alt = -1;
  543         nr_alt_lineNumber = -1;
  544       }
  545       if(valid && isSpecialLine(line, QS_START)) {
  546         // new start line (graceful restart)
  547         // we expect now msg nummber 1
  548         // but still acept the old until we get the end marker
  549         nr_usr1_lineNumber = m_nr;
  550         m_nr = 1; 
  551       }
  552       if(valid && nr_usr1_lineNumber == msgSeqNr) {
  553         // msg from old process is okay...
  554         nr_usr1_lineNumber++;
  555         isOldProcess = 1;
  556       } else {
  557         if(m_nr != msgSeqNr) {
  558           if(msgSeqNr == 1) {
  559         if(!end_seen) {
  560           err++;
  561           fprintf(stderr, "ERROR on line %ld: wrong sequence, server restart? (expect %."SEQDIG"ld)\n",
  562               lineNumber, m_nr);
  563         }
  564           } else {
  565         err++;
  566         fprintf(stderr, "ERROR on line %ld: wrong sequence (expect %."SEQDIG"ld)\n", lineNumber, m_nr);
  567           }
  568         } else {
  569           // well done - this is the sequence number we expet
  570         }
  571       }
  572     } else if(m_logend) {
  573       // log should (if not rotated) start with message 1
  574       if(msgSeqNr != 1) {
  575         fprintf(stderr, "NOTICE: log starts with sequence %."SEQDIG"ld, log rotation?"
  576             " (expect %."SEQDIG"d)\n", msgSeqNr, 1);
  577       }
  578     }
  579     if(valid && !isOldProcess) {
  580       // adjust
  581       m_nr = msgSeqNr;
  582     }
  583       }
  584     } else {
  585       err++;
  586       fprintf(stderr, "ERROR on line %ld: missing signature/sequence\n", lineNumber);
  587     }
  588     end_seen = 0;
  589     if(valid) {
  590       if(!isOldProcess) {
  591     m_nr++;
  592       }
  593       if(isSpecialLine(line, QS_END)) {
  594     if(nr_usr1_lineNumber == -1) {
  595       end_seen = 1;
  596     } else {
  597       nr_usr1_lineNumber = -1; // no more messages from an old process
  598     }
  599       }
  600     }
  601   }
  602   if(m_logend && !end_seen) {
  603     fprintf(stderr, "NOTICE: no end marker seen, log rotation? (expect %."SEQDIG"ld)\n", m_nr);
  604   }
  605   return err;
  606 }
  607 
  608 static void usage(char *cmd, int man) {
  609   if(man) {
  610     //.TH [name of program] [section number] [center footer] [left footer] [center header]
  611     printf(".TH %s 1 \"%s\" \"mod_qos utilities %s\" \"%s man page\"\n", qs_CMD(cmd), man_date,
  612        man_version, cmd);
  613   }
  614   printf("\n");
  615   if(man) {
  616     printf(".SH NAME\n");
  617   }
  618   qs_man_print(man, "%s - an utility to sign and verify the integrity of log data.\n", cmd);
  619   printf("\n");
  620   if(man) {
  621     printf(".SH SYNOPSIS\n");
  622   }
  623   qs_man_print(man, "%s%s -s|S <secret> [-e] [-v] [-u <name>] [-f <regex>] [-a 'sha1'|'sha256']\n", man ? "" : "Usage: ", cmd);
  624   printf("\n");
  625   if(man) {
  626     printf(".SH DESCRIPTION\n");
  627   } else {
  628     printf("Summary\n");
  629   }
  630   qs_man_print(man, "%s is a log data integrity check tool. It reads log data\n", cmd);
  631   qs_man_print(man, "from stdin (pipe) and writes the data to stdout adding a sequence\n");
  632   qs_man_print(man, "number and signatur to ever log line.\n");
  633   printf("\n");
  634   if(man) {
  635     printf(".SH OPTIONS\n");
  636   } else {
  637     printf("Options\n");
  638   }
  639   if(man) printf(".TP\n");
  640   qs_man_print(man, "  -s <secret>\n");
  641   if(man) printf("\n");
  642   qs_man_print(man, "     Passphrase used to calculate signature.\n");
  643   if(man) printf("\n.TP\n");
  644   qs_man_print(man, "  -S <program>\n");
  645   if(man) printf("\n");
  646   qs_man_print(man, "     Specifies a program which writes the passphrase to stdout.\n");
  647   if(man) printf("\n.TP\n");
  648   qs_man_print(man, "  -e\n");
  649   if(man) printf("\n");
  650   qs_man_print(man, "     Writes start/end marker when starting/stopping data signing.\n");
  651   if(man) printf("\n.TP\n");
  652   qs_man_print(man, "  -v\n");
  653   if(man) printf("\n");
  654   qs_man_print(man, "     Verification mode checking the integrity of signed data.\n");
  655   if(man) printf("\n.TP\n");
  656   qs_man_print(man, "  -u <name>\n");
  657   if(man) printf("\n");
  658   qs_man_print(man, "     Becomes another user, e.g. www-data.\n");
  659   if(man) printf("\n.TP\n");
  660   qs_man_print(man, "  -f <regex>\n");
  661   if(man) printf("\n");
  662   qs_man_print(man, "     Filter pattern (case sensitive regular expression) for messages\n");
  663   qs_man_print(man, "     which do not need to be signed.\n");
  664   if(man) printf("\n.TP\n");
  665   qs_man_print(man, "  -a 'sha1'|'sha256'\n");
  666   if(man) printf("\n");
  667   qs_man_print(man, "     Specifes the algorithm to use. Default is sha1.\n");
  668   printf("\n");
  669   if(man) {
  670     printf(".SH EXAMPLE\n");
  671     printf("Sign:\n");
  672     printf("\n");
  673   } else {
  674     printf("Example (sign):\n");
  675   }
  676   qs_man_println(man, " TransferLog \"|/usr/bin/%s -s password -e |/usr/bin/qsrotate -o /var/log/apache/access.log\"\n", cmd);
  677   printf("\n");
  678   if(man) {
  679     printf("\n");
  680     printf("Verify:\n");
  681     printf("\n");
  682   } else {
  683     qs_man_print(man, "Example (verify):\n");
  684   }
  685   qs_man_println(man, " cat access.log | %s -s password -v\n", cmd);
  686   printf("\n");
  687   if(man) {
  688     printf(".SH SEE ALSO\n");
  689     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");
  690     printf(".SH AUTHOR\n");
  691     printf("Pascal Buchbinder, http://mod-qos.sourceforge.net/\n");
  692   } else {
  693     printf("See http://mod-qos.sourceforge.net/ for further details.\n");
  694   }
  695   if(man) {
  696     exit(0);
  697   } else {
  698     exit(1);
  699   }
  700 }
  701 
  702 int main(int argc, const char * const argv[]) {
  703   apr_pool_t *pool;
  704   int verify = 0;
  705   char *cmd = strrchr(argv[0], '/');
  706   const char *username = NULL;
  707   const char *filter = NULL;
  708   if(cmd == NULL) {
  709     cmd = (char *)argv[0];
  710   } else {
  711     cmd++;
  712   }
  713   apr_app_initialize(&argc, &argv, NULL);
  714   apr_pool_create(&pool, NULL);
  715   m_evp = EVP_sha1();
  716   argc--;
  717   argv++;
  718   while(argc >= 1) {
  719     if(strcmp(*argv,"-s") == 0) {
  720       if (--argc >= 1) {
  721     m_sec = *(++argv);
  722       }
  723     } else if(strcmp(*argv,"-S") == 0) {
  724       if (--argc >= 1) {
  725     m_sec = qs_readpwd(pool, *(++argv));
  726       } 
  727     } else if(strcmp(*argv,"-v") == 0) {
  728       verify = 1;
  729     } else if(strcmp(*argv,"-e") == 0) {
  730       m_logend = 1;
  731     } else if(strcmp(*argv,"-u") == 0) { /* switch user id */
  732       if (--argc >= 1) {
  733         username = *(++argv);
  734       }
  735     } else if(strcmp(*argv,"-f") == 0) { /* filter */
  736       if (--argc >= 1) {
  737         filter = *(++argv);
  738       }
  739     } else if(strcmp(*argv,"-a") == 0) { /* set alg */
  740       if (--argc >= 1) {
  741         const char *alg = *(++argv);
  742     if(strcasecmp(alg, "SHA256") == 0) {
  743       m_evp = EVP_sha256();
  744     } else if(strcasecmp(alg, "SHA1") != 0) {
  745       m_evp = NULL;
  746     }
  747       } else {
  748     m_evp = NULL;
  749       }
  750     } else if(strcmp(*argv,"-?") == 0) {
  751       usage(cmd, 0);
  752     } else if(strcmp(*argv,"-help") == 0) {
  753       usage(cmd, 0);
  754     } else if(strcmp(*argv,"--help") == 0) {
  755       usage(cmd, 0);
  756     } else if(strcmp(*argv,"--man") == 0) {
  757       usage(cmd, 1);
  758     }
  759     argc--;
  760     argv++;
  761   }
  762 
  763   if(filter != NULL) {
  764     const char *errptr = NULL;
  765     int erroffset;
  766     m_filter = pcre_compile(filter, 0, &errptr, &erroffset, NULL);
  767     if(m_filter == NULL) {
  768       fprintf(stderr, "failed to compile filter pattern <%s> at position %d,"
  769             " reason: %s\n", filter, erroffset, errptr);
  770       exit(1);
  771     }
  772   }
  773 
  774   if(m_evp == NULL) {
  775     usage(cmd, 0);
  776   }
  777     
  778   if(m_sec == NULL) {
  779     usage(cmd, 0);
  780   }
  781 
  782   qs_setuid(username, cmd);
  783 
  784   if(verify) {
  785     long err = qs_verify(m_sec);
  786     if(err != 0) {
  787       return 1;
  788     }
  789   } else {
  790     if(m_logend) {
  791       signal(SIGTERM, qs_signal_exit);
  792     }
  793     qs_sign(m_sec);
  794     if(m_logend && (m_end != NULL)) {
  795       m_end(m_sec, 0);
  796     }
  797   }
  798 
  799   apr_pool_destroy(pool);
  800   return 0;
  801 }