xref: /illumos-gate/usr/src/common/smbsrv/smb_oem.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
26  */
27 
28 /*
29  * Support for oem <-> unicode translations.
30  */
31 
32 #if !defined(_KERNEL) && !defined(_FAKE_KERNEL)
33 #include <stdlib.h>
34 #include <thread.h>
35 #include <synch.h>
36 #include <string.h>
37 #else
38 #include <sys/ksynch.h>
39 #endif /* _KERNEL */
40 
41 #include <sys/byteorder.h>
42 #include <smbsrv/alloc.h>
43 #include <smbsrv/string.h>
44 
45 /*
46  * cpid		The oemcpg_table index for this oempage.
47  * value	The conversion values.
48  */
49 typedef struct oempage {
50 	uint32_t	cpid;
51 	smb_wchar_t	*value;
52 } oempage_t;
53 
54 /*
55  * filename	The actual filename contains the codepage.
56  * bytesperchar	The codepage uses double or single bytes per char.
57  * oempage	The oempage is used to convert Unicode characters to
58  *		OEM characters.  Memory needs to be allocated for
59  *		the value field of oempage to store the table.
60  * ucspage	The unicode page is used to convert OEM characters
61  *		to Unicode characters.  Memory needs to be allocated
62  *		for the value field of ucspage to store the table.
63  * valid	True if the codepage has been initialized.
64  */
65 typedef struct oem_codepage {
66 	char		*filename;
67 	uint32_t	bytesperchar;
68 	oempage_t	oempage;
69 	oempage_t	ucspage;
70 	boolean_t	valid;
71 } oem_codepage_t;
72 
73 static oem_codepage_t oemcpg_table[] = {
74 	{"850.cpg",  1, {0, 0},  {0, 0},  0},	/* Multilingual Latin1 */
75 	{"950.cpg",  2, {1, 0},  {1, 0},  0},	/* Chinese Traditional */
76 	{"1252.cpg", 1, {2, 0},  {2, 0},  0},	/* MS Latin1 */
77 	{"949.cpg",  2, {3, 0},  {3, 0},  0},	/* Korean */
78 	{"936.cpg",  2, {4, 0},  {4, 0},  0},	/* Chinese Simplified */
79 	{"932.cpg",  2, {5, 0},  {5, 0},  0},	/* Japanese */
80 	{"852.cpg",  1, {6, 0},  {6, 0},  0},	/* Multilingual Latin2 */
81 	{"1250.cpg", 1, {7, 0},  {7, 0},  0},	/* MS Latin2 */
82 	{"1253.cpg", 1, {8, 0},  {8, 0},  0},	/* MS Greek */
83 	{"737.cpg",  1, {9, 0},  {9, 0},  0},	/* Greek */
84 	{"1254.cpg", 1, {10, 0}, {10, 0}, 0},	/* MS Turkish */
85 	{"857.cpg",  1, {11, 0}, {11, 0}, 0},	/* Multilingual Latin5 */
86 	{"1251.cpg", 1, {12, 0}, {12, 0}, 0},	/* MS Cyrillic */
87 	{"866.cpg",  1, {13, 0}, {13, 0}, 0},	/* Cyrillic II */
88 	{"1255.cpg", 1, {14, 0}, {14, 0}, 0},	/* MS Hebrew */
89 	{"862.cpg",  1, {15, 0}, {15, 0}, 0},	/* Hebrew */
90 	{"1256.cpg", 1, {16, 0}, {16, 0}, 0},	/* MS Arabic */
91 	{"720.cpg",  1, {17, 0}, {17, 0}, 0}	/* Arabic */
92 };
93 
94 #define	MAX_OEMPAGES	(sizeof (oemcpg_table) / sizeof (oemcpg_table[0]))
95 #define	MAX_UNICODE_IDX	65536
96 
97 /*
98  * The default SMB OEM codepage for English is codepage 850.
99  */
100 const smb_wchar_t oem_codepage_850[256] = {
101 	0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
102 	0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
103 	0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
104 	0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
105 	0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
106 	0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
107 	0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
108 	0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
109 	0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
110 	0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
111 	0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
112 	0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
113 	0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
114 	0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
115 	0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
116 	0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
117 	0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
118 	0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
119 	0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
120 	0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
121 	0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
122 	0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
123 	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
124 	0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
125 	0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
126 	0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
127 	0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE,
128 	0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
129 	0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE,
130 	0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
131 	0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
132 	0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
133 };
134 
135 /*
136  * The default telnet OEM codepage for English is codepage 1252.
137  */
138 const smb_wchar_t oem_codepage_1252[256] = {
139 	0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,
140 	0x9, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 0x10,
141 	0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
142 	0x19, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x20,
143 	0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
144 	0x29, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x30,
145 	0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
146 	0x39, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, 0x40,
147 	0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
148 	0x49, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x50,
149 	0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
150 	0x59, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, 0x60,
151 	0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
152 	0x69, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x70,
153 	0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
154 	0x79, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, 0x20AC,
155 	0x81, 0x201A, 0x192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6,
156 	0x2030, 0x160, 0x2039, 0x152, 0x8D, 0x017D, 0x8F, 0x90,
157 	0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC,
158 	0x2122, 0x161, 0x203A, 0x153, 0x9D, 0x017E, 0x178, 0x00A0,
159 	0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8,
160 	0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0,
161 	0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8,
162 	0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x00C0,
163 	0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8,
164 	0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0,
165 	0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8,
166 	0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0,
167 	0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8,
168 	0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F0,
169 	0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8,
170 	0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
171 };
172 
173 static oempage_t *oem_get_oempage(uint32_t);
174 static oempage_t *oem_get_ucspage(uint32_t);
175 static void oem_codepage_init(uint32_t);
176 static void oem_codepage_setup(uint32_t);
177 
178 /*
179  * Convert a unicode string to an oem string.
180  *
181  * The conversion will stop at the end of the unicode string
182  * or when (nbytes - 1) oem characters have been stored.
183  *
184  * The number of converted unicode characters is returned,
185  * or 0 on error.
186  */
187 size_t
188 ucstooem(char *oem, const smb_wchar_t *ucs, size_t nbytes, uint32_t cpid)
189 {
190 	oempage_t	*ucspage;
191 	uint32_t	count = 0;
192 	smb_wchar_t	oemchar;
193 
194 	if (ucs == NULL || oem == NULL)
195 		return (0);
196 
197 	if ((ucspage = oem_get_ucspage(cpid)) == NULL)
198 		return (0);
199 
200 	while (nbytes != 0 && (oemchar = ucspage->value[*ucs]) != 0) {
201 		if (oemchar & 0xff00 && nbytes >= MTS_MB_CHAR_MAX) {
202 			*oem++ = oemchar >> 8;
203 			*oem++ = (char)oemchar;
204 			nbytes -= 2;
205 		} else if (nbytes > 1) {
206 			*oem++ = (char)oemchar;
207 			nbytes--;
208 		} else {
209 			break;
210 		}
211 
212 		count++;
213 		ucs++;
214 	}
215 
216 	*oem = '\0';
217 	return (count);
218 }
219 
220 /*
221  * Convert an oem string to a unicode string.
222  *
223  * The conversion will stop at the end of the oem string or
224  * when nwchars - 1 have been converted.
225  *
226  * The number of converted oem chars is returned, or 0 on error.
227  * An oem char may be either 1 or 2 bytes.
228  */
229 size_t
230 oemtoucs(smb_wchar_t *ucs, const char *oem, size_t nwchars, uint32_t cpid)
231 {
232 	oempage_t	*oempage;
233 	size_t		count = nwchars;
234 	smb_wchar_t	oemchar;
235 
236 	if (ucs == NULL || oem == NULL)
237 		return (0);
238 
239 	if ((oempage = oem_get_oempage(cpid)) == NULL)
240 		return (0);
241 
242 	while ((oemchar = (smb_wchar_t)*oem++ & 0xff) != 0) {
243 		/*
244 		 * Cannot find one byte oemchar in table.
245 		 * Must be a lead byte. Try two bytes.
246 		 */
247 		if ((oempage->value[oemchar] == 0) && (oemchar != 0)) {
248 			oemchar = oemchar << 8 | (*oem++ & 0xff);
249 			if (oempage->value[oemchar] == 0) {
250 				*ucs = 0;
251 				break;
252 			}
253 		}
254 #ifdef _BIG_ENDIAN
255 		*ucs = LE_IN16(&oempage->value[oemchar]);
256 #else
257 		*ucs = oempage->value[oemchar];
258 #endif
259 		count--;
260 		ucs++;
261 	}
262 
263 	*ucs = 0;
264 	return (nwchars - count);
265 }
266 
267 /*
268  * Get a pointer to the oem page for the specific codepage id.
269  */
270 static oempage_t *
271 oem_get_oempage(uint32_t cpid)
272 {
273 	if (cpid >= MAX_OEMPAGES)
274 		return (NULL);
275 
276 	if (!oemcpg_table[cpid].valid) {
277 		oem_codepage_init(cpid);
278 
279 		if (!oemcpg_table[cpid].valid)
280 			return (NULL);
281 	}
282 
283 	return (&oemcpg_table[cpid].oempage);
284 }
285 
286 /*
287  * Get a pointer to the ucs page for the specific codepage id.
288  */
289 static oempage_t *
290 oem_get_ucspage(uint32_t cpid)
291 {
292 	if (cpid >= MAX_OEMPAGES)
293 		return (NULL);
294 
295 	if (!oemcpg_table[cpid].valid) {
296 		oem_codepage_init(cpid);
297 
298 		if (!oemcpg_table[cpid].valid)
299 			return (NULL);
300 	}
301 
302 	return (&oemcpg_table[cpid].ucspage);
303 }
304 
305 /*
306  * Initialize the oem page in the oem table.
307  */
308 static void
309 oem_codepage_init(uint32_t cpid)
310 {
311 #if !defined(_KERNEL) && !defined(_FAKE_KERNEL)
312 	static mutex_t mutex;
313 
314 	(void) mutex_lock(&mutex);
315 	oem_codepage_setup(cpid);
316 	(void) mutex_unlock(&mutex);
317 #else
318 	static kmutex_t mutex;
319 
320 	mutex_enter(&mutex);
321 	oem_codepage_setup(cpid);
322 	mutex_exit(&mutex);
323 #endif /* _KERNEL */
324 }
325 
326 static void
327 oem_codepage_setup(uint32_t cpid)
328 {
329 	const smb_wchar_t *default_oem_cp;
330 	oem_codepage_t	*oemcpg;
331 	uint32_t	bytesperchar;
332 	uint32_t	max_oem_index;
333 	int		i;
334 
335 	switch (cpid) {
336 	case OEM_CPG_850:
337 		default_oem_cp = oem_codepage_850;
338 		break;
339 	case OEM_CPG_1252:
340 		default_oem_cp = oem_codepage_1252;
341 	default:
342 		return;
343 	}
344 
345 	oemcpg = &oemcpg_table[cpid];
346 	if (oemcpg->valid)
347 		return;
348 
349 	/*
350 	 * max_oem_index will be 256 or 65536 dependent
351 	 * on the OEM codepage.
352 	 */
353 	bytesperchar = oemcpg_table[cpid].bytesperchar;
354 	max_oem_index = 1 << (bytesperchar * 8);
355 
356 	oemcpg->oempage.value =
357 	    MEM_ZALLOC("oem", max_oem_index * sizeof (smb_wchar_t));
358 	if (oemcpg->oempage.value == NULL)
359 		return;
360 
361 	oemcpg->ucspage.value =
362 	    MEM_ZALLOC("oem", MAX_UNICODE_IDX * sizeof (smb_wchar_t));
363 	if (oemcpg->ucspage.value == NULL) {
364 		MEM_FREE("oem", oemcpg->oempage.value);
365 		oemcpg->oempage.value = NULL;
366 		return;
367 	}
368 
369 	for (i = 0; i < max_oem_index; i++) {
370 		oemcpg->oempage.value[i] = default_oem_cp[i];
371 		oemcpg->ucspage.value[default_oem_cp[i]] = (smb_wchar_t)i;
372 	}
373 
374 	oemcpg->valid = B_TRUE;
375 }
376