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