1 /* zxlogview.c - Encrypted and signed log decoder
2 * Copyright (c) 2012 Synergetics NV (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: zxlogview.c,v 1.15 2009-11-24 23:53:40 sampo Exp $
11 *
12 * 19.11.2006, started --Sampo
13 * 29.8.2009, added hmac chaining field --Sampo
14 * 6.9.2012, added tests for receipts --Sampo
15 *
16 * TODO Ideas
17 *
18 * 1. DONE: Decrypt log lines and validate signatures
19 * 2. Add some form of summary report gathered from the processed log lines.
20 * - sig failures
21 * - other errors
22 * - activity by IdP
23 * - activity by user (name ID)
24 * - activity in timeline
25 * - activity by operation
26 * 3. Using /var/zxid/cot dereference the sha1 names to entity IDs
27 * 4. Using /var/zxid/log/rely (and issue) chase the referenced
28 * assertions and validate them (sig, conditions, etc.) and
29 * extract statistics from them.
30 * 5. Simple decryptor and signature verificator for encrypted
31 * files (e.g. rely assertions) (gpg or other standard compatible?)
32 * 6. Regression test mode
33 */
34
35 #include "platform.h"
36 #include "errmac.h"
37
38 #include <string.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <errno.h>
42
43 #ifdef USE_OPENSSL
44 #include <openssl/x509.h>
45 #include <openssl/rsa.h>
46 #include <openssl/aes.h>
47 #endif
48
49 #include "zx.h"
50 #include "zxid.h"
51 #include "zxidutil.h"
52 #include "c/zxidvers.h"
53
54 /*
55 zxlogview -rv sign-nopw-cert.pem receipt body # Check receipt\n\
56 zxlogview -rg sign-nopw-cert.pem receipt body # Generate receipt\n\
57 -rv CERT RCPT BODY Receipt verification mode.\n\
58 -rg PRIV RCPT BODY Receipt generation mode.\n\
59 */
60
61 char* help =
62 "zxlogview - Decrypt logs and validate log signatures - R" ZXID_REL "\n\
63 SAML 2.0 is a standard for federated identity and Single Sign-On.\n\
64 Copyright (c) 2012 Synergetics NV (sampo@synergetics.be), All Rights Reserved.\n\
65 Copyright (c) 2010-2011 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.\n\
66 Copyright (c) 2006-2009 Symlabs (symlabs@symlabs.com), All Rights Reserved.\n\
67 Author: Sampo Kellomaki (sampo@iki.fi)\n\
68 NO WARRANTY, not even implied warranties. Licensed under Apache License v2.0\n\
69 See http://www.apache.org/licenses/LICENSE-2.0\n\
70 Send well researched bug reports to the author. Home: zxid.org\n\
71 \n\
72 Usage: zxlogview [options] logsign-nopw-cert.pem logenc-nopw-cert.pem <loglines\n\
73 zxlogview -t sign-nopw-cert.pem sign-nopw-cert.pem\n\
74 -t Test mode. The certificates are interpretted from enc & sign perspective.\n\
75 -v Verbose messages.\n\
76 -q Be extra quiet.\n\
77 -d Turn on debugging.\n\
78 -license Show licensing and NO WARRANTY details.\n\
79 -h This help message\n\
80 -- End of options\n";
81
82 #define DIE(reason) MB fprintf(stderr, "%s\n", reason); exit(2); ME
83
84 int verbose = 1;
85 extern int errmac_debug;
86 int leak_free = 0;
87
88 X509* log_verify_cert;
89 EVP_PKEY* log_decrypt_pkey;
90 char log_symkey[20];
91 char buf[4096];
92
93 static void test_mode(int* argc, char*** argv, char*** env);
94 static void test_receipt(int* argc, char*** argv, char*** env);
95
96 /* Called by: main x8, zxbusd_main, zxbuslist_main, zxbustailf_main, zxcall_main, zxcot_main, zxdecode_main */
opt(int * argc,char *** argv,char *** env)97 static void opt(int* argc, char*** argv, char*** env)
98 {
99 int gotall;
100 if (*argc <= 1) goto argerr;
101
102 while (1) {
103 ++(*argv); --(*argc);
104
105 if (!(*argc) || ((*argv)[0][0] != '-')) break; /* start of non option arguments */
106
107 switch ((*argv)[0][1]) {
108 case '-': if ((*argv)[0][2]) break;
109 ++(*argv); --(*argc);
110 DD("End of options by --");
111 return; /* -- ends the options */
112
113 case 'd':
114 switch ((*argv)[0][2]) {
115 case '\0':
116 ++errmac_debug;
117 continue;
118 case 'i': if ((*argv)[0][3]) break;
119 ++(*argv); --(*argc);
120 if (!(*argc)) break;
121 strcpy(errmac_instance, (*argv)[0]);
122 continue;
123 }
124 break;
125
126 case 'v':
127 switch ((*argv)[0][2]) {
128 case '\0':
129 ++verbose;
130 continue;
131 }
132 break;
133
134 case 'q':
135 switch ((*argv)[0][2]) {
136 case '\0':
137 verbose = 0;
138 continue;
139 }
140 break;
141
142 case 'r':
143 switch ((*argv)[0][2]) {
144 case 'f':
145 /*AK_TS(LEAK, 0, "memory leaks enabled");*/
146 #if 1
147 ERR("*** WARNING: You have turned memory frees to memory leaks. We will (eventually) run out of memory. Using -rf is not recommended. %d\n", 0);
148 #endif
149 ++leak_free;
150 continue;
151 #if 0
152 case 'e':
153 if ((*argv)[0][3]) break;
154 ++(*argv); --(*argc);
155 if ((*argc) < 4) break;
156 sscanf((*argv)[0], "%i", &abort_funcno);
157 ++(*argv); --(*argc);
158 sscanf((*argv)[0], "%i", &abort_line);
159 ++(*argv); --(*argc);
160 sscanf((*argv)[0], "%i", &abort_error_code);
161 ++(*argv); --(*argc);
162 sscanf((*argv)[0], "%i", &abort_iter);
163 fprintf(stderr, "Will force core upon %x:%x err=%d iter=%d\n",
164 abort_funcno, abort_line, abort_error_code, abort_iter);
165 continue;
166 #endif
167 case 'a':
168 if ((*argv)[0][3] == 0) {
169 /*AK_TS(ASSERT_NONFATAL, 0, "assert nonfatal enabled");*/
170 #if 1
171 ERR("*** WARNING: YOU HAVE TURNED ASSERTS OFF USING -ra FLAG. THIS MEANS THAT YOU WILL NOT BE ABLE TO OBTAIN ANY SUPPORT. IF PROGRAM NOW TRIES TO ASSERT IT MAY MYSTERIOUSLY AND UNPREDICTABLY CRASH INSTEAD, AND NOBODY WILL BE ABLE TO FIGURE OUT WHAT WENT WRONG OR HOW MUCH DAMAGE MAY BE DONE. USING -ra IS NOT RECOMMENDED. %d\n", assert_nonfatal);
172 #endif
173 ++assert_nonfatal;
174 continue;
175 }
176 break;
177 }
178 break;
179
180 case 'l':
181 switch ((*argv)[0][2]) {
182 case 'i':
183 if (!strcmp((*argv)[0],"-license")) {
184 extern char* license;
185 fprintf(stderr, "%s", license);
186 exit(0);
187 }
188 break;
189 }
190 break;
191
192 case 't':
193 switch ((*argv)[0][2]) {
194 case '\0': test_mode(argc, argv, env);
195 case '1': test_receipt(argc, argv, env);
196 }
197 break;
198 }
199 /* fall thru means unrecognized flag */
200 if (*argc)
201 fprintf(stderr, "Unrecognized flag `%s'\n", (*argv)[0]);
202 argerr:
203 if (verbose>1) {
204 printf("%s", help);
205 exit(0);
206 }
207 fprintf(stderr, "%s", help);
208 exit(3);
209 }
210
211 if (*argc) { /* Signature verification certificate (logsign-nopw-cert.pem) */
212 if ((*argv)[0][0]) {
213 read_all(sizeof(buf), buf, "logview opt cert", 1, "%s", (*argv)[0]);
214 log_verify_cert = zxid_extract_cert(buf, (*argv)[0]);
215 }
216 ++(*argv); --(*argc);
217 }
218
219 if (*argc) { /* Log decryption key (logenc-nopw-cert.pem) */
220 if ((*argv)[0][0]) {
221 gotall = read_all(sizeof(buf), buf, "logview opt key", 1, "%s", (*argv)[0]);
222 SHA1((unsigned char*)buf, gotall, (unsigned char*)log_symkey);
223 log_decrypt_pkey = zxid_extract_private_key(buf, (*argv)[0]);
224 }
225 ++(*argv); --(*argc);
226 }
227 }
228
229 /* Called by: opt */
test_mode(int * argc,char *** argv,char *** env)230 static void test_mode(int* argc, char*** argv, char*** env)
231 {
232 int gotall;
233 zxid_conf* cf = zxid_new_conf(0);
234
235 ++(*argv); --(*argc);
236 if (*argc) { /* Signature verification certificate (logsign-nopw-cert.pem) */
237 if ((*argv)[0][0]) {
238 read_all(sizeof(buf), buf, "logview test_mode private key", 1, "%s", (*argv)[0]);
239 cf->log_sign_pkey = zxid_extract_private_key(buf, (*argv)[0]);
240 cf->sign_pkey = cf->enc_pkey = cf->log_sign_pkey;
241 }
242 ++(*argv); --(*argc);
243 }
244
245 if (*argc) { /* Log encryption key (logenc-nopw-cert.pem) */
246 if ((*argv)[0][0]) {
247 gotall = read_all(sizeof(buf), buf, "logview test_mode cert", 1, "%s", (*argv)[0]);
248 SHA1((unsigned char*)buf, gotall, (unsigned char*)cf->log_symkey);
249 cf->log_enc_cert = zxid_extract_cert(buf, (*argv)[0]);
250 cf->sign_cert = cf->enc_cert = cf->log_enc_cert;
251 }
252 ++(*argv); --(*argc);
253 }
254
255 zxlog_write_line(cf, "zxlogview-test.out", 0x01, -2, "test1 Plain none\n");
256 zxlog_write_line(cf, "zxlogview-test.out", 0x03, -2, "test2 Plain SHA1\n");
257 zxlog_write_line(cf, "zxlogview-test.out", 0x05, -2, "test3 Plain RSA sig\n");
258
259 zxlog_write_line(cf, "zxlogview-test.out", 0x11, -2, "test4 zip none\n");
260 zxlog_write_line(cf, "zxlogview-test.out", 0x13, -2, "test5 zip SHA1\n");
261 zxlog_write_line(cf, "zxlogview-test.out", 0x15, -2, "test6 zip RSA sig\n");
262
263 zxlog_write_line(cf, "zxlogview-test.out", 0x21, -2, "test7 RSA-AES none\n");
264 zxlog_write_line(cf, "zxlogview-test.out", 0x23, -2, "test8 RSA-AES SHA1\n");
265 zxlog_write_line(cf, "zxlogview-test.out", 0x25, -2, "test9 RSA-AES RSA sig\n");
266
267 zxlog_write_line(cf, "zxlogview-test.out", 0x41, -2, "test10 AES none\n");
268 zxlog_write_line(cf, "zxlogview-test.out", 0x43, -2, "test11 AES SHA1\n");
269 zxlog_write_line(cf, "zxlogview-test.out", 0x45, -2, "test12 AES RSA sig\n");
270 exit(0);
271 }
272
273 /* Called by: opt */
test_receipt(int * argc,char *** argv,char *** env)274 static void test_receipt(int* argc, char*** argv, char*** env)
275 {
276 char sigbuf[1024];
277 char* eid;
278 zxid_conf* cf = zxid_new_conf_to_cf("CPATH=/var/zxid/bus/&NON_STANDARD_ENTITYID=stomp://localhost:2229/");
279
280 eid = zxid_my_ent_id_cstr(cf);
281
282 cf->bus_rcpt = 5; /* RSA+SHA */
283 zxbus_mint_receipt(cf, sizeof(sigbuf), sigbuf, -1, "mid1", -1, "default", -1, "eid1", -1, "test13");
284 printf("13 vfy=%d\n", zxbus_verify_receipt(cf, eid, -1, sigbuf, -1, "mid1", -1, "default", -1, "eid1", -1, "test13"));
285
286 zxbus_mint_receipt(cf, sizeof(sigbuf), sigbuf, -1, "", -1, "", -1, "", -1, "");
287 printf("14 vfy=%d\n", zxbus_verify_receipt(cf, eid, -1, sigbuf, -1, "", -1, "", -1, "", -1, ""));
288
289 zxbus_mint_receipt(cf, sizeof(sigbuf), sigbuf, -1, "mid1", -1, "default", -1, "eid1", -1, "t15");
290 printf("15 vfy=%d\n", zxbus_verify_receipt(cf, eid, -1, sigbuf, -1, "mid1", -1, "default", -1, "eid1", -1, "t15"));
291 sigbuf[3] = '0';
292 printf("15fail expected vfy=%d\n", zxbus_verify_receipt(cf, eid, -1, sigbuf, -1, "mid1", -1, "default", -1, "eid1", -1, "t15"));
293
294 cf->bus_rcpt = 3; /* SHA */
295 zxbus_mint_receipt(cf, sizeof(sigbuf), sigbuf, -1, "mid1", -1, "default", -1, "eid1", -1, "test16");
296 printf("16 vfy=%d\n", zxbus_verify_receipt(cf, eid, -1, sigbuf, -1, "mid1", -1, "default", -1, "eid1", -1, "test16"));
297 sigbuf[3] = '0';
298 printf("16fail expected vfy=%d\n", zxbus_verify_receipt(cf, eid, -1, sigbuf, -1, "mid1", -1, "default", -1, "eid1", -1, "test16"));
299
300 zxbus_mint_receipt(cf, sizeof(sigbuf), sigbuf, -1, "mid1", -1, "default", -1, "eid1", -1, "");
301 printf("17 vfy=%d\n", zxbus_verify_receipt(cf, eid, -1, sigbuf, -1, "mid1", -1, "default", -1, "eid1", -1, ""));
302
303 zxbus_mint_receipt(cf, sizeof(sigbuf), sigbuf, -1, "mid1", -1, "default", -1, "eid1", -1, "t18");
304 printf("18 vfy=%d\n", zxbus_verify_receipt(cf, eid, -1, sigbuf, -1, "mid1", -1, "default", -1, "eid1", -1, "t18"));
305
306 cf->bus_rcpt = 1; /* Plain */
307 zxbus_mint_receipt(cf, sizeof(sigbuf), sigbuf, -1, "mid1", -1, "default", -1, "eid1", -1, "test19");
308 printf("19 vfy=%d\n", zxbus_verify_receipt(cf, eid, -1, sigbuf, -1, "mid1", -1, "default", -1, "eid1", -1, "test19"));
309
310 zxbus_mint_receipt(cf, sizeof(sigbuf), sigbuf, -1, "mid1", -1, "default", -1, "eid1", -1, "");
311 printf("20 vfy=%d\n", zxbus_verify_receipt(cf, eid, -1, sigbuf, -1, "mid1", -1, "default", -1, "eid1", -1, ""));
312
313 zxbus_mint_receipt(cf, sizeof(sigbuf), sigbuf, -1, "mid1", -1, "default", -1, "eid1", -1, "t21");
314 printf("21 vfy=%d\n", zxbus_verify_receipt(cf, eid, -1, sigbuf, -1, "mid1", -1, "default", -1, "eid1", -1, "t21"));
315
316 exit(0);
317 }
318
319
320 /* Called by: main x3 */
zxlog_zsig_verify_print(zxid_conf * cf,int len,char * buf,char * se,char * p)321 static void zxlog_zsig_verify_print(zxid_conf* cf, int len, char* buf, char* se, char* p)
322 {
323 char sha1[20];
324 char* sig;
325 int verdict, siglen = (unsigned char)p[0] << 8 | (unsigned char)p[1];
326 D("siglen(%d)", siglen);
327 sig = p+2;
328 p += 2+siglen;
329
330 switch (se[0]) {
331 case 'P':
332 if (siglen) {
333 ERR("No sig, siglen should be zero. Found %d", siglen);
334 break;
335 }
336 break;
337 case 'R': /* RSA detected from key */
338 case 'D': /* DSA detected from key */
339 if (!siglen) {
340 ERR("RSA sig claimed by no sig found. %d", siglen);
341 break;
342 }
343 verdict = zxsig_verify_data(len-(p-buf), p, siglen, sig, log_verify_cert, "log vfy", 0);
344 if (verdict) {
345 printf("RSA signature FAIL. Log file may have been tampered - or bad certificate.\n");
346 } else {
347 printf("RSA signature OK\n");
348 }
349 break;
350 case 'S':
351 if (siglen != 20) {
352 ERR("Wrong sha1 length in input %d", siglen);
353 break;
354 }
355 SHA1((unsigned char*)p, len-(p-buf), (unsigned char*)sha1);
356 if (memcmp(sha1, buf+2, 20)) {
357 printf("SHA1 FAIL\n");
358 } else {
359 printf("SHA1 OK\n");
360 }
361 break;
362 default: ERR("Unimplemented sign %c", se[1]); break;
363 }
364 sig = zx_zlib_raw_inflate(cf->ctx, len-(p-buf), p, &siglen);
365 printf("----- %03d %c%c %.*s\n", siglen, se[0], se[1], siglen, sig); /* Inflated plain text */
366 ZX_FREE(cf->ctx, sig);
367 }
368
369 /* ============== M A I N ============== */
370
371 extern int zxid_suppress_vpath_warning;
372
373 /*(-) Control starts here */
374 /* Called by: */
main(int argc,char ** argv,char ** env)375 int main(int argc, char** argv, char** env)
376 {
377 RSA* rsa;
378 int len, seslen, ver;
379 char* p;
380 char* pp;
381 char se[4];
382 char ses[16];
383 char sha1[20];
384 struct aes_key_st aes_key;
385 zxid_suppress_vpath_warning = 2;
386 zxid_conf* cf = zxid_new_conf(0);
387 strcpy(errmac_instance, "\tzxlogview");
388 opt(&argc, &argv, &env);
389
390 while (1) {
391 if (scanf("%2s %4095[^\n]\n", se, buf) <= 0) {
392 D("scanf unable to scan line. Presme end of file. %d", 0);
393 return 0;
394 }
395 len = strlen(buf);
396 switch (se[1]) {
397 case 'P': /* Plain: neither compressed nor encrypted. */
398 printf("----+ %03d %c%c %.*s\n", len, se[0], se[1], len, buf);
399 p = strchr(buf, ' ');
400 if (!p) {
401 ERR("Separator space not found. Corrupt line(%.*s)", len, buf);
402 break;
403 }
404
405 switch (se[0]) {
406 case 'P': /* No sig */
407 break;
408 case 'R': /* RSA detected from key */
409 case 'D': /* DSA detected from key */
410 pp = unbase64_raw(buf, p, buf, zx_std_index_64); /* In place, overwrite. */
411 ++p;
412 ver = zxsig_verify_data(len-(p-buf), p, pp-buf, buf, log_verify_cert, "log vfy", "SHA1");
413 if (ver) {
414 printf("RSA signature FAIL. Log file may have been tampered - or bad certificate.\n");
415 } else {
416 printf("RSA signature OK\n");
417 }
418 break;
419 case 'S':
420 unbase64_raw(buf, p, buf, zx_std_index_64); /* In place, overwrite. */
421 ++p;
422 SHA1((unsigned char*)p, len-(p-buf), (unsigned char*)sha1);
423 if (memcmp(buf, sha1, 20)) {
424 printf("SHA1 FAIL\n");
425 } else {
426 printf("SHA1 OK\n");
427 }
428 break;
429 default: ERR("Unimplemented sign %c", se[1]); break;
430 }
431 break;
432
433 case 'A': /* RSA-AES encrypted */
434 p = unbase64_raw(buf, buf+len, buf, zx_std_index_64);
435 len = p-buf;
436 D("unbase64 len(%d)", len);
437 seslen = (unsigned char)buf[0] << 8 | (unsigned char)buf[1];
438 D("seslen(%d)", seslen);
439
440 rsa = EVP_PKEY_get1_RSA(log_decrypt_pkey);
441 if (!rsa) {
442 ERR("No RSA key found in log decryption key type=0x%x", log_decrypt_pkey->type);
443 break;
444 }
445 seslen = RSA_private_decrypt(seslen, (unsigned char*)buf+2, (unsigned char*)ses, rsa, RSA_PKCS1_OAEP_PADDING);
446 if (seslen < 0) {
447 ERR("RSA dec fail %x", seslen);
448 zx_report_openssl_err("zxlog rsa enc");
449 break;
450 }
451 D("decrypted session key len(%d)", seslen);
452 if (seslen != 16) {
453 ERR("Decrypted session key not right size(%d), should be 16.", seslen);
454 break;
455 }
456
457 p = buf+2+seslen+16;
458 AES_set_decrypt_key((unsigned char*)ses, 128, &aes_key);
459 AES_cbc_encrypt((unsigned char*)p, (unsigned char*)p, len-(p-buf), &aes_key, (unsigned char*)buf+2+seslen,0);
460 zxlog_zsig_verify_print(cf, len, buf, se, p);
461 break;
462 case 'T': ERR("Unimplemented decryption %c", se[1]); break;
463 case 'B': /* Symmetric AES encrypted */
464 p = unbase64_raw(buf, buf+len, buf, zx_std_index_64);
465 len = p-buf;
466 D("unbase64 len(%d)", len);
467 p = buf+16;
468 AES_set_decrypt_key((unsigned char*)log_symkey, 128, &aes_key);
469 AES_cbc_encrypt((unsigned char*)p, (unsigned char*)p, len-16, &aes_key, (unsigned char*)buf, 0);
470 zxlog_zsig_verify_print(cf, len, buf, se, p);
471 break;
472 case 'U': ERR("Unimplemented decryption %c", se[1]); break;
473 case 'Z': /* RFC1951 zip + safe base64 only */
474 p = unbase64_raw(buf, buf+len, buf, zx_std_index_64);
475 len = p-buf;
476 D("unbase64 len(%d)", len);
477 zxlog_zsig_verify_print(cf, len, buf, se, buf);
478 break;
479 default: ERR("Unknown decryption %c", se[1]); break;
480 }
481 }
482 return 0;
483 }
484
485 //char* assert_msg = "%s: Internal error caused an ASSERT to fire. Deliberately provoking a core dump.\nSorry for the inconvenience.\n";
486
487 /* EOF -- zxlogview.c */
488