1da6c28aaSamw /*
2da6c28aaSamw * CDDL HEADER START
3da6c28aaSamw *
4da6c28aaSamw * The contents of this file are subject to the terms of the
5da6c28aaSamw * Common Development and Distribution License (the "License").
6da6c28aaSamw * You may not use this file except in compliance with the License.
7da6c28aaSamw *
8da6c28aaSamw * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9da6c28aaSamw * or http://www.opensolaris.org/os/licensing.
10da6c28aaSamw * See the License for the specific language governing permissions
11da6c28aaSamw * and limitations under the License.
12da6c28aaSamw *
13da6c28aaSamw * When distributing Covered Code, include this CDDL HEADER in each
14da6c28aaSamw * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15da6c28aaSamw * If applicable, add the following below this CDDL HEADER, with the
16da6c28aaSamw * fields enclosed by brackets "[]" replaced with your own identifying
17da6c28aaSamw * information: Portions Copyright [yyyy] [name of copyright owner]
18da6c28aaSamw *
19da6c28aaSamw * CDDL HEADER END
20da6c28aaSamw */
21da6c28aaSamw /*
22bbf6f00cSJordan Brown * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23da6c28aaSamw * Use is subject to license terms.
24b819cea2SGordon Ross *
25b819cea2SGordon Ross * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
26da6c28aaSamw */
27da6c28aaSamw
28b819cea2SGordon Ross #if defined(_KERNEL) || defined(_FAKE_KERNEL)
29da6c28aaSamw #include <sys/types.h>
30da6c28aaSamw #include <sys/sunddi.h>
31da6c28aaSamw #else
32da6c28aaSamw #include <string.h>
33da6c28aaSamw #endif
34da6c28aaSamw #include <smbsrv/string.h>
35da6c28aaSamw #include <smbsrv/netbios.h>
36da6c28aaSamw
37da6c28aaSamw /*
38da6c28aaSamw * Routines than support name compression.
39da6c28aaSamw *
40da6c28aaSamw * The NetBIOS name representation in all NetBIOS packets (for NAME,
41da6c28aaSamw * SESSION, and DATAGRAM services) is defined in the Domain Name
42da6c28aaSamw * Service RFC 883[3] as "compressed" name messages. This format is
43da6c28aaSamw * called "second-level encoding" in the section entitled
44da6c28aaSamw * "Representation of NetBIOS Names" in the Concepts and Methods
45da6c28aaSamw * document.
46da6c28aaSamw *
47da6c28aaSamw * For ease of description, the first two paragraphs from page 31,
48da6c28aaSamw * the section titled "Domain name representation and compression",
49da6c28aaSamw * of RFC 883 are replicated here:
50da6c28aaSamw *
51da6c28aaSamw * Domain names messages are expressed in terms of a sequence
52da6c28aaSamw * of labels. Each label is represented as a one octet length
53da6c28aaSamw * field followed by that number of octets. Since every domain
54da6c28aaSamw * name ends with the null label of the root, a compressed
55da6c28aaSamw * domain name is terminated by a length byte of zero. The
56da6c28aaSamw * high order two bits of the length field must be zero, and
57da6c28aaSamw * the remaining six bits of the length field limit the label
58da6c28aaSamw * to 63 octets or less.
59da6c28aaSamw *
60da6c28aaSamw * To simplify implementations, the total length of label
61da6c28aaSamw * octets and label length octets that make up a domain name is
62da6c28aaSamw * restricted to 255 octets or less.
63da6c28aaSamw *
64da6c28aaSamw * The following is the uncompressed representation of the NetBIOS name
65da6c28aaSamw * "FRED ", which is the 4 ASCII characters, F, R, E, D, followed by 12
66da6c28aaSamw * space characters (0x20). This name has the SCOPE_ID: "NETBIOS.COM"
67da6c28aaSamw *
68da6c28aaSamw * EGFCEFEECACACACACACACACACACACACA.NETBIOS.COM
69da6c28aaSamw *
70da6c28aaSamw * This uncompressed representation of names is called "first-level
71da6c28aaSamw * encoding" in the section entitled "Representation of NetBIOS Names"
72da6c28aaSamw * in the Concepts and Methods document.
73da6c28aaSamw *
74da6c28aaSamw * The following is a pictographic representation of the compressed
75da6c28aaSamw * representation of the previous uncompressed Domain Name
76da6c28aaSamw * representation.
77da6c28aaSamw *
78da6c28aaSamw * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
79da6c28aaSamw * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
80da6c28aaSamw * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
81da6c28aaSamw * | 0x20 | E (0x45) | G (0x47) | F (0x46) |
82da6c28aaSamw * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
83da6c28aaSamw * | C (0x43) | E (0x45) | F (0x46) | E (0x45) |
84da6c28aaSamw * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
85da6c28aaSamw * | E (0x45) | C (0x43) | A (0x41) | C (0x43) |
86da6c28aaSamw * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
87da6c28aaSamw * | A (0x41) | C (0x43) | A (0x41) | C (0x43) |
88da6c28aaSamw * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
89da6c28aaSamw * | A (0x41) | C (0x43) | A (0x41) | C (0x43) |
90da6c28aaSamw * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
91da6c28aaSamw * | A (0x41) | C (0x43) | A (0x41) | C (0x43) |
92da6c28aaSamw * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
93da6c28aaSamw * | A (0x41) | C (0x43) | A (0x41) | C (0x43) |
94da6c28aaSamw * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
95da6c28aaSamw * | A (0x41) | C (0x43) | A (0x41) | C (0x43) |
96da6c28aaSamw * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
97da6c28aaSamw * | A (0X41) | 0x07 | N (0x4E) | E (0x45) |
98da6c28aaSamw * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
99da6c28aaSamw * | T (0x54) | B (0x42) | I (0x49) | O (0x4F) |
100da6c28aaSamw * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
101da6c28aaSamw * | S (0x53) | 0x03 | C (0x43) | O (0x4F) |
102da6c28aaSamw * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
103da6c28aaSamw * | M (0x4D) | 0x00 |
104da6c28aaSamw * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
105da6c28aaSamw *
106da6c28aaSamw * Each section of a domain name is called a label [7 (page 31)]. A
107da6c28aaSamw * label can be a maximum of 63 bytes. The first byte of a label in
108da6c28aaSamw * compressed representation is the number of bytes in the label. For
109da6c28aaSamw * the above example, the first 0x20 is the number of bytes in the
110da6c28aaSamw * left-most label, EGFCEFEECACACACACACACACACACACACA, of the domain
111da6c28aaSamw * name. The bytes following the label length count are the characters
112da6c28aaSamw * of the label. The following labels are in sequence after the first
113da6c28aaSamw * label, which is the encoded NetBIOS name, until a zero (0x00) length
114da6c28aaSamw * count. The zero length count represents the root label, which is
115da6c28aaSamw * always null.
116da6c28aaSamw *
117da6c28aaSamw * A label length count is actually a 6-bit field in the label length
118da6c28aaSamw * field. The most significant 2 bits of the field, bits 7 and 6, are
119da6c28aaSamw * flags allowing an escape from the above compressed representation.
120da6c28aaSamw * If bits 7 and 6 are both set (11), the following 14 bits are an
121da6c28aaSamw * offset pointer into the full message to the actual label string from
122da6c28aaSamw * another domain name that belongs in this name. This label pointer
123da6c28aaSamw * allows for a further compression of a domain name in a packet.
124da6c28aaSamw *
125da6c28aaSamw * NetBIOS implementations can only use label string pointers in Name
126da6c28aaSamw * Service packets. They cannot be used in Session or Datagram Service
127da6c28aaSamw * packets.
128da6c28aaSamw *
129da6c28aaSamw * The other two possible values for bits 7 and 6 (01 and 10) of a label
130da6c28aaSamw * length field are reserved for future use by RFC 883[2 (page 32)].
131da6c28aaSamw *
132da6c28aaSamw * Note that the first octet of a compressed name must contain one of
133da6c28aaSamw * the following bit patterns. (An "x" indicates a bit whose value may
134da6c28aaSamw * be either 0 or 1.):
135da6c28aaSamw *
136da6c28aaSamw * 00100000 - Netbios name, length must be 32 (decimal)
137da6c28aaSamw * 11xxxxxx - Label string pointer
138da6c28aaSamw * 10xxxxxx - Reserved
139da6c28aaSamw * 01xxxxxx - Reserved
140da6c28aaSamw */
141da6c28aaSamw
142da6c28aaSamw /*
143da6c28aaSamw * netbios_first_level_name_encode
144da6c28aaSamw *
145da6c28aaSamw * Put test description here.
146da6c28aaSamw *
147da6c28aaSamw * Inputs:
148da6c28aaSamw * char * in -> Name to encode
149da6c28aaSamw * char * out -> Buffer to encode into.
150da6c28aaSamw * int length -> # of bytes to encode.
151da6c28aaSamw *
152da6c28aaSamw * Returns:
153da6c28aaSamw * Nothing
154da6c28aaSamw */
155da6c28aaSamw int
netbios_first_level_name_encode(unsigned char * name,unsigned char * scope,unsigned char * out,int max_out)156da6c28aaSamw netbios_first_level_name_encode(unsigned char *name, unsigned char *scope,
157da6c28aaSamw unsigned char *out, int max_out)
158da6c28aaSamw {
159da6c28aaSamw unsigned char ch, len;
160da6c28aaSamw unsigned char *in;
161da6c28aaSamw unsigned char *lp;
162da6c28aaSamw unsigned char *op = out;
163da6c28aaSamw
164da6c28aaSamw if (max_out < 0x21)
165da6c28aaSamw return (-1);
166da6c28aaSamw
167da6c28aaSamw in = name;
168da6c28aaSamw *op++ = 0x20;
169da6c28aaSamw for (len = 0; len < NETBIOS_NAME_SZ; len++) {
170da6c28aaSamw ch = *in++;
171da6c28aaSamw *op++ = 'A' + ((ch >> 4) & 0xF);
172da6c28aaSamw *op++ = 'A' + ((ch) & 0xF);
173da6c28aaSamw }
174da6c28aaSamw
175da6c28aaSamw max_out -= 0x21;
176da6c28aaSamw
177da6c28aaSamw in = scope;
178da6c28aaSamw len = 0;
179da6c28aaSamw lp = op++;
180da6c28aaSamw while (((ch = *in++) != 0) && (max_out-- > 1)) {
181da6c28aaSamw if (ch == 0) {
182da6c28aaSamw if ((*lp = len) != 0)
183da6c28aaSamw *op++ = 0;
184da6c28aaSamw break;
185da6c28aaSamw }
186da6c28aaSamw if (ch == '.') {
187da6c28aaSamw *lp = len;
188da6c28aaSamw lp = op++;
189da6c28aaSamw len = 0;
190da6c28aaSamw } else {
191da6c28aaSamw *op++ = ch;
192da6c28aaSamw len++;
193da6c28aaSamw }
194da6c28aaSamw }
195da6c28aaSamw *lp = len;
196da6c28aaSamw if (len != 0)
197da6c28aaSamw *op = 0;
198da6c28aaSamw
199da6c28aaSamw /*LINTED E_PTRDIFF_OVERFLOW*/
200da6c28aaSamw return (op - out);
201da6c28aaSamw }
202da6c28aaSamw
203da6c28aaSamw /*
204da6c28aaSamw * smb_first_level_name_decode
205da6c28aaSamw *
206da6c28aaSamw * The null terminated string "in" is the name to decode. The output
207da6c28aaSamw * is placed in the name_entry structure "name".
208da6c28aaSamw *
209da6c28aaSamw * The scope field is a series of length designated labels as described
210da6c28aaSamw * in the "Domain name representation and compression" section of RFC883.
211da6c28aaSamw * The two high order two bits of the length field must be zero, the
212da6c28aaSamw * remaining six bits contain the field length. The total length of the
213da6c28aaSamw * domain name is restricted to 255 octets but note that the trailing
214da6c28aaSamw * root label and its dot are not printed. When converting the labels,
215da6c28aaSamw * the length fields are replaced by dots.
216da6c28aaSamw *
217da6c28aaSamw * Returns the number of bytes scanned or -1 to indicate an error.
218da6c28aaSamw */
219da6c28aaSamw int
netbios_first_level_name_decode(char * in,char * name,char * scope)220da6c28aaSamw netbios_first_level_name_decode(char *in, char *name, char *scope)
221da6c28aaSamw {
222*b3988cf6SGordon Ross unsigned int length;
223da6c28aaSamw char c1, c2;
224da6c28aaSamw char *cp;
225da6c28aaSamw char *out;
226da6c28aaSamw
227da6c28aaSamw cp = in;
228da6c28aaSamw
229da6c28aaSamw if ((length = *cp++) != 0x20) {
230da6c28aaSamw return (-1);
231da6c28aaSamw }
232da6c28aaSamw
233da6c28aaSamw out = name;
234da6c28aaSamw while (length > 0) {
235da6c28aaSamw c1 = *cp++;
236da6c28aaSamw c2 = *cp++;
237da6c28aaSamw
238da6c28aaSamw if ('A' <= c1 && c1 <= 'P' && 'A' <= c2 && c2 <= 'P') {
239da6c28aaSamw c1 -= 'A';
240da6c28aaSamw c2 -= 'A';
241da6c28aaSamw *out++ = (c1 << 4) | (c2);
242da6c28aaSamw } else {
243da6c28aaSamw return (-1); /* conversion error */
244da6c28aaSamw }
245da6c28aaSamw length -= 2;
246da6c28aaSamw }
247da6c28aaSamw
248da6c28aaSamw /*
249*b3988cf6SGordon Ross * Don't bother decoding the scope. Not supported.
250da6c28aaSamw */
251*b3988cf6SGordon Ross if ((length = *cp++) != 0)
252*b3988cf6SGordon Ross return (-1);
253da6c28aaSamw scope[0] = '\0';
254*b3988cf6SGordon Ross
255*b3988cf6SGordon Ross /*LINTED E_PTRDIFF_OVERFLOW*/
256*b3988cf6SGordon Ross return (cp - in);
257da6c28aaSamw }
258da6c28aaSamw
259da6c28aaSamw /*
260da6c28aaSamw * smb_netbios_name_isvalid
261da6c28aaSamw *
262da6c28aaSamw * This function is provided to be used by session service
263da6c28aaSamw * which runs in kernel in order to hide name_entry definition.
264da6c28aaSamw *
265da6c28aaSamw * It returns the decoded name in the provided buffer as 'out'
266da6c28aaSamw * if it's not null.
267da6c28aaSamw *
268da6c28aaSamw * Returns 0 if decode fails, 1 if it succeeds.
269da6c28aaSamw */
270da6c28aaSamw int
netbios_name_isvalid(char * in,char * out)271da6c28aaSamw netbios_name_isvalid(char *in, char *out)
272da6c28aaSamw {
273da6c28aaSamw char name[NETBIOS_NAME_SZ];
274da6c28aaSamw char scope[NETBIOS_DOMAIN_NAME_MAX];
275da6c28aaSamw
276da6c28aaSamw if (netbios_first_level_name_decode(in, name, scope) < 0)
277da6c28aaSamw return (0);
278da6c28aaSamw
279da6c28aaSamw if (out)
280da6c28aaSamw (void) strlcpy(out, name, NETBIOS_NAME_SZ);
281da6c28aaSamw
282da6c28aaSamw return (1);
283da6c28aaSamw }
284