/src/samba/source3/lib/charcnv.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | Character set conversion Extensions |
4 | | Copyright (C) Igor Vergeichik <iverg@mail.ru> 2001 |
5 | | Copyright (C) Andrew Tridgell 2001 |
6 | | Copyright (C) Simo Sorce 2001 |
7 | | Copyright (C) Martin Pool 2003 |
8 | | |
9 | | This program is free software; you can redistribute it and/or modify |
10 | | it under the terms of the GNU General Public License as published by |
11 | | the Free Software Foundation; either version 3 of the License, or |
12 | | (at your option) any later version. |
13 | | |
14 | | This program is distributed in the hope that it will be useful, |
15 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | | GNU General Public License for more details. |
18 | | |
19 | | You should have received a copy of the GNU General Public License |
20 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
21 | | |
22 | | */ |
23 | | #include "includes.h" |
24 | | |
25 | | /** |
26 | | * Destroy global objects allocated by init_iconv() |
27 | | **/ |
28 | | void gfree_charcnv(void) |
29 | 0 | { |
30 | 0 | free_iconv_handle(); |
31 | 0 | } |
32 | | |
33 | | /** |
34 | | * Copy a string from a char* unix src to a dos codepage string destination. |
35 | | * |
36 | | * @return the number of bytes occupied by the string in the destination. |
37 | | * |
38 | | * @param flags can include |
39 | | * <dl> |
40 | | * <dt>STR_TERMINATE</dt> <dd>means include the null termination</dd> |
41 | | * <dt>STR_UPPER</dt> <dd>means uppercase in the destination</dd> |
42 | | * </dl> |
43 | | * |
44 | | * @param dest_len the maximum length in bytes allowed in the |
45 | | * destination. |
46 | | **/ |
47 | | size_t push_ascii(void *dest, const char *src, size_t dest_len, int flags) |
48 | 0 | { |
49 | 0 | size_t src_len = 0; |
50 | 0 | char *tmpbuf = NULL; |
51 | 0 | size_t size = 0; |
52 | 0 | bool ret; |
53 | | |
54 | | /* No longer allow a length of -1. */ |
55 | 0 | if (dest_len == (size_t)-1) { |
56 | 0 | smb_panic("push_ascii - dest_len == -1"); |
57 | 0 | } |
58 | | |
59 | 0 | if (flags & STR_UPPER) { |
60 | 0 | tmpbuf = SMB_STRDUP(src); |
61 | 0 | if (!tmpbuf) { |
62 | 0 | smb_panic("malloc fail"); |
63 | 0 | } |
64 | 0 | if (!strupper_m(tmpbuf)) { |
65 | 0 | if ((flags & (STR_TERMINATE|STR_TERMINATE_ASCII)) && |
66 | 0 | dest && |
67 | 0 | dest_len > 0) { |
68 | 0 | *(char *)dest = 0; |
69 | 0 | } |
70 | 0 | SAFE_FREE(tmpbuf); |
71 | 0 | return 0; |
72 | 0 | } |
73 | 0 | src = tmpbuf; |
74 | 0 | } |
75 | | |
76 | 0 | src_len = strlen(src); |
77 | 0 | if (flags & (STR_TERMINATE | STR_TERMINATE_ASCII)) { |
78 | 0 | src_len++; |
79 | 0 | } |
80 | |
|
81 | 0 | ret = convert_string(CH_UNIX, CH_DOS, src, src_len, dest, dest_len, &size); |
82 | 0 | SAFE_FREE(tmpbuf); |
83 | 0 | if (ret == false) { |
84 | 0 | if ((flags & (STR_TERMINATE | STR_TERMINATE_ASCII)) && |
85 | 0 | dest_len > 0) { |
86 | 0 | ((char *)dest)[0] = '\0'; |
87 | 0 | } |
88 | 0 | return 0; |
89 | 0 | } |
90 | 0 | return size; |
91 | 0 | } |
92 | | |
93 | | /******************************************************************** |
94 | | Push and malloc an ascii string. src and dest null terminated. |
95 | | ********************************************************************/ |
96 | | |
97 | | /** |
98 | | * Copy a string from a dos codepage source to a unix char* destination. |
99 | | * |
100 | | * The resulting string in "dest" is always null terminated. |
101 | | * |
102 | | * @param flags can have: |
103 | | * <dl> |
104 | | * <dt>STR_TERMINATE</dt> |
105 | | * <dd>STR_TERMINATE means the string in @p src |
106 | | * is null terminated, and src_len is ignored.</dd> |
107 | | * </dl> |
108 | | * |
109 | | * @param src_len is the length of the source area in bytes. |
110 | | * @returns the number of bytes occupied by the string in @p src. |
111 | | **/ |
112 | | size_t pull_ascii(char *dest, const void *src, size_t dest_len, size_t src_len, int flags) |
113 | 335 | { |
114 | 335 | bool ret; |
115 | 335 | size_t size = 0; |
116 | | |
117 | 335 | if (dest_len == (size_t)-1) { |
118 | | /* No longer allow dest_len of -1. */ |
119 | 0 | smb_panic("pull_ascii - invalid dest_len of -1"); |
120 | 0 | } |
121 | | |
122 | 335 | if (flags & STR_TERMINATE) { |
123 | 335 | if (src_len == (size_t)-1) { |
124 | 0 | src_len = strlen((const char *)src) + 1; |
125 | 335 | } else { |
126 | 335 | size_t len = strnlen((const char *)src, src_len); |
127 | 335 | if (len < src_len) |
128 | 335 | len++; |
129 | 335 | src_len = len; |
130 | 335 | } |
131 | 335 | } |
132 | | |
133 | 335 | ret = convert_string(CH_DOS, CH_UNIX, src, src_len, dest, dest_len, &size); |
134 | 335 | if (ret == false) { |
135 | 62 | size = 0; |
136 | 62 | dest_len = 0; |
137 | 62 | } |
138 | | |
139 | 335 | if (dest_len && size) { |
140 | | /* Did we already process the terminating zero ? */ |
141 | 273 | if (dest[MIN(size-1, dest_len-1)] != 0) { |
142 | 0 | dest[MIN(size, dest_len-1)] = 0; |
143 | 0 | } |
144 | 273 | } else { |
145 | 62 | dest[0] = 0; |
146 | 62 | } |
147 | | |
148 | 335 | return src_len; |
149 | 335 | } |
150 | | |
151 | | /** |
152 | | * Copy a string from a dos codepage source to a unix char* destination. |
153 | | * Talloc version. |
154 | | * |
155 | | * The resulting string in "dest" is always null terminated. |
156 | | * |
157 | | * @param flags can have: |
158 | | * <dl> |
159 | | * <dt>STR_TERMINATE</dt> |
160 | | * <dd>STR_TERMINATE means the string in @p src |
161 | | * is null terminated, and src_len is ignored.</dd> |
162 | | * </dl> |
163 | | * |
164 | | * @param src_len is the length of the source area in bytes. |
165 | | * @returns the number of bytes occupied by the string in @p src. |
166 | | **/ |
167 | | |
168 | | static size_t pull_ascii_base_talloc(TALLOC_CTX *ctx, |
169 | | char **ppdest, |
170 | | const void *src, |
171 | | size_t src_len, |
172 | | int flags) |
173 | 0 | { |
174 | 0 | char *dest = NULL; |
175 | 0 | size_t dest_len; |
176 | |
|
177 | 0 | *ppdest = NULL; |
178 | |
|
179 | 0 | if (!src_len) { |
180 | 0 | return 0; |
181 | 0 | } |
182 | | |
183 | 0 | if (src_len == (size_t)-1) { |
184 | 0 | smb_panic("src_len == -1 in pull_ascii_base_talloc"); |
185 | 0 | } |
186 | | |
187 | 0 | if (flags & STR_TERMINATE) { |
188 | 0 | size_t len = strnlen((const char *)src, src_len); |
189 | 0 | if (len < src_len) |
190 | 0 | len++; |
191 | 0 | src_len = len; |
192 | | /* Ensure we don't use an insane length from the client. */ |
193 | 0 | if (src_len >= 1024*1024) { |
194 | 0 | char *msg = talloc_asprintf(ctx, |
195 | 0 | "Bad src length (%u) in " |
196 | 0 | "pull_ascii_base_talloc", |
197 | 0 | (unsigned int)src_len); |
198 | 0 | smb_panic(msg); |
199 | 0 | } |
200 | 0 | } |
201 | | |
202 | | /* src_len != -1 here. */ |
203 | | |
204 | 0 | if (!convert_string_talloc(ctx, CH_DOS, CH_UNIX, src, src_len, &dest, |
205 | 0 | &dest_len)) { |
206 | 0 | dest_len = 0; |
207 | 0 | } |
208 | |
|
209 | 0 | if (dest_len && dest) { |
210 | | /* Did we already process the terminating zero ? */ |
211 | 0 | if (dest[dest_len-1] != 0) { |
212 | 0 | size_t size = talloc_get_size(dest); |
213 | | /* Have we got space to append the '\0' ? */ |
214 | 0 | if (size <= dest_len) { |
215 | | /* No, realloc. */ |
216 | 0 | dest = talloc_realloc(ctx, dest, char, |
217 | 0 | dest_len+1); |
218 | 0 | if (!dest) { |
219 | | /* talloc fail. */ |
220 | 0 | dest_len = (size_t)-1; |
221 | 0 | return 0; |
222 | 0 | } |
223 | 0 | } |
224 | | /* Yay - space ! */ |
225 | 0 | dest[dest_len] = '\0'; |
226 | 0 | dest_len++; |
227 | 0 | } |
228 | 0 | } else if (dest) { |
229 | 0 | dest[0] = 0; |
230 | 0 | } |
231 | | |
232 | 0 | *ppdest = dest; |
233 | 0 | return src_len; |
234 | 0 | } |
235 | | |
236 | | /** |
237 | | * Copy a string from a char* src to a unicode destination. |
238 | | * |
239 | | * @returns the number of bytes occupied by the string in the destination. |
240 | | * |
241 | | * @param flags can have: |
242 | | * |
243 | | * <dl> |
244 | | * <dt>STR_TERMINATE <dd>means include the null termination. |
245 | | * <dt>STR_UPPER <dd>means uppercase in the destination. |
246 | | * <dt>STR_NOALIGN <dd>means don't do alignment. |
247 | | * </dl> |
248 | | * |
249 | | * @param dest_len is the maximum length allowed in the |
250 | | * destination. |
251 | | **/ |
252 | | |
253 | | static size_t push_ucs2(const void *base_ptr, void *dest, const char *src, size_t dest_len, int flags) |
254 | 0 | { |
255 | 0 | size_t len=0; |
256 | 0 | size_t src_len; |
257 | 0 | size_t size = 0; |
258 | 0 | bool ret; |
259 | |
|
260 | 0 | if (dest_len == (size_t)-1) { |
261 | | /* No longer allow dest_len of -1. */ |
262 | 0 | smb_panic("push_ucs2 - invalid dest_len of -1"); |
263 | 0 | } |
264 | | |
265 | 0 | if (flags & STR_TERMINATE) |
266 | 0 | src_len = (size_t)-1; |
267 | 0 | else |
268 | 0 | src_len = strlen(src); |
269 | |
|
270 | 0 | if (ucs2_align(base_ptr, dest, flags)) { |
271 | 0 | *(char *)dest = 0; |
272 | 0 | dest = (void *)((char *)dest + 1); |
273 | 0 | if (dest_len) |
274 | 0 | dest_len--; |
275 | 0 | len++; |
276 | 0 | } |
277 | | |
278 | | /* ucs2 is always a multiple of 2 bytes */ |
279 | 0 | dest_len &= ~1; |
280 | |
|
281 | 0 | ret = convert_string(CH_UNIX, CH_UTF16LE, src, src_len, dest, dest_len, &size); |
282 | 0 | if (ret == false) { |
283 | 0 | if ((flags & STR_TERMINATE) && |
284 | 0 | dest && |
285 | 0 | dest_len) { |
286 | 0 | *(char *)dest = 0; |
287 | 0 | } |
288 | 0 | return len; |
289 | 0 | } |
290 | | |
291 | 0 | len += size; |
292 | |
|
293 | 0 | if (flags & STR_UPPER) { |
294 | 0 | smb_ucs2_t *dest_ucs2 = (smb_ucs2_t *)dest; |
295 | 0 | size_t i; |
296 | | |
297 | | /* We check for i < (size / 2) below as the dest string isn't null |
298 | | terminated if STR_TERMINATE isn't set. */ |
299 | |
|
300 | 0 | for (i = 0; i < (size / 2) && i < (dest_len / 2) && dest_ucs2[i]; i++) { |
301 | 0 | smb_ucs2_t v = toupper_w(dest_ucs2[i]); |
302 | 0 | if (v != dest_ucs2[i]) { |
303 | 0 | dest_ucs2[i] = v; |
304 | 0 | } |
305 | 0 | } |
306 | 0 | } |
307 | |
|
308 | 0 | return len; |
309 | 0 | } |
310 | | |
311 | | /** |
312 | | Copy a string from a ucs2 source to a unix char* destination. |
313 | | Talloc version with a base pointer. |
314 | | Uses malloc if TALLOC_CTX is NULL (this is a bad interface and |
315 | | needs fixing. JRA). |
316 | | Flags can have: |
317 | | STR_TERMINATE means the string in src is null terminated. |
318 | | STR_NOALIGN means don't try to align. |
319 | | if STR_TERMINATE is set then src_len is ignored if it is -1. |
320 | | src_len is the length of the source area in bytes |
321 | | Return the number of bytes occupied by the string in src. |
322 | | The resulting string in "dest" is always null terminated. |
323 | | **/ |
324 | | |
325 | | static size_t pull_ucs2_base_talloc(TALLOC_CTX *ctx, |
326 | | const void *base_ptr, |
327 | | char **ppdest, |
328 | | const void *src, |
329 | | size_t src_len, |
330 | | int flags) |
331 | 0 | { |
332 | 0 | char *dest; |
333 | 0 | size_t dest_len; |
334 | 0 | size_t ucs2_align_len = 0; |
335 | |
|
336 | 0 | *ppdest = NULL; |
337 | |
|
338 | 0 | #ifdef DEVELOPER |
339 | | /* Ensure we never use the braindead "malloc" variant. */ |
340 | 0 | if (ctx == NULL) { |
341 | 0 | smb_panic("NULL talloc CTX in pull_ucs2_base_talloc\n"); |
342 | 0 | } |
343 | 0 | #endif |
344 | | |
345 | 0 | if (!src_len) { |
346 | 0 | return 0; |
347 | 0 | } |
348 | | |
349 | 0 | if (src_len == (size_t)-1) { |
350 | | /* no longer used anywhere, but worth checking */ |
351 | 0 | smb_panic("sec_len == -1 in pull_ucs2_base_talloc"); |
352 | 0 | } |
353 | | |
354 | 0 | if (ucs2_align(base_ptr, src, flags)) { |
355 | 0 | src = (const void *)((const char *)src + 1); |
356 | 0 | src_len--; |
357 | 0 | ucs2_align_len = 1; |
358 | 0 | } |
359 | |
|
360 | 0 | if (flags & STR_TERMINATE) { |
361 | | /* src_len -1 is the default for null terminated strings. */ |
362 | 0 | size_t len = strnlen_w((const smb_ucs2_t *)src, |
363 | 0 | src_len/2); |
364 | 0 | if (len < src_len/2) |
365 | 0 | len++; |
366 | 0 | src_len = len*2; |
367 | | |
368 | | /* Ensure we don't use an insane length from the client. */ |
369 | 0 | if (src_len >= 1024*1024) { |
370 | 0 | smb_panic("Bad src length in pull_ucs2_base_talloc\n"); |
371 | 0 | } |
372 | 0 | } |
373 | | |
374 | | /* ucs2 is always a multiple of 2 bytes */ |
375 | 0 | src_len &= ~1; |
376 | |
|
377 | 0 | if (!convert_string_talloc(ctx, CH_UTF16LE, CH_UNIX, src, src_len, |
378 | 0 | (void *)&dest, &dest_len)) { |
379 | 0 | dest_len = 0; |
380 | 0 | } |
381 | |
|
382 | 0 | if (dest_len) { |
383 | | /* Did we already process the terminating zero ? */ |
384 | 0 | if (dest[dest_len-1] != 0) { |
385 | 0 | size_t size = talloc_get_size(dest); |
386 | | /* Have we got space to append the '\0' ? */ |
387 | 0 | if (size <= dest_len) { |
388 | | /* No, realloc. */ |
389 | 0 | dest = talloc_realloc(ctx, dest, char, |
390 | 0 | dest_len+1); |
391 | 0 | if (!dest) { |
392 | | /* talloc fail. */ |
393 | 0 | dest_len = (size_t)-1; |
394 | 0 | return 0; |
395 | 0 | } |
396 | 0 | } |
397 | | /* Yay - space ! */ |
398 | 0 | dest[dest_len] = '\0'; |
399 | 0 | dest_len++; |
400 | 0 | } |
401 | 0 | } else if (dest) { |
402 | 0 | dest[0] = 0; |
403 | 0 | } |
404 | | |
405 | 0 | *ppdest = dest; |
406 | 0 | return src_len + ucs2_align_len; |
407 | 0 | } |
408 | | |
409 | | /** |
410 | | Copy a string from a char* src to a unicode or ascii |
411 | | dos codepage destination choosing unicode or ascii based on the |
412 | | flags supplied |
413 | | Return the number of bytes occupied by the string in the destination. |
414 | | flags can have: |
415 | | STR_TERMINATE means include the null termination. |
416 | | STR_UPPER means uppercase in the destination. |
417 | | STR_ASCII use ascii even with unicode packet. |
418 | | STR_NOALIGN means don't do alignment. |
419 | | dest_len is the maximum length allowed in the destination. If dest_len |
420 | | is -1 then no maximum is used. |
421 | | **/ |
422 | | |
423 | | size_t push_string_check_fn(void *dest, const char *src, |
424 | | size_t dest_len, int flags) |
425 | 0 | { |
426 | 0 | if (!(flags & STR_ASCII) && (flags & STR_UNICODE)) { |
427 | 0 | return push_ucs2(NULL, dest, src, dest_len, flags); |
428 | 0 | } |
429 | 0 | return push_ascii(dest, src, dest_len, flags); |
430 | 0 | } |
431 | | |
432 | | |
433 | | /** |
434 | | Copy a string from a char* src to a unicode or ascii |
435 | | dos codepage destination choosing unicode or ascii based on the |
436 | | flags in the SMB buffer starting at base_ptr. |
437 | | Return the number of bytes occupied by the string in the destination. |
438 | | flags can have: |
439 | | STR_TERMINATE means include the null termination. |
440 | | STR_UPPER means uppercase in the destination. |
441 | | STR_ASCII use ascii even with unicode packet. |
442 | | STR_NOALIGN means don't do alignment. |
443 | | dest_len is the maximum length allowed in the destination. If dest_len |
444 | | is -1 then no maximum is used. |
445 | | **/ |
446 | | |
447 | | size_t push_string_base(const char *base, uint16_t flags2, |
448 | | void *dest, const char *src, |
449 | | size_t dest_len, int flags) |
450 | 0 | { |
451 | |
|
452 | 0 | if (!(flags & STR_ASCII) && \ |
453 | 0 | ((flags & STR_UNICODE || \ |
454 | 0 | (flags2 & FLAGS2_UNICODE_STRINGS)))) { |
455 | 0 | return push_ucs2(base, dest, src, dest_len, flags); |
456 | 0 | } |
457 | 0 | return push_ascii(dest, src, dest_len, flags); |
458 | 0 | } |
459 | | |
460 | | /** |
461 | | Copy a string from a unicode or ascii source (depending on |
462 | | the packet flags) to a char* destination. |
463 | | Variant that uses talloc. |
464 | | Flags can have: |
465 | | STR_TERMINATE means the string in src is null terminated. |
466 | | STR_UNICODE means to force as unicode. |
467 | | STR_ASCII use ascii even with unicode packet. |
468 | | STR_NOALIGN means don't do alignment. |
469 | | if STR_TERMINATE is set then src_len is ignored is it is -1 |
470 | | src_len is the length of the source area in bytes. |
471 | | Return the number of bytes occupied by the string in src. |
472 | | The resulting string in "dest" is always null terminated. |
473 | | **/ |
474 | | |
475 | | size_t pull_string_talloc(TALLOC_CTX *ctx, |
476 | | const void *base_ptr, |
477 | | uint16_t smb_flags2, |
478 | | char **ppdest, |
479 | | const void *src, |
480 | | size_t src_len, |
481 | | int flags) |
482 | 0 | { |
483 | 0 | if ((base_ptr == NULL) && ((flags & (STR_ASCII|STR_UNICODE)) == 0)) { |
484 | 0 | smb_panic("No base ptr to get flg2 and neither ASCII nor " |
485 | 0 | "UNICODE defined"); |
486 | 0 | } |
487 | | |
488 | 0 | if (!(flags & STR_ASCII) && |
489 | 0 | ((flags & STR_UNICODE || (smb_flags2 & FLAGS2_UNICODE_STRINGS)))) { |
490 | 0 | return pull_ucs2_base_talloc(ctx, |
491 | 0 | base_ptr, |
492 | 0 | ppdest, |
493 | 0 | src, |
494 | 0 | src_len, |
495 | 0 | flags); |
496 | 0 | } |
497 | 0 | return pull_ascii_base_talloc(ctx, |
498 | 0 | ppdest, |
499 | 0 | src, |
500 | 0 | src_len, |
501 | 0 | flags); |
502 | 0 | } |
503 | | |
504 | | /******************************************************************* |
505 | | Write a string in (little-endian) unicode format. src is in |
506 | | the current DOS codepage. len is the length in bytes of the |
507 | | string pointed to by dst. |
508 | | |
509 | | if null_terminate is True then null terminate the packet (adds 2 bytes) |
510 | | |
511 | | the return value is the length in bytes consumed by the string, including the |
512 | | null termination if applied |
513 | | ********************************************************************/ |
514 | | |
515 | | size_t dos_PutUniCode(char *dst,const char *src, size_t len, bool null_terminate) |
516 | 0 | { |
517 | 0 | int flags = null_terminate ? STR_UNICODE|STR_NOALIGN|STR_TERMINATE |
518 | 0 | : STR_UNICODE|STR_NOALIGN; |
519 | 0 | return push_ucs2(NULL, dst, src, len, flags); |
520 | 0 | } |
521 | | |
522 | | |
523 | | /* Converts a string from internal samba format to unicode. Always terminates. |
524 | | * Actually just a wrapper round push_ucs2_talloc(). |
525 | | */ |
526 | | |
527 | | int rpcstr_push_talloc(TALLOC_CTX *ctx, smb_ucs2_t **dest, const char *src) |
528 | 0 | { |
529 | 0 | size_t size; |
530 | 0 | if (push_ucs2_talloc(ctx, dest, src, &size)) |
531 | 0 | return size; |
532 | 0 | else |
533 | 0 | return -1; |
534 | 0 | } |