1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 /*
21  * XSEC
22  *
23  * txfmout:= tool to output the results of the various transforms
24  *		     used when validating the attached signature.
25  *
26  * Author(s): Berin Lautenbach
27  *
28  * $Id: txfmout.cpp 1894293 2021-10-15 14:14:50Z scantor $
29  *
30  */
31 
32 // XSEC
33 
34 #include <xsec/utils/XSECPlatformUtils.hpp>
35 #include <xsec/framework/XSECProvider.hpp>
36 #include <xsec/canon/XSECC14n20010315.hpp>
37 #include <xsec/dsig/DSIGSignature.hpp>
38 #include <xsec/dsig/DSIGReference.hpp>
39 #include <xsec/framework/XSECException.hpp>
40 #include <xsec/framework/XSECURIResolver.hpp>
41 #include <xsec/enc/XSECCryptoException.hpp>
42 #include <xsec/utils/XSECBinTXFMInputStream.hpp>
43 
44 #include "../../utils/XSECDOMUtils.hpp"
45 
46 // General
47 
48 #include <memory.h>
49 #include <string.h>
50 #include <iostream>
51 #include <fstream>
52 #include <stdlib.h>
53 
54 #if defined(HAVE_UNISTD_H)
55 # include <unistd.h>
56 #else
57 # if defined(HAVE_DIRECT_H)
58 #  include <direct.h>
59 # endif
60 #endif
61 
62 
63 #include <xercesc/util/PlatformUtils.hpp>
64 #include <xercesc/util/XMLString.hpp>
65 
66 #include <xercesc/dom/DOM.hpp>
67 #include <xercesc/parsers/XercesDOMParser.hpp>
68 #include <xercesc/util/XMLException.hpp>
69 #include <xercesc/util/XMLNetAccessor.hpp>
70 #include <xercesc/util/XMLUri.hpp>
71 
72 XERCES_CPP_NAMESPACE_USE
73 
74 using std::ios;
75 using std::cout;
76 using std::cerr;
77 using std::endl;
78 using std::ofstream;
79 
80 #ifdef XSEC_HAVE_XALAN
81 
82 // XALAN
83 
84 #include <xalanc/XPath/XPathEvaluator.hpp>
85 #include <xalanc/XalanTransformer/XalanTransformer.hpp>
86 
87 // If this isn't defined, we're on Xalan 1.12+ and require modern C++
88 #ifndef XALAN_USING_XALAN
89 # define XALAN_USING_XALAN(NAME) using xalanc :: NAME;
90 #endif
91 
92 XALAN_USING_XALAN(XPathEvaluator)
93 XALAN_USING_XALAN(XalanTransformer)
94 
95 #else
96 
97 std::ostream& operator<< (std::ostream& target, const XMLCh * s)
98 {
99     char *p = XMLString::transcode(s);
100     target << p;
101     XSEC_RELEASE_XMLCH(p);
102     return target;
103 }
104 
105 #endif
106 
107 // ---------------------------------------------------------------------------
108 //  Outputter
109 // ---------------------------------------------------------------------------
110 
111 class outputter {
112 
113 public:
114 
115 	outputter();
116 	~outputter();
117 
118 	// Set methods
119 
120 	// Will tell not to use cout and open on this base
121 	void setFilename(const char * name);
122 	// Will append a number and re-open for each "open" call
123 	void setNewFilePerOpen();
124 
125 	// Re-open the file if necessary (new output)
126 	void openSection();
127 	// Close if necessary (output section finished)
128 	void closeSection();
129 	// Close of and finish
130 	void closeAll();
131 
132 	// Output a buffer
133 	void output(const unsigned char * buf, unsigned int sz);
134 
135 	// Info
136 	int getIndex(void);
137 
138 private:
139 
140 	char			* m_name;			// Name of the file (or base name)
141 	bool			m_cout;				// Are we using cout?
142 	bool			m_newFilePerOpen;	// Should we re-open?
143 	bool			m_fileOpen;			// Do we have an open file we should close?
144 	int				m_counter;			// The counter
145 	ofstream		m_out;				// Current output file
146 
147 };
148 
outputter()149 outputter::outputter() :
150 m_name(0),
151 m_cout(true),
152 m_newFilePerOpen(false),
153 m_fileOpen(false),
154 m_counter(0) {
155 
156 }
157 
~outputter()158 outputter::~outputter() {
159 
160 	if (m_fileOpen == true) {
161 
162 		m_out.close();
163 
164 		m_fileOpen = false;
165 
166 	}
167 
168 	if (m_name != 0)
169 		delete[] m_name;
170 
171 }
172 
setFilename(const char * name)173 void outputter::setFilename(const char * name) {
174 
175 	m_name = strdup(name);
176 	m_cout = false;
177 
178 }
179 
setNewFilePerOpen()180 void outputter::setNewFilePerOpen() {
181 
182 	m_newFilePerOpen = true;
183 
184 }
185 
openSection()186 void outputter::openSection() {
187 
188 	if (m_cout == true)
189 		return;
190 
191 	if (m_fileOpen == true && m_newFilePerOpen == false)
192 		return;
193 
194 	if (m_out.is_open() != 0) {
195 
196 		m_out.close();
197 
198 	}
199 
200 	char * buf = new char[strlen(m_name) + 10];
201 	strcpy(buf, m_name);
202 
203 	if (m_newFilePerOpen == true) {
204 
205 		char numBuf[10];
206 		sprintf(numBuf, "%d", m_counter);
207 		//_itoa(m_counter, numBuf, 10);
208 		strcat(buf, ".");
209 		strcat(buf, numBuf);
210 
211 	}
212 
213 	m_out.open(buf, ios::out | ios::binary);
214 	m_fileOpen = true;
215 	delete [] buf;
216 }
217 
closeSection()218 void outputter::closeSection() {
219 
220 	m_counter++;
221 
222 	if (m_cout == false && m_newFilePerOpen == true && m_out.is_open() != 0) {
223 
224 		m_out.close();
225 		m_fileOpen = false;
226 
227 	}
228 
229 
230 }
231 
getIndex(void)232 int outputter::getIndex(void) {
233 
234 	return m_counter;
235 
236 }
237 
closeAll()238 void outputter::closeAll() {
239 
240 	if (m_out.is_open() != 0)
241 		m_out.close();
242 
243 	m_fileOpen = false;
244 
245 }
246 
247 
output(const unsigned char * buf,unsigned int sz)248 void outputter::output(const unsigned char * buf, unsigned int sz) {
249 
250 	if (m_cout || m_out.is_open() == false) {
251 
252 		cout.write((const char *) buf,sz);
253 
254 	}
255 	else {
256 
257 		m_out.write((const char *) buf, sz);
258 
259 	}
260 
261 }
262 
263 
264 // ---------------------------------------------------------------------------
265 //  Main Program
266 // ---------------------------------------------------------------------------
267 
268 
269 
printUsage(void)270 void printUsage(void) {
271 
272 	cerr << "\nUsage: txfmout [options] <input file name>\n\n";
273 	cerr << "     Where options are :\n\n";
274 	cerr << "     --signedinfo/-s\n";
275 	cerr << "         Output canonicalised SignedInfo only\n";
276 	cerr << "     --out/-o\n";
277 	cerr << "         Output to the nominated file name\n";
278 	cerr << "     --references/-r [num]\n";
279 	cerr << "         Output only references. [num] defines a single reference to output\n";
280 	cerr << "     --newfiles/-n\n";
281 	cerr << "         Create a new file for each reference/SignedInfo (append .#)\n";
282 
283 }
284 
285 // ---------------------------------------------------------------------------
286 //		Reference Outputter
287 // ---------------------------------------------------------------------------
288 
outputReferenceList(DSIGReferenceList * lst,outputter & theOutputter,int refNum)289 void outputReferenceList (DSIGReferenceList * lst, outputter & theOutputter, int refNum) {
290 
291 	if (lst == 0)
292 		return;
293 
294 	DSIGReference * ref;
295 	unsigned int sz;
296 	XSECBinTXFMInputStream * is;
297 	unsigned char buf[1024];
298 
299     DSIGReferenceList::size_type lstSz = (int) lst->getSize();
300 
301 	for (DSIGReferenceList::size_type i = 0; i < lstSz; ++i) {
302 
303 		ref = lst->item(i);
304 		if (refNum == -1 || theOutputter.getIndex() == refNum) {
305 			theOutputter.openSection();
306 
307 			try {
308 				is = ref->makeBinInputStream();
309 			}
310 			catch (const NetAccessorException&) {
311 
312 				cerr << "Network error in reference " << theOutputter.getIndex() << endl;
313 				is = 0;
314 			}
315 
316 
317 			if (is != 0) {
318 
319 				sz = (unsigned int) is->readBytes(buf, 1023);
320 
321 				while (sz != 0) {
322 
323 					buf[sz] = '\0';
324 					theOutputter.output(buf, sz);
325 
326 					sz = (unsigned int) is->readBytes(buf, 1023);
327 
328 				}
329 
330 				delete is;
331 
332 			}
333 		}
334 		theOutputter.closeSection();
335 
336 	}
337 
338 	// Look for manifests
339 	for (DSIGReferenceList::size_type i = 0; i < lstSz; ++i) {
340 
341 		ref = lst->item(i);
342 
343 		if (ref->isManifest() == true) {
344 			outputReferenceList(ref->getManifestReferenceList(), theOutputter, refNum);
345 		}
346 
347 	}
348 
349 }
350 
351 // ---------------------------------------------------------------------------
352 //  Main Program
353 // ---------------------------------------------------------------------------
354 
355 
main(int argc,char ** argv)356 int main(int argc, char **argv) {
357 
358 	char					* filename = NULL;
359 	bool					signedInfo = true;
360 	bool					references = true;
361 	outputter				theOutputter;
362 	int						refNum = -1;
363 
364 	if (argc < 2) {
365 
366 		printUsage();
367 		exit (2);
368 	}
369 
370 	// Run through parameters
371 	int paramCount = 1;
372 
373 	while (paramCount < argc - 1) {
374 
375 		if (_stricmp(argv[paramCount], "--signedinfo") == 0 || _stricmp(argv[paramCount], "-s") == 0) {
376 			paramCount++;
377 			references = false;
378 		}
379 		else if (_stricmp(argv[paramCount], "--out") == 0 || _stricmp(argv[paramCount], "-o") == 0) {
380 			paramCount++;
381 			theOutputter.setFilename(argv[paramCount++]);
382 		}
383 		else if (_stricmp(argv[paramCount], "--references") == 0 || _stricmp(argv[paramCount], "-r") == 0) {
384 			paramCount++;
385 			signedInfo = false;
386 			if (argv[paramCount][0] >= '0' && argv[paramCount][0] <= '9')
387 				refNum = atoi(argv[paramCount++]);
388 		}
389 		else if (_stricmp(argv[paramCount], "--newfiles") == 0 || _stricmp(argv[paramCount], "-n") == 0) {
390 			paramCount++;
391 			theOutputter.setNewFilePerOpen();
392 		}
393 		else {
394 			printUsage();
395 			exit(2);
396 		}
397 	}
398 
399 	if (paramCount >= argc) {
400 		printUsage();
401 		exit (2);
402 	}
403 
404 	filename = argv[paramCount];
405 
406 	// Initialise the XML system
407 
408 	try {
409 
410 		XMLPlatformUtils::Initialize();
411 #ifdef XSEC_HAVE_XALAN
412 		XPathEvaluator::initialize();
413 		XalanTransformer::initialize();
414 #endif
415 		XSECPlatformUtils::Initialise();
416 
417 	}
418 	catch (const XMLException &e) {
419 
420 		cerr << "Error during initialisation of Xerces" << endl;
421 		cerr << "Error Message = : "
422 		     << e.getMessage() << endl;
423 
424 	}
425 
426 	// Create and set up the parser
427 
428 	XercesDOMParser * parser = new XercesDOMParser;
429 
430 	parser->setDoNamespaces(true);
431 	parser->setCreateEntityReferenceNodes(true);
432 
433 	// Now parse out file
434 
435 	bool errorsOccured = false;
436 	XMLSize_t errorCount = 0;
437     try
438     {
439     	parser->parse(filename);
440         errorCount = parser->getErrorCount();
441         if (errorCount > 0)
442             errorsOccured = true;
443     }
444 
445     catch (const XMLException& e)
446     {
447         cerr << "An error occurred during parsing\n   Message: "
448              << e.getMessage() << endl;
449         errorsOccured = true;
450     }
451 
452 
453     catch (const DOMException& e)
454     {
455        cerr << "A DOM error occurred during parsing\n   DOMException code: "
456              << e.code << endl;
457         errorsOccured = true;
458     }
459 
460 	if (errorsOccured) {
461 
462 		cout << "Errors during parse" << endl;
463 		exit (2);
464 
465 	}
466 
467 	/*
468 
469 		Now that we have the parsed file, get the DOM document and start looking at it
470 
471 	*/
472 
473 	DOMNode *doc;		// The document that we parsed
474 
475 	doc = parser->getDocument();
476 	DOMDocument *theDOM = parser->getDocument();
477 
478 	// Find the signature node
479 
480 	DOMNode *sigNode = findDSIGNode(doc, "Signature");
481 
482 	// Create the signature checker
483 
484 	if (sigNode == 0) {
485 
486 		cerr << "Could not find <Signature> node in " << argv[argc-1] << endl;
487 		exit(2);
488 	}
489 
490 	XSECProvider prov;
491 	DSIGSignature * sig = prov.newSignatureFromDOM(theDOM, sigNode);
492 	sig->registerIdAttributeName(MAKE_UNICODE_STRING("ID"));
493 
494 	// Map out base path of the file
495 #if XSEC_HAVE_GETCWD_DYN
496 	char *path = getcwd(NULL, 0);
497 	char *baseURI = (char*)malloc(strlen(path) + 8 + 1 + strlen(filename) + 1);
498 #else
499 	char path[PATH_MAX];
500 	char baseURI[(PATH_MAX * 2) + 10];
501 	getcwd(path, PATH_MAX);
502 #endif
503 	strcpy(baseURI, "file:///");
504 	strcat(baseURI, path);
505 	strcat(baseURI, "/");
506 	strcat(baseURI, filename);
507 
508 	// Find any ':' and "\" characters
509 	int lastSlash = 0;
510 	for (unsigned int i = 8; i < strlen(baseURI); ++i) {
511 		if (baseURI[i] == '\\') {
512 			lastSlash = i;
513 			baseURI[i] = '/';
514 		}
515 		else if (baseURI[i] == '/')
516 			lastSlash = i;
517 	}
518 
519 	// The last "\\" must prefix the filename
520 	baseURI[lastSlash + 1] = '\0';
521 
522 	sig->getURIResolver()->setBaseURI(MAKE_UNICODE_STRING(baseURI));
523 #if XSEC_HAVE_GETCWD_DYN
524 	free(path);
525 	free(baseURI);
526 #endif
527 
528 
529 	try {
530 
531 		XSECBinTXFMInputStream * is;
532 		XMLByte buf[1024];
533 		unsigned int sz;
534 
535 		sig->load();
536 		if (references) {
537 			outputReferenceList(sig->getReferenceList(), theOutputter, refNum);
538 
539 		}
540 		if (signedInfo) {
541 			is = sig->makeBinInputStream();
542 			if (is != NULL) {
543 
544 				theOutputter.openSection();
545 				sz = (unsigned int) is->readBytes(buf, 1023);
546 
547 				while (sz != 0) {
548 
549 					buf[sz] = '\0';
550 					theOutputter.output(buf, sz);
551 
552 					sz = (unsigned int) is->readBytes(buf, 1023);
553 
554 				}
555 				theOutputter.closeSection();
556 
557 				delete is;
558 
559 			}
560 		}
561 	}
562 
563 	catch (const XSECException &e) {
564 		char * m = XMLString::transcode(e.getMsg());
565 		cerr << "An error occurred during signature processing\n   Message: "
566 		<< m << endl;
567 		XSEC_RELEASE_XMLCH(m);
568 		errorsOccured = true;
569 		exit (2);
570 	}
571 
572 	theOutputter.closeAll();
573 
574 	prov.releaseSignature(sig);
575 
576 	return 0;
577 }
578