1 /**
2  * XML Security Library example: Decrypting an encrypted file using a custom keys manager.
3  *
4  * Decrypts encrypted XML file using a custom files based keys manager.
5  * We assume that key's name in <dsig:KeyName/> element is just
6  * key's file name in the current folder.
7  *
8  * Usage:
9  *      ./decrypt3 <xml-enc>
10  *
11  * Example:
12  *      ./decrypt3 encrypt1-res.xml
13  *      ./decrypt3 encrypt2-res.xml
14  *
15  * This is free software; see Copyright file in the source
16  * distribution for preciese wording.
17  *
18  * Copyright (C) 2002-2016 Aleksey Sanin <aleksey@aleksey.com>. All Rights Reserved.
19  */
20 #include <stdlib.h>
21 #include <string.h>
22 #include <ctype.h>
23 #include <assert.h>
24 
25 #include <libxml/tree.h>
26 #include <libxml/xmlmemory.h>
27 #include <libxml/parser.h>
28 
29 #ifndef XMLSEC_NO_XSLT
30 #include <libxslt/xslt.h>
31 #include <libxslt/security.h>
32 #endif /* XMLSEC_NO_XSLT */
33 
34 #include <xmlsec/xmlsec.h>
35 #include <xmlsec/xmltree.h>
36 #include <xmlsec/xmlenc.h>
37 #include <xmlsec/crypto.h>
38 
39 xmlSecKeyStoreId  files_keys_store_get_klass(void);
40 xmlSecKeysMngrPtr create_files_keys_mngr(void);
41 int decrypt_file(xmlSecKeysMngrPtr mngr, const char* enc_file);
42 
43 int
main(int argc,char ** argv)44 main(int argc, char **argv) {
45     xmlSecKeysMngrPtr mngr;
46 #ifndef XMLSEC_NO_XSLT
47     xsltSecurityPrefsPtr xsltSecPrefs = NULL;
48 #endif /* XMLSEC_NO_XSLT */
49 
50     assert(argv);
51 
52     if(argc != 2) {
53         fprintf(stderr, "Error: wrong number of arguments.\n");
54         fprintf(stderr, "Usage: %s <enc-file>\n", argv[0]);
55         return(1);
56     }
57 
58     /* Init libxml and libxslt libraries */
59     xmlInitParser();
60     LIBXML_TEST_VERSION
61     xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
62     xmlSubstituteEntitiesDefault(1);
63 #ifndef XMLSEC_NO_XSLT
64     xmlIndentTreeOutput = 1;
65 #endif /* XMLSEC_NO_XSLT */
66 
67     /* Init libxslt */
68 #ifndef XMLSEC_NO_XSLT
69     /* disable everything */
70     xsltSecPrefs = xsltNewSecurityPrefs();
71     xsltSetSecurityPrefs(xsltSecPrefs,  XSLT_SECPREF_READ_FILE,        xsltSecurityForbid);
72     xsltSetSecurityPrefs(xsltSecPrefs,  XSLT_SECPREF_WRITE_FILE,       xsltSecurityForbid);
73     xsltSetSecurityPrefs(xsltSecPrefs,  XSLT_SECPREF_CREATE_DIRECTORY, xsltSecurityForbid);
74     xsltSetSecurityPrefs(xsltSecPrefs,  XSLT_SECPREF_READ_NETWORK,     xsltSecurityForbid);
75     xsltSetSecurityPrefs(xsltSecPrefs,  XSLT_SECPREF_WRITE_NETWORK,    xsltSecurityForbid);
76     xsltSetDefaultSecurityPrefs(xsltSecPrefs);
77 #endif /* XMLSEC_NO_XSLT */
78 
79     /* Init xmlsec library */
80     if(xmlSecInit() < 0) {
81         fprintf(stderr, "Error: xmlsec initialization failed.\n");
82         return(-1);
83     }
84 
85     /* Check loaded library version */
86     if(xmlSecCheckVersion() != 1) {
87         fprintf(stderr, "Error: loaded xmlsec library version is not compatible.\n");
88         return(-1);
89     }
90 
91     /* Load default crypto engine if we are supporting dynamic
92      * loading for xmlsec-crypto libraries. Use the crypto library
93      * name ("openssl", "nss", etc.) to load corresponding
94      * xmlsec-crypto library.
95      */
96 #ifdef XMLSEC_CRYPTO_DYNAMIC_LOADING
97     if(xmlSecCryptoDLLoadLibrary(NULL) < 0) {
98         fprintf(stderr, "Error: unable to load default xmlsec-crypto library. Make sure\n"
99                         "that you have it installed and check shared libraries path\n"
100                         "(LD_LIBRARY_PATH and/or LTDL_LIBRARY_PATH) environment variables.\n");
101         return(-1);
102     }
103 #endif /* XMLSEC_CRYPTO_DYNAMIC_LOADING */
104 
105     /* Init crypto library */
106     if(xmlSecCryptoAppInit(NULL) < 0) {
107         fprintf(stderr, "Error: crypto initialization failed.\n");
108         return(-1);
109     }
110 
111     /* Init xmlsec-crypto library */
112     if(xmlSecCryptoInit() < 0) {
113         fprintf(stderr, "Error: xmlsec-crypto initialization failed.\n");
114         return(-1);
115     }
116 
117     /* create keys manager and load keys */
118     mngr = create_files_keys_mngr();
119     if(mngr == NULL) {
120         return(-1);
121     }
122 
123     if(decrypt_file(mngr, argv[1]) < 0) {
124         xmlSecKeysMngrDestroy(mngr);
125         return(-1);
126     }
127 
128     /* destroy keys manager */
129     xmlSecKeysMngrDestroy(mngr);
130 
131     /* Shutdown xmlsec-crypto library */
132     xmlSecCryptoShutdown();
133 
134     /* Shutdown crypto library */
135     xmlSecCryptoAppShutdown();
136 
137     /* Shutdown xmlsec library */
138     xmlSecShutdown();
139 
140     /* Shutdown libxslt/libxml */
141 #ifndef XMLSEC_NO_XSLT
142     xsltFreeSecurityPrefs(xsltSecPrefs);
143     xsltCleanupGlobals();
144 #endif /* XMLSEC_NO_XSLT */
145     xmlCleanupParser();
146 
147     return(0);
148 }
149 
150 /**
151  * decrypt_file:
152  * @mngr:               the pointer to keys manager.
153  * @enc_file:           the encrypted XML  file name.
154  *
155  * Decrypts the XML file #enc_file using DES key from #key_file and
156  * prints results to stdout.
157  *
158  * Returns 0 on success or a negative value if an error occurs.
159  */
160 int
decrypt_file(xmlSecKeysMngrPtr mngr,const char * enc_file)161 decrypt_file(xmlSecKeysMngrPtr mngr, const char* enc_file) {
162     xmlDocPtr doc = NULL;
163     xmlNodePtr node = NULL;
164     xmlSecEncCtxPtr encCtx = NULL;
165     int res = -1;
166 
167     assert(mngr);
168     assert(enc_file);
169 
170     /* load template */
171     doc = xmlParseFile(enc_file);
172     if ((doc == NULL) || (xmlDocGetRootElement(doc) == NULL)){
173         fprintf(stderr, "Error: unable to parse file \"%s\"\n", enc_file);
174         goto done;
175     }
176 
177     /* find start node */
178     node = xmlSecFindNode(xmlDocGetRootElement(doc), xmlSecNodeEncryptedData, xmlSecEncNs);
179     if(node == NULL) {
180         fprintf(stderr, "Error: start node not found in \"%s\"\n", enc_file);
181         goto done;
182     }
183 
184     /* create encryption context */
185     encCtx = xmlSecEncCtxCreate(mngr);
186     if(encCtx == NULL) {
187         fprintf(stderr,"Error: failed to create encryption context\n");
188         goto done;
189     }
190 
191     /* decrypt the data */
192     if((xmlSecEncCtxDecrypt(encCtx, node) < 0) || (encCtx->result == NULL)) {
193         fprintf(stderr,"Error: decryption failed\n");
194         goto done;
195     }
196 
197     /* print decrypted data to stdout */
198     if(encCtx->resultReplaced != 0) {
199         fprintf(stdout, "Decrypted XML data:\n");
200         xmlDocDump(stdout, doc);
201     } else {
202         fprintf(stdout, "Decrypted binary data (%d bytes):\n", xmlSecBufferGetSize(encCtx->result));
203         if(xmlSecBufferGetData(encCtx->result) != NULL) {
204             fwrite(xmlSecBufferGetData(encCtx->result),
205                   1,
206                   xmlSecBufferGetSize(encCtx->result),
207                   stdout);
208         }
209     }
210     fprintf(stdout, "\n");
211 
212     /* success */
213     res = 0;
214 
215 done:
216     /* cleanup */
217     if(encCtx != NULL) {
218         xmlSecEncCtxDestroy(encCtx);
219     }
220 
221     if(doc != NULL) {
222         xmlFreeDoc(doc);
223     }
224     return(res);
225 }
226 
227 /**
228  * create_files_keys_mngr:
229  *
230  * Creates a files based keys manager: we assume that key name is
231  * the key file name,
232  *
233  * Returns pointer to newly created keys manager or NULL if an error occurs.
234  */
235 xmlSecKeysMngrPtr
create_files_keys_mngr(void)236 create_files_keys_mngr(void) {
237     xmlSecKeyStorePtr keysStore;
238     xmlSecKeysMngrPtr mngr;
239 
240     /* create files based keys store */
241     keysStore = xmlSecKeyStoreCreate(files_keys_store_get_klass());
242     if(keysStore == NULL) {
243         fprintf(stderr, "Error: failed to create keys store.\n");
244         return(NULL);
245     }
246 
247     /* create keys manager */
248     mngr = xmlSecKeysMngrCreate();
249     if(mngr == NULL) {
250         fprintf(stderr, "Error: failed to create keys manager.\n");
251         xmlSecKeyStoreDestroy(keysStore);
252         return(NULL);
253     }
254 
255     /* add store to keys manager, from now on keys manager destroys the store if needed */
256     if(xmlSecKeysMngrAdoptKeysStore(mngr, keysStore) < 0) {
257         fprintf(stderr, "Error: failed to add keys store to keys manager.\n");
258         xmlSecKeyStoreDestroy(keysStore);
259         xmlSecKeysMngrDestroy(mngr);
260         return(NULL);
261     }
262 
263     /* initialize crypto library specific data in keys manager */
264     if(xmlSecCryptoKeysMngrInit(mngr) < 0) {
265         fprintf(stderr, "Error: failed to initialize crypto data in keys manager.\n");
266         xmlSecKeysMngrDestroy(mngr);
267         return(NULL);
268     }
269 
270     /* set the get key callback */
271     mngr->getKey = xmlSecKeysMngrGetKey;
272     return(mngr);
273 }
274 
275 /****************************************************************************
276  *
277  * Files Keys Store: we assume that key's name (content of the
278  * <dsig:KeyName/> element is a name of the file with a key (in the
279  * current folder).
280  * Attention: this probably not a good solution for high traffic systems.
281  *
282  ***************************************************************************/
283 static xmlSecKeyPtr             files_keys_store_find_key       (xmlSecKeyStorePtr store,
284                                                                  const xmlChar* name,
285                                                                  xmlSecKeyInfoCtxPtr keyInfoCtx);
286 static xmlSecKeyStoreKlass files_keys_store_klass = {
287     sizeof(xmlSecKeyStoreKlass),
288     sizeof(xmlSecKeyStore),
289     BAD_CAST "files-based-keys-store",  /* const xmlChar* name; */
290     NULL,                               /* xmlSecKeyStoreInitializeMethod initialize; */
291     NULL,                               /* xmlSecKeyStoreFinalizeMethod finalize; */
292     files_keys_store_find_key,          /* xmlSecKeyStoreFindKeyMethod findKey; */
293 
294     /* reserved for the future */
295     NULL,                               /* void* reserved0; */
296     NULL,                               /* void* reserved1; */
297 };
298 
299 /**
300  * files_keys_store_get_klass:
301  *
302  * The files based keys store klass: we assume that key name is the
303  * key file name,
304  *
305  * Returns files based keys store klass.
306  */
307 xmlSecKeyStoreId
files_keys_store_get_klass(void)308 files_keys_store_get_klass(void) {
309     return(&files_keys_store_klass);
310 }
311 
312 /**
313  * files_keys_store_find_key:
314  * @store:              the pointer to simple keys store.
315  * @name:               the desired key name.
316  * @keyInfoCtx:         the pointer to <dsig:KeyInfo/> node processing context.
317  *
318  * Lookups key in the @store. The caller is responsible for destroying
319  * returned key with #xmlSecKeyDestroy function.
320  *
321  * Returns pointer to key or NULL if key not found or an error occurs.
322  */
323 static xmlSecKeyPtr
files_keys_store_find_key(xmlSecKeyStorePtr store,const xmlChar * name,xmlSecKeyInfoCtxPtr keyInfoCtx)324 files_keys_store_find_key(xmlSecKeyStorePtr store, const xmlChar* name, xmlSecKeyInfoCtxPtr keyInfoCtx) {
325     xmlSecKeyPtr key;
326     const xmlChar* p;
327 
328     assert(store);
329     assert(keyInfoCtx);
330 
331     /* it's possible to do not have the key name or desired key type
332      * but we could do nothing in this case */
333     if((name == NULL) || (keyInfoCtx->keyReq.keyId == xmlSecKeyDataIdUnknown)){
334         return(NULL);
335     }
336 
337     /* we don't want to open files in a folder other than "current";
338      * to prevent it limit the characters in the key name to alpha/digit,
339      * '.', '-' or '_'.
340      */
341     for(p = name; (*p) != '\0'; ++p) {
342         if(!isalnum((*p)) && ((*p) != '.') && ((*p) != '-') && ((*p) != '_')) {
343             return(NULL);
344         }
345     }
346 
347     if((keyInfoCtx->keyReq.keyId == xmlSecKeyDataDsaId) || (keyInfoCtx->keyReq.keyId == xmlSecKeyDataRsaId)) {
348         /* load key from a pem file, if key is not found then it's an error (is it?) */
349         key = xmlSecCryptoAppKeyLoad(name, xmlSecKeyDataFormatPem, NULL, NULL, NULL);
350         if(key == NULL) {
351             fprintf(stderr,"Error: failed to load public pem key from \"%s\"\n", name);
352             return(NULL);
353         }
354     } else {
355         /* otherwise it's a binary key, if key is not found then it's an error (is it?) */
356         key = xmlSecKeyReadBinaryFile(keyInfoCtx->keyReq.keyId, name);
357         if(key == NULL) {
358             fprintf(stderr,"Error: failed to load key from binary file \"%s\"\n", name);
359             return(NULL);
360         }
361     }
362 
363     /* set key name */
364     if(xmlSecKeySetName(key, name) < 0) {
365         fprintf(stderr,"Error: failed to set key name for key from \"%s\"\n", name);
366         xmlSecKeyDestroy(key);
367         return(NULL);
368     }
369 
370     return(key);
371 }
372 
373