Postfix3.3.1
dict_utf8.c
[詳解]
1 /*++
2 /* NAME
3 /* dict_utf8 3
4 /* SUMMARY
5 /* dictionary UTF-8 helpers
6 /* SYNOPSIS
7 /* #include <dict.h>
8 /*
9 /* DICT *dict_utf8_activate(
10 /* DICT *dict)
11 /* DESCRIPTION
12 /* dict_utf8_activate() wraps a dictionary's lookup/update/delete
13 /* methods with code that enforces UTF-8 checks on keys and
14 /* values, and that logs a warning when incorrect UTF-8 is
15 /* encountered. The original dictionary handle becomes invalid.
16 /*
17 /* The wrapper code enforces a policy that maximizes application
18 /* robustness (it avoids the need for new error-handling code
19 /* paths in application code). Attempts to store non-UTF-8
20 /* keys or values are skipped while reporting a non-error
21 /* status, attempts to look up or delete non-UTF-8 keys are
22 /* skipped while reporting a non-error status, and lookup
23 /* results that contain a non-UTF-8 value are blocked while
24 /* reporting a configuration error.
25 /* BUGS
26 /* dict_utf8_activate() does not nest.
27 /* LICENSE
28 /* .ad
29 /* .fi
30 /* The Secure Mailer license must be distributed with this software.
31 /* AUTHOR(S)
32 /* Wietse Venema
33 /* IBM T.J. Watson Research
34 /* P.O. Box 704
35 /* Yorktown Heights, NY 10598, USA
36 /*--*/
37 
38  /*
39  * System library.
40  */
41 #include <sys_defs.h>
42 #include <string.h>
43 
44  /*
45  * Utility library.
46  */
47 #include <msg.h>
48 #include <stringops.h>
49 #include <dict.h>
50 #include <mymalloc.h>
51 #include <msg.h>
52 
53  /*
54  * The goal is to maximize robustness: bad UTF-8 should not appear in keys,
55  * because those are derived from controlled inputs, and values should be
56  * printable before they are stored. But if we failed to check something
57  * then it should not result in fatal errors and thus open up the system for
58  * a denial-of-service attack.
59  *
60  * Proposed over-all policy: skip attempts to store invalid UTF-8 lookup keys
61  * or values. Rationale: some storage may not permit malformed UTF-8. This
62  * maximizes program robustness. If we get an invalid lookup result, report
63  * a configuration error.
64  *
65  * LOOKUP
66  *
67  * If the key is invalid, log a warning and skip the request. Rationale: the
68  * item cannot exist.
69  *
70  * If the lookup result is invalid, log a warning and return a configuration
71  * error.
72  *
73  * UPDATE
74  *
75  * If the key is invalid, then log a warning and skip the request. Rationale:
76  * the item cannot exist.
77  *
78  * If the value is invalid, log a warning and skip the request. Rationale:
79  * storage may not permit malformed UTF-8. This maximizes program
80  * robustness.
81  *
82  * DELETE
83  *
84  * If the key is invalid, then skip the request. Rationale: the item cannot
85  * exist.
86  */
87 
88 /* dict_utf8_check_fold - casefold or validate string */
89 
90 static char *dict_utf8_check_fold(DICT *dict, const char *string,
91  CONST_CHAR_STAR *err)
92 {
93  int fold_flag = (dict->flags & DICT_FLAG_FOLD_ANY);
94 
95  /*
96  * Validate UTF-8 without casefolding.
97  */
98  if (!allascii(string) && valid_utf8_string(string, strlen(string)) == 0) {
99  if (err)
100  *err = "malformed UTF-8 or invalid codepoint";
101  return (0);
102  }
103 
104  /*
105  * Casefold UTF-8.
106  */
107  if (fold_flag != 0 && (fold_flag & (dict->flags & DICT_FLAG_FIXED) ?
109  if (dict->fold_buf == 0)
110  dict->fold_buf = vstring_alloc(10);
111  return (casefold(dict->fold_buf, string));
112  }
113  return ((char *) string);
114 }
115 
116 /* dict_utf8_check validate UTF-8 string */
117 
118 static int dict_utf8_check(const char *string, CONST_CHAR_STAR *err)
119 {
120  if (!allascii(string) && valid_utf8_string(string, strlen(string)) == 0) {
121  if (err)
122  *err = "malformed UTF-8 or invalid codepoint";
123  return (0);
124  }
125  return (1);
126 }
127 
128 /* dict_utf8_lookup - UTF-8 lookup method wrapper */
129 
130 static const char *dict_utf8_lookup(DICT *dict, const char *key)
131 {
132  DICT_UTF8_BACKUP *backup;
133  const char *utf8_err;
134  const char *fold_res;
135  const char *value;
136  int saved_flags;
137 
138  /*
139  * Validate and optionally fold the key, and if invalid skip the request.
140  */
141  if ((fold_res = dict_utf8_check_fold(dict, key, &utf8_err)) == 0) {
142  msg_warn("%s:%s: non-UTF-8 key \"%s\": %s",
143  dict->type, dict->name, key, utf8_err);
144  dict->error = DICT_ERR_NONE;
145  return (0);
146  }
147 
148  /*
149  * Proxy the request with casefolding turned off.
150  */
151  saved_flags = (dict->flags & DICT_FLAG_FOLD_ANY);
152  dict->flags &= ~DICT_FLAG_FOLD_ANY;
153  backup = dict->utf8_backup;
154  value = backup->lookup(dict, fold_res);
155  dict->flags |= saved_flags;
156 
157  /*
158  * Validate the result, and if invalid fail the request.
159  */
160  if (value != 0 && dict_utf8_check(value, &utf8_err) == 0) {
161  msg_warn("%s:%s: key \"%s\": non-UTF-8 value \"%s\": %s",
162  dict->type, dict->name, key, value, utf8_err);
163  dict->error = DICT_ERR_CONFIG;
164  return (0);
165  } else {
166  return (value);
167  }
168 }
169 
170 /* dict_utf8_update - UTF-8 update method wrapper */
171 
172 static int dict_utf8_update(DICT *dict, const char *key, const char *value)
173 {
174  DICT_UTF8_BACKUP *backup;
175  const char *utf8_err;
176  const char *fold_res;
177  int saved_flags;
178  int status;
179 
180  /*
181  * Validate or fold the key, and if invalid skip the request.
182  */
183  if ((fold_res = dict_utf8_check_fold(dict, key, &utf8_err)) == 0) {
184  msg_warn("%s:%s: non-UTF-8 key \"%s\": %s",
185  dict->type, dict->name, key, utf8_err);
186  dict->error = DICT_ERR_NONE;
187  return (DICT_STAT_SUCCESS);
188  }
189 
190  /*
191  * Validate the value, and if invalid skip the request.
192  */
193  else if (dict_utf8_check(value, &utf8_err) == 0) {
194  msg_warn("%s:%s: key \"%s\": non-UTF-8 value \"%s\": %s",
195  dict->type, dict->name, key, value, utf8_err);
196  dict->error = DICT_ERR_NONE;
197  return (DICT_STAT_SUCCESS);
198  }
199 
200  /*
201  * Proxy the request with casefolding turned off.
202  */
203  else {
204  saved_flags = (dict->flags & DICT_FLAG_FOLD_ANY);
205  dict->flags &= ~DICT_FLAG_FOLD_ANY;
206  backup = dict->utf8_backup;
207  status = backup->update(dict, fold_res, value);
208  dict->flags |= saved_flags;
209  return (status);
210  }
211 }
212 
213 /* dict_utf8_delete - UTF-8 delete method wrapper */
214 
215 static int dict_utf8_delete(DICT *dict, const char *key)
216 {
217  DICT_UTF8_BACKUP *backup;
218  const char *utf8_err;
219  const char *fold_res;
220  int saved_flags;
221  int status;
222 
223  /*
224  * Validate and optionally fold the key, and if invalid skip the request.
225  */
226  if ((fold_res = dict_utf8_check_fold(dict, key, &utf8_err)) == 0) {
227  msg_warn("%s:%s: non-UTF-8 key \"%s\": %s",
228  dict->type, dict->name, key, utf8_err);
229  dict->error = DICT_ERR_NONE;
230  return (DICT_STAT_SUCCESS);
231  }
232 
233  /*
234  * Proxy the request with casefolding turned off.
235  */
236  else {
237  saved_flags = (dict->flags & DICT_FLAG_FOLD_ANY);
238  dict->flags &= ~DICT_FLAG_FOLD_ANY;
239  backup = dict->utf8_backup;
240  status = backup->delete(dict, fold_res);
241  dict->flags |= saved_flags;
242  return (status);
243  }
244 }
245 
246 /* dict_utf8_activate - wrap a legacy dict object for UTF-8 processing */
247 
249 {
250  const char myname[] = "dict_utf8_activate";
251  DICT_UTF8_BACKUP *backup;
252 
253  /*
254  * Sanity check.
255  */
256  if (util_utf8_enable == 0)
257  msg_panic("%s: Unicode support is not available", myname);
258  if ((dict->flags & DICT_FLAG_UTF8_REQUEST) == 0)
259  msg_panic("%s: %s:%s does not request Unicode support",
260  myname, dict->type, dict->name);
261  if ((dict->flags & DICT_FLAG_UTF8_ACTIVE) || dict->utf8_backup != 0)
262  msg_panic("%s: %s:%s Unicode support is already activated",
263  myname, dict->type, dict->name);
264 
265  /*
266  * Unlike dict_debug(3) we do not put a proxy dict object in front of the
267  * encapsulated object, because then we would have to bidirectionally
268  * propagate changes in the data members (errors, flags, jbuf, and so on)
269  * between proxy object and encapsulated object.
270  *
271  * Instead we attach ourselves behind the encapsulated dict object, and
272  * redirect some function pointers to ourselves.
273  */
274  backup = dict->utf8_backup = (DICT_UTF8_BACKUP *) mymalloc(sizeof(*backup));
275 
276  /*
277  * Interpose on the lookup/update/delete methods. It is a conscious
278  * decision not to tinker with the iterator or destructor.
279  */
280  backup->lookup = dict->lookup;
281  backup->update = dict->update;
282  backup->delete = dict->delete;
283 
284  dict->lookup = dict_utf8_lookup;
285  dict->update = dict_utf8_update;
286  dict->delete = dict_utf8_delete;
287 
288  /*
289  * Leave our mark. See sanity check above.
290  */
291  dict->flags |= DICT_FLAG_UTF8_ACTIVE;
292 
293  return (dict);
294 }
#define DICT_ERR_CONFIG
Definition: dict.h:179
#define DICT_FLAG_FOLD_ANY
Definition: dict.h:126
int(* delete)(struct DICT *, const char *)
Definition: dict.h:255
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
int(* delete)(struct DICT *, const char *)
Definition: dict.h:84
char * name
Definition: dict.h:80
#define DICT_FLAG_FIXED
Definition: dict.h:114
int flags
Definition: dict.h:81
int valid_utf8_string(const char *, ssize_t)
int(* update)(struct DICT *, const char *, const char *)
Definition: dict.h:254
#define DICT_FLAG_UTF8_REQUEST
Definition: dict.h:130
#define DICT_FLAG_FOLD_FIX
Definition: dict.h:124
#define DICT_ERR_NONE
Definition: dict.h:177
#define casefold(dst, src)
Definition: stringops.h:67
Definition: dict.h:78
char * type
Definition: dict.h:79
int(* update)(struct DICT *, const char *, const char *)
Definition: dict.h:83
#define DICT_STAT_SUCCESS
Definition: dict.h:186
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
int error
Definition: dict.h:94
const char *(* lookup)(struct DICT *, const char *)
Definition: dict.h:82
#define allascii(s)
Definition: stringops.h:66
struct DICT_UTF8_BACKUP * utf8_backup
Definition: dict.h:96
DICT * dict_utf8_activate(DICT *dict)
Definition: dict_utf8.c:248
#define DICT_FLAG_FOLD_MUL
Definition: dict.h:125
const char *(* lookup)(struct DICT *, const char *)
Definition: dict.h:253
const char * CONST_CHAR_STAR
Definition: sys_defs.h:1730
VSTRING * fold_buf
Definition: dict.h:92
int util_utf8_enable
Definition: printable.c:47
#define DICT_FLAG_UTF8_ACTIVE
Definition: dict.h:131
void * mymalloc(ssize_t len)
Definition: mymalloc.c:150