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