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