1 /* zxlog.c  -  Liberty oriented logging facility with log signing and encryption
2  * Copyright (c) 2012-2013 Synergetics (sampo@synergetics.be), All Rights Reserved.
3  * Copyright (c) 2010-2011 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
4  * Copyright (c) 2006-2009 Symlabs (symlabs@symlabs.com), All Rights Reserved.
5  * Author: Sampo Kellomaki (sampo@iki.fi)
6  * This is confidential unpublished proprietary source code of the author.
7  * NO WARRANTY, not even implied warranties. Contains trade secrets.
8  * Distribution prohibited unless authorized in writing.
9  * Licensed under Apache License 2.0, see file COPYING.
10  * $Id: zxlog.c,v 1.32 2009-11-24 23:53:40 sampo Exp $
11  *
12  * 18.11.2006, created --Sampo
13  * 10.10.2007, added ipport --Sampo
14  * 7.10.2008,  added inline documentation --Sampo
15  * 29.8.2009,  added hmac chaining field --Sampo
16  * 12.3.2010,  added per user logging facility --Sampo
17  * 9.9.2012,   added persist support --Sampo
18  * 30.11.2013, fixed seconds handling re gmtime_r() - found by valgrind --Sampo
19  * 18.12.2015, applied patch from soconnor, perceptyx --Sampo
20  *
21  * See also: Logging chapter in README.zxid
22  */
23 
24 #include "platform.h"  /* needed on Win32 for pthread_mutex_lock() et al. */
25 
26 #include <fcntl.h>
27 #include <string.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <errno.h>
32 #include <time.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 
36 #ifdef USE_OPENSSL
37 #include <openssl/x509.h>
38 #include <openssl/rsa.h>
39 #include <openssl/evp.h>
40 #include <openssl/aes.h>
41 #endif
42 
43 #include "errmac.h"
44 #include "zxid.h"
45 #include "zxidutil.h"  /* for zx_zlib_raw_deflate(), safe_basis_64, and name_from_path */
46 #include "zxidconf.h"
47 #include "c/zx-data.h"  /* Generated. If missing, run `make dep ENA_GEN=1' */
48 
49 /*() Allocate memory for logging purposes.
50  * Generally memory allocation goes via zx_alloc() family of functions. However
51  * dues to special requirements of cryptographically implemeted logging,
52  * we maintain this special allocation function (which backends to zx_alloc()).
53  * Among the special features: This function makes sure the buffer size is
54  * rounded up to multiple of nonce to accommodate block ciphers.
55  *
56  * This function is considered internal. Do not use unless you know what you are doing. */
57 
58 /* Called by:  zxlog_write_line x3 */
zxlog_alloc_zbuf(zxid_conf * cf,int * zlen,char * zbuf,int len,char * sig,int nonce)59 static char* zxlog_alloc_zbuf(zxid_conf* cf, int *zlen, char* zbuf, int len, char* sig, int nonce)
60 {
61   char* p;
62   int siz = nonce + 2 + len + *zlen;
63   ROUND_UP(siz, nonce);        /* Round up to block size */
64   p = ZX_ALLOC(cf->ctx, siz);
65   if (nonce)
66     zx_rand(p, nonce);
67   p[nonce] = (len >> 8) & 0xff;
68   p[nonce+1] = len & 0xff;
69   if (len) {
70     memcpy(p+nonce+2, sig, len);
71     ZX_FREE(cf->ctx, sig);
72   }
73   memcpy(p+nonce+2+len, zbuf, *zlen);
74   ZX_FREE(cf->ctx, zbuf);
75   *zlen += nonce + 2 + len;
76   return p;
77 }
78 
79 /*() Write a line to a log, taking care of all formalities of locking and
80 * observing all special options for signing and encryption of the logs.
81 * Not usually called directly (but you can if you want to), this is the
82 * work horse behind zxlog().
83 *
84 * cf::  ZXID configuration object, used for memory allocation.
85 * c_path:: Path to the log file, as C string
86 * encflags:: Encryption flags. See LOG_ERR or LOG_ACT configuration options in zxidconf.h
87 * n:: length of log data
88 * logbuf:: The data that should be logged
89 */
90 
91 /* Called by:  test_mode x12, zxlog_output x2 */
zxlog_write_line(zxid_conf * cf,char * c_path,int encflags,int n,const char * logbuf)92 void zxlog_write_line(zxid_conf* cf, char* c_path, int encflags, int n, const char* logbuf)
93 {
94   EVP_PKEY* log_sign_pkey;
95   struct rsa_st* rsa_pkey;
96   struct aes_key_st aes_key;
97   int len = 0, blen, zlen;
98   char sigletter = 'P';
99   char encletter = 'P';
100   char* p;
101   char* sig = 0;
102   char* zbuf;
103   char* b64;
104   char sigbuf[28+4];   /* Space for "SP " and sha1 */
105   char keybuf[16];
106   char ivec[16];
107   if (n == -2)
108     n = strlen(logbuf);
109   if (encflags & 0x70) {          /* Encrypt check */
110     zbuf = zx_zlib_raw_deflate(cf->ctx, n-1, logbuf, &zlen);
111     switch (encflags & 0x06) {     /* Sign check */
112     case 0x02:      /* Sx plain sha1 */
113       sigletter = 'S';
114       sig = ZX_ALLOC(cf->ctx, 20);
115       SHA1((unsigned char*)zbuf, zlen, (unsigned char*)sig);
116       len = 20;
117       break;
118     case 0x04:      /* Rx RSA-SHA1 signature */
119       sigletter = 'R';
120       LOCK(cf->mx, "logsign wrln");
121       if (!(log_sign_pkey = cf->log_sign_pkey))
122 	log_sign_pkey = cf->log_sign_pkey = zxid_read_private_key(cf, "logsign-nopw-cert.pem");
123       UNLOCK(cf->mx, "logsign wrln");
124       if (!log_sign_pkey)
125 	break;
126       len = zxsig_data(cf->ctx, zlen, zbuf, &sig, log_sign_pkey, "enc log line", cf->blobsig_digest_algo);
127       break;
128     case 0x06:      /* Dx DSA-SHA1 signature */
129       ERR("DSA-SHA1 sig not implemented in encrypted mode. Use RSA-SHA1 or none. %x", encflags);
130       break;
131     case 0: break;  /* Px no signing */
132     }
133 
134     switch (encflags & 0x70) {
135     case 0x10:  /* xZ RFC1951 zip + safe base64 */
136       encletter = 'Z';
137       zbuf = zxlog_alloc_zbuf(cf, &zlen, zbuf, len, sig, 0);
138       break;
139     case 0x20:  /* xA RSA-AES */
140       encletter = 'A';
141       zbuf = zxlog_alloc_zbuf(cf, &zlen, zbuf, len, sig, 16);
142       zx_rand(keybuf, 16);
143       AES_set_encrypt_key((unsigned char*)keybuf, 128, &aes_key);
144       memcpy(ivec, zbuf, sizeof(ivec));
145       AES_cbc_encrypt((unsigned char*)zbuf+16, (unsigned char*)zbuf+16, zlen-16, &aes_key, (unsigned char*)ivec, 1);
146       ROUND_UP(zlen, 16);        /* Round up to block size */
147 
148       LOCK(cf->mx, "logenc wrln");
149       if (!cf->log_enc_cert)
150 	cf->log_enc_cert = zxid_read_cert(cf, "logenc-nopw-cert.pem");
151       rsa_pkey = zx_get_rsa_pub_from_cert(cf->log_enc_cert, "log_enc_cert");
152       UNLOCK(cf->mx, "logenc wrln");
153       if (!rsa_pkey)
154 	break;
155 
156       len = RSA_size(rsa_pkey);
157       sig = ZX_ALLOC(cf->ctx, len);
158       if (RSA_public_encrypt(16, (unsigned char*)keybuf, (unsigned char*)sig, rsa_pkey, RSA_PKCS1_OAEP_PADDING) < 0) {
159 	ERR("RSA enc fail %x", encflags);
160 	zx_report_openssl_err("zxlog rsa enc");
161 	return;
162       }
163       p = ZX_ALLOC(cf->ctx, 2 + len + zlen);
164       p[0] = (len >> 8) & 0xff;
165       p[1] = len & 0xff;
166       memcpy(p+2, sig, len);
167       memcpy(p+2+len, zbuf, zlen);
168       ZX_FREE(cf->ctx, sig);
169       ZX_FREE(cf->ctx, zbuf);
170       zbuf = p;
171       zlen += 2 + len;
172       break;
173     case 0x30:  /* xT RSA-3DES */
174       encletter = 'T';
175       ERR("Enc not implemented %x", encflags);
176       break;
177     case 0x40:  /* xB AES */
178       encletter = 'B';
179       zbuf = zxlog_alloc_zbuf(cf, &zlen, zbuf, len, sig, 16);
180       if (!cf->log_symkey[0])
181 	zx_get_symkey(cf, "logenc.key", cf->log_symkey);
182       AES_set_encrypt_key((unsigned char*)cf->log_symkey, 128, &aes_key);
183       memcpy(ivec, zbuf, sizeof(ivec));
184       AES_cbc_encrypt((unsigned char*)zbuf+16, (unsigned char*)zbuf+16, zlen-16, &aes_key, (unsigned char*)ivec, 1);
185       ROUND_UP(zlen, 16);        /* Round up to block size */
186       break;
187     case 0x50:  /* xU 3DES */
188       encletter = 'U';
189       ERR("Enc not implemented %x", encflags);
190       break;
191     default:
192       ERR("Enc not implemented %x", encflags);
193       break;
194     }
195 
196     blen = SIMPLE_BASE64_LEN(zlen) + 3 + 1;
197     b64 = ZX_ALLOC(cf->ctx, blen);
198     b64[0] = sigletter;
199     b64[1] = encletter;
200     b64[2] = ' ';
201     p = base64_fancy_raw(zbuf, zlen, b64+3, safe_basis_64, 1<<31, 0, 0, '.');
202     blen = p-b64 + 1;
203     *p = '\n';
204     write2_or_append_lock_c_path(c_path, 0, 0, blen, b64, "zxlog enc", SEEK_END, O_APPEND);
205     return;
206   }
207 
208   /* Plain text, possibly signed. */
209 
210   switch (encflags & 0x06) {
211   case 0x02:   /* SP plain sha1 */
212     strcpy(sigbuf, "SP ");
213     sha1_safe_base64(sigbuf+3, n-1, logbuf);
214     sigbuf[3+27] = ' ';
215     len = 3+27+1;
216     p = sigbuf;
217     break;
218   case 0x04:   /* RP RSA-SHA1 signature */
219     LOCK(cf->mx, "logsign wrln");
220     if (!(log_sign_pkey = cf->log_sign_pkey))
221       log_sign_pkey = cf->log_sign_pkey = zxid_read_private_key(cf, "logsign-nopw-cert.pem");
222     UNLOCK(cf->mx, "logsign wrln");
223     if (!log_sign_pkey)
224       break;
225     zlen = zxsig_data(cf->ctx, n-1, logbuf, &zbuf, log_sign_pkey, "log line", cf->blobsig_digest_algo);
226     len = SIMPLE_BASE64_LEN(zlen) + 4;
227     sig = ZX_ALLOC(cf->ctx, len);
228     strcpy(sig, "RP ");
229     p = base64_fancy_raw(zbuf, zlen, sig+3, safe_basis_64, 1<<31, 0, 0, '.');
230     len = p-sig + 1;
231     *p = ' ';
232     p = sig;
233     break;
234   case 0x06:   /* DP DSA-SHA1 signature */
235     ERR("DSA-SHA1 signature not implemented %x", encflags);
236     break;
237   case 0:      /* Plain logging, no signing, no encryption. */
238     len = 5;
239     p = "PP - ";
240     break;
241   }
242   write2_or_append_lock_c_path(c_path, len, p, n, logbuf, "zxlog sig", SEEK_END, O_APPEND);
243   if (sig)
244     ZX_FREE(cf->ctx, sig);
245 }
246 
247 /*() Helper function for formatting all kinds of logs.
248  * This is the real workhorse. */
249 
zxlog_fmt(zxid_conf * cf,int len,char * logbuf,struct timeval * ourts,struct timeval * srcts,const char * ipport,struct zx_str * entid,struct zx_str * msgid,struct zx_str * a7nid,struct zx_str * nid,const char * sigval,const char * res,const char * op,const char * arg,const char * fmt,va_list ap)250 static int zxlog_fmt(zxid_conf* cf,   /* 1 */
251 		     int len, char* logbuf,
252 		     struct timeval* ourts,  /* 2 null allowed, will use current time */
253 		     struct timeval* srcts,  /* 3 null allowed, will use start of unix epoch... */
254 		     const char* ipport,     /* 4 null allowed, -:- or cf->ipport if not given */
255 		     struct zx_str* entid,   /* 5 null allowed, - if not given */
256 		     struct zx_str* msgid,   /* 6 null allowed, - if not given */
257 		     struct zx_str* a7nid,   /* 7 null allowed, - if not given */
258 		     struct zx_str* nid,     /* 8 null allowed, - if not given */
259 		     const char* sigval,     /* 9 null allowed, - if not given */
260 		     const char* res,        /* 10 */
261 		     const char* op,         /* 11 */
262 		     const char* arg,        /* 12 null allowed, - if not given */
263 		     const char* fmt,        /* 13 null allowed as format, ends the line */
264 		     va_list ap)
265 {
266   int n;
267   char* p;
268   char sha1_name[28];
269   struct tm ot;
270   struct tm st;
271   struct timeval ourtsdefault;
272   struct timeval srctsdefault;
273 
274   /* Prepare values */
275 
276   if (!ourts) {
277     ourts = &ourtsdefault;
278     GETTIMEOFDAY(ourts, 0);
279   }
280   if (!srcts) {
281     srcts = &srctsdefault;
282     srctsdefault.tv_sec = 0;
283     srctsdefault.tv_usec = 501000;
284   }
285   GMTIME_R(ourts->tv_sec, ot);
286   GMTIME_R(srcts->tv_sec, st);
287 
288   if (entid && entid->len && entid->s) {
289     sha1_safe_base64(sha1_name, entid->len, entid->s);
290     sha1_name[27] = 0;
291   } else {
292     sha1_name[0] = '-';
293     sha1_name[1] = 0;
294   }
295 
296   if (!ipport) {
297     ipport = cf->ipport;
298     if (!ipport)
299       ipport = "-:-";
300   }
301 
302   /* Format */
303 
304   n = snprintf(logbuf, len-3, ZXLOG_TIME_FMT " " ZXLOG_TIME_FMT
305 	       " %s %s"  /* ipport  sha1_name-of-ent */
306 	       " %.*s"
307 	       " %.*s"
308 	       " %.*s"
309 	       " %s %s %s %s %s ",
310 	       ZXLOG_TIME_ARG(ot, ourts->tv_usec), ZXLOG_TIME_ARG(st, srcts->tv_usec),
311 	       ipport, sha1_name,
312 	       msgid?msgid->len:1, msgid?msgid->s:"-",
313 	       a7nid?a7nid->len:1, a7nid?a7nid->s:"-",
314 	       nid?nid->len:1,     nid?nid->s:"-",
315 	       errmac_instance, STRNULLCHKD(sigval), res, op, arg?arg:"-");
316   logbuf[len-1] = 0; /* must terminate manually as on win32 nul is not guaranteed */
317   if (n <= 0 || n >= len-3) {
318     if (n < 0) platform_broken_snprintf(n, __FUNCTION__, len-3, "log line");
319     D("Log buffer too short: %d chars needed", n);
320     if (n <= 0)
321       n = 0;
322     else
323       n = len-3;
324   } else { /* Space left: try printing the format string as well! */
325     p = logbuf+n;
326     if (fmt && fmt[0]) {
327       n = vsnprintf(p, len-n-2, fmt, ap);
328       logbuf[len-1] = 0;  /* must terminate manually as on win32 nul term is not guaranteed */
329       if (n <= 0 || n >= len-(p-logbuf)-2) {
330 	if (n < 0) platform_broken_snprintf(n, __FUNCTION__, len-n-2, fmt);
331 	D("Log buffer truncated during format print: %d chars needed", n);
332 	if (n <= 0)
333 	  n = p-logbuf;
334 	else
335 	  n = len-(p-logbuf)-2;
336       } else
337 	n += p-logbuf;
338     } else {
339       logbuf[n++] = '-';
340     }
341   }
342   logbuf[n++] = '\n';
343   logbuf[n] = 0;
344   /*logbuf[len-1] = 0;*/
345   return n;
346 }
347 
348 /*() Figure out which log file should receive the message */
349 
350 /* Called by: */
zxlog_output(zxid_conf * cf,int n,const char * logbuf,const char * res)351 static int zxlog_output(zxid_conf* cf, int n, const char* logbuf, const char* res)
352 {
353   char c_path[ZXID_MAX_BUF];
354   DD("LOG(%.*s)", n-1, logbuf);
355   if ((cf->log_err_in_act || res[0] == 'K') && cf->log_act) {
356     name_from_path(c_path, sizeof(c_path), "%s" ZXID_LOG_DIR "act", cf->cpath);
357     zxlog_write_line(cf, c_path, cf->log_act, n, logbuf);
358   }
359   if (cf->log_err && (cf->log_act_in_err || res[0] != 'K')) {  /* If enabled, everything goes to err */
360     name_from_path(c_path, sizeof(c_path), "%s" ZXID_LOG_DIR "err", cf->cpath);
361     zxlog_write_line(cf, c_path, cf->log_err, n, logbuf);
362   }
363   return 0;
364 }
365 
366 /*(i) Log to activity and/or error log depending on ~res~ and configuration settings.
367  * This is the main audit logging function you should call. Please see <<link:../../html/zxid-log.html: zxid-log.pd>>
368  * for detailed description of the log format and features. See <<link:../../html/zxid-conf.html: zxid-conf.pd>> for
369  * configuration options governing the logging. (*** check the links)
370  *
371  * Proper audit trail is essential for any high value transactions based on SSO. Also
372  * some SAML protocol Processing Rules, such as duplicate detection, depend on the
373  * logging.
374  *
375  * cf     (1)::  ZXID configuration object, used for configuration options and memory allocation
376  * ourts  (2)::  Timestamp as observed by localhost. Typically the wall clock
377  *     time. See gettimeofday(3)
378  * srcts  (3)::  Timestamp claimed by the message to which the log entry pertains
379  * ipport (4)::  IP address and port number from which the message appears to have originated
380  * entid  (5)::  Entity ID to which the message pertains, usually the issuer. Null ok.
381  * msgid  (6)::  Message ID, can be used for correlation to establish audit trail continuity
382  *     from request to response. Null ok.
383  * a7nid  (7)::  Assertion ID, if message contained assertion (outermost and first
384  *     assertion if there are multiple relevant assertions). Null ok.
385  * nid    (8)::  Name ID pertaining to the message
386  * sigval (9)::  Signature validation letters
387  * res   (10)::  Result letters
388  * op    (11)::  Operation code for the message
389  * arg   (12)::  Operation specific argument
390  * fmt, ...  ::  Free format message conveying additional information
391  * return:: 0 on success, nonzero on failure (often ignored as zxlog() is very
392  *     robust and rarely fails - and when it does, situation is so hopeless that
393  *     you would not be able to report its failure anyway)
394  */
395 
396 /* Called by:  zxid_an_page_cf, zxid_anoint_sso_a7n, zxid_anoint_sso_resp, zxid_chk_sig, zxid_decode_redir_or_post x2, zxid_fed_mgmt_cf, zxid_get_ent_by_sha1_name, zxid_get_ent_ss, zxid_get_meta x2, zxid_idp_dispatch, zxid_idp_select_zxstr_cf_cgi, zxid_idp_soap_dispatch x2, zxid_idp_soap_parse, zxid_parse_conf_raw, zxid_parse_meta, zxid_saml_ok x2, zxid_simple_render_ses, zxid_simple_ses_active_cf, zxid_sp_anon_finalize, zxid_sp_deref_art x5, zxid_sp_dig_sso_a7n x2, zxid_sp_dispatch, zxid_sp_meta, zxid_sp_mni_redir, zxid_sp_mni_soap, zxid_sp_slo_redir, zxid_sp_slo_soap, zxid_sp_soap_dispatch x2, zxid_sp_soap_parse, zxid_sp_sso_finalize x2, zxid_start_sso_url x3 */
zxlog(zxid_conf * cf,struct timeval * ourts,struct timeval * srcts,const char * ipport,struct zx_str * entid,struct zx_str * msgid,struct zx_str * a7nid,struct zx_str * nid,const char * sigval,const char * res,const char * op,const char * arg,const char * fmt,...)397 int zxlog(zxid_conf* cf,   /* 1 */
398 	  struct timeval* ourts,  /* 2 null allowed, will use current time */
399 	  struct timeval* srcts,  /* 3 null allowed, will use start of unix epoch + 501 usec */
400 	  const char* ipport,     /* 4 null allowed, -:- or cf->ipport if not given */
401 	  struct zx_str* entid,   /* 5 null allowed, - if not given */
402 	  struct zx_str* msgid,   /* 6 null allowed, - if not given */
403 	  struct zx_str* a7nid,   /* 7 null allowed, - if not given */
404 	  struct zx_str* nid,     /* 8 null allowed, - if not given */
405 	  const char* sigval,     /* 9 null allowed, - if not given */
406 	  const char* res,        /* 10 */
407 	  const char* op,         /* 11 */
408 	  const char* arg,        /* 12 null allowed, - if not given */
409 	  const char* fmt, ...)   /* 13 null allowed as format, ends the line w/o further ado */
410 {
411   int n;
412   char logbuf[1024];
413   va_list ap;
414 
415   /* Avoid computation if logging is hopeless. */
416 
417   if (!((cf->log_err_in_act || res[0] == 'K') && cf->log_act)
418       && !(cf->log_err && res[0] != 'K')) {
419     return 0;
420   }
421 
422   va_start(ap, fmt);
423   n = zxlog_fmt(cf, sizeof(logbuf), logbuf,
424 		ourts, srcts, ipport, entid, msgid, a7nid, nid, sigval, res,
425 		op, arg, fmt, ap);
426   va_end(ap);
427   return zxlog_output(cf, n, logbuf, res);
428 }
429 
430 /*() Log to activity and/or error log depending on ~res~ and configuration settings.
431  * This variant uses the ses object to extract many of the log fields. These fields
432  * were populated to ses by zxid_wsp_validate()
433  */
434 
zxlogwsp(zxid_conf * cf,zxid_ses * ses,const char * res,const char * op,const char * arg,const char * fmt,...)435 int zxlogwsp(zxid_conf* cf,    /* 1 */
436 	     zxid_ses* ses,    /* 2 */
437 	     const char* res,  /* 3 */
438 	     const char* op,   /* 4 */
439 	     const char* arg,  /* 5 null allowed, - if not given */
440 	     const char* fmt, ...)   /* 13 null allowed as format, ends the line w/o further ado */
441 {
442   int n;
443   char logbuf[1024];
444   va_list ap;
445 
446   /* Avoid computation if logging is hopeless. */
447 
448   if (!((cf->log_err_in_act || res[0] == 'K') && cf->log_act)
449       && !(cf->log_err && res[0] != 'K')) {
450     return 0;
451   }
452 
453   va_start(ap, fmt);
454   n = zxlog_fmt(cf, sizeof(logbuf), logbuf,
455 		0, ses?&ses->srcts:0, ses?ses->ipport:0,
456 		ses?ses->issuer:0, ses?ses->wsp_msgid:0,
457 		ses&&ses->a7n?&ses->a7n->ID->g:0,
458 		ses?ZX_GET_CONTENT(ses->nameid):0,
459 		ses&&ses->sigres?&ses->sigres:"-", res,
460 		op, arg, fmt, ap);
461   va_end(ap);
462   return zxlog_output(cf, n, logbuf, res);
463 }
464 
465 /*() Log user specific data */
466 
zxlogusr(zxid_conf * cf,const char * uid,struct timeval * ourts,struct timeval * srcts,const char * ipport,struct zx_str * entid,struct zx_str * msgid,struct zx_str * a7nid,struct zx_str * nid,const char * sigval,const char * res,const char * op,const char * arg,const char * fmt,...)467 int zxlogusr(zxid_conf* cf,   /* 1 */
468 	     const char* uid,
469 	     struct timeval* ourts,  /* 2 null allowed, will use current time */
470 	     struct timeval* srcts,  /* 3 null allowed, will use start of unix epoch + 501 usec */
471 	     const char* ipport,     /* 4 null allowed, -:- or cf->ipport if not given */
472 	     struct zx_str* entid,   /* 5 null allowed, - if not given */
473 	     struct zx_str* msgid,   /* 6 null allowed, - if not given */
474 	     struct zx_str* a7nid,   /* 7 null allowed, - if not given */
475 	     struct zx_str* nid,     /* 8 null allowed, - if not given */
476 	     const char* sigval,     /* 9 null allowed, - if not given */
477 	     const char* res,        /* 10 */
478 	     const char* op,         /* 11 */
479 	     const char* arg,        /* 12 null allowed, - if not given */
480 	     const char* fmt, ...)   /* 13 null allowed as format, ends the line w/o further ado */
481 {
482   int n;
483   char logbuf[1024];
484   char c_path[ZXID_MAX_BUF];
485   va_list ap;
486 
487   if (!uid) {
488     ERR("NULL uid argument %p", cf);
489     return 1;
490   }
491 
492   va_start(ap, fmt);
493   n = zxlog_fmt(cf, sizeof(logbuf), logbuf,
494 		ourts, srcts, ipport, entid, msgid, a7nid, nid, sigval, res,
495 		op, arg, fmt, ap);
496   va_end(ap);
497 
498   /* Output stage */
499 
500   D("UID(%s) LOG(%.*s)", uid, n-1, logbuf);
501   name_from_path(c_path, sizeof(c_path), "%s" ZXID_UID_DIR "%s/.log", cf->cpath, uid);
502   zxlog_write_line(cf, c_path, cf->log_act, n, logbuf);
503   return 0;
504 }
505 
506 /*(-) Create a directory and perform error checking. */
507 
508 /* Called by:  zxlog_path x3 */
zx_create_dir_with_check(zxid_conf * cf,const char * dir,int create_dirs)509 static int zx_create_dir_with_check(zxid_conf* cf, const char* dir, int create_dirs)
510 {
511   struct stat st;
512   if (stat(dir, &st)) {
513     if (create_dirs) {
514       if (MKDIR(dir, 0777)) {
515 	ERR("mkdir path(%s) failed: %d %s; euid=%d egid=%d", dir, errno, STRERROR(errno), geteuid(), getegid());
516 	return 0;
517       }
518     } else {
519       ERR("directory missing path(%s) and no create_dirs (stat: %d %s; euid=%d egid=%d)", dir, errno, STRERROR(errno), geteuid(), getegid());
520       return 0;
521     }
522   }
523   return 1;
524 }
525 
526 /*() Compute path for logging. Optionally attempt to create the necessary
527  * directories if they are missing (you should do `zxcot -dirs' rather than
528  * depend on this).
529  *
530  * cf::     ZXID configuration object uded for deternining root if the logging
531  *     hierarchy, see PATH configuration option. Also used for memory allocation.
532  * entid::  Issuer or target entity ID. For wire messages the URL.
533  * objid::  AssertionID or MessageID. For wire messages the payload.
534  * dir::    Directory prefix indicating branch of audit trail ("rely/" or "issue/")
535  * kind::   Kind of object, used as path component ("/a7n/" or "/msg/")
536  * create_dirs::  Flag: should creating directories be attempted. Usually 1 if intent
537  *     is to write a file to the computed path. Usually 0 if the intent is to read.
538  * return:: The path, as zx_str or 0 if failure */
539 
540 /* Called by:  zxbus_send_cmdf, zxid_anoint_a7n, zxid_anoint_sso_resp, zxid_decode_redir_or_post x2, zxid_saml2_post_enc, zxid_saml2_redir_enc, zxid_soap_cgi_resp_body, zxid_sp_sso_finalize, zxid_sso_issue_jwt, zxid_wsc_valid_re_env, zxid_wsf_validate_a7n, zxid_wsp_validate */
zxlog_path(zxid_conf * cf,struct zx_str * entid,struct zx_str * objid,const char * dir,const char * kind,int create_dirs)541 struct zx_str* zxlog_path(zxid_conf* cf,
542 			  struct zx_str* entid,  /* issuer or target entity ID */
543 			  struct zx_str* objid,  /* AssertionID or MessageID */
544 			  const char* dir,       /* rely/ or issue/ */
545 			  const char* kind,      /* /a7n/ or /msg/ */
546 			  int create_dirs)
547 {
548   struct stat st;
549   int dir_len = strlen(dir);
550   int kind_len = strlen(kind);
551   int len = cf->cpath_len + sizeof("log/")-1 + dir_len + 27 + kind_len + 27;
552   char* s = ZX_ALLOC(cf->ctx, len+1);
553   char* p;
554 
555   if (!entid) {
556     ERR("No EntityID supplied %p dir(%s) kind(%s)", objid, STRNULLCHK(dir), STRNULLCHK(kind));
557     ZX_FREE(cf->ctx, s);
558     return 0;
559   }
560 
561   memcpy(s, cf->cpath, cf->cpath_len);
562   p = s + cf->cpath_len;
563   memcpy(p, "log/", sizeof("log/"));
564   p += sizeof("log/")-1;
565   if (stat(s, &st)) {
566     ERR("zxid log directory missing path(%s): giving up (stat: %d %s; euid=%d egid=%d). Consider checking permissions and running zxcot -dirs", s, errno, STRERROR(errno), geteuid(), getegid());
567     goto nodir;
568   }
569 
570   memcpy(p, dir, dir_len+1);
571   p += dir_len;
572   if (!zx_create_dir_with_check(cf, s, create_dirs)) goto nodir;
573 
574   sha1_safe_base64(p, entid->len, entid->s);
575   p[27] = 0;
576   p+=27;
577   if (!zx_create_dir_with_check(cf, s, create_dirs)) goto nodir;
578 
579   memcpy(p, kind, kind_len+1);
580   p += kind_len;
581   if (!zx_create_dir_with_check(cf, s, create_dirs)) goto nodir;
582 
583   sha1_safe_base64(p, objid->len, objid->s);
584   p[27] = 0;
585   p+=27;
586   return zx_ref_len_str(cf->ctx, len, s);
587  nodir:
588   ZX_FREE(cf->ctx, s);
589   return 0;
590 }
591 
592 /*() Check if file by path already exist.
593  * Since each uniquely ID'd object has unique path, mere existence of a file
594  * serves as duplicate ID check. This is used to satisfy some SAML processing rule
595  * requirements such as duplicate ID check for assertions.
596  *
597  * cf::      ZXID configuration object, used for memory allocation
598  * path::    Path where file is to be written, usually from zxlog_path()
599  * logkey::  String that will help to identify reason of failure
600  * return::  0 if no duplicate (success), 1 if duplicate (failure)
601  */
602 
603 /* Called by:  zxid_anoint_a7n, zxid_anoint_sso_resp, zxid_decode_redir_or_post x2, zxid_saml2_post_enc, zxid_saml2_redir_enc, zxid_soap_cgi_resp_body, zxid_sp_sso_finalize, zxid_sso_issue_jwt, zxid_wsc_valid_re_env, zxid_wsf_validate_a7n, zxid_wsp_validate */
zxlog_dup_check(zxid_conf * cf,struct zx_str * path,const char * logkey)604 int zxlog_dup_check(zxid_conf* cf, struct zx_str* path, const char* logkey)
605 {
606   struct stat st;
607   if (!cf || !path || !logkey) {
608     ERR("Missing config, path, or logkey argument %p %p (programmer error)", path, logkey);
609     return 0;
610   }
611   /* We need a c path, but get zx_str. However, the zx_str will come from zxlog_path()
612    * so we should be having the nul termination as needed. Just checking. */
613   ASSERTOPI(path->s[path->len], ==, 0);
614   if (!stat(path->s, &st)) {
615     ERR("Duplicate %s path(%.*s)", logkey, path->len, path->s);
616     zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "C", "EDUP", path->s, "%s", logkey);
617     return 1;
618   }
619   return 0;
620 }
621 
622 /*() Write a blob of content to log file according to logflag (see zxidconf.h). If
623  * the file already exists, i.e. there is a duplicate, the data is simply appended.
624  * When logging objects such as assertions, the duplicate check should be done
625  * as preprocessing step, see example below.
626  *
627  * cf::      ZXID configuration object, used for memory allocation
628  * logflag:: 0 if logging should not happen, 1 for normal logging, other values reserved
629  * path::    Path where file is to be written, usually from zxlog_path()
630  * blob::    The data to be logged.
631  * lk::      Log key. Indicates which part of the program invoked the logging function.
632  * return::  0 if no log written (failure or logflag false), 1 if log written. Often ignored.
633  *
634  * *Example*
635  *
636  *   logpath = zxlog_path(cf, issuer, a7n->ID, "rely/", "/a7n/", 1);
637  *   if (logpath) {
638  *     if (zxlog_dup_check(cf, logpath, "SSO assertion")) {
639  *       zxlog_blob(cf, cf->log_rely_a7n, logpath, zx_easy_enc_elem_sig(cf,&a7n->gg), "E");
640  *       goto erro;
641  *     }
642  *     zxlog_blob(cf, cf->log_rely_a7n, logpath, zx_easy_enc_elem_sig(cf, a7n), "OK");
643  *   }
644  *
645  * In the above example we determine the logpath and check for the duplicate and then log even
646  * if duplicate. The logic of this is that in case of duplicate, the audit trail
647  * captures both the original and the duplicate assertion (the logging is an append),
648  * which may have forensic value. */
649 
650 /* Called by:  zxbus_send_cmdf, zxid_anoint_a7n x2, zxid_anoint_sso_resp x2, zxid_decode_redir_or_post x2, zxid_saml2_post_enc x2, zxid_saml2_redir_enc x2, zxid_soap_cgi_resp_body x2, zxid_sp_sso_finalize x2, zxid_sso_issue_jwt x2, zxid_wsc_valid_re_env x2, zxid_wsf_validate_a7n x2, zxid_wsp_validate x2 */
zxlog_blob(zxid_conf * cf,int logflag,struct zx_str * path,struct zx_str * blob,const char * lk)651 int zxlog_blob(zxid_conf* cf, int logflag, struct zx_str* path, struct zx_str* blob, const char* lk)
652 {
653   if (!logflag || !blob)
654     return 0;
655   if (logflag != 1) {
656     ERR("Unimplemented blob logging format: %x", logflag);
657     return 0;
658   }
659 
660   /* We need a c path, but get zx_str. However, the zx_str will come from zxlog_path()
661    * so we should be having the nul termination as needed. Just checking. */
662   D("%s: LOGBLOB15(%.*s) len=%d path(%.*s)", lk, MIN(blob->len,15), blob->s, blob->len, path->len, path->s);
663   DD("%s: LOGBLOB(%.*s)", lk, blob->len, blob->s);
664   ASSERTOPI(path->s[path->len], ==, 0);
665   if (!write2_or_append_lock_c_path(path->s, blob->len, blob->s, 0, 0, "zxlog blob", SEEK_END,O_APPEND)) {
666     zxlog(cf, 0, 0, 0, 0, 0, 0, 0, "N", "S", "EFILE", 0, "Could not write blob. Permissions?");
667   }
668   return 1;
669 }
670 
671 #define XML_LOG_FILE ZXID_PATH "log/xml.dbg"
672 FILE* zx_xml_debug_log = 0;
673 int zx_xml_debug_log_err = 0;
674 int zxlog_seq = 0;
675 
676 #if !defined(USE_STDIO) && !defined(MINGW)
677 /* *** Static initialization of struct flock is suspect since man fcntl() documentation
678  * does not guarantee ordering of the fields, or that they would be the first fields.
679  * On Linux-2.4 and 2.6 as well as Solaris-8 the ordering is as follows, but this needs
680  * to be checked on other platforms.
681  *                       l_type,  l_whence, l_start, l_len */
682 extern struct flock errmac_rdlk; /* = { F_RDLCK, SEEK_SET, 0, 1 };*/
683 extern struct flock errmac_wrlk; /* = { F_WRLCK, SEEK_SET, 0, 1 };*/
684 extern struct flock errmac_unlk; /* = { F_UNLCK, SEEK_SET, 0, 1 };*/
685 #endif
686 
687 /* Called by:  errmac_debug_xml_blob */
zx_open_xml_log_file(zxid_conf * cf)688 static FILE* zx_open_xml_log_file(zxid_conf* cf)
689 {
690   FILE* f;
691   char buf[ZXID_MAX_DIR];
692   if (!cf||!cf->cpath) {
693     strncpy(buf, XML_LOG_FILE, sizeof(buf));
694   } else {
695     snprintf(buf, sizeof(buf)-1, "%slog/xml.dbg", cf->cpath);
696     buf[sizeof(buf)-1]=0;
697   }
698   f = fopen(buf, "a+");
699   if (!f) {  /* If it did not work out, do not insist. */
700     perror(buf);
701     ERR("Can't open for appending %s: %d %s; euid=%d egid=%d", buf, errno, STRERROR(errno), geteuid(), getegid());
702     zx_xml_debug_log_err = 1;
703     return 0;
704   }
705   D("OPEN BLOB LOG: tailf %s | ./xml-pretty.pl", buf);
706   return f;
707 }
708 
709 /*() Log a blob of XML data to auxiliary log file. This avoids
710  * mega clutter in the main debug logs. You are supposed
711  * to view this file with:
712  * tailf /var/zxid/log/xml.dbg | ./xml-pretty.pl
713  *
714  * cf:: Config (and memory allocation) object
715  * file:: Source code file, see __FILE__ in D_XML_BLOB() macro, in errmac.h
716  * line:: Source code line number, see __LINE__ in D_XML_BLOB()
717  * func:: Source code function name, see __FUNCTION__ in D_XML_BLOB()
718  * lk:: Log key
719  * len:: Length of the blob, or -1 for error or -2 to use strlen()
720  * xml:: blob data (not always XML)
721  */
722 
723 /* Called by: */
errmac_debug_xml_blob(zxid_conf * cf,const char * file,int line,const char * func,const char * lk,int len,const char * xml)724 void errmac_debug_xml_blob(zxid_conf* cf, const char* file, int line, const char* func, const char* lk, int len, const char* xml)
725 {
726   int bdy_len;
727   const char* bdy;
728   const char* p;
729   const char* q;
730   if (!(errmac_debug & ERRMAC_XMLDBG) || len == -1 || !xml)
731     return;
732   if (len == -2)
733     len = strlen(xml);
734 
735   /* Detect body */
736 
737   for (p = xml; p; p+=4) {
738     p = strstr(p, "Body");
739     if (!p) {
740 nobody:
741       bdy = xml;
742       bdy_len = 40;
743       goto print_it;
744     }
745     if (p > xml && ONE_OF_2(p[-1], '<', ':') && ONE_OF_5(p[4], '>', ' ', '\t', '\r', '\n'))
746       break; /* Opening <Body> detected. */
747   }
748   if (!p)
749     goto nobody;
750 
751   p = strchr(p+4, '>');  /* Scan for close of opening <Body */
752   if (!p)
753     goto nobody;
754 
755   for (q = ++p; q; q+=5) {
756     q = strstr(q, "Body>");
757     if (!q)
758       goto nobody;  /* Missing closing </Body> tag */
759     if (ONE_OF_2(q[-1], '<', ':'))
760       break;
761   }
762   for (--q; *q != '<'; --q) ;  /* Scan for the start of </Body>, skipping any namespace prefix */
763   bdy = p;
764   bdy_len = MIN(q-p, 100);
765 
766 print_it:
767   ++zxlog_seq;
768 #ifdef USE_PTHREAD
769 # ifdef USE_AKBOX_FN
770   fprintf(stderr, "%d.%lx %04x:%-3d %s d %s%s(%.*s) len=%d %d:%d\n", getpid(), (long)pthread_self(), akbox_fn(func), __LINE__, ERRMAC_INSTANCE, errmac_indent, lk, bdy_len, bdy, len, getpid(), zxlog_seq);
771 # else
772   fprintf(stderr, "%d.%lx %10s:%-3d %-16s %s d %s%s(%.*s) len=%d %d:%d\n", getpid(), (long)pthread_self(), file, line, func, ERRMAC_INSTANCE, errmac_indent, lk, bdy_len, bdy, len, getpid(), zxlog_seq);
773 # endif
774 #else
775 # ifdef USE_AKBOX_FN
776   fprintf(stderr, "%d %04x:%-3d %s d %s%s(%.*s) len=%d %d:%d\n", getpid(), akbox_fn(func), __LINE__, ERRMAC_INSTANCE, errmac_indent, lk, bdy_len, bdy, len, getpid(), zxlog_seq);
777 # else
778   fprintf(stderr, "%d %10s:%-3d %-16s %s d %s%s(%.*s) len=%d %d:%d\n", getpid(), file, line, func, ERRMAC_INSTANCE, errmac_indent, lk, bdy_len, bdy, len, getpid(), zxlog_seq);
779 # endif
780 #endif
781 
782   if (!zx_xml_debug_log) {
783     if (zx_xml_debug_log_err)
784       return;
785     zx_xml_debug_log = zx_open_xml_log_file(cf);
786     if (!zx_xml_debug_log)
787       return;
788   }
789 
790   if (FLOCKEX(fileno(zx_xml_debug_log)) == -1) {
791     ERR("Locking exclusively file `%s' failed: %d %s. Check permissions and that the file system supports locking. euid=%d egid=%d", XML_LOG_FILE, errno, STRERROR(errno), geteuid(), getegid());
792     /* Fall thru to print without locking */
793   }
794 #ifdef USE_PTHREAD
795 # ifdef USE_AKBOX_FN
796   fprintf(zx_xml_debug_log, "<!-- XMLBEG %d.%lx:%d %04x:%-3d %s d %s %s len=%d -->\n%.*s\n<!-- XMLEND %d.%lx:%d %s -->\n", getpid(), (long)pthread_self(), zxlog_seq, akbox_fn(func), line, ERRMAC_INSTANCE, errmac_indent, lk, len, len, xml, getpid(), (long)pthread_self(), zxlog_seq, lk);
797 # else
798   fprintf(zx_xml_debug_log, "<!-- XMLBEG %d.%lx:%d %10s:%-3d %-16s %s d %s %s len=%d -->\n%.*s\n<!-- XMLEND %d.%lx:%d %s -->\n", getpid(), (long)pthread_self(), zxlog_seq, file, line, func, ERRMAC_INSTANCE, errmac_indent, lk, len, len, xml, getpid(), (long)pthread_self(), zxlog_seq, lk);
799 # endif
800 #else
801 # ifdef USE_AKBOX_FN
802   fprintf(zx_xml_debug_log, "<!-- XMLBEG %d:%d %04x:%-3d %s d %s %s len=%d -->\n%.*s\n<!-- XMLEND %d:%d %s -->\n", getpid(), zxlog_seq, akbox_fn(func), line, ERRMAC_INSTANCE, errmac_indent, lk, len, len, xml, getpid(), zxlog_seq, lk);
803 # else
804   fprintf(zx_xml_debug_log, "<!-- XMLBEG %d:%d %10s:%-3d %-16s %s d %s %s len=%d -->\n%.*s\n<!-- XMLEND %d:%d %s -->\n", getpid(), zxlog_seq, file, line, func, ERRMAC_INSTANCE, errmac_indent, lk, len, len, xml, getpid(), zxlog_seq, lk);
805 # endif
806 #endif
807   fflush(zx_xml_debug_log);
808   FUNLOCK(fileno(zx_xml_debug_log));
809 }
810 
811 /*() Generate a timestamped receipt for data.
812  * Typically used for issuing receipts on audit bus. The current time
813  * and our own signing certificate are used.
814  *
815  * cf::         ZXID configuration object, used for memory allocation and cert mgmt
816  * sigbuf_len:: Maximum length of signature buffer, e.g. 1024. On return buffer is nul terminated.
817  * sigbuf::     Result parameter. Caller allocated buffer that receives the receipt. nul term.
818  * mid_len::    Length of message id to issue receipt about (-1 to use strlen(mid))
819  * mid::        Message ID to issue receipt about, will be part of signature.
820  * dest_len::   Length of destination to issue receipt about (-1 to use strlen(dest))
821  * dest::       Destination channel to issue receipt about, will be signed.
822  * eid_len::    Length of entity id to issue receipt to (-1 to use strlen(eid))
823  * eid::        Entity ID to issue receipt about, will be part of signature.
824  * body_len::   Length of data to issue receipt about (-1 to use strlen(body))
825  * body::       Data to issue receipt about, i.e. data that will be signed.
826  * return::     sigbuf. If there was error, first character of sigbuf is set to 'E' */
827 
828 /* Called by:  stomp_send_receipt, test_receipt x9 */
zxbus_mint_receipt(zxid_conf * cf,int sigbuf_len,char * sigbuf,int mid_len,const char * mid,int dest_len,const char * dest,int eid_len,const char * eid,int body_len,const char * body)829 char* zxbus_mint_receipt(zxid_conf* cf, int sigbuf_len, char* sigbuf, int mid_len, const char* mid, int dest_len, const char* dest, int eid_len, const char* eid, int body_len, const char* body)
830 {
831   int len, zlen;
832   char* zbuf = 0;
833   char* p;
834   char* buf;
835   struct tm ot;
836   struct timeval ourts;
837 
838   if (!mid)
839     mid_len = 0;
840   if (mid_len == -1)
841     mid_len = strlen(mid);
842   else if (mid_len == -2)
843     mid_len = strchr(mid, '\n') - mid;
844 
845   if (!dest)
846     dest_len = 0;
847   if (dest_len == -1)
848     dest_len = strlen(dest);
849   else if (dest_len == -2)
850     dest_len = strchr(dest, '\n') - dest;
851 
852   if (!eid)
853     eid_len = 0;
854   if (eid_len == -1)
855     eid_len = strlen(eid);
856   else if (eid_len == -2)
857     eid_len = strchr(eid, '\n') - eid;
858 
859   if (!body)
860     body_len = 0;
861   if (body_len == -1)
862     body_len = strlen(body);
863   else if (body_len == -2)
864     body_len = strchr(body, '\n') - body;
865 
866   /* Prepare values */
867 
868   GETTIMEOFDAY(&ourts, 0);
869   GMTIME_R(ourts.tv_sec, ot);
870 
871   /* Prepare timestamp prepended data for hashing */
872   len = ZXLOG_TIME_SIZ+1+mid_len+1+dest_len+1+eid_len+1+body_len;
873   buf = ZX_ALLOC(cf->ctx, len+1);
874   zlen = snprintf(buf, len+1, ZXLOG_TIME_FMT " %.*s %.*s %.*s %.*s",
875 		  ZXLOG_TIME_ARG(ot, ourts.tv_usec),
876 		  mid_len, mid_len?mid:"",
877 		  dest_len, dest_len?dest:"",
878 		  eid_len, eid_len?eid:"",
879 		  body_len, body_len?body:"");
880   ASSERTOPI(zlen, ==, len);
881   buf[len] = 0; /* must terminate manually as on win32 nul is not guaranteed */
882 
883   ASSERT(sigbuf_len >= 3+ZXLOG_TIME_SIZ+1);
884   strcpy(sigbuf, "EP ");
885   memcpy(sigbuf+3, buf, ZXLOG_TIME_SIZ);
886   sigbuf[3+ZXLOG_TIME_SIZ] = ' ';
887   memcpy(sigbuf+3+ZXLOG_TIME_SIZ+1, mid, mid_len);
888   sigbuf[3+ZXLOG_TIME_SIZ+1+mid_len] = 0;
889 
890   switch (cf->bus_rcpt & 0x06) {
891   case 0x02:   /* SP plain sha */
892     if (sigbuf_len < 3+ZXLOG_TIME_SIZ+1+mid_len+1+27+1) { ERR("Too small sigbuf %d", sigbuf_len); break; }
893     D("sha len=%d input(%.*s)", len, len, buf);
894     sigbuf[3+ZXLOG_TIME_SIZ+1+mid_len] = ' ';
895     sha1_safe_base64(sigbuf+3+ZXLOG_TIME_SIZ+1+mid_len+1, len, buf);
896     sigbuf[3+ZXLOG_TIME_SIZ+1+mid_len+1+27] = 0;
897     sigbuf[0] = 'S';
898     break;
899   case 0x04:   /* RP RSA-SHA signature (detected from key) */
900   case 0x06:   /* RP DSA-SHA signature (detected from key) */
901     LOCK(cf->mx, "mint_receipt");
902     /* The sign_pkey is used instead of log_sign_pkey because metadata is used to distribute it. */
903     if (!cf->sign_pkey)
904       cf->sign_pkey = zxid_read_private_key(cf, "sign-nopw-cert.pem");
905     UNLOCK(cf->mx, "mint_receipt");
906     DD("sign_pkey=%p buf(%.*s) len=%d buf(%s)", cf->sign_pkey, len, buf, len, buf);
907     if (!cf->sign_pkey)
908       break;
909 
910     zlen = zxsig_data(cf->ctx, len, buf, &zbuf, cf->sign_pkey, "receipt", cf->blobsig_digest_algo);
911 
912     if (errmac_debug>2) HEXDUMP("zbuf:", zbuf, zbuf+zlen, 4096);
913     len = 3+ZXLOG_TIME_SIZ+1+mid_len+1+SIMPLE_BASE64_LEN(zlen)+1;
914     if (sigbuf_len < len) { ERR("Too small sigbuf_len=%d, need=%d", sigbuf_len, len); break; }
915     sigbuf[3+ZXLOG_TIME_SIZ+1+mid_len] = ' ';
916     p = base64_fancy_raw(zbuf, zlen, sigbuf+3+ZXLOG_TIME_SIZ+1+mid_len+1, safe_basis_64, 1<<31, 0, 0, '.');
917     *p = 0;
918     switch (EVP_PKEY_type(cf->sign_pkey->type)) {
919     case EVP_PKEY_RSA: sigbuf[0] = 'R'; break;
920     case EVP_PKEY_DSA: sigbuf[0] = 'D'; break;
921     case EVP_PKEY_EC:  sigbuf[0] = 'C'; break;
922     default: sigbuf[0] = 'E'; ERR("Unknown pkey type=%d", EVP_PKEY_type(cf->sign_pkey->type));
923     }
924     break;
925   case 0:      /* Plain logging, no signing, no encryption. */
926     sigbuf[0] = 'P';
927     break;
928   }
929 
930   DD("body(%.*s) body_len=%d", body_len, body_len?body:"", body_len);
931   if (errmac_debug>1)
932     D("zx-rcpt-sig(%s) sigbuf_len=%d len=%d\nbuf(%s) buflen=%d %x %x", sigbuf, (int)strlen(sigbuf), len, buf, (int)strlen(buf), cf->bus_rcpt, cf->bus_rcpt&0x06);
933   else
934     D("zx-rcpt-sig(%s) %x", sigbuf, cf->bus_rcpt);
935   if (zbuf)
936     ZX_FREE(cf->ctx, zbuf);
937   ZX_FREE(cf->ctx, buf);
938   return sigbuf;
939 }
940 
941 /*() Verify a zxbus receipt signature.
942  *
943  * cf::         ZXID configuration object, used for memory allocation and CoT mgmt
944  * eid::        EntityID of the receipt issuing party, used to lookup metadata
945  * sigbuf_len:: Length of signature buffer (from zx-rcpt-sig header) or -1 for strlen(sigbuf)
946  * sigbuf::     The receipt (from zx-rcpt-sig header)
947  * mid_len::    Length of message id (-1 to use strlen(mid))
948  * mid::        Message ID
949  * dest_len::   Length of destination (-1 to use strlen(dest))
950  * dest::       Destination channel for the receipt
951  * deid_len::   Length of destination entity id (-1 to use strlen(eid))
952  * deid::       Entity ID of receiving party
953  * body_len::   Length of data pertaining to receipt (-1 to use strlen(body))
954  * body::       Data pertaining to receipt
955  * return::     0 (ZXSIG_OK) on success, nonzero on failure. */
956 
957 /* Called by:  stomp_got_ack, test_receipt x10, zxbus_send_cmdf */
zxbus_verify_receipt(zxid_conf * cf,const char * eid,int sigbuf_len,char * sigbuf,int mid_len,const char * mid,int dest_len,const char * dest,int deid_len,const char * deid,int body_len,const char * body)958 int zxbus_verify_receipt(zxid_conf* cf, const char* eid, int sigbuf_len, char* sigbuf, int mid_len, const char* mid, int dest_len, const char* dest, int deid_len, const char* deid, int body_len, const char* body)
959 {
960   int ver = -4, len, zlen;
961   char* p;
962   char* buf;
963   char sig[1024];
964   char sha1[20];
965   zxid_entity* meta;
966 
967   if (sigbuf_len == -1)
968     sigbuf_len = strlen(sigbuf);
969   else if (sigbuf_len == -2)
970     sigbuf_len = strchr(sigbuf, '\n') - sigbuf;
971 
972   if (!mid)
973     mid_len = 0;
974   if (mid_len == -1)
975     mid_len = strlen(mid);
976   else if (mid_len == -2)
977     mid_len = strchr(mid, '\n') - mid;
978 
979   if (!dest)
980     dest_len = 0;
981   if (dest_len == -1)
982     dest_len = strlen(dest);
983   else if (dest_len == -2)
984     dest_len = strchr(dest, '\n') - dest;
985 
986   if (!deid)
987     deid_len = 0;
988   if (deid_len == -1)
989     deid_len = strlen(deid);
990   else if (deid_len == -2)
991     deid_len = strchr(deid, '\n') - deid;
992 
993   if (!body)
994     body_len = 0;
995   if (body_len == -1)
996     body_len = strlen(body);
997   else if (body_len == -2)
998     body_len = strchr(body, '\n') - body;
999 
1000   DD("body(%.*s) body_len=%d", body_len, body_len?body:"", body_len);
1001   D("zx-rcpt-sig(%.*s) sigbuf_len=%d", sigbuf_len, sigbuf, sigbuf_len);
1002 
1003   len = ZXLOG_TIME_SIZ+1+mid_len+1+dest_len+1+deid_len+1+body_len;
1004   //len = ZXLOG_TIME_SIZ+1+body_len;
1005   buf = ZX_ALLOC(cf->ctx, len+1);
1006   zlen = snprintf(buf, len+1, "%.*s %.*s %.*s %.*s %.*s",
1007 		  ZXLOG_TIME_SIZ, sigbuf+3,
1008 		  mid_len, mid_len?mid:"",
1009 		  dest_len, dest_len?dest:"",
1010 		  deid_len, deid_len?deid:"",
1011 		  body_len, body_len?body:"");
1012   ASSERTOPI(zlen, ==, len);
1013   buf[len] = 0; /* must terminate manually as on win32 nul is not guaranteed */
1014 
1015   switch (sigbuf[0]) {
1016   case 'R':
1017   case 'D':
1018   case 'C':
1019     meta = zxid_get_ent(cf, eid);
1020     if (!meta) {
1021       ERR("Unable to find metadata for eid(%s) in verify receipt", eid);
1022       ver = -2;
1023       break;
1024     }
1025     //D("check_private_key(%d)",X509_check_private_key(meta->sign_cert, cf->sign_pkey));
1026     if (SIMPLE_BASE64_PESSIMISTIC_DECODE_LEN(sigbuf_len) > sizeof(sig)) {
1027       ERR("Available signature decoding buffer is too short len=%d, need=%d", (int)sizeof(sig), SIMPLE_BASE64_PESSIMISTIC_DECODE_LEN(sigbuf_len));
1028       ver = -3;
1029       break;
1030     }
1031     p = sigbuf+3+ZXLOG_TIME_SIZ+1+mid_len+1;
1032     DD("zx-rcpt-sig(%.*s) sigbuf_len=%d", sigbuf_len, sigbuf, sigbuf_len);
1033     D("sigbuf(%.*s) len=%d sigbuf=%p lim=%p", (int)(sigbuf_len-(p-sigbuf)), p, (int)(sigbuf_len-(p-sigbuf)), p, sigbuf+sigbuf_len);
1034     p = unbase64_raw(p, sigbuf+sigbuf_len, sig, zx_std_index_64);
1035 
1036     ver = zxsig_verify_data(len, buf, p-sig, sig, meta->sign_cert, "rcpt vfy", cf->blobsig_digest_algo);
1037 
1038     if (ver)
1039       D("ver=%d buf(%.*s) len=%d", ver, len, buf, len);
1040     break;
1041   case 'S':
1042     if (SIMPLE_BASE64_PESSIMISTIC_DECODE_LEN(sigbuf_len) > sizeof(sig)) {
1043       ERR("Available signature decoding buffer is too short len=%d, need=%d", (int)sizeof(sig), SIMPLE_BASE64_PESSIMISTIC_DECODE_LEN(sigbuf_len));
1044       ver = -3;
1045       break;
1046     }
1047     p = sigbuf+3+ZXLOG_TIME_SIZ+1+mid_len+1;
1048     unbase64_raw(p, sigbuf+sigbuf_len, sig, zx_std_index_64);
1049     SHA1((unsigned char*)buf, len, (unsigned char*)sha1);
1050     ver = memcmp(sig, sha1, 20);  /* 0 on success */
1051     if (ver) {
1052       ERR("SHA1 mismatch in receipt %d",ver);
1053       D("sha len=%d input(%.*s)", len, len, buf);
1054       D("sigbuf(%.*s) len=%d sigbuf=%p lim=%p", (int)(sigbuf_len-(p-sigbuf)), p, (int)(sigbuf_len-(p-sigbuf)), p, sigbuf+sigbuf_len);
1055       D("old sha1 %d", hexdmp("old sha1",sig,20,20));
1056       D("new sha1 %d", hexdmp("new sha1",sha1,20,20));
1057     }
1058     break;
1059   case 'P': D("P: no sig to check %d",0); ver = 0; break;
1060   default:
1061     ERR("Unsupported receipt signature algo(%c) sig(%.*s)", sigbuf[0], sigbuf_len, sigbuf);
1062   }
1063   ZX_FREE(cf->ctx, buf);
1064   return ver;
1065 }
1066 
1067 int zxbus_persist_flag = 1;
1068 
1069 /*() Attempt to persist a message.
1070  * Persisting involves synchronous write and an atomic filesystem rename
1071  * operation, ala Maildir. The persisted message is a file that contains
1072  * the entire STOMP 1.1 PDU including headers and body. Filename is the sha1
1073  * hash of the contents of the file.
1074  *
1075  * return:: 0 on failure, nonzero len of c_path on success.
1076  * See also:: persist feature in zxbus_listen_msg() */
1077 
1078 /* Called by:  zxbus_persist */
zxbus_persist_msg(zxid_conf * cf,int c_path_len,char * c_path,int dest_len,const char * dest,int data_len,const char * data)1079 int zxbus_persist_msg(zxid_conf* cf, int c_path_len, char* c_path, int dest_len, const char* dest, int data_len, const char* data)
1080 {
1081   int len;
1082    const char* p;
1083   char t_path[ZXID_MAX_BUF];  /* temp path before atomic rename */
1084 
1085   if (dest_len < 1)
1086     return 0;
1087   while (*dest == '/') {      /* skip initial /s, if any. I.e. no absolute path permitted */
1088     ++dest;
1089     --dest_len;
1090   }
1091   if (dest_len < 1)
1092     return 0;
1093   if (ONE_OF_3(*dest, '\n', 0, '\r')) {
1094     ERR("Empty dest (or one consisting etirely of slashes) %x", *dest);
1095     return 0;
1096   }
1097 
1098   /* Sanity check destination for any cracking attempts. */
1099   for (p = dest; p < dest+dest_len; ++p) {
1100     if (p[0] == '.' && p[1] == '.') {
1101       ERR("SEND destination is a .. hack(%.*s)", dest_len, dest);
1102       return 0;
1103     }
1104     if (ONE_OF_2(*p, '~', '\\') || *p > 122 || *p < 33) {
1105       ERR("SEND destination bad char 0x%x hack(%.*s)", *p, dest_len, dest);
1106       return 0;
1107     }
1108   }
1109 
1110   /* Persist the message, use Maildir style rename from tmp/ to ch/ */
1111 
1112   len = name_from_path(c_path, c_path_len, "%s" ZXBUS_CH_DIR "%.*s/", cf->cpath, dest_len, dest);
1113   if (sizeof(c_path)-len < 28+1 /* +1 accounts for t_path having one more char (tmp vs. ch) */) {
1114     ERR("The c_path for persisting exceeds limit. len=%d", len);
1115     return 0;
1116   }
1117   DD("c_path(%s) len=%d PATH(%s) dest(%.*s)", c_path, len, cf->cpath, dest_len, dest);
1118   sha1_safe_base64(c_path+len, data_len, data);
1119   len += 27;
1120   c_path[len] = 0;
1121   DD("c_path(%s)", c_path);
1122 
1123   name_from_path(t_path, sizeof(t_path), "%stmp/%s", cf->cpath, c_path+len-27);
1124 
1125   /* Perform synchronous write to disk. Read man 2 open for discussion. It is not
1126    * completely clear, but it appears that this is still not sufficient to guarantee
1127    * the appearance of the file in the respective directory, but perhaps fsck(8) could
1128    * recover it. *** we may want to make a fsync(2) call on the directory fd as well!
1129    * The disk should not be NFS mounted as O_SYNC is illdefined in NFS. Also, the
1130    * tmp/, ch/DEST/, and ch/DEST/.del directories should be on the same filesystem - otherwise
1131    * the rename(2) will not work.*/
1132   //  | O_DIRECT  -- seems to give alignment problems, i.e. 22 EINVAL Invalid Argument
1133   if (!write2_or_append_lock_c_path(t_path, 0, 0, data_len, data, "zxbus persist", SEEK_SET, O_TRUNC | O_SYNC)) {
1134     return 0;
1135   }
1136 
1137   if (rename(t_path, c_path)) {
1138     ERR("Renaming file(%s) to(%s) for atomicity failed: %d %s. Check permissions and that directories exist. Directories must be on the same filesystem. euid=%d egid=%d", t_path, c_path, errno, STRERROR(errno), geteuid(), getegid());
1139     return 0;
1140   }
1141   return len;
1142 }
1143 
1144 /* EOF  --  zxlog.c */
1145