source: trunk/src/common.c @ 156

Last change on this file since 156 was 154, checked in by tim, 15 years ago

fixed a parsing flaw in big data chunk concatenation

  • Property svn:keywords set to Id
File size: 10.2 KB
Line 
1/*
2 * This file stores code common to the command line tools.
3 * XXX: This should be converted to a proper library.
4 *
5 * Copyright (C) 2005-2008 Timothy D. Morgan
6 * Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 3 of the License.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
20 *
21 * $Id: common.c 154 2009-06-03 15:21:47Z tim $
22 */
23
24#include <iconv.h>
25iconv_t conv_desc;
26
27const char* key_special_chars = ",\"\\/";
28const char* subfield_special_chars = ",\"\\|";
29const char* common_special_chars = ",\"\\";
30
31#define REGLOOKUP_VERSION "0.11.0"
32
33#define REGLOOKUP_EXIT_OK       0
34#define REGLOOKUP_EXIT_OSERR   71
35#define REGLOOKUP_EXIT_USAGE   64
36#define REGLOOKUP_EXIT_DATAERR 65
37#define REGLOOKUP_EXIT_NOINPUT 66
38
39
40void bailOut(int code, char* message)
41{
42  fprintf(stderr, message);
43  exit(code);
44}
45
46void printMsgs(REGFI_FILE* f)
47{
48  char* msgs = regfi_get_messages(f);
49  if(msgs != NULL)
50  {
51    fprintf(stderr, "%s", msgs);
52    free(msgs);
53  }
54}
55
56void clearMsgs(REGFI_FILE* f)
57{
58  char* msgs = regfi_get_messages(f);
59  if(msgs != NULL)
60    free(msgs);
61}
62
63
64/* Returns a newly malloc()ed string which contains original buffer,
65 * except for non-printable or special characters are quoted in hex
66 * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted
67 * character.  A null terminator is added, since only ascii, not binary,
68 * is returned.
69 */
70static char* quote_buffer(const unsigned char* str, 
71                          unsigned int len, const char* special)
72{
73  unsigned int i, added_len;
74  unsigned int num_written = 0;
75
76  unsigned int buf_len = sizeof(char)*(len+1);
77  char* ret_val = NULL; 
78  char* tmp_buf;
79
80  if(buf_len > 0) 
81    ret_val = malloc(buf_len);
82  if(ret_val == NULL)
83    return NULL;
84
85  for(i=0; i<len; i++)
86  {
87    if(buf_len <= (num_written+5))
88    {
89      /* Expand the buffer by the memory consumption rate seen so far
90       * times the amount of input left to process.  The expansion is bounded
91       * below by a minimum safety increase, and above by the maximum possible
92       * output string length.  This should minimize both the number of
93       * reallocs() and the amount of wasted memory.
94       */
95      added_len = (len-i)*num_written/(i+1);
96      if((buf_len+added_len) > (len*4+1))
97        buf_len = len*4+1;
98      else
99      {
100        if (added_len < 5)
101          buf_len += 5;
102        else
103          buf_len += added_len;
104      }
105
106      tmp_buf = realloc(ret_val, buf_len);
107      if(tmp_buf == NULL)
108      {
109        free(ret_val);
110        return NULL;
111      }
112      ret_val = tmp_buf;
113    }
114   
115    if(str[i] < 32 || str[i] > 126 || strchr(special, str[i]) != NULL)
116    {
117      num_written += snprintf(ret_val + num_written, buf_len - num_written,
118                              "\\x%.2X", str[i]);
119    }
120    else
121      ret_val[num_written++] = str[i];
122  }
123  ret_val[num_written] = '\0';
124
125  return ret_val;
126}
127
128
129/* Returns a newly malloc()ed string which contains original string,
130 * except for non-printable or special characters are quoted in hex
131 * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted
132 * character.
133 */
134static char* quote_string(const char* str, const char* special)
135{
136  unsigned int len;
137
138  if(str == NULL)
139    return NULL;
140
141  len = strlen(str);
142  return quote_buffer((const unsigned char*)str, len, special);
143}
144
145
146/*
147 * Convert from UTF-16LE to ASCII.  Accepts a Unicode buffer, uni, and
148 * it's length, uni_max.  Writes ASCII to the buffer ascii, whose size
149 * is ascii_max.  Writes at most (ascii_max-1) bytes to ascii, and null
150 * terminates the string.  Returns the length of the data written to
151 * ascii.  On error, returns a negative errno code.
152 */
153static int uni_to_ascii(unsigned char* uni, char* ascii, 
154                        uint32 uni_max, uint32 ascii_max)
155{
156  char* inbuf = (char*)uni;
157  char* outbuf = ascii;
158  size_t in_len = (size_t)uni_max;
159  size_t out_len = (size_t)(ascii_max-1);
160  int ret;
161
162  /* Set up conversion descriptor. */
163  conv_desc = iconv_open("US-ASCII//TRANSLIT", "UTF-16LE");
164
165  ret = iconv(conv_desc, &inbuf, &in_len, &outbuf, &out_len);
166  if(ret == -1)
167  {
168    iconv_close(conv_desc);
169    return -errno;
170  }
171  *outbuf = '\0';
172
173  iconv_close(conv_desc); 
174  return ascii_max-out_len-1;
175}
176
177
178static char* quote_unicode(unsigned char* uni, uint32 length, 
179                           const char* special, char** error_msg)
180{
181  char* ret_val;
182  char* ascii = NULL;
183  char* tmp_err;
184  int ret_err;
185  *error_msg = NULL;
186
187  if(length+1 > 0)
188    ascii = malloc(length+1);
189  if(ascii == NULL)
190  {
191    *error_msg = (char*)malloc(27);
192    if(*error_msg == NULL)
193      return NULL;
194    strcpy(*error_msg, "Memory allocation failure.");
195    return NULL;
196  }
197 
198  ret_err = uni_to_ascii(uni, ascii, length, length+1);
199  if(ret_err < 0)
200  {
201    free(ascii);
202    tmp_err = strerror(-ret_err);
203    *error_msg = (char*)malloc(61+strlen(tmp_err));
204    if(*error_msg == NULL)
205      return NULL;
206
207    sprintf(*error_msg, 
208            "Unicode conversion failed with '%s'. Quoting as binary.", tmp_err);
209    ret_val = quote_buffer(uni, length, special);
210  }
211  else
212  {
213    ret_val = quote_string(ascii, special);
214    free(ascii);
215  }
216 
217  return ret_val;
218}
219
220
221/*
222 * Convert a data value to a string for display.  Returns NULL on error,
223 * and the string to display if there is no error, or a non-fatal
224 * error.  On any error (fatal or non-fatal) occurs, (*error_msg) will
225 * be set to a newly allocated string, containing an error message.  If
226 * a memory allocation failure occurs while generating the error
227 * message, both the return value and (*error_msg) will be NULL.  It
228 * is the responsibility of the caller to free both a non-NULL return
229 * value, and a non-NULL (*error_msg).
230 */
231/* XXX: Part of this function's logic should be pushed into the regfi API.
232 *      The structures should be parsed and stored with VK records and only
233 *      escaped/encoded later in reglookup and reglookup-recover.
234 */
235static char* data_to_ascii(unsigned char* datap, uint32 len, uint32 type, 
236                           char** error_msg)
237{
238  char* asciip;
239  char* ascii;
240  char* ascii_tmp;
241  char* cur_quoted;
242  char* tmp_err = NULL;
243  const char* delim;
244  uint32 i;
245  uint32 cur_str_len;
246  uint32 ascii_max;
247  uint32 str_rem, alen;
248  int ret_err;
249
250  if(datap == NULL)
251  {
252    *error_msg = (char*)malloc(24);
253    if(*error_msg == NULL)
254      return NULL;
255    strcpy(*error_msg, "Data pointer was NULL.");
256    return NULL;
257  }
258  *error_msg = NULL;
259
260  switch (type) 
261  {
262  case REG_SZ:
263  case REG_EXPAND_SZ:
264    /* REG_LINK is a symbolic link, stored as a unicode string. */
265  case REG_LINK:
266    /* Sometimes values have binary stored in them.  If the unicode
267     * conversion fails, just quote it raw.
268     */
269    cur_quoted = quote_unicode(datap, len, common_special_chars, &tmp_err);
270    if(cur_quoted == NULL)
271    {
272      if(tmp_err == NULL && (*error_msg = (char*)malloc(49)) != NULL)
273        strcpy(*error_msg, "Buffer could not be quoted due to unknown error.");
274      else if((*error_msg = (char*)malloc(42+strlen(tmp_err))) != NULL)
275      {
276        sprintf(*error_msg, "Buffer could not be quoted due to error: %s", 
277                tmp_err);
278        free(tmp_err);
279      }
280    }
281    else if (tmp_err != NULL)
282      *error_msg = tmp_err;
283    return cur_quoted;
284    break;
285
286  case REG_DWORD:
287    ascii_max = sizeof(char)*(8+2+1);
288    ascii = malloc(ascii_max);
289    if(ascii == NULL)
290      return NULL;
291
292    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X", 
293             datap[3], datap[2], datap[1], datap[0]);
294    return ascii;
295    break;
296
297  case REG_DWORD_BE:
298    ascii_max = sizeof(char)*(8+2+1);
299    ascii = malloc(ascii_max);
300    if(ascii == NULL)
301      return NULL;
302
303    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X", 
304             datap[0], datap[1], datap[2], datap[3]);
305    return ascii;
306    break;
307
308  case REG_QWORD:
309    ascii_max = sizeof(char)*(16+2+1);
310    ascii = malloc(ascii_max);
311    if(ascii == NULL)
312      return NULL;
313
314    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X",
315             datap[7], datap[6], datap[5], datap[4],
316             datap[3], datap[2], datap[1], datap[0]);
317    return ascii;
318    break;
319   
320  case REG_MULTI_SZ:
321    ascii_max = sizeof(char)*(len*4+1);
322    ascii_tmp = malloc(ascii_max);
323    if(ascii_tmp == NULL)
324      return NULL;
325
326    /* Attempt to convert entire string from UTF-16LE to ASCII,
327     * then parse and quote fields individually.
328     * If this fails, simply quote entire buffer as binary.
329     */
330    ret_err = uni_to_ascii(datap, ascii_tmp, len, ascii_max);
331    if(ret_err < 0)
332    {
333      tmp_err = strerror(-ret_err);
334      *error_msg = (char*)malloc(61+strlen(tmp_err));
335      if(*error_msg == NULL)
336      {
337        free(ascii_tmp);
338        return NULL;
339      }
340
341      sprintf(*error_msg, "MULTI_SZ unicode conversion"
342              " failed with '%s'. Quoting as binary.", tmp_err);
343      ascii = quote_buffer(datap, len, subfield_special_chars);
344    }
345    else
346    {
347      ascii = malloc(ascii_max);
348      if(ascii == NULL)
349      {
350        free(ascii_tmp);
351        return NULL;
352      }
353      asciip = ascii;
354      asciip[0] = '\0';
355      str_rem = ascii_max;
356      delim = "";
357      for(i=0; i<ret_err; i+=cur_str_len+1)
358      {
359        cur_str_len = strlen(ascii_tmp+i);
360        if(ascii_tmp[i] != '\0')
361        {
362          cur_quoted = quote_string(ascii_tmp+i, subfield_special_chars);
363          if(cur_quoted != NULL)
364          {
365            alen = snprintf(asciip, str_rem, "%s%s", delim, cur_quoted);
366            asciip += alen;
367            str_rem -= alen;
368            free(cur_quoted);
369          }
370        }
371        delim = "|";
372      }
373    }
374
375    free(ascii_tmp);
376    return ascii;
377    break;
378
379  /* XXX: Dont know what to do with these yet, just print as binary... */
380  default:
381    *error_msg = (char*)malloc(65);
382    if(*error_msg == NULL)
383      return NULL;
384    sprintf(*error_msg,
385            "Unrecognized registry data type (0x%.8X); quoting as binary.",
386            type);
387   
388  case REG_NONE:
389  case REG_RESOURCE_LIST:
390  case REG_FULL_RESOURCE_DESCRIPTOR:
391  case REG_RESOURCE_REQUIREMENTS_LIST:
392
393  case REG_BINARY:
394    return quote_buffer(datap, len, common_special_chars);
395    break;
396  }
397
398  return NULL;
399}
Note: See TracBrowser for help on using the repository browser.