1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Some of the source code in this file came from fs/cifs/cifs_unicode.c
4 *
5 * Copyright (c) International Business Machines Corp., 2000,2009
6 * Modified by Steve French (sfrench@us.ibm.com)
7 * Modified by Namjae Jeon (linkinjeon@kernel.org)
8 */
9 #include <linux/fs.h>
10 #include <linux/slab.h>
11 #include <asm/unaligned.h>
12 #include "glob.h"
13 #include "unicode.h"
14 #include "smb_common.h"
15
16 /*
17 * smb_utf16_bytes() - how long will a string be after conversion?
18 * @from: pointer to input string
19 * @maxbytes: don't go past this many bytes of input string
20 * @codepage: destination codepage
21 *
22 * Walk a utf16le string and return the number of bytes that the string will
23 * be after being converted to the given charset, not including any null
24 * termination required. Don't walk past maxbytes in the source buffer.
25 *
26 * Return: string length after conversion
27 */
smb_utf16_bytes(const __le16 * from,int maxbytes,const struct nls_table * codepage)28 static int smb_utf16_bytes(const __le16 *from, int maxbytes,
29 const struct nls_table *codepage)
30 {
31 int i;
32 int charlen, outlen = 0;
33 int maxwords = maxbytes / 2;
34 char tmp[NLS_MAX_CHARSET_SIZE];
35 __u16 ftmp;
36
37 for (i = 0; i < maxwords; i++) {
38 ftmp = get_unaligned_le16(&from[i]);
39 if (ftmp == 0)
40 break;
41
42 charlen = codepage->uni2char(ftmp, tmp, NLS_MAX_CHARSET_SIZE);
43 if (charlen > 0)
44 outlen += charlen;
45 else
46 outlen++;
47 }
48
49 return outlen;
50 }
51
52 /*
53 * cifs_mapchar() - convert a host-endian char to proper char in codepage
54 * @target: where converted character should be copied
55 * @src_char: 2 byte host-endian source character
56 * @cp: codepage to which character should be converted
57 * @mapchar: should character be mapped according to mapchars mount option?
58 *
59 * This function handles the conversion of a single character. It is the
60 * responsibility of the caller to ensure that the target buffer is large
61 * enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE).
62 *
63 * Return: string length after conversion
64 */
65 static int
cifs_mapchar(char * target,const __u16 src_char,const struct nls_table * cp,bool mapchar)66 cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp,
67 bool mapchar)
68 {
69 int len = 1;
70
71 if (!mapchar)
72 goto cp_convert;
73
74 /*
75 * BB: Cannot handle remapping UNI_SLASH until all the calls to
76 * build_path_from_dentry are modified, as they use slash as
77 * separator.
78 */
79 switch (src_char) {
80 case UNI_COLON:
81 *target = ':';
82 break;
83 case UNI_ASTERISK:
84 *target = '*';
85 break;
86 case UNI_QUESTION:
87 *target = '?';
88 break;
89 case UNI_PIPE:
90 *target = '|';
91 break;
92 case UNI_GRTRTHAN:
93 *target = '>';
94 break;
95 case UNI_LESSTHAN:
96 *target = '<';
97 break;
98 default:
99 goto cp_convert;
100 }
101
102 out:
103 return len;
104
105 cp_convert:
106 len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE);
107 if (len <= 0) {
108 *target = '?';
109 len = 1;
110 }
111
112 goto out;
113 }
114
115 /*
116 * smb_from_utf16() - convert utf16le string to local charset
117 * @to: destination buffer
118 * @from: source buffer
119 * @tolen: destination buffer size (in bytes)
120 * @fromlen: source buffer size (in bytes)
121 * @codepage: codepage to which characters should be converted
122 * @mapchar: should characters be remapped according to the mapchars option?
123 *
124 * Convert a little-endian utf16le string (as sent by the server) to a string
125 * in the provided codepage. The tolen and fromlen parameters are to ensure
126 * that the code doesn't walk off of the end of the buffer (which is always
127 * a danger if the alignment of the source buffer is off). The destination
128 * string is always properly null terminated and fits in the destination
129 * buffer. Returns the length of the destination string in bytes (including
130 * null terminator).
131 *
132 * Note that some windows versions actually send multiword UTF-16 characters
133 * instead of straight UTF16-2. The linux nls routines however aren't able to
134 * deal with those characters properly. In the event that we get some of
135 * those characters, they won't be translated properly.
136 *
137 * Return: string length after conversion
138 */
smb_from_utf16(char * to,const __le16 * from,int tolen,int fromlen,const struct nls_table * codepage,bool mapchar)139 static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,
140 const struct nls_table *codepage, bool mapchar)
141 {
142 int i, charlen, safelen;
143 int outlen = 0;
144 int nullsize = nls_nullsize(codepage);
145 int fromwords = fromlen / 2;
146 char tmp[NLS_MAX_CHARSET_SIZE];
147 __u16 ftmp;
148
149 /*
150 * because the chars can be of varying widths, we need to take care
151 * not to overflow the destination buffer when we get close to the
152 * end of it. Until we get to this offset, we don't need to check
153 * for overflow however.
154 */
155 safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize);
156
157 for (i = 0; i < fromwords; i++) {
158 ftmp = get_unaligned_le16(&from[i]);
159 if (ftmp == 0)
160 break;
161
162 /*
163 * check to see if converting this character might make the
164 * conversion bleed into the null terminator
165 */
166 if (outlen >= safelen) {
167 charlen = cifs_mapchar(tmp, ftmp, codepage, mapchar);
168 if ((outlen + charlen) > (tolen - nullsize))
169 break;
170 }
171
172 /* put converted char into 'to' buffer */
173 charlen = cifs_mapchar(&to[outlen], ftmp, codepage, mapchar);
174 outlen += charlen;
175 }
176
177 /* properly null-terminate string */
178 for (i = 0; i < nullsize; i++)
179 to[outlen++] = 0;
180
181 return outlen;
182 }
183
184 /*
185 * smb_strtoUTF16() - Convert character string to unicode string
186 * @to: destination buffer
187 * @from: source buffer
188 * @len: destination buffer size (in bytes)
189 * @codepage: codepage to which characters should be converted
190 *
191 * Return: string length after conversion
192 */
smb_strtoUTF16(__le16 * to,const char * from,int len,const struct nls_table * codepage)193 int smb_strtoUTF16(__le16 *to, const char *from, int len,
194 const struct nls_table *codepage)
195 {
196 int charlen;
197 int i;
198 wchar_t wchar_to; /* needed to quiet sparse */
199
200 /* special case for utf8 to handle no plane0 chars */
201 if (!strcmp(codepage->charset, "utf8")) {
202 /*
203 * convert utf8 -> utf16, we assume we have enough space
204 * as caller should have assumed conversion does not overflow
205 * in destination len is length in wchar_t units (16bits)
206 */
207 i = utf8s_to_utf16s(from, len, UTF16_LITTLE_ENDIAN,
208 (wchar_t *)to, len);
209
210 /* if success terminate and exit */
211 if (i >= 0)
212 goto success;
213 /*
214 * if fails fall back to UCS encoding as this
215 * function should not return negative values
216 * currently can fail only if source contains
217 * invalid encoded characters
218 */
219 }
220
221 for (i = 0; len > 0 && *from; i++, from += charlen, len -= charlen) {
222 charlen = codepage->char2uni(from, len, &wchar_to);
223 if (charlen < 1) {
224 /* A question mark */
225 wchar_to = 0x003f;
226 charlen = 1;
227 }
228 put_unaligned_le16(wchar_to, &to[i]);
229 }
230
231 success:
232 put_unaligned_le16(0, &to[i]);
233 return i;
234 }
235
236 /*
237 * smb_strndup_from_utf16() - copy a string from wire format to the local
238 * codepage
239 * @src: source string
240 * @maxlen: don't walk past this many bytes in the source string
241 * @is_unicode: is this a unicode string?
242 * @codepage: destination codepage
243 *
244 * Take a string given by the server, convert it to the local codepage and
245 * put it in a new buffer. Returns a pointer to the new string or NULL on
246 * error.
247 *
248 * Return: destination string buffer or error ptr
249 */
smb_strndup_from_utf16(const char * src,const int maxlen,const bool is_unicode,const struct nls_table * codepage)250 char *smb_strndup_from_utf16(const char *src, const int maxlen,
251 const bool is_unicode,
252 const struct nls_table *codepage)
253 {
254 int len, ret;
255 char *dst;
256
257 if (is_unicode) {
258 len = smb_utf16_bytes((__le16 *)src, maxlen, codepage);
259 len += nls_nullsize(codepage);
260 dst = kmalloc(len, GFP_KERNEL);
261 if (!dst)
262 return ERR_PTR(-ENOMEM);
263 ret = smb_from_utf16(dst, (__le16 *)src, len, maxlen, codepage,
264 false);
265 if (ret < 0) {
266 kfree(dst);
267 return ERR_PTR(-EINVAL);
268 }
269 } else {
270 len = strnlen(src, maxlen);
271 len++;
272 dst = kmalloc(len, GFP_KERNEL);
273 if (!dst)
274 return ERR_PTR(-ENOMEM);
275 strscpy(dst, src, len);
276 }
277
278 return dst;
279 }
280
281 /*
282 * Convert 16 bit Unicode pathname to wire format from string in current code
283 * page. Conversion may involve remapping up the six characters that are
284 * only legal in POSIX-like OS (if they are present in the string). Path
285 * names are little endian 16 bit Unicode on the wire
286 */
287 /*
288 * smbConvertToUTF16() - convert string from local charset to utf16
289 * @target: destination buffer
290 * @source: source buffer
291 * @srclen: source buffer size (in bytes)
292 * @cp: codepage to which characters should be converted
293 * @mapchar: should characters be remapped according to the mapchars option?
294 *
295 * Convert 16 bit Unicode pathname to wire format from string in current code
296 * page. Conversion may involve remapping up the six characters that are
297 * only legal in POSIX-like OS (if they are present in the string). Path
298 * names are little endian 16 bit Unicode on the wire
299 *
300 * Return: char length after conversion
301 */
smbConvertToUTF16(__le16 * target,const char * source,int srclen,const struct nls_table * cp,int mapchars)302 int smbConvertToUTF16(__le16 *target, const char *source, int srclen,
303 const struct nls_table *cp, int mapchars)
304 {
305 int i, j, charlen;
306 char src_char;
307 __le16 dst_char;
308 wchar_t tmp;
309
310 if (!mapchars)
311 return smb_strtoUTF16(target, source, srclen, cp);
312
313 for (i = 0, j = 0; i < srclen; j++) {
314 src_char = source[i];
315 charlen = 1;
316 switch (src_char) {
317 case 0:
318 put_unaligned(0, &target[j]);
319 return j;
320 case ':':
321 dst_char = cpu_to_le16(UNI_COLON);
322 break;
323 case '*':
324 dst_char = cpu_to_le16(UNI_ASTERISK);
325 break;
326 case '?':
327 dst_char = cpu_to_le16(UNI_QUESTION);
328 break;
329 case '<':
330 dst_char = cpu_to_le16(UNI_LESSTHAN);
331 break;
332 case '>':
333 dst_char = cpu_to_le16(UNI_GRTRTHAN);
334 break;
335 case '|':
336 dst_char = cpu_to_le16(UNI_PIPE);
337 break;
338 /*
339 * FIXME: We can not handle remapping backslash (UNI_SLASH)
340 * until all the calls to build_path_from_dentry are modified,
341 * as they use backslash as separator.
342 */
343 default:
344 charlen = cp->char2uni(source + i, srclen - i, &tmp);
345 dst_char = cpu_to_le16(tmp);
346
347 /*
348 * if no match, use question mark, which at least in
349 * some cases serves as wild card
350 */
351 if (charlen < 1) {
352 dst_char = cpu_to_le16(0x003f);
353 charlen = 1;
354 }
355 }
356 /*
357 * character may take more than one byte in the source string,
358 * but will take exactly two bytes in the target string
359 */
360 i += charlen;
361 put_unaligned(dst_char, &target[j]);
362 }
363
364 return j;
365 }
366