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 /* 22*bbf6f00cSJordan Brown * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23da6c28aaSamw * Use is subject to license terms. 24da6c28aaSamw */ 25da6c28aaSamw 26da6c28aaSamw #ifdef _KERNEL 27da6c28aaSamw #include <sys/types.h> 28da6c28aaSamw #include <sys/sunddi.h> 29da6c28aaSamw #else 30da6c28aaSamw #include <string.h> 31da6c28aaSamw #endif 32da6c28aaSamw #include <smbsrv/string.h> 33da6c28aaSamw #include <smbsrv/netbios.h> 34da6c28aaSamw 35da6c28aaSamw static int domainname_is_valid(char *domain_name); 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 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 220da6c28aaSamw netbios_first_level_name_decode(char *in, char *name, char *scope) 221da6c28aaSamw { 222da6c28aaSamw unsigned int length, bytes; 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 out = scope; 249da6c28aaSamw bytes = 0; 250da6c28aaSamw for (length = *cp++; length != 0; length = *cp++) { 251da6c28aaSamw if ((length & 0xc0) != 0x00) { 252da6c28aaSamw /* 253da6c28aaSamw * This is a pointer or a reserved field. If it's 254da6c28aaSamw * a pointer (16-bits) we have to skip the next byte. 255da6c28aaSamw */ 256da6c28aaSamw if ((length & 0xc0) == 0xc0) { 257da6c28aaSamw cp++; 258da6c28aaSamw continue; 259da6c28aaSamw } 260da6c28aaSamw } 261da6c28aaSamw 262da6c28aaSamw /* 263da6c28aaSamw * Replace the length with a '.', except for the first one. 264da6c28aaSamw */ 265da6c28aaSamw if (out != scope) { 266da6c28aaSamw *out++ = '.'; 267da6c28aaSamw bytes++; 268da6c28aaSamw } 269da6c28aaSamw 270da6c28aaSamw while (length-- > 0) { 271da6c28aaSamw if (bytes++ >= (NETBIOS_DOMAIN_NAME_MAX - 1)) { 272da6c28aaSamw return (-1); 273da6c28aaSamw } 274da6c28aaSamw *out++ = *cp++; 275da6c28aaSamw } 276da6c28aaSamw } 277da6c28aaSamw *out = 0; 278da6c28aaSamw 279da6c28aaSamw /* 280da6c28aaSamw * We are supposed to preserve all 8-bits of the domain name 281da6c28aaSamw * but due to the single byte representation in the name cache 282da6c28aaSamw * and UTF-8 encoding everywhere else, we restrict domain names 283da6c28aaSamw * to Appendix 1 - Domain Name Syntax Specification in RFC883. 284da6c28aaSamw */ 285da6c28aaSamw if (domainname_is_valid(scope)) { 286*bbf6f00cSJordan Brown (void) smb_strupr(scope); 287da6c28aaSamw /*LINTED E_PTRDIFF_OVERFLOW*/ 288da6c28aaSamw return (cp - in); 289da6c28aaSamw } 290da6c28aaSamw 291da6c28aaSamw scope[0] = '\0'; 292da6c28aaSamw return (-1); 293da6c28aaSamw } 294da6c28aaSamw 295da6c28aaSamw /* 296da6c28aaSamw * smb_netbios_name_isvalid 297da6c28aaSamw * 298da6c28aaSamw * This function is provided to be used by session service 299da6c28aaSamw * which runs in kernel in order to hide name_entry definition. 300da6c28aaSamw * 301da6c28aaSamw * It returns the decoded name in the provided buffer as 'out' 302da6c28aaSamw * if it's not null. 303da6c28aaSamw * 304da6c28aaSamw * Returns 0 if decode fails, 1 if it succeeds. 305da6c28aaSamw */ 306da6c28aaSamw int 307da6c28aaSamw netbios_name_isvalid(char *in, char *out) 308da6c28aaSamw { 309da6c28aaSamw char name[NETBIOS_NAME_SZ]; 310da6c28aaSamw char scope[NETBIOS_DOMAIN_NAME_MAX]; 311da6c28aaSamw 312da6c28aaSamw if (netbios_first_level_name_decode(in, name, scope) < 0) 313da6c28aaSamw return (0); 314da6c28aaSamw 315da6c28aaSamw if (out) 316da6c28aaSamw (void) strlcpy(out, name, NETBIOS_NAME_SZ); 317da6c28aaSamw 318da6c28aaSamw return (1); 319da6c28aaSamw } 320da6c28aaSamw 321da6c28aaSamw /* 322da6c28aaSamw * Characters that we allow in DNS domain names, in addition to 323da6c28aaSamw * alphanumeric characters. This is not quite consistent with 324da6c28aaSamw * RFC883. This is global so that it can be patched if there is 325da6c28aaSamw * a need to change the valid characters in the field. 326da6c28aaSamw */ 327da6c28aaSamw unsigned char *dns_allowed = (unsigned char *)"-_"; 328da6c28aaSamw 329da6c28aaSamw /* 330da6c28aaSamw * dns_is_allowed 331da6c28aaSamw * 332da6c28aaSamw * Check the dns_allowed characters and return true (1) if the character 333da6c28aaSamw * is in the table. Otherwise return false (0). 334da6c28aaSamw */ 335da6c28aaSamw static int 336da6c28aaSamw dns_is_allowed(unsigned char c) 337da6c28aaSamw { 338da6c28aaSamw unsigned char *p = dns_allowed; 339da6c28aaSamw 340da6c28aaSamw while (*p) { 341da6c28aaSamw if (c == *p++) 342da6c28aaSamw return (1); 343da6c28aaSamw } 344da6c28aaSamw 345da6c28aaSamw return (0); 346da6c28aaSamw } 347da6c28aaSamw 348da6c28aaSamw 349da6c28aaSamw /* 350da6c28aaSamw * domainname_is_valid 351da6c28aaSamw * 352da6c28aaSamw * Check the specified domain name for mostly compliance with RFC883 353da6c28aaSamw * Appendix 1. Names may contain alphanumeric characters, hyphens, 354da6c28aaSamw * underscores and dots. The first character after a dot must be an 355da6c28aaSamw * alphabetic character. RFC883 doesn't mention underscores but we 356da6c28aaSamw * allow it due to common use, and we don't check that labels end 357da6c28aaSamw * with an alphanumeric character. 358da6c28aaSamw * 359da6c28aaSamw * Returns true (1) if the name is valid. Otherwise returns false (0). 360da6c28aaSamw */ 361da6c28aaSamw static int 362da6c28aaSamw domainname_is_valid(char *domain_name) 363da6c28aaSamw { 364da6c28aaSamw char *name; 365da6c28aaSamw int first_char = 1; 366da6c28aaSamw 367da6c28aaSamw if (domain_name == 0) 368da6c28aaSamw return (0); 369da6c28aaSamw 370da6c28aaSamw for (name = domain_name; *name != 0; ++name) { 371da6c28aaSamw if (*name == '.') { 372da6c28aaSamw first_char = 1; 373da6c28aaSamw continue; 374da6c28aaSamw } 375da6c28aaSamw 376da6c28aaSamw if (first_char) { 377*bbf6f00cSJordan Brown if (smb_isalpha_ascii(*name) == 0) 378da6c28aaSamw return (0); 379da6c28aaSamw 380da6c28aaSamw first_char = 0; 381da6c28aaSamw continue; 382da6c28aaSamw } 383da6c28aaSamw 384*bbf6f00cSJordan Brown if (smb_isalnum_ascii(*name) || dns_is_allowed(*name)) 385da6c28aaSamw continue; 386da6c28aaSamw 387da6c28aaSamw return (0); 388da6c28aaSamw } 389da6c28aaSamw 390da6c28aaSamw return (1); 391da6c28aaSamw } 392