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 26 #include <assert.h> 27 #include <sys/types.h> 28 #include <stdarg.h> 29 #include <unistd.h> 30 #include <stdlib.h> 31 #include <time.h> 32 #include <synch.h> 33 #include <syslog.h> 34 #include <string.h> 35 #include <strings.h> 36 #include <errno.h> 37 #include <net/if.h> 38 #include <netdb.h> 39 #include <netinet/in.h> 40 #include <arpa/nameser.h> 41 #include <resolv.h> 42 #include <sys/sockio.h> 43 #include <sys/socket.h> 44 #include <smbsrv/smbinfo.h> 45 #include <smbsrv/netbios.h> 46 #include <smbsrv/libsmb.h> 47 48 static mutex_t seqnum_mtx; 49 50 /* 51 * Some older clients (Windows 98) only handle the low byte 52 * of the max workers value. If the low byte is less than 53 * SMB_PI_MAX_WORKERS_MIN set it to SMB_PI_MAX_WORKERS_MIN. 54 */ 55 void 56 smb_load_kconfig(smb_kmod_cfg_t *kcfg) 57 { 58 int64_t citem; 59 60 bzero(kcfg, sizeof (smb_kmod_cfg_t)); 61 62 (void) smb_config_getnum(SMB_CI_MAX_WORKERS, &citem); 63 kcfg->skc_maxworkers = (uint32_t)citem; 64 if ((kcfg->skc_maxworkers & 0xFF) < SMB_PI_MAX_WORKERS_MIN) { 65 kcfg->skc_maxworkers &= ~0xFF; 66 kcfg->skc_maxworkers += SMB_PI_MAX_WORKERS_MIN; 67 } 68 69 (void) smb_config_getnum(SMB_CI_KEEPALIVE, &citem); 70 kcfg->skc_keepalive = (uint32_t)citem; 71 if ((kcfg->skc_keepalive != 0) && 72 (kcfg->skc_keepalive < SMB_PI_KEEP_ALIVE_MIN)) 73 kcfg->skc_keepalive = SMB_PI_KEEP_ALIVE_MIN; 74 75 (void) smb_config_getnum(SMB_CI_MAX_CONNECTIONS, &citem); 76 kcfg->skc_maxconnections = (uint32_t)citem; 77 kcfg->skc_restrict_anon = smb_config_getbool(SMB_CI_RESTRICT_ANON); 78 kcfg->skc_signing_enable = smb_config_getbool(SMB_CI_SIGNING_ENABLE); 79 kcfg->skc_signing_required = smb_config_getbool(SMB_CI_SIGNING_REQD); 80 kcfg->skc_ipv6_enable = smb_config_getbool(SMB_CI_IPV6_ENABLE); 81 kcfg->skc_oplock_enable = smb_config_getbool(SMB_CI_OPLOCK_ENABLE); 82 kcfg->skc_sync_enable = smb_config_getbool(SMB_CI_SYNC_ENABLE); 83 kcfg->skc_secmode = smb_config_get_secmode(); 84 (void) smb_getdomainname(kcfg->skc_nbdomain, 85 sizeof (kcfg->skc_nbdomain)); 86 (void) smb_getfqdomainname(kcfg->skc_fqdn, 87 sizeof (kcfg->skc_fqdn)); 88 (void) smb_getnetbiosname(kcfg->skc_hostname, 89 sizeof (kcfg->skc_hostname)); 90 (void) smb_config_getstr(SMB_CI_SYS_CMNT, kcfg->skc_system_comment, 91 sizeof (kcfg->skc_system_comment)); 92 } 93 94 /* 95 * Get the current system NetBIOS name. The hostname is truncated at 96 * the first `.` or 15 bytes, whichever occurs first, and converted 97 * to uppercase (by smb_gethostname). Text that appears after the 98 * first '.' is considered to be part of the NetBIOS scope. 99 * 100 * Returns 0 on success, otherwise -1 to indicate an error. 101 */ 102 int 103 smb_getnetbiosname(char *buf, size_t buflen) 104 { 105 if (smb_gethostname(buf, buflen, 1) != 0) 106 return (-1); 107 108 if (buflen >= NETBIOS_NAME_SZ) 109 buf[NETBIOS_NAME_SZ - 1] = '\0'; 110 111 return (0); 112 } 113 114 /* 115 * Get the SAM account of the current system. 116 * Returns 0 on success, otherwise, -1 to indicate an error. 117 */ 118 int 119 smb_getsamaccount(char *buf, size_t buflen) 120 { 121 if (smb_getnetbiosname(buf, buflen - 1) != 0) 122 return (-1); 123 124 (void) strlcat(buf, "$", buflen); 125 return (0); 126 } 127 128 /* 129 * Get the current system node name. The returned name is guaranteed 130 * to be null-terminated (gethostname may not null terminate the name). 131 * If the hostname has been fully-qualified for some reason, the domain 132 * part will be removed. If the caller would like the name in upper 133 * case, it is folded to uppercase. 134 * 135 * If gethostname fails, the returned buffer will contain an empty 136 * string. 137 */ 138 int 139 smb_gethostname(char *buf, size_t buflen, int upcase) 140 { 141 char *p; 142 143 if (buf == NULL || buflen == 0) 144 return (-1); 145 146 if (gethostname(buf, buflen) != 0) { 147 *buf = '\0'; 148 return (-1); 149 } 150 151 buf[buflen - 1] = '\0'; 152 153 if ((p = strchr(buf, '.')) != NULL) 154 *p = '\0'; 155 156 if (upcase) 157 (void) utf8_strupr(buf); 158 159 return (0); 160 } 161 162 /* 163 * Obtain the fully-qualified name for this machine. If the 164 * hostname is fully-qualified, accept it. Otherwise, try to 165 * find an appropriate domain name to append to the hostname. 166 */ 167 int 168 smb_getfqhostname(char *buf, size_t buflen) 169 { 170 char hostname[MAXHOSTNAMELEN]; 171 char domain[MAXHOSTNAMELEN]; 172 173 hostname[0] = '\0'; 174 domain[0] = '\0'; 175 176 if (smb_gethostname(hostname, MAXHOSTNAMELEN, 0) != 0) 177 return (-1); 178 179 if (smb_getfqdomainname(domain, MAXHOSTNAMELEN) != 0) 180 return (-1); 181 182 if (hostname[0] == '\0') 183 return (-1); 184 185 if (domain[0] == '\0') { 186 (void) strlcpy(buf, hostname, buflen); 187 return (0); 188 } 189 190 (void) snprintf(buf, buflen, "%s.%s", hostname, domain); 191 return (0); 192 } 193 194 /* 195 * smb_getdomainname 196 * 197 * Returns NETBIOS name of the domain if the system is in domain 198 * mode. Or returns workgroup name if the system is in workgroup 199 * mode. 200 */ 201 int 202 smb_getdomainname(char *buf, size_t buflen) 203 { 204 int rc; 205 206 if (buf == NULL || buflen == 0) 207 return (-1); 208 209 *buf = '\0'; 210 rc = smb_config_getstr(SMB_CI_DOMAIN_NAME, buf, buflen); 211 212 if ((rc != SMBD_SMF_OK) || (*buf == '\0')) 213 return (-1); 214 215 return (0); 216 } 217 218 /* 219 * smb_getfqdomainname 220 * 221 * In the system is in domain mode, the dns_domain property value 222 * is returned. Otherwise, it returns the local domain obtained via 223 * resolver. 224 * 225 * Returns 0 upon success. Otherwise, returns -1. 226 */ 227 int 228 smb_getfqdomainname(char *buf, size_t buflen) 229 { 230 struct __res_state res_state; 231 int rc; 232 233 if (buf == NULL || buflen == 0) 234 return (-1); 235 236 *buf = '\0'; 237 if (smb_config_get_secmode() == SMB_SECMODE_DOMAIN) { 238 rc = smb_config_getstr(SMB_CI_DOMAIN_FQDN, buf, buflen); 239 240 if ((rc != SMBD_SMF_OK) || (*buf == '\0')) 241 return (-1); 242 } else { 243 bzero(&res_state, sizeof (struct __res_state)); 244 if (res_ninit(&res_state)) 245 return (-1); 246 247 if (*res_state.defdname == '\0') { 248 res_ndestroy(&res_state); 249 return (-1); 250 } 251 252 (void) strlcpy(buf, res_state.defdname, buflen); 253 res_ndestroy(&res_state); 254 rc = 0; 255 } 256 257 return (rc); 258 } 259 260 261 /* 262 * smb_set_machine_passwd 263 * 264 * This function should be used when setting the machine password property. 265 * The associated sequence number is incremented. 266 */ 267 static int 268 smb_set_machine_passwd(char *passwd) 269 { 270 int64_t num; 271 int rc = -1; 272 273 if (smb_config_set(SMB_CI_MACHINE_PASSWD, passwd) != SMBD_SMF_OK) 274 return (-1); 275 276 (void) mutex_lock(&seqnum_mtx); 277 (void) smb_config_getnum(SMB_CI_KPASSWD_SEQNUM, &num); 278 if (smb_config_setnum(SMB_CI_KPASSWD_SEQNUM, ++num) 279 == SMBD_SMF_OK) 280 rc = 0; 281 (void) mutex_unlock(&seqnum_mtx); 282 return (rc); 283 } 284 285 /* 286 * smb_match_netlogon_seqnum 287 * 288 * A sequence number is associated with each machine password property 289 * update and the netlogon credential chain setup. If the 290 * sequence numbers don't match, a NETLOGON credential chain 291 * establishment is required. 292 * 293 * Returns 0 if kpasswd_seqnum equals to netlogon_seqnum. Otherwise, 294 * returns -1. 295 */ 296 boolean_t 297 smb_match_netlogon_seqnum(void) 298 { 299 int64_t setpasswd_seqnum; 300 int64_t netlogon_seqnum; 301 302 (void) mutex_lock(&seqnum_mtx); 303 (void) smb_config_getnum(SMB_CI_KPASSWD_SEQNUM, &setpasswd_seqnum); 304 (void) smb_config_getnum(SMB_CI_NETLOGON_SEQNUM, &netlogon_seqnum); 305 (void) mutex_unlock(&seqnum_mtx); 306 return (setpasswd_seqnum == netlogon_seqnum); 307 } 308 309 /* 310 * smb_setdomainprops 311 * 312 * This function should be called after joining an AD to 313 * set all the domain related SMF properties. 314 * 315 * The kpasswd_domain property is the AD domain to which the system 316 * is joined via kclient. If this function is invoked by the SMB 317 * daemon, fqdn should be set to NULL. 318 */ 319 int 320 smb_setdomainprops(char *fqdn, char *server, char *passwd) 321 { 322 if (server == NULL || passwd == NULL) 323 return (-1); 324 325 if ((*server == '\0') || (*passwd == '\0')) 326 return (-1); 327 328 if (fqdn && (smb_config_set(SMB_CI_KPASSWD_DOMAIN, fqdn) != 0)) 329 return (-1); 330 331 if (smb_config_set(SMB_CI_KPASSWD_SRV, server) != 0) 332 return (-1); 333 334 if (smb_set_machine_passwd(passwd) != 0) { 335 syslog(LOG_ERR, "smb_setdomainprops: failed to set" 336 " machine account password"); 337 return (-1); 338 } 339 340 /* 341 * If we successfully create a trust account, we mark 342 * ourselves as a domain member in the environment so 343 * that we use the SAMLOGON version of the NETLOGON 344 * PDC location protocol. 345 */ 346 (void) smb_config_setbool(SMB_CI_DOMAIN_MEMB, B_TRUE); 347 348 return (0); 349 } 350 351 /* 352 * smb_update_netlogon_seqnum 353 * 354 * This function should only be called upon a successful netlogon 355 * credential chain establishment to set the sequence number of the 356 * netlogon to match with that of the kpasswd. 357 */ 358 void 359 smb_update_netlogon_seqnum(void) 360 { 361 int64_t num; 362 363 (void) mutex_lock(&seqnum_mtx); 364 (void) smb_config_getnum(SMB_CI_KPASSWD_SEQNUM, &num); 365 (void) smb_config_setnum(SMB_CI_NETLOGON_SEQNUM, num); 366 (void) mutex_unlock(&seqnum_mtx); 367 } 368 369 370 /* 371 * Temporary fbt for dtrace until user space sdt enabled. 372 */ 373 void 374 smb_tracef(const char *fmt, ...) 375 { 376 va_list ap; 377 char buf[128]; 378 379 va_start(ap, fmt); 380 (void) vsnprintf(buf, 128, fmt, ap); 381 va_end(ap); 382 383 smb_trace(buf); 384 } 385 386 /* 387 * Temporary fbt for dtrace until user space sdt enabled. 388 */ 389 void 390 smb_trace(const char *s) 391 { 392 syslog(LOG_DEBUG, "%s", s); 393 } 394 395 /* 396 * smb_tonetbiosname 397 * 398 * Creates a NetBIOS name based on the given name and suffix. 399 * NetBIOS name is 15 capital characters, padded with space if needed 400 * and the 16th byte is the suffix. 401 */ 402 void 403 smb_tonetbiosname(char *name, char *nb_name, char suffix) 404 { 405 char tmp_name[NETBIOS_NAME_SZ]; 406 mts_wchar_t wtmp_name[NETBIOS_NAME_SZ]; 407 unsigned int cpid; 408 int len; 409 size_t rc; 410 411 len = 0; 412 rc = mts_mbstowcs(wtmp_name, (const char *)name, NETBIOS_NAME_SZ); 413 414 if (rc != (size_t)-1) { 415 wtmp_name[NETBIOS_NAME_SZ - 1] = 0; 416 cpid = oem_get_smb_cpid(); 417 rc = unicodestooems(tmp_name, wtmp_name, NETBIOS_NAME_SZ, cpid); 418 if (rc > 0) 419 len = strlen(tmp_name); 420 } 421 422 (void) memset(nb_name, ' ', NETBIOS_NAME_SZ - 1); 423 if (len) { 424 (void) utf8_strupr(tmp_name); 425 (void) memcpy(nb_name, tmp_name, len); 426 } 427 nb_name[NETBIOS_NAME_SZ - 1] = suffix; 428 } 429 430 int 431 smb_get_nameservers(smb_inaddr_t *ips, int sz) 432 { 433 union res_sockaddr_union set[MAXNS]; 434 int i, cnt; 435 struct __res_state res_state; 436 char ipstr[INET6_ADDRSTRLEN]; 437 438 if (ips == NULL) 439 return (0); 440 441 bzero(&res_state, sizeof (struct __res_state)); 442 if (res_ninit(&res_state) < 0) 443 return (0); 444 445 cnt = res_getservers(&res_state, set, MAXNS); 446 for (i = 0; i < cnt; i++) { 447 if (i >= sz) 448 break; 449 ips[i].a_family = AF_INET; 450 bcopy(&set[i].sin.sin_addr, &ips[i].a_ipv4, INADDRSZ); 451 if (inet_ntop(AF_INET, &ips[i].a_ipv4, ipstr, 452 INET_ADDRSTRLEN)) { 453 syslog(LOG_DEBUG, "Found %s name server\n", ipstr); 454 continue; 455 } 456 ips[i].a_family = AF_INET6; 457 bcopy(&set[i].sin.sin_addr, &ips[i].a_ipv6, IPV6_ADDR_LEN); 458 if (inet_ntop(AF_INET6, &ips[i].a_ipv6, ipstr, 459 INET6_ADDRSTRLEN)) { 460 syslog(LOG_DEBUG, "Found %s name server\n", ipstr); 461 } 462 } 463 res_ndestroy(&res_state); 464 return (i); 465 } 466 467 /* 468 * smb_gethostbyname 469 * 470 * Looks up a host by the given name. The host entry can come 471 * from any of the sources for hosts specified in the 472 * /etc/nsswitch.conf and the NetBIOS cache. 473 * 474 * XXX Invokes nbt_name_resolve API once the NBTD is integrated 475 * to look in the NetBIOS cache if getipnodebyname fails. 476 * 477 * Caller should invoke freehostent to free the returned hostent. 478 */ 479 struct hostent * 480 smb_gethostbyname(const char *name, int *err_num) 481 { 482 struct hostent *h; 483 484 h = getipnodebyname(name, AF_INET, 0, err_num); 485 if ((h == NULL) || h->h_length != INADDRSZ) 486 h = getipnodebyname(name, AF_INET6, AI_DEFAULT, err_num); 487 return (h); 488 } 489 490 /* 491 * smb_gethostbyaddr 492 * 493 * Looks up a host by the given IP address. The host entry can come 494 * from any of the sources for hosts specified in the 495 * /etc/nsswitch.conf and the NetBIOS cache. 496 * 497 * XXX Invokes nbt API to resolve name by IP once the NBTD is integrated 498 * to look in the NetBIOS cache if getipnodebyaddr fails. 499 * 500 * Caller should invoke freehostent to free the returned hostent. 501 */ 502 struct hostent * 503 smb_gethostbyaddr(const char *addr, int len, int type, int *err_num) 504 { 505 struct hostent *h; 506 507 h = getipnodebyaddr(addr, len, type, err_num); 508 509 return (h); 510 } 511