CVE-2015-5330: strupper_talloc_n_handle(): properly count characters
[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                 n -= c_size;
119
120                 c = toupper_m(c);
121
122                 c_size = push_codepoint_handle(iconv_handle, dest+size, c);
123                 if (c_size == -1) {
124                         talloc_free(dest);
125                         return NULL;
126                 }
127                 size += c_size;
128         }
129
130         dest[size] = 0;
131
132         /* trim it so talloc_append_string() works */
133         dest = talloc_realloc(ctx, dest, char, size+1);
134
135         talloc_set_name_const(dest, dest);
136
137         return dest;
138 }
139
140 /**
141  Convert a string to UPPER case, allocated with talloc
142  source length limited to n bytes
143 **/
144 _PUBLIC_ char *strupper_talloc_n(TALLOC_CTX *ctx, const char *src, size_t n)
145 {
146         struct smb_iconv_handle *iconv_handle = get_iconv_handle();
147         return strupper_talloc_n_handle(iconv_handle, ctx, src, n);
148 }
149 /**
150  Convert a string to UPPER case, allocated with talloc
151 **/
152 _PUBLIC_ char *strupper_talloc(TALLOC_CTX *ctx, const char *src)
153 {
154         return strupper_talloc_n(ctx, src, src?strlen(src):0);
155 }
156
157 /**
158  talloc_strdup() a unix string to upper case.
159 **/
160 _PUBLIC_ char *talloc_strdup_upper(TALLOC_CTX *ctx, const char *src)
161 {
162         return strupper_talloc(ctx, src);
163 }
164
165 /**
166  Find the number of 'c' chars in a string
167 **/
168 _PUBLIC_ size_t count_chars_m(const char *s, char c)
169 {
170         struct smb_iconv_handle *ic = get_iconv_handle();
171         size_t count = 0;
172
173         while (*s) {
174                 size_t size;
175                 codepoint_t c2 = next_codepoint_handle(ic, s, &size);
176                 if (c2 == c) count++;
177                 s += size;
178         }
179
180         return count;
181 }
182
183
184 /**
185  * Copy a string from a char* unix src to a dos codepage string destination.
186  *
187  * @converted_size the number of bytes occupied by the string in the destination.
188  * @return bool true if success.
189  *
190  * @param flags can include
191  * <dl>
192  * <dt>STR_TERMINATE</dt> <dd>means include the null termination</dd>
193  * <dt>STR_UPPER</dt> <dd>means uppercase in the destination</dd>
194  * </dl>
195  *
196  * @param dest_len the maximum length in bytes allowed in the
197  * destination.  If @p dest_len is -1 then no maximum is used.
198  **/
199 static bool push_ascii_string(void *dest, const char *src, size_t dest_len, int flags, size_t *converted_size)
200 {
201         size_t src_len;
202         bool ret;
203
204         if (flags & STR_UPPER) {
205                 char *tmpbuf = strupper_talloc(NULL, src);
206                 if (tmpbuf == NULL) {
207                         return false;
208                 }
209                 ret = push_ascii_string(dest, tmpbuf, dest_len, flags & ~STR_UPPER, converted_size);
210                 talloc_free(tmpbuf);
211                 return ret;
212         }
213
214         src_len = strlen(src);
215
216         if (flags & (STR_TERMINATE | STR_TERMINATE_ASCII))
217                 src_len++;
218
219         return convert_string(CH_UNIX, CH_DOS, src, src_len, dest, dest_len, converted_size);
220 }
221
222 /**
223  * Copy a string from a dos codepage source to a unix char* destination.
224  *
225  * The resulting string in "dest" is always null terminated.
226  *
227  * @param flags can have:
228  * <dl>
229  * <dt>STR_TERMINATE</dt>
230  * <dd>STR_TERMINATE means the string in @p src
231  * is null terminated, and src_len is ignored.</dd>
232  * </dl>
233  *
234  * @param src_len is the length of the source area in bytes.
235  * @returns the number of bytes occupied by the string in @p src.
236  **/
237 static ssize_t pull_ascii_string(char *dest, const void *src, size_t dest_len, size_t src_len, int flags)
238 {
239         size_t size = 0;
240
241         if (flags & (STR_TERMINATE | STR_TERMINATE_ASCII)) {
242                 if (src_len == (size_t)-1) {
243                         src_len = strlen((const char *)src) + 1;
244                 } else {
245                         size_t len = strnlen((const char *)src, src_len);
246                         if (len < src_len)
247                                 len++;
248                         src_len = len;
249                 }
250         }
251
252         /* We're ignoring the return here.. */
253         (void)convert_string(CH_DOS, CH_UNIX, src, src_len, dest, dest_len, &size);
254
255         if (dest_len)
256                 dest[MIN(size, dest_len-1)] = 0;
257
258         return src_len;
259 }
260
261 /**
262  * Copy a string from a char* src to a unicode destination.
263  *
264  * @returns the number of bytes occupied by the string in the destination.
265  *
266  * @param flags can have:
267  *
268  * <dl>
269  * <dt>STR_TERMINATE <dd>means include the null termination.
270  * <dt>STR_UPPER     <dd>means uppercase in the destination.
271  * <dt>STR_NOALIGN   <dd>means don't do alignment.
272  * </dl>
273  *
274  * @param dest_len is the maximum length allowed in the
275  * destination. If dest_len is -1 then no maxiumum is used.
276  **/
277 static ssize_t push_ucs2(void *dest, const char *src, size_t dest_len, int flags)
278 {
279         size_t len=0;
280         size_t src_len = strlen(src);
281         size_t size = 0;
282         bool ret;
283
284         if (flags & STR_UPPER) {
285                 char *tmpbuf = strupper_talloc(NULL, src);
286                 ssize_t retval;
287                 if (tmpbuf == NULL) {
288                         return -1;
289                 }
290                 retval = push_ucs2(dest, tmpbuf, dest_len, flags & ~STR_UPPER);
291                 talloc_free(tmpbuf);
292                 return retval;
293         }
294
295         if (flags & STR_TERMINATE)
296                 src_len++;
297
298         if (ucs2_align(NULL, dest, flags)) {
299                 *(char *)dest = 0;
300                 dest = (void *)((char *)dest + 1);
301                 if (dest_len) dest_len--;
302                 len++;
303         }
304
305         /* ucs2 is always a multiple of 2 bytes */
306         dest_len &= ~1;
307
308         ret = convert_string(CH_UNIX, CH_UTF16, src, src_len, dest, dest_len, &size);
309         if (ret == false) {
310                 return 0;
311         }
312
313         len += size;
314
315         return (ssize_t)len;
316 }
317
318
319 /**
320  Copy a string from a ucs2 source to a unix char* destination.
321  Flags can have:
322   STR_TERMINATE means the string in src is null terminated.
323   STR_NOALIGN   means don't try to align.
324  if STR_TERMINATE is set then src_len is ignored if it is -1.
325  src_len is the length of the source area in bytes
326  Return the number of bytes occupied by the string in src.
327  The resulting string in "dest" is always null terminated.
328 **/
329
330 static size_t pull_ucs2(char *dest, const void *src, size_t dest_len, size_t src_len, int flags)
331 {
332         size_t size = 0;
333
334         if (ucs2_align(NULL, src, flags)) {
335                 src = (const void *)((const char *)src + 1);
336                 if (src_len > 0)
337                         src_len--;
338         }
339
340         if (flags & STR_TERMINATE) {
341                 if (src_len == (size_t)-1) {
342                         src_len = utf16_len(src);
343                 } else {
344                         src_len = utf16_len_n(src, src_len);
345                 }
346         }
347
348         /* ucs2 is always a multiple of 2 bytes */
349         if (src_len != (size_t)-1)
350                 src_len &= ~1;
351
352         /* We're ignoring the return here.. */
353         (void)convert_string(CH_UTF16, CH_UNIX, src, src_len, dest, dest_len, &size);
354         if (dest_len)
355                 dest[MIN(size, dest_len-1)] = 0;
356
357         return src_len;
358 }
359
360 /**
361  Copy a string from a char* src to a unicode or ascii
362  dos codepage destination choosing unicode or ascii based on the 
363  flags in the SMB buffer starting at base_ptr.
364  Return the number of bytes occupied by the string in the destination.
365  flags can have:
366   STR_TERMINATE means include the null termination.
367   STR_UPPER     means uppercase in the destination.
368   STR_ASCII     use ascii even with unicode packet.
369   STR_NOALIGN   means don't do alignment.
370  dest_len is the maximum length allowed in the destination. If dest_len
371  is -1 then no maxiumum is used.
372 **/
373
374 _PUBLIC_ ssize_t push_string(void *dest, const char *src, size_t dest_len, int flags)
375 {
376         if (flags & STR_ASCII) {
377                 size_t size = 0;
378                 if (push_ascii_string(dest, src, dest_len, flags, &size)) {
379                         return (ssize_t)size;
380                 } else {
381                         return (ssize_t)-1;
382                 }
383         } else if (flags & STR_UNICODE) {
384                 return push_ucs2(dest, src, dest_len, flags);
385         } else {
386                 smb_panic("push_string requires either STR_ASCII or STR_UNICODE flag to be set");
387                 return -1;
388         }
389 }
390
391
392 /**
393  Copy a string from a unicode or ascii source (depending on
394  the packet flags) to a char* destination.
395  Flags can have:
396   STR_TERMINATE means the string in src is null terminated.
397   STR_UNICODE   means to force as unicode.
398   STR_ASCII     use ascii even with unicode packet.
399   STR_NOALIGN   means don't do alignment.
400  if STR_TERMINATE is set then src_len is ignored is it is -1
401  src_len is the length of the source area in bytes.
402  Return the number of bytes occupied by the string in src.
403  The resulting string in "dest" is always null terminated.
404 **/
405
406 _PUBLIC_ ssize_t pull_string(char *dest, const void *src, size_t dest_len, size_t src_len, int flags)
407 {
408         if (flags & STR_ASCII) {
409                 return pull_ascii_string(dest, src, dest_len, src_len, flags);
410         } else if (flags & STR_UNICODE) {
411                 return pull_ucs2(dest, src, dest_len, src_len, flags);
412         } else {
413                 smb_panic("pull_string requires either STR_ASCII or STR_UNICODE flag to be set");
414                 return -1;
415         }
416 }