xref: /openbmc/linux/fs/smb/server/unicode.c (revision 479965a2)
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  */
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
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  */
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  */
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  */
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  */
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