hivex: Added gnulib includes from builddir, as suggested by the Gnulib documentation...
[hivex.git] / xml / hivexml.c
1 /* hivexml - Convert Windows Registry "hive" to XML file.
2  * Copyright (C) 2009 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdint.h>
25 #include <inttypes.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <time.h>
29 #include <locale.h>
30
31 #ifdef HAVE_LIBINTL_H
32 #include <libintl.h>
33 #endif
34
35 #include <getopt.h>
36
37 #include <libxml/xmlwriter.h>
38
39 #include "hivex.h"
40
41 #ifdef HAVE_GETTEXT
42 #include "gettext.h"
43 #define _(str) dgettext(PACKAGE, (str))
44 //#define N_(str) dgettext(PACKAGE, (str))
45 #else
46 #define _(str) str
47 //#define N_(str) str
48 #endif
49
50 static char *filetime_to_8601 (int64_t windows_ticks);
51
52 /* Callback functions. */
53 static int node_start (hive_h *, void *, hive_node_h, const char *name);
54 static int node_end (hive_h *, void *, hive_node_h, const char *name);
55 static int value_string (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *str);
56 static int value_multiple_strings (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, char **argv);
57 static int value_string_invalid_utf16 (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *str);
58 static int value_dword (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, int32_t);
59 static int value_qword (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, int64_t);
60 static int value_binary (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value);
61 static int value_none (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value);
62 static int value_other (hive_h *, void *, hive_node_h, hive_value_h, hive_type t, size_t len, const char *key, const char *value);
63
64 static struct hivex_visitor visitor = {
65   .node_start = node_start,
66   .node_end = node_end,
67   .value_string = value_string,
68   .value_multiple_strings = value_multiple_strings,
69   .value_string_invalid_utf16 = value_string_invalid_utf16,
70   .value_dword = value_dword,
71   .value_qword = value_qword,
72   .value_binary = value_binary,
73   .value_none = value_none,
74   .value_other = value_other
75 };
76
77 #define XML_CHECK(proc, args)                                           \
78   do {                                                                  \
79     if ((proc args) == -1) {                                            \
80       fprintf (stderr, _("%s: failed to write XML document\n"), #proc); \
81       exit (EXIT_FAILURE);                                              \
82     }                                                                   \
83   } while (0)
84
85 int
86 main (int argc, char *argv[])
87 {
88   setlocale (LC_ALL, "");
89 #ifdef HAVE_BINDTEXTDOMAIN
90   bindtextdomain (PACKAGE, LOCALEBASEDIR);
91   textdomain (PACKAGE);
92 #endif
93
94   int c;
95   int open_flags = 0;
96   int visit_flags = 0;
97
98   while ((c = getopt (argc, argv, "dk")) != EOF) {
99     switch (c) {
100     case 'd':
101       open_flags |= HIVEX_OPEN_DEBUG;
102       break;
103     case 'k':
104       visit_flags |= HIVEX_VISIT_SKIP_BAD;
105       break;
106     default:
107       fprintf (stderr, "hivexml [-dk] regfile > output.xml\n");
108       exit (EXIT_FAILURE);
109     }
110   }
111
112   if (optind + 1 != argc) {
113     fprintf (stderr, _("hivexml: missing name of input file\n"));
114     exit (EXIT_FAILURE);
115   }
116
117   hive_h *h = hivex_open (argv[optind], open_flags);
118   if (h == NULL) {
119     perror (argv[optind]);
120     exit (EXIT_FAILURE);
121   }
122
123   /* Note both this macro, and xmlTextWriterStartDocument leak memory.  There
124    * doesn't seem to be any way to recover that memory, but it's not a
125    * large amount.
126    */
127   LIBXML_TEST_VERSION;
128
129   xmlTextWriterPtr writer;
130   writer = xmlNewTextWriterFilename ("/dev/stdout", 0);
131   if (writer == NULL) {
132     fprintf (stderr, _("xmlNewTextWriterFilename: failed to create XML writer\n"));
133     exit (EXIT_FAILURE);
134   }
135
136   XML_CHECK (xmlTextWriterStartDocument, (writer, NULL, "utf-8", NULL));
137   XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "hive"));
138
139   int64_t hive_mtime = hivex_last_modified (h);
140   if (hive_mtime >= 0) {
141     char *timebuf = filetime_to_8601 (hive_mtime);
142     if (timebuf) {
143       XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "mtime"));
144       XML_CHECK (xmlTextWriterWriteString, (writer, BAD_CAST timebuf));
145       XML_CHECK (xmlTextWriterEndElement, (writer));
146       free (timebuf);
147     }
148   }
149
150   if (hivex_visit (h, &visitor, sizeof visitor, writer, visit_flags) == -1) {
151     perror (argv[optind]);
152     exit (EXIT_FAILURE);
153   }
154
155   if (hivex_close (h) == -1) {
156     perror (argv[optind]);
157     exit (EXIT_FAILURE);
158   }
159
160   XML_CHECK (xmlTextWriterEndElement, (writer));
161   XML_CHECK (xmlTextWriterEndDocument, (writer));
162   xmlFreeTextWriter (writer);
163
164   exit (EXIT_SUCCESS);
165 }
166
167 /* Convert Windows filetime to ISO 8601 format.
168  * http://stackoverflow.com/questions/6161776/convert-windows-filetime-to-second-in-unix-linux/6161842#6161842
169  *
170  * Source for time_t->char* conversion: Fiwalk version 0.6.14's
171  * fiwalk.cpp.
172  *
173  * The caller should free the returned buffer.
174  *
175  * This function returns NULL on a 0 input.  In the context of
176  * hives, which only have mtimes, 0 will always be a complete
177  * absence of data.
178  */
179
180 #define WINDOWS_TICK 10000000LL
181 #define SEC_TO_UNIX_EPOCH 11644473600LL
182 #define TIMESTAMP_BUF_LEN 32
183
184 static char *
185 filetime_to_8601 (int64_t windows_ticks)
186 {
187   char *ret;
188   time_t t;
189   struct tm *tm;
190
191   if (windows_ticks == 0LL)
192     return NULL;
193
194   t = windows_ticks / WINDOWS_TICK - SEC_TO_UNIX_EPOCH;
195   tm = gmtime (&t);
196   if (tm == NULL)
197     return NULL;
198
199   ret = malloc (TIMESTAMP_BUF_LEN);
200   if (ret == NULL) {
201     perror ("malloc");
202     exit (EXIT_FAILURE);
203   }
204
205   if (strftime (ret, TIMESTAMP_BUF_LEN, "%FT%TZ", tm) == 0) {
206     perror ("strftime");
207     exit (EXIT_FAILURE);
208   }
209
210   return ret;
211 }
212
213 static int
214 node_start (hive_h *h, void *writer_v, hive_node_h node, const char *name)
215 {
216   int64_t last_modified;
217   char *timebuf;
218   int ret = 0;
219
220   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
221   XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "node"));
222   XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "name", BAD_CAST name));
223
224   if (node == hivex_root (h)) {
225     XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "root", BAD_CAST "1"));
226   }
227
228   last_modified = hivex_node_timestamp (h, node);
229   if (last_modified >= 0) {
230     timebuf = filetime_to_8601 (last_modified);
231     if (timebuf) {
232       XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "mtime"));
233       XML_CHECK (xmlTextWriterWriteString, (writer, BAD_CAST timebuf));
234       XML_CHECK (xmlTextWriterEndElement, (writer));
235       free (timebuf);
236     }
237   }
238
239   return 0;
240 }
241
242 static int
243 node_end (hive_h *h, void *writer_v, hive_node_h node, const char *name)
244 {
245   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
246   XML_CHECK (xmlTextWriterEndElement, (writer));
247   return 0;
248 }
249
250 static void
251 start_value (xmlTextWriterPtr writer,
252              const char *key, const char *type, const char *encoding)
253 {
254   XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "value"));
255   XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "type", BAD_CAST type));
256   if (encoding)
257     XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "encoding", BAD_CAST encoding));
258   if (*key)
259     XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "key", BAD_CAST key));
260   else                          /* default key */
261     XML_CHECK (xmlTextWriterWriteAttribute, (writer, BAD_CAST "default", BAD_CAST "1"));
262 }
263
264 static void
265 end_value (xmlTextWriterPtr writer)
266 {
267   XML_CHECK (xmlTextWriterEndElement, (writer));
268 }
269
270 static int
271 value_string (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value,
272               hive_type t, size_t len, const char *key, const char *str)
273 {
274   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
275   const char *type;
276
277   switch (t) {
278   case hive_t_string: type = "string"; break;
279   case hive_t_expand_string: type = "expand"; break;
280   case hive_t_link: type = "link"; break;
281
282   case hive_t_none:
283   case hive_t_binary:
284   case hive_t_dword:
285   case hive_t_dword_be:
286   case hive_t_multiple_strings:
287   case hive_t_resource_list:
288   case hive_t_full_resource_description:
289   case hive_t_resource_requirements_list:
290   case hive_t_qword:
291     abort ();                   /* internal error - should not happen */
292
293   default:
294     type = "unknown";
295   }
296
297   start_value (writer, key, type, NULL);
298   XML_CHECK (xmlTextWriterStartAttribute, (writer, BAD_CAST "value"));
299   XML_CHECK (xmlTextWriterWriteString, (writer, BAD_CAST str));
300   XML_CHECK (xmlTextWriterEndAttribute, (writer));
301   end_value (writer);
302   return 0;
303 }
304
305 static int
306 value_multiple_strings (hive_h *h, void *writer_v, hive_node_h node,
307                         hive_value_h value, hive_type t, size_t len,
308                         const char *key, char **argv)
309 {
310   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
311   start_value (writer, key, "string-list", NULL);
312
313   size_t i;
314   for (i = 0; argv[i] != NULL; ++i) {
315     XML_CHECK (xmlTextWriterStartElement, (writer, BAD_CAST "string"));
316     XML_CHECK (xmlTextWriterWriteString, (writer, BAD_CAST argv[i]));
317     XML_CHECK (xmlTextWriterEndElement, (writer));
318   }
319
320   end_value (writer);
321   return 0;
322 }
323
324 static int
325 value_string_invalid_utf16 (hive_h *h, void *writer_v, hive_node_h node,
326                             hive_value_h value, hive_type t, size_t len,
327                             const char *key,
328                             const char *str /* original data */)
329 {
330   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
331   const char *type;
332
333   switch (t) {
334   case hive_t_string: type = "bad-string"; break;
335   case hive_t_expand_string: type = "bad-expand"; break;
336   case hive_t_link: type = "bad-link"; break;
337   case hive_t_multiple_strings: type = "bad-string-list"; break;
338
339   case hive_t_none:
340   case hive_t_binary:
341   case hive_t_dword:
342   case hive_t_dword_be:
343   case hive_t_resource_list:
344   case hive_t_full_resource_description:
345   case hive_t_resource_requirements_list:
346   case hive_t_qword:
347     abort ();                   /* internal error - should not happen */
348
349   default:
350     type = "unknown";
351   }
352
353   start_value (writer, key, type, "base64");
354   XML_CHECK (xmlTextWriterStartAttribute, (writer, BAD_CAST "value"));
355   XML_CHECK (xmlTextWriterWriteBase64, (writer, str, 0, len));
356   XML_CHECK (xmlTextWriterEndAttribute, (writer));
357   end_value (writer);
358
359   return 0;
360 }
361
362 static int
363 value_dword (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value,
364              hive_type t, size_t len, const char *key, int32_t v)
365 {
366   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
367   start_value (writer, key, "int32", NULL);
368   XML_CHECK (xmlTextWriterWriteFormatAttribute, (writer, BAD_CAST "value", "%" PRIi32, v));
369   end_value (writer);
370   return 0;
371 }
372
373 static int
374 value_qword (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value,
375              hive_type t, size_t len, const char *key, int64_t v)
376 {
377   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
378   start_value (writer, key, "int64", NULL);
379   XML_CHECK (xmlTextWriterWriteFormatAttribute, (writer, BAD_CAST "value", "%" PRIi64, v));
380   end_value (writer);
381   return 0;
382 }
383
384 static int
385 value_binary (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value,
386               hive_type t, size_t len, const char *key, const char *v)
387 {
388   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
389   start_value (writer, key, "binary", "base64");
390   XML_CHECK (xmlTextWriterStartAttribute, (writer, BAD_CAST "value"));
391   XML_CHECK (xmlTextWriterWriteBase64, (writer, v, 0, len));
392   XML_CHECK (xmlTextWriterEndAttribute, (writer));
393   end_value (writer);
394   return 0;
395 }
396
397 static int
398 value_none (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value,
399             hive_type t, size_t len, const char *key, const char *v)
400 {
401   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
402   start_value (writer, key, "none", "base64");
403   if (len > 0) {
404     XML_CHECK (xmlTextWriterStartAttribute, (writer, BAD_CAST "value"));
405     XML_CHECK (xmlTextWriterWriteBase64, (writer, v, 0, len));
406     XML_CHECK (xmlTextWriterEndAttribute, (writer));
407   }
408   end_value (writer);
409   return 0;
410 }
411
412 static int
413 value_other (hive_h *h, void *writer_v, hive_node_h node, hive_value_h value,
414              hive_type t, size_t len, const char *key, const char *v)
415 {
416   xmlTextWriterPtr writer = (xmlTextWriterPtr) writer_v;
417   const char *type;
418
419   switch (t) {
420   case hive_t_none:
421   case hive_t_binary:
422   case hive_t_dword:
423   case hive_t_dword_be:
424   case hive_t_qword:
425   case hive_t_string:
426   case hive_t_expand_string:
427   case hive_t_link:
428   case hive_t_multiple_strings:
429     abort ();                   /* internal error - should not happen */
430
431   case hive_t_resource_list: type = "resource-list"; break;
432   case hive_t_full_resource_description: type = "resource-description"; break;
433   case hive_t_resource_requirements_list: type = "resource-requirements"; break;
434
435   default:
436     type = "unknown";
437   }
438
439   start_value (writer, key, type, "base64");
440   if (len > 0) {
441     XML_CHECK (xmlTextWriterStartAttribute, (writer, BAD_CAST "value"));
442     XML_CHECK (xmlTextWriterWriteBase64, (writer, v, 0, len));
443     XML_CHECK (xmlTextWriterEndAttribute, (writer));
444   }
445   end_value (writer);
446
447   return 0;
448 }