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