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. 24*b819cea2SGordon Ross * 25*b819cea2SGordon Ross * Copyright 2014 Nexenta Systems, Inc. All rights reserved. 26da6c28aaSamw */ 27da6c28aaSamw 28*b819cea2SGordon 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 static int domainname_is_valid(char *domain_name); 38da6c28aaSamw 39da6c28aaSamw /* 40da6c28aaSamw * Routines than support name compression. 41da6c28aaSamw * 42da6c28aaSamw * The NetBIOS name representation in all NetBIOS packets (for NAME, 43da6c28aaSamw * SESSION, and DATAGRAM services) is defined in the Domain Name 44da6c28aaSamw * Service RFC 883[3] as "compressed" name messages. This format is 45da6c28aaSamw * called "second-level encoding" in the section entitled 46da6c28aaSamw * "Representation of NetBIOS Names" in the Concepts and Methods 47da6c28aaSamw * document. 48da6c28aaSamw * 49da6c28aaSamw * For ease of description, the first two paragraphs from page 31, 50da6c28aaSamw * the section titled "Domain name representation and compression", 51da6c28aaSamw * of RFC 883 are replicated here: 52da6c28aaSamw * 53da6c28aaSamw * Domain names messages are expressed in terms of a sequence 54da6c28aaSamw * of labels. Each label is represented as a one octet length 55da6c28aaSamw * field followed by that number of octets. Since every domain 56da6c28aaSamw * name ends with the null label of the root, a compressed 57da6c28aaSamw * domain name is terminated by a length byte of zero. The 58da6c28aaSamw * high order two bits of the length field must be zero, and 59da6c28aaSamw * the remaining six bits of the length field limit the label 60da6c28aaSamw * to 63 octets or less. 61da6c28aaSamw * 62da6c28aaSamw * To simplify implementations, the total length of label 63da6c28aaSamw * octets and label length octets that make up a domain name is 64da6c28aaSamw * restricted to 255 octets or less. 65da6c28aaSamw * 66da6c28aaSamw * The following is the uncompressed representation of the NetBIOS name 67da6c28aaSamw * "FRED ", which is the 4 ASCII characters, F, R, E, D, followed by 12 68da6c28aaSamw * space characters (0x20). This name has the SCOPE_ID: "NETBIOS.COM" 69da6c28aaSamw * 70da6c28aaSamw * EGFCEFEECACACACACACACACACACACACA.NETBIOS.COM 71da6c28aaSamw * 72da6c28aaSamw * This uncompressed representation of names is called "first-level 73da6c28aaSamw * encoding" in the section entitled "Representation of NetBIOS Names" 74da6c28aaSamw * in the Concepts and Methods document. 75da6c28aaSamw * 76da6c28aaSamw * The following is a pictographic representation of the compressed 77da6c28aaSamw * representation of the previous uncompressed Domain Name 78da6c28aaSamw * representation. 79da6c28aaSamw * 80da6c28aaSamw * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 81da6c28aaSamw * 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 82da6c28aaSamw * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 83da6c28aaSamw * | 0x20 | E (0x45) | G (0x47) | F (0x46) | 84da6c28aaSamw * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 85da6c28aaSamw * | C (0x43) | E (0x45) | F (0x46) | E (0x45) | 86da6c28aaSamw * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 87da6c28aaSamw * | E (0x45) | 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) | C (0x43) | A (0x41) | C (0x43) | 98da6c28aaSamw * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 99da6c28aaSamw * | A (0X41) | 0x07 | N (0x4E) | E (0x45) | 100da6c28aaSamw * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 101da6c28aaSamw * | T (0x54) | B (0x42) | I (0x49) | O (0x4F) | 102da6c28aaSamw * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 103da6c28aaSamw * | S (0x53) | 0x03 | C (0x43) | O (0x4F) | 104da6c28aaSamw * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 105da6c28aaSamw * | M (0x4D) | 0x00 | 106da6c28aaSamw * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 107da6c28aaSamw * 108da6c28aaSamw * Each section of a domain name is called a label [7 (page 31)]. A 109da6c28aaSamw * label can be a maximum of 63 bytes. The first byte of a label in 110da6c28aaSamw * compressed representation is the number of bytes in the label. For 111da6c28aaSamw * the above example, the first 0x20 is the number of bytes in the 112da6c28aaSamw * left-most label, EGFCEFEECACACACACACACACACACACACA, of the domain 113da6c28aaSamw * name. The bytes following the label length count are the characters 114da6c28aaSamw * of the label. The following labels are in sequence after the first 115da6c28aaSamw * label, which is the encoded NetBIOS name, until a zero (0x00) length 116da6c28aaSamw * count. The zero length count represents the root label, which is 117da6c28aaSamw * always null. 118da6c28aaSamw * 119da6c28aaSamw * A label length count is actually a 6-bit field in the label length 120da6c28aaSamw * field. The most significant 2 bits of the field, bits 7 and 6, are 121da6c28aaSamw * flags allowing an escape from the above compressed representation. 122da6c28aaSamw * If bits 7 and 6 are both set (11), the following 14 bits are an 123da6c28aaSamw * offset pointer into the full message to the actual label string from 124da6c28aaSamw * another domain name that belongs in this name. This label pointer 125da6c28aaSamw * allows for a further compression of a domain name in a packet. 126da6c28aaSamw * 127da6c28aaSamw * NetBIOS implementations can only use label string pointers in Name 128da6c28aaSamw * Service packets. They cannot be used in Session or Datagram Service 129da6c28aaSamw * packets. 130da6c28aaSamw * 131da6c28aaSamw * The other two possible values for bits 7 and 6 (01 and 10) of a label 132da6c28aaSamw * length field are reserved for future use by RFC 883[2 (page 32)]. 133da6c28aaSamw * 134da6c28aaSamw * Note that the first octet of a compressed name must contain one of 135da6c28aaSamw * the following bit patterns. (An "x" indicates a bit whose value may 136da6c28aaSamw * be either 0 or 1.): 137da6c28aaSamw * 138da6c28aaSamw * 00100000 - Netbios name, length must be 32 (decimal) 139da6c28aaSamw * 11xxxxxx - Label string pointer 140da6c28aaSamw * 10xxxxxx - Reserved 141da6c28aaSamw * 01xxxxxx - Reserved 142da6c28aaSamw */ 143da6c28aaSamw 144da6c28aaSamw /* 145da6c28aaSamw * netbios_first_level_name_encode 146da6c28aaSamw * 147da6c28aaSamw * Put test description here. 148da6c28aaSamw * 149da6c28aaSamw * Inputs: 150da6c28aaSamw * char * in -> Name to encode 151da6c28aaSamw * char * out -> Buffer to encode into. 152da6c28aaSamw * int length -> # of bytes to encode. 153da6c28aaSamw * 154da6c28aaSamw * Returns: 155da6c28aaSamw * Nothing 156da6c28aaSamw */ 157da6c28aaSamw int 158da6c28aaSamw netbios_first_level_name_encode(unsigned char *name, unsigned char *scope, 159da6c28aaSamw unsigned char *out, int max_out) 160da6c28aaSamw { 161da6c28aaSamw unsigned char ch, len; 162da6c28aaSamw unsigned char *in; 163da6c28aaSamw unsigned char *lp; 164da6c28aaSamw unsigned char *op = out; 165da6c28aaSamw 166da6c28aaSamw if (max_out < 0x21) 167da6c28aaSamw return (-1); 168da6c28aaSamw 169da6c28aaSamw in = name; 170da6c28aaSamw *op++ = 0x20; 171da6c28aaSamw for (len = 0; len < NETBIOS_NAME_SZ; len++) { 172da6c28aaSamw ch = *in++; 173da6c28aaSamw *op++ = 'A' + ((ch >> 4) & 0xF); 174da6c28aaSamw *op++ = 'A' + ((ch) & 0xF); 175da6c28aaSamw } 176da6c28aaSamw 177da6c28aaSamw max_out -= 0x21; 178da6c28aaSamw 179da6c28aaSamw in = scope; 180da6c28aaSamw len = 0; 181da6c28aaSamw lp = op++; 182da6c28aaSamw while (((ch = *in++) != 0) && (max_out-- > 1)) { 183da6c28aaSamw if (ch == 0) { 184da6c28aaSamw if ((*lp = len) != 0) 185da6c28aaSamw *op++ = 0; 186da6c28aaSamw break; 187da6c28aaSamw } 188da6c28aaSamw if (ch == '.') { 189da6c28aaSamw *lp = len; 190da6c28aaSamw lp = op++; 191da6c28aaSamw len = 0; 192da6c28aaSamw } else { 193da6c28aaSamw *op++ = ch; 194da6c28aaSamw len++; 195da6c28aaSamw } 196da6c28aaSamw } 197da6c28aaSamw *lp = len; 198da6c28aaSamw if (len != 0) 199da6c28aaSamw *op = 0; 200da6c28aaSamw 201da6c28aaSamw /*LINTED E_PTRDIFF_OVERFLOW*/ 202da6c28aaSamw return (op - out); 203da6c28aaSamw } 204da6c28aaSamw 205da6c28aaSamw /* 206da6c28aaSamw * smb_first_level_name_decode 207da6c28aaSamw * 208da6c28aaSamw * The null terminated string "in" is the name to decode. The output 209da6c28aaSamw * is placed in the name_entry structure "name". 210da6c28aaSamw * 211da6c28aaSamw * The scope field is a series of length designated labels as described 212da6c28aaSamw * in the "Domain name representation and compression" section of RFC883. 213da6c28aaSamw * The two high order two bits of the length field must be zero, the 214da6c28aaSamw * remaining six bits contain the field length. The total length of the 215da6c28aaSamw * domain name is restricted to 255 octets but note that the trailing 216da6c28aaSamw * root label and its dot are not printed. When converting the labels, 217da6c28aaSamw * the length fields are replaced by dots. 218da6c28aaSamw * 219da6c28aaSamw * Returns the number of bytes scanned or -1 to indicate an error. 220da6c28aaSamw */ 221da6c28aaSamw int 222da6c28aaSamw netbios_first_level_name_decode(char *in, char *name, char *scope) 223da6c28aaSamw { 224da6c28aaSamw unsigned int length, bytes; 225da6c28aaSamw char c1, c2; 226da6c28aaSamw char *cp; 227da6c28aaSamw char *out; 228da6c28aaSamw 229da6c28aaSamw cp = in; 230da6c28aaSamw 231da6c28aaSamw if ((length = *cp++) != 0x20) { 232da6c28aaSamw return (-1); 233da6c28aaSamw } 234da6c28aaSamw 235da6c28aaSamw out = name; 236da6c28aaSamw while (length > 0) { 237da6c28aaSamw c1 = *cp++; 238da6c28aaSamw c2 = *cp++; 239da6c28aaSamw 240da6c28aaSamw if ('A' <= c1 && c1 <= 'P' && 'A' <= c2 && c2 <= 'P') { 241da6c28aaSamw c1 -= 'A'; 242da6c28aaSamw c2 -= 'A'; 243da6c28aaSamw *out++ = (c1 << 4) | (c2); 244da6c28aaSamw } else { 245da6c28aaSamw return (-1); /* conversion error */ 246da6c28aaSamw } 247da6c28aaSamw length -= 2; 248da6c28aaSamw } 249da6c28aaSamw 250da6c28aaSamw out = scope; 251da6c28aaSamw bytes = 0; 252da6c28aaSamw for (length = *cp++; length != 0; length = *cp++) { 253da6c28aaSamw if ((length & 0xc0) != 0x00) { 254da6c28aaSamw /* 255da6c28aaSamw * This is a pointer or a reserved field. If it's 256da6c28aaSamw * a pointer (16-bits) we have to skip the next byte. 257da6c28aaSamw */ 258da6c28aaSamw if ((length & 0xc0) == 0xc0) { 259da6c28aaSamw cp++; 260da6c28aaSamw continue; 261da6c28aaSamw } 262da6c28aaSamw } 263da6c28aaSamw 264da6c28aaSamw /* 265da6c28aaSamw * Replace the length with a '.', except for the first one. 266da6c28aaSamw */ 267da6c28aaSamw if (out != scope) { 268da6c28aaSamw *out++ = '.'; 269da6c28aaSamw bytes++; 270da6c28aaSamw } 271da6c28aaSamw 272da6c28aaSamw while (length-- > 0) { 273da6c28aaSamw if (bytes++ >= (NETBIOS_DOMAIN_NAME_MAX - 1)) { 274da6c28aaSamw return (-1); 275da6c28aaSamw } 276da6c28aaSamw *out++ = *cp++; 277da6c28aaSamw } 278da6c28aaSamw } 279da6c28aaSamw *out = 0; 280da6c28aaSamw 281da6c28aaSamw /* 282da6c28aaSamw * We are supposed to preserve all 8-bits of the domain name 283da6c28aaSamw * but due to the single byte representation in the name cache 284da6c28aaSamw * and UTF-8 encoding everywhere else, we restrict domain names 285da6c28aaSamw * to Appendix 1 - Domain Name Syntax Specification in RFC883. 286da6c28aaSamw */ 287da6c28aaSamw if (domainname_is_valid(scope)) { 288bbf6f00cSJordan Brown (void) smb_strupr(scope); 289da6c28aaSamw /*LINTED E_PTRDIFF_OVERFLOW*/ 290da6c28aaSamw return (cp - in); 291da6c28aaSamw } 292da6c28aaSamw 293da6c28aaSamw scope[0] = '\0'; 294da6c28aaSamw return (-1); 295da6c28aaSamw } 296da6c28aaSamw 297da6c28aaSamw /* 298da6c28aaSamw * smb_netbios_name_isvalid 299da6c28aaSamw * 300da6c28aaSamw * This function is provided to be used by session service 301da6c28aaSamw * which runs in kernel in order to hide name_entry definition. 302da6c28aaSamw * 303da6c28aaSamw * It returns the decoded name in the provided buffer as 'out' 304da6c28aaSamw * if it's not null. 305da6c28aaSamw * 306da6c28aaSamw * Returns 0 if decode fails, 1 if it succeeds. 307da6c28aaSamw */ 308da6c28aaSamw int 309da6c28aaSamw netbios_name_isvalid(char *in, char *out) 310da6c28aaSamw { 311da6c28aaSamw char name[NETBIOS_NAME_SZ]; 312da6c28aaSamw char scope[NETBIOS_DOMAIN_NAME_MAX]; 313da6c28aaSamw 314da6c28aaSamw if (netbios_first_level_name_decode(in, name, scope) < 0) 315da6c28aaSamw return (0); 316da6c28aaSamw 317da6c28aaSamw if (out) 318da6c28aaSamw (void) strlcpy(out, name, NETBIOS_NAME_SZ); 319da6c28aaSamw 320da6c28aaSamw return (1); 321da6c28aaSamw } 322da6c28aaSamw 323da6c28aaSamw /* 324da6c28aaSamw * Characters that we allow in DNS domain names, in addition to 325da6c28aaSamw * alphanumeric characters. This is not quite consistent with 326da6c28aaSamw * RFC883. This is global so that it can be patched if there is 327da6c28aaSamw * a need to change the valid characters in the field. 328da6c28aaSamw */ 3298622ec45SGordon Ross static const char dns_allowed[] = "-_"; 330da6c28aaSamw 331da6c28aaSamw /* 332da6c28aaSamw * dns_is_allowed 333da6c28aaSamw * 334da6c28aaSamw * Check the dns_allowed characters and return true (1) if the character 335da6c28aaSamw * is in the table. Otherwise return false (0). 336da6c28aaSamw */ 337da6c28aaSamw static int 338da6c28aaSamw dns_is_allowed(unsigned char c) 339da6c28aaSamw { 3408622ec45SGordon Ross const char *p = dns_allowed; 341da6c28aaSamw 342da6c28aaSamw while (*p) { 3438622ec45SGordon Ross if ((char)c == *p++) 344da6c28aaSamw return (1); 345da6c28aaSamw } 346da6c28aaSamw 347da6c28aaSamw return (0); 348da6c28aaSamw } 349da6c28aaSamw 350da6c28aaSamw 351da6c28aaSamw /* 352da6c28aaSamw * domainname_is_valid 353da6c28aaSamw * 354da6c28aaSamw * Check the specified domain name for mostly compliance with RFC883 355da6c28aaSamw * Appendix 1. Names may contain alphanumeric characters, hyphens, 356da6c28aaSamw * underscores and dots. The first character after a dot must be an 357da6c28aaSamw * alphabetic character. RFC883 doesn't mention underscores but we 358da6c28aaSamw * allow it due to common use, and we don't check that labels end 359da6c28aaSamw * with an alphanumeric character. 360da6c28aaSamw * 361da6c28aaSamw * Returns true (1) if the name is valid. Otherwise returns false (0). 362da6c28aaSamw */ 363da6c28aaSamw static int 364da6c28aaSamw domainname_is_valid(char *domain_name) 365da6c28aaSamw { 366da6c28aaSamw char *name; 367da6c28aaSamw int first_char = 1; 368da6c28aaSamw 369da6c28aaSamw if (domain_name == 0) 370da6c28aaSamw return (0); 371da6c28aaSamw 372da6c28aaSamw for (name = domain_name; *name != 0; ++name) { 373da6c28aaSamw if (*name == '.') { 374da6c28aaSamw first_char = 1; 375da6c28aaSamw continue; 376da6c28aaSamw } 377da6c28aaSamw 378da6c28aaSamw if (first_char) { 379bbf6f00cSJordan Brown if (smb_isalpha_ascii(*name) == 0) 380da6c28aaSamw return (0); 381da6c28aaSamw 382da6c28aaSamw first_char = 0; 383da6c28aaSamw continue; 384da6c28aaSamw } 385da6c28aaSamw 386bbf6f00cSJordan Brown if (smb_isalnum_ascii(*name) || dns_is_allowed(*name)) 387da6c28aaSamw continue; 388da6c28aaSamw 389da6c28aaSamw return (0); 390da6c28aaSamw } 391da6c28aaSamw 392da6c28aaSamw return (1); 393da6c28aaSamw } 394