1 /*
2 * Copyright (c) Edward Thomson. All rights reserved.
3 *
4 * This file is part of ntlmclient, distributed under the MIT license.
5 * For full terms and copyright information, and for third-party
6 * copyright information, see the included LICENSE.txt file.
7 */
8
9 #include <locale.h>
10 #include <iconv.h>
11 #include <string.h>
12 #include <errno.h>
13
14 #include "ntlmclient.h"
15 #include "unicode.h"
16 #include "ntlm.h"
17 #include "compat.h"
18
19 typedef enum {
20 unicode_iconv_utf8_to_16,
21 unicode_iconv_utf16_to_8
22 } unicode_iconv_encoding_direction;
23
ntlm_unicode_init(ntlm_client * ntlm)24 bool ntlm_unicode_init(ntlm_client *ntlm)
25 {
26 ntlm->unicode_ctx.utf8_to_16 = iconv_open("UTF-16LE", "UTF-8");
27 ntlm->unicode_ctx.utf16_to_8 = iconv_open("UTF-8", "UTF-16LE");
28
29 if (ntlm->unicode_ctx.utf8_to_16 == (iconv_t)-1 ||
30 ntlm->unicode_ctx.utf16_to_8 == (iconv_t)-1) {
31 if (errno == EINVAL)
32 ntlm_client_set_errmsg(ntlm,
33 "iconv does not support UTF8 <-> UTF16 conversion");
34 else
35 ntlm_client_set_errmsg(ntlm, strerror(errno));
36
37 return false;
38 }
39
40 return true;
41 }
42
unicode_iconv_encoding_convert(char ** converted,size_t * converted_len,ntlm_client * ntlm,const char * string,size_t string_len,unicode_iconv_encoding_direction direction)43 static inline bool unicode_iconv_encoding_convert(
44 char **converted,
45 size_t *converted_len,
46 ntlm_client *ntlm,
47 const char *string,
48 size_t string_len,
49 unicode_iconv_encoding_direction direction)
50 {
51 char *in_start, *out_start, *out, *new_out;
52 size_t in_start_len, out_start_len, out_size, nul_size, ret, written = 0;
53 iconv_t converter;
54
55 *converted = NULL;
56 *converted_len = 0;
57
58 /*
59 * When translating UTF8 to UTF16, these strings are only used
60 * internally, and we obey the given length, so we can simply
61 * use a buffer that is 2x the size. When translating from UTF16
62 * to UTF8, we may need to return to callers, so we need to NUL
63 * terminate and expect an extra byte for UTF8, two for UTF16.
64 */
65 if (direction == unicode_iconv_utf8_to_16) {
66 converter = ntlm->unicode_ctx.utf8_to_16;
67 out_size = (string_len * 2) + 2;
68 nul_size = 2;
69 } else {
70 converter = ntlm->unicode_ctx.utf16_to_8;
71 out_size = (string_len / 2) + 1;
72 nul_size = 1;
73 }
74
75 /* Round to the nearest multiple of 8 */
76 out_size = (out_size + 7) & ~7;
77
78 if ((out = malloc(out_size)) == NULL) {
79 ntlm_client_set_errmsg(ntlm, "out of memory");
80 return false;
81 }
82
83 in_start = (char *)string;
84 in_start_len = string_len;
85
86 while (true) {
87 out_start = out + written;
88 out_start_len = (out_size - nul_size) - written;
89
90 ret = iconv(converter, &in_start, &in_start_len, &out_start, &out_start_len);
91 written = (out_size - nul_size) - out_start_len;
92
93 if (ret == 0)
94 break;
95
96 if (ret == (size_t)-1 && errno != E2BIG) {
97 ntlm_client_set_errmsg(ntlm, strerror(errno));
98 goto on_error;
99 }
100
101 /* Grow buffer size by 1.5 (rounded up to a multiple of 8) */
102 out_size = ((((out_size << 1) - (out_size >> 1)) + 7) & ~7);
103
104 if (out_size > NTLM_UNICODE_MAX_LEN) {
105 ntlm_client_set_errmsg(ntlm, "unicode conversion too large");
106 goto on_error;
107 }
108
109 if ((new_out = realloc(out, out_size)) == NULL) {
110 ntlm_client_set_errmsg(ntlm, "out of memory");
111 goto on_error;
112 }
113
114 out = new_out;
115 }
116
117 if (in_start_len != 0) {
118 ntlm_client_set_errmsg(ntlm,
119 "invalid unicode string; trailing data remains");
120 goto on_error;
121 }
122
123 /* NUL terminate */
124 out[written] = '\0';
125
126 if (direction == unicode_iconv_utf8_to_16)
127 out[written + 1] = '\0';
128
129 *converted = out;
130
131 if (converted_len)
132 *converted_len = written;
133
134 return true;
135
136 on_error:
137 free(out);
138 return false;
139 }
140
ntlm_unicode_utf8_to_16(char ** converted,size_t * converted_len,ntlm_client * ntlm,const char * string,size_t string_len)141 bool ntlm_unicode_utf8_to_16(
142 char **converted,
143 size_t *converted_len,
144 ntlm_client *ntlm,
145 const char *string,
146 size_t string_len)
147 {
148 return unicode_iconv_encoding_convert(
149 converted, converted_len, ntlm, string, string_len,
150 unicode_iconv_utf8_to_16);
151 }
152
ntlm_unicode_utf16_to_8(char ** converted,size_t * converted_len,ntlm_client * ntlm,const char * string,size_t string_len)153 bool ntlm_unicode_utf16_to_8(
154 char **converted,
155 size_t *converted_len,
156 ntlm_client *ntlm,
157 const char *string,
158 size_t string_len)
159 {
160 return unicode_iconv_encoding_convert(
161 converted, converted_len, ntlm, string, string_len,
162 unicode_iconv_utf16_to_8);
163 }
164
ntlm_unicode_shutdown(ntlm_client * ntlm)165 void ntlm_unicode_shutdown(ntlm_client *ntlm)
166 {
167 if (ntlm->unicode_ctx.utf16_to_8 != (iconv_t)0 &&
168 ntlm->unicode_ctx.utf16_to_8 != (iconv_t)-1)
169 iconv_close(ntlm->unicode_ctx.utf16_to_8);
170
171 if (ntlm->unicode_ctx.utf8_to_16 != (iconv_t)0 &&
172 ntlm->unicode_ctx.utf8_to_16 != (iconv_t)-1)
173 iconv_close(ntlm->unicode_ctx.utf8_to_16);
174
175 ntlm->unicode_ctx.utf8_to_16 = (iconv_t)-1;
176 ntlm->unicode_ctx.utf16_to_8 = (iconv_t)-1;
177 }
178