CVE-2015-5330: Fix handling of unicode near string endings
[samba.git] / lib / util / charset / util_unistr.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Samba utility functions
4    Copyright (C) Andrew Tridgell 1992-2001
5    Copyright (C) Simo Sorce 2001
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
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, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "system/locale.h"
23
24 /**
25  String replace.
26  NOTE: oldc and newc must be 7 bit characters
27 **/
28 _PUBLIC_ void string_replace_m(char *s, char oldc, char newc)
29 {
30         struct smb_iconv_handle *ic = get_iconv_handle();
31         while (s && *s) {
32                 size_t size;
33                 codepoint_t c = next_codepoint_handle(ic, s, &size);
34                 if (c == oldc) {
35                         *s = newc;
36                 }
37                 s += size;
38         }
39 }
40
41 /**
42  Convert a string to lower case, allocated with talloc
43 **/
44 _PUBLIC_ char *strlower_talloc_handle(struct smb_iconv_handle *iconv_handle,
45                                       TALLOC_CTX *ctx, const char *src)
46 {
47         size_t size=0;
48         char *dest;
49
50         if(src == NULL) {
51                 return NULL;
52         }
53
54         /* this takes advantage of the fact that upper/lower can't
55            change the length of a character by more than 1 byte */
56         dest = talloc_array(ctx, char, 2*(strlen(src))+1);
57         if (dest == NULL) {
58                 return NULL;
59         }
60
61         while (*src) {
62                 size_t c_size;
63                 codepoint_t c = next_codepoint_handle(iconv_handle, src, &c_size);
64                 src += c_size;
65
66                 c = tolower_m(c);
67
68                 c_size = push_codepoint_handle(iconv_handle, dest+size, c);
69                 if (c_size == -1) {
70                         talloc_free(dest);
71                         return NULL;
72                 }
73                 size += c_size;
74         }
75
76         dest[size] = 0;
77
78         /* trim it so talloc_append_string() works */
79         dest = talloc_realloc(ctx, dest, char, size+1);
80
81         talloc_set_name_const(dest, dest);
82
83         return dest;
84 }
85
86 _PUBLIC_ char *strlower_talloc(TALLOC_CTX *ctx, const char *src)
87 {
88         struct smb_iconv_handle *iconv_handle = get_iconv_handle();
89         return strlower_talloc_handle(iconv_handle, ctx, src);
90 }
91
92 /**
93  Convert a string to UPPER case, allocated with talloc
94  source length limited to n bytes, iconv handle supplied
95 **/
96 _PUBLIC_ char *strupper_talloc_n_handle(struct smb_iconv_handle *iconv_handle,
97                                         TALLOC_CTX *ctx, const char *src, size_t n)
98 {
99         size_t size=0;
100         char *dest;
101
102         if (!src) {
103                 return NULL;
104         }
105
106         /* this takes advantage of the fact that upper/lower can't
107            change the length of a character by more than 1 byte */
108         dest = talloc_array(ctx, char, 2*(n+1));
109         if (dest == NULL) {
110                 return NULL;
111         }
112
113         while (n-- && *src) {
114                 size_t c_size;
115                 codepoint_t c = next_codepoint_handle_ext(iconv_handle, src, n,
116                                                           CH_UNIX, &c_size);
117                 src += c_size;
118
119                 c = toupper_m(c);
120
121                 c_size = push_codepoint_handle(iconv_handle, dest+size, c);
122                 if (c_size == -1) {
123                         talloc_free(dest);
124                         return NULL;
125                 }
126                 size += c_size;
127         }
128
129         dest[size] = 0;
130
131         /* trim it so talloc_append_string() works */
132         dest = talloc_realloc(ctx, dest, char, size+1);
133
134         talloc_set_name_const(dest, dest);
135
136         return dest;
137 }
138
139 /**
140  Convert a string to UPPER case, allocated with talloc
141  source length limited to n bytes
142 **/
143 _PUBLIC_ char *strupper_talloc_n(TALLOC_CTX *ctx, const char *src, size_t n)
144 {
145         struct smb_iconv_handle *iconv_handle = get_iconv_handle();
146         return strupper_talloc_n_handle(iconv_handle, ctx, src, n);
147 }
148 /**
149  Convert a string to UPPER case, allocated with talloc
150 **/
151 _PUBLIC_ char *strupper_talloc(TALLOC_CTX *ctx, const char *src)
152 {
153         return strupper_talloc_n(ctx, src, src?strlen(src):0);
154 }
155
156 /**
157  talloc_strdup() a unix string to upper case.
158 **/
159 _PUBLIC_ char *talloc_strdup_upper(TALLOC_CTX *ctx, const char *src)
160 {
161         return strupper_talloc(ctx, src);
162 }
163
164 /**
165  Find the number of 'c' chars in a string
166 **/
167 _PUBLIC_ size_t count_chars_m(const char *s, char c)
168 {
169         struct smb_iconv_handle *ic = get_iconv_handle();
170         size_t count = 0;
171
172         while (*s) {
173                 size_t size;
174                 codepoint_t c2 = next_codepoint_handle(ic, s, &size);
175                 if (c2 == c) count++;
176                 s += size;
177         }
178
179         return count;
180 }
181
182
183 /**
184  * Copy a string from a char* unix src to a dos codepage string destination.
185  *
186  * @converted_size the number of bytes occupied by the string in the destination.
187  * @return bool true if success.
188  *
189  * @param flags can include
190  * <dl>
191  * <dt>STR_TERMINATE</dt> <dd>means include the null termination</dd>
192  * <dt>STR_UPPER</dt> <dd>means uppercase in the destination</dd>
193  * </dl>
194  *
195  * @param dest_len the maximum length in bytes allowed in the
196  * destination.  If @p dest_len is -1 then no maximum is used.
197  **/
198 static bool push_ascii_string(void *dest, const char *src, size_t dest_len, int flags, size_t *converted_size)
199 {
200         size_t src_len;
201         bool ret;
202
203         if (flags & STR_UPPER) {
204                 char *tmpbuf = strupper_talloc(NULL, src);
205                 if (tmpbuf == NULL) {
206                         return false;
207                 }
208                 ret = push_ascii_string(dest, tmpbuf, dest_len, flags & ~STR_UPPER, converted_size);
209                 talloc_free(tmpbuf);
210                 return ret;
211         }
212
213         src_len = strlen(src);
214
215         if (flags & (STR_TERMINATE | STR_TERMINATE_ASCII))
216                 src_len++;
217
218         return convert_string(CH_UNIX, CH_DOS, src, src_len, dest, dest_len, converted_size);
219 }
220
221 /**
222  * Copy a string from a dos codepage source to a unix char* destination.
223  *
224  * The resulting string in "dest" is always null terminated.
225  *
226  * @param flags can have:
227  * <dl>
228  * <dt>STR_TERMINATE</dt>
229  * <dd>STR_TERMINATE means the string in @p src
230  * is null terminated, and src_len is ignored.</dd>
231  * </dl>
232  *
233  * @param src_len is the length of the source area in bytes.
234  * @returns the number of bytes occupied by the string in @p src.
235  **/
236 static ssize_t pull_ascii_string(char *dest, const void *src, size_t dest_len, size_t src_len, int flags)
237 {
238         size_t size = 0;
239
240         if (flags & (STR_TERMINATE | STR_TERMINATE_ASCII)) {
241                 if (src_len == (size_t)-1) {
242                         src_len = strlen((const char *)src) + 1;
243                 } else {
244                         size_t len = strnlen((const char *)src, src_len);
245                         if (len < src_len)
246                                 len++;
247                         src_len = len;
248                 }
249         }
250
251         /* We're ignoring the return here.. */
252         (void)convert_string(CH_DOS, CH_UNIX, src, src_len, dest, dest_len, &size);
253
254         if (dest_len)
255                 dest[MIN(size, dest_len-1)] = 0;
256
257         return src_len;
258 }
259
260 /**
261  * Copy a string from a char* src to a unicode destination.
262  *
263  * @returns the number of bytes occupied by the string in the destination.
264  *
265  * @param flags can have:
266  *
267  * <dl>
268  * <dt>STR_TERMINATE <dd>means include the null termination.
269  * <dt>STR_UPPER     <dd>means uppercase in the destination.
270  * <dt>STR_NOALIGN   <dd>means don't do alignment.
271  * </dl>
272  *
273  * @param dest_len is the maximum length allowed in the
274  * destination. If dest_len is -1 then no maxiumum is used.
275  **/
276 static ssize_t push_ucs2(void *dest, const char *src, size_t dest_len, int flags)
277 {
278         size_t len=0;
279         size_t src_len = strlen(src);
280         size_t size = 0;
281         bool ret;
282
283         if (flags & STR_UPPER) {
284                 char *tmpbuf = strupper_talloc(NULL, src);
285                 ssize_t retval;
286                 if (tmpbuf == NULL) {
287                         return -1;
288                 }
289                 retval = push_ucs2(dest, tmpbuf, dest_len, flags & ~STR_UPPER);
290                 talloc_free(tmpbuf);
291                 return retval;
292         }
293
294         if (flags & STR_TERMINATE)
295                 src_len++;
296
297         if (ucs2_align(NULL, dest, flags)) {
298                 *(char *)dest = 0;
299                 dest = (void *)((char *)dest + 1);
300                 if (dest_len) dest_len--;
301                 len++;
302         }
303
304         /* ucs2 is always a multiple of 2 bytes */
305         dest_len &= ~1;
306
307         ret = convert_string(CH_UNIX, CH_UTF16, src, src_len, dest, dest_len, &size);
308         if (ret == false) {
309                 return 0;
310         }
311
312         len += size;
313
314         return (ssize_t)len;
315 }
316
317
318 /**
319  Copy a string from a ucs2 source to a unix char* destination.
320  Flags can have:
321   STR_TERMINATE means the string in src is null terminated.
322   STR_NOALIGN   means don't try to align.
323  if STR_TERMINATE is set then src_len is ignored if it is -1.
324  src_len is the length of the source area in bytes
325  Return the number of bytes occupied by the string in src.
326  The resulting string in "dest" is always null terminated.
327 **/
328
329 static size_t pull_ucs2(char *dest, const void *src, size_t dest_len, size_t src_len, int flags)
330 {
331         size_t size = 0;
332
333         if (ucs2_align(NULL, src, flags)) {
334                 src = (const void *)((const char *)src + 1);
335                 if (src_len > 0)
336                         src_len--;
337         }
338
339         if (flags & STR_TERMINATE) {
340                 if (src_len == (size_t)-1) {
341                         src_len = utf16_len(src);
342                 } else {
343                         src_len = utf16_len_n(src, src_len);
344                 }
345         }
346
347         /* ucs2 is always a multiple of 2 bytes */
348         if (src_len != (size_t)-1)
349                 src_len &= ~1;
350
351         /* We're ignoring the return here.. */
352         (void)convert_string(CH_UTF16, CH_UNIX, src, src_len, dest, dest_len, &size);
353         if (dest_len)
354                 dest[MIN(size, dest_len-1)] = 0;
355
356         return src_len;
357 }
358
359 /**
360  Copy a string from a char* src to a unicode or ascii
361  dos codepage destination choosing unicode or ascii based on the 
362  flags in the SMB buffer starting at base_ptr.
363  Return the number of bytes occupied by the string in the destination.
364  flags can have:
365   STR_TERMINATE means include the null termination.
366   STR_UPPER     means uppercase in the destination.
367   STR_ASCII     use ascii even with unicode packet.
368   STR_NOALIGN   means don't do alignment.
369  dest_len is the maximum length allowed in the destination. If dest_len
370  is -1 then no maxiumum is used.
371 **/
372
373 _PUBLIC_ ssize_t push_string(void *dest, const char *src, size_t dest_len, int flags)
374 {
375         if (flags & STR_ASCII) {
376                 size_t size = 0;
377                 if (push_ascii_string(dest, src, dest_len, flags, &size)) {
378                         return (ssize_t)size;
379                 } else {
380                         return (ssize_t)-1;
381                 }
382         } else if (flags & STR_UNICODE) {
383                 return push_ucs2(dest, src, dest_len, flags);
384         } else {
385                 smb_panic("push_string requires either STR_ASCII or STR_UNICODE flag to be set");
386                 return -1;
387         }
388 }
389
390
391 /**
392  Copy a string from a unicode or ascii source (depending on
393  the packet flags) to a char* destination.
394  Flags can have:
395   STR_TERMINATE means the string in src is null terminated.
396   STR_UNICODE   means to force as unicode.
397   STR_ASCII     use ascii even with unicode packet.
398   STR_NOALIGN   means don't do alignment.
399  if STR_TERMINATE is set then src_len is ignored is it is -1
400  src_len is the length of the source area in bytes.
401  Return the number of bytes occupied by the string in src.
402  The resulting string in "dest" is always null terminated.
403 **/
404
405 _PUBLIC_ ssize_t pull_string(char *dest, const void *src, size_t dest_len, size_t src_len, int flags)
406 {
407         if (flags & STR_ASCII) {
408                 return pull_ascii_string(dest, src, dest_len, src_len, flags);
409         } else if (flags & STR_UNICODE) {
410                 return pull_ucs2(dest, src, dest_len, src_len, flags);
411         } else {
412                 smb_panic("pull_string requires either STR_ASCII or STR_UNICODE flag to be set");
413                 return -1;
414         }
415 }