source: trunk/src/common.c @ 137

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

Added error messages to most parse functions in regfi

Relaxed validation requirements on NK types and missing value data

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