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 2010 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 * IPC connection information that may be passed to the SMB Redirector. 52 */ 53 typedef struct { 54 char user[SMB_USERNAME_MAXLEN]; 55 uint8_t passwd[SMBAUTH_HASH_SZ]; 56 } smb_ipc_t; 57 58 static smb_ipc_t ipc_info; 59 static smb_ipc_t ipc_orig_info; 60 static rwlock_t smb_ipc_lock; 61 62 /* 63 * Some older clients (Windows 98) only handle the low byte 64 * of the max workers value. If the low byte is less than 65 * SMB_PI_MAX_WORKERS_MIN set it to SMB_PI_MAX_WORKERS_MIN. 66 */ 67 void 68 smb_load_kconfig(smb_kmod_cfg_t *kcfg) 69 { 70 int64_t citem; 71 72 bzero(kcfg, sizeof (smb_kmod_cfg_t)); 73 74 (void) smb_config_getnum(SMB_CI_MAX_WORKERS, &citem); 75 kcfg->skc_maxworkers = (uint32_t)citem; 76 if ((kcfg->skc_maxworkers & 0xFF) < SMB_PI_MAX_WORKERS_MIN) { 77 kcfg->skc_maxworkers &= ~0xFF; 78 kcfg->skc_maxworkers += SMB_PI_MAX_WORKERS_MIN; 79 } 80 81 (void) smb_config_getnum(SMB_CI_KEEPALIVE, &citem); 82 kcfg->skc_keepalive = (uint32_t)citem; 83 if ((kcfg->skc_keepalive != 0) && 84 (kcfg->skc_keepalive < SMB_PI_KEEP_ALIVE_MIN)) 85 kcfg->skc_keepalive = SMB_PI_KEEP_ALIVE_MIN; 86 87 (void) smb_config_getnum(SMB_CI_MAX_CONNECTIONS, &citem); 88 kcfg->skc_maxconnections = (uint32_t)citem; 89 kcfg->skc_restrict_anon = smb_config_getbool(SMB_CI_RESTRICT_ANON); 90 kcfg->skc_signing_enable = smb_config_getbool(SMB_CI_SIGNING_ENABLE); 91 kcfg->skc_signing_required = smb_config_getbool(SMB_CI_SIGNING_REQD); 92 kcfg->skc_ipv6_enable = smb_config_getbool(SMB_CI_IPV6_ENABLE); 93 kcfg->skc_oplock_enable = smb_config_getbool(SMB_CI_OPLOCK_ENABLE); 94 kcfg->skc_sync_enable = smb_config_getbool(SMB_CI_SYNC_ENABLE); 95 kcfg->skc_secmode = smb_config_get_secmode(); 96 (void) smb_getdomainname(kcfg->skc_nbdomain, 97 sizeof (kcfg->skc_nbdomain)); 98 (void) smb_getfqdomainname(kcfg->skc_fqdn, 99 sizeof (kcfg->skc_fqdn)); 100 (void) smb_getnetbiosname(kcfg->skc_hostname, 101 sizeof (kcfg->skc_hostname)); 102 (void) smb_config_getstr(SMB_CI_SYS_CMNT, kcfg->skc_system_comment, 103 sizeof (kcfg->skc_system_comment)); 104 smb_config_get_version(&kcfg->skc_version); 105 } 106 107 /* 108 * Get the current system NetBIOS name. The hostname is truncated at 109 * the first `.` or 15 bytes, whichever occurs first, and converted 110 * to uppercase (by smb_gethostname). Text that appears after the 111 * first '.' is considered to be part of the NetBIOS scope. 112 * 113 * Returns 0 on success, otherwise -1 to indicate an error. 114 */ 115 int 116 smb_getnetbiosname(char *buf, size_t buflen) 117 { 118 if (smb_gethostname(buf, buflen, SMB_CASE_UPPER) != 0) 119 return (-1); 120 121 if (buflen >= NETBIOS_NAME_SZ) 122 buf[NETBIOS_NAME_SZ - 1] = '\0'; 123 124 return (0); 125 } 126 127 /* 128 * Get the SAM account of the current system. 129 * Returns 0 on success, otherwise, -1 to indicate an error. 130 */ 131 int 132 smb_getsamaccount(char *buf, size_t buflen) 133 { 134 if (smb_getnetbiosname(buf, buflen - 1) != 0) 135 return (-1); 136 137 (void) strlcat(buf, "$", buflen); 138 return (0); 139 } 140 141 /* 142 * Get the current system node name. The returned name is guaranteed 143 * to be null-terminated (gethostname may not null terminate the name). 144 * If the hostname has been fully-qualified for some reason, the domain 145 * part will be removed. The returned hostname is converted to the 146 * specified case (lower, upper, or preserved). 147 * 148 * If gethostname fails, the returned buffer will contain an empty 149 * string. 150 */ 151 int 152 smb_gethostname(char *buf, size_t buflen, smb_caseconv_t which) 153 { 154 char *p; 155 156 if (buf == NULL || buflen == 0) 157 return (-1); 158 159 if (gethostname(buf, buflen) != 0) { 160 *buf = '\0'; 161 return (-1); 162 } 163 164 buf[buflen - 1] = '\0'; 165 166 if ((p = strchr(buf, '.')) != NULL) 167 *p = '\0'; 168 169 switch (which) { 170 case SMB_CASE_LOWER: 171 (void) smb_strlwr(buf); 172 break; 173 174 case SMB_CASE_UPPER: 175 (void) smb_strupr(buf); 176 break; 177 178 case SMB_CASE_PRESERVE: 179 default: 180 break; 181 } 182 183 return (0); 184 } 185 186 /* 187 * Obtain the fully-qualified name for this machine in lower case. If 188 * the hostname is fully-qualified, accept it. Otherwise, try to find an 189 * appropriate domain name to append to the hostname. 190 */ 191 int 192 smb_getfqhostname(char *buf, size_t buflen) 193 { 194 char hostname[MAXHOSTNAMELEN]; 195 char domain[MAXHOSTNAMELEN]; 196 197 hostname[0] = '\0'; 198 domain[0] = '\0'; 199 200 if (smb_gethostname(hostname, MAXHOSTNAMELEN, 201 SMB_CASE_LOWER) != 0) 202 return (-1); 203 204 if (smb_getfqdomainname(domain, MAXHOSTNAMELEN) != 0) 205 return (-1); 206 207 if (hostname[0] == '\0') 208 return (-1); 209 210 if (domain[0] == '\0') { 211 (void) strlcpy(buf, hostname, buflen); 212 return (0); 213 } 214 215 (void) snprintf(buf, buflen, "%s.%s", hostname, domain); 216 return (0); 217 } 218 219 /* 220 * smb_getdomainname 221 * 222 * Returns NETBIOS name of the domain if the system is in domain 223 * mode. Or returns workgroup name if the system is in workgroup 224 * mode. 225 */ 226 int 227 smb_getdomainname(char *buf, size_t buflen) 228 { 229 int rc; 230 231 if (buf == NULL || buflen == 0) 232 return (-1); 233 234 *buf = '\0'; 235 rc = smb_config_getstr(SMB_CI_DOMAIN_NAME, buf, buflen); 236 237 if ((rc != SMBD_SMF_OK) || (*buf == '\0')) 238 return (-1); 239 240 return (0); 241 } 242 243 /* 244 * smb_getfqdomainname 245 * 246 * In the system is in domain mode, the dns_domain property value 247 * is returned. Otherwise, it returns the local domain obtained via 248 * resolver. 249 * 250 * Returns 0 upon success. Otherwise, returns -1. 251 */ 252 int 253 smb_getfqdomainname(char *buf, size_t buflen) 254 { 255 struct __res_state res_state; 256 int rc; 257 258 if (buf == NULL || buflen == 0) 259 return (-1); 260 261 *buf = '\0'; 262 if (smb_config_get_secmode() == SMB_SECMODE_DOMAIN) { 263 rc = smb_config_getstr(SMB_CI_DOMAIN_FQDN, buf, buflen); 264 265 if ((rc != SMBD_SMF_OK) || (*buf == '\0')) 266 return (-1); 267 } else { 268 bzero(&res_state, sizeof (struct __res_state)); 269 if (res_ninit(&res_state)) 270 return (-1); 271 272 if (*res_state.defdname == '\0') { 273 res_ndestroy(&res_state); 274 return (-1); 275 } 276 277 (void) strlcpy(buf, res_state.defdname, buflen); 278 res_ndestroy(&res_state); 279 rc = 0; 280 } 281 282 return (rc); 283 } 284 285 286 /* 287 * smb_set_machine_passwd 288 * 289 * This function should be used when setting the machine password property. 290 * The associated sequence number is incremented. 291 */ 292 static int 293 smb_set_machine_passwd(char *passwd) 294 { 295 int64_t num; 296 int rc = -1; 297 298 if (smb_config_set(SMB_CI_MACHINE_PASSWD, passwd) != SMBD_SMF_OK) 299 return (-1); 300 301 (void) mutex_lock(&seqnum_mtx); 302 (void) smb_config_getnum(SMB_CI_KPASSWD_SEQNUM, &num); 303 if (smb_config_setnum(SMB_CI_KPASSWD_SEQNUM, ++num) 304 == SMBD_SMF_OK) 305 rc = 0; 306 (void) mutex_unlock(&seqnum_mtx); 307 return (rc); 308 } 309 310 static int 311 smb_get_machine_passwd(uint8_t *buf, size_t buflen) 312 { 313 char pwd[SMB_PASSWD_MAXLEN + 1]; 314 int rc; 315 316 if (buflen < SMBAUTH_HASH_SZ) 317 return (-1); 318 319 rc = smb_config_getstr(SMB_CI_MACHINE_PASSWD, pwd, sizeof (pwd)); 320 if ((rc != SMBD_SMF_OK) || *pwd == '\0') 321 return (-1); 322 323 if (smb_auth_ntlm_hash(pwd, buf) != 0) 324 return (-1); 325 326 return (rc); 327 } 328 329 /* 330 * Set up IPC connection credentials. 331 */ 332 void 333 smb_ipc_init(void) 334 { 335 int rc; 336 337 (void) rw_wrlock(&smb_ipc_lock); 338 bzero(&ipc_info, sizeof (smb_ipc_t)); 339 bzero(&ipc_orig_info, sizeof (smb_ipc_t)); 340 341 (void) smb_getsamaccount(ipc_info.user, SMB_USERNAME_MAXLEN); 342 rc = smb_get_machine_passwd(ipc_info.passwd, SMBAUTH_HASH_SZ); 343 if (rc != 0) 344 *ipc_info.passwd = 0; 345 (void) rw_unlock(&smb_ipc_lock); 346 347 } 348 349 /* 350 * Set the IPC username and password hash in memory. If the domain 351 * join succeeds, the credentials will be committed for use with 352 * authenticated IPC. Otherwise, they should be rolled back. 353 */ 354 void 355 smb_ipc_set(char *plain_user, uint8_t *passwd_hash) 356 { 357 (void) rw_wrlock(&smb_ipc_lock); 358 (void) strlcpy(ipc_info.user, plain_user, sizeof (ipc_info.user)); 359 (void) memcpy(ipc_info.passwd, passwd_hash, SMBAUTH_HASH_SZ); 360 (void) rw_unlock(&smb_ipc_lock); 361 362 } 363 364 /* 365 * Save the host credentials to be used for authenticated IPC. 366 * The credentials are also saved to the original IPC info as 367 * rollback data in case the join domain process fails later. 368 */ 369 void 370 smb_ipc_commit(void) 371 { 372 (void) rw_wrlock(&smb_ipc_lock); 373 (void) smb_getsamaccount(ipc_info.user, SMB_USERNAME_MAXLEN); 374 (void) smb_get_machine_passwd(ipc_info.passwd, SMBAUTH_HASH_SZ); 375 (void) memcpy(&ipc_orig_info, &ipc_info, sizeof (smb_ipc_t)); 376 (void) rw_unlock(&smb_ipc_lock); 377 } 378 379 /* 380 * Restore the original credentials 381 */ 382 void 383 smb_ipc_rollback(void) 384 { 385 (void) rw_wrlock(&smb_ipc_lock); 386 (void) strlcpy(ipc_info.user, ipc_orig_info.user, 387 sizeof (ipc_info.user)); 388 (void) memcpy(ipc_info.passwd, ipc_orig_info.passwd, 389 sizeof (ipc_info.passwd)); 390 (void) rw_unlock(&smb_ipc_lock); 391 } 392 393 void 394 smb_ipc_get_user(char *buf, size_t buflen) 395 { 396 (void) rw_rdlock(&smb_ipc_lock); 397 (void) strlcpy(buf, ipc_info.user, buflen); 398 (void) rw_unlock(&smb_ipc_lock); 399 } 400 401 void 402 smb_ipc_get_passwd(uint8_t *buf, size_t buflen) 403 { 404 if (buflen < SMBAUTH_HASH_SZ) 405 return; 406 407 (void) rw_rdlock(&smb_ipc_lock); 408 (void) memcpy(buf, ipc_info.passwd, SMBAUTH_HASH_SZ); 409 (void) rw_unlock(&smb_ipc_lock); 410 } 411 412 /* 413 * smb_match_netlogon_seqnum 414 * 415 * A sequence number is associated with each machine password property 416 * update and the netlogon credential chain setup. If the 417 * sequence numbers don't match, a NETLOGON credential chain 418 * establishment is required. 419 * 420 * Returns 0 if kpasswd_seqnum equals to netlogon_seqnum. Otherwise, 421 * returns -1. 422 */ 423 boolean_t 424 smb_match_netlogon_seqnum(void) 425 { 426 int64_t setpasswd_seqnum; 427 int64_t netlogon_seqnum; 428 429 (void) mutex_lock(&seqnum_mtx); 430 (void) smb_config_getnum(SMB_CI_KPASSWD_SEQNUM, &setpasswd_seqnum); 431 (void) smb_config_getnum(SMB_CI_NETLOGON_SEQNUM, &netlogon_seqnum); 432 (void) mutex_unlock(&seqnum_mtx); 433 return (setpasswd_seqnum == netlogon_seqnum); 434 } 435 436 /* 437 * smb_setdomainprops 438 * 439 * This function should be called after joining an AD to 440 * set all the domain related SMF properties. 441 * 442 * The kpasswd_domain property is the AD domain to which the system 443 * is joined via kclient. If this function is invoked by the SMB 444 * daemon, fqdn should be set to NULL. 445 */ 446 int 447 smb_setdomainprops(char *fqdn, char *server, char *passwd) 448 { 449 if (server == NULL || passwd == NULL) 450 return (-1); 451 452 if ((*server == '\0') || (*passwd == '\0')) 453 return (-1); 454 455 if (fqdn && (smb_config_set(SMB_CI_KPASSWD_DOMAIN, fqdn) != 0)) 456 return (-1); 457 458 if (smb_config_set(SMB_CI_KPASSWD_SRV, server) != 0) 459 return (-1); 460 461 if (smb_set_machine_passwd(passwd) != 0) { 462 syslog(LOG_ERR, "smb_setdomainprops: failed to set" 463 " machine account password"); 464 return (-1); 465 } 466 467 /* 468 * If we successfully create a trust account, we mark 469 * ourselves as a domain member in the environment so 470 * that we use the SAMLOGON version of the NETLOGON 471 * PDC location protocol. 472 */ 473 (void) smb_config_setbool(SMB_CI_DOMAIN_MEMB, B_TRUE); 474 475 return (0); 476 } 477 478 /* 479 * smb_update_netlogon_seqnum 480 * 481 * This function should only be called upon a successful netlogon 482 * credential chain establishment to set the sequence number of the 483 * netlogon to match with that of the kpasswd. 484 */ 485 void 486 smb_update_netlogon_seqnum(void) 487 { 488 int64_t num; 489 490 (void) mutex_lock(&seqnum_mtx); 491 (void) smb_config_getnum(SMB_CI_KPASSWD_SEQNUM, &num); 492 (void) smb_config_setnum(SMB_CI_NETLOGON_SEQNUM, num); 493 (void) mutex_unlock(&seqnum_mtx); 494 } 495 496 497 /* 498 * Temporary fbt for dtrace until user space sdt enabled. 499 */ 500 void 501 smb_tracef(const char *fmt, ...) 502 { 503 va_list ap; 504 char buf[128]; 505 506 va_start(ap, fmt); 507 (void) vsnprintf(buf, 128, fmt, ap); 508 va_end(ap); 509 510 smb_trace(buf); 511 } 512 513 /* 514 * Temporary fbt for dtrace until user space sdt enabled. 515 */ 516 void 517 smb_trace(const char *s) 518 { 519 syslog(LOG_DEBUG, "%s", s); 520 } 521 522 /* 523 * smb_tonetbiosname 524 * 525 * Creates a NetBIOS name based on the given name and suffix. 526 * NetBIOS name is 15 capital characters, padded with space if needed 527 * and the 16th byte is the suffix. 528 */ 529 void 530 smb_tonetbiosname(char *name, char *nb_name, char suffix) 531 { 532 char tmp_name[NETBIOS_NAME_SZ]; 533 smb_wchar_t wtmp_name[NETBIOS_NAME_SZ]; 534 int len; 535 size_t rc; 536 537 len = 0; 538 rc = smb_mbstowcs(wtmp_name, (const char *)name, NETBIOS_NAME_SZ); 539 540 if (rc != (size_t)-1) { 541 wtmp_name[NETBIOS_NAME_SZ - 1] = 0; 542 rc = ucstooem(tmp_name, wtmp_name, NETBIOS_NAME_SZ, 543 OEM_CPG_850); 544 if (rc > 0) 545 len = strlen(tmp_name); 546 } 547 548 (void) memset(nb_name, ' ', NETBIOS_NAME_SZ - 1); 549 if (len) { 550 (void) smb_strupr(tmp_name); 551 (void) memcpy(nb_name, tmp_name, len); 552 } 553 nb_name[NETBIOS_NAME_SZ - 1] = suffix; 554 } 555 556 int 557 smb_get_nameservers(smb_inaddr_t *ips, int sz) 558 { 559 union res_sockaddr_union set[MAXNS]; 560 int i, cnt; 561 struct __res_state res_state; 562 char ipstr[INET6_ADDRSTRLEN]; 563 564 if (ips == NULL) 565 return (0); 566 567 bzero(&res_state, sizeof (struct __res_state)); 568 if (res_ninit(&res_state) < 0) 569 return (0); 570 571 cnt = res_getservers(&res_state, set, MAXNS); 572 for (i = 0; i < cnt; i++) { 573 if (i >= sz) 574 break; 575 ips[i].a_family = AF_INET; 576 bcopy(&set[i].sin.sin_addr, &ips[i].a_ipv4, INADDRSZ); 577 if (inet_ntop(AF_INET, &ips[i].a_ipv4, ipstr, 578 INET_ADDRSTRLEN)) { 579 syslog(LOG_DEBUG, "Found %s name server\n", ipstr); 580 continue; 581 } 582 ips[i].a_family = AF_INET6; 583 bcopy(&set[i].sin.sin_addr, &ips[i].a_ipv6, IPV6_ADDR_LEN); 584 if (inet_ntop(AF_INET6, &ips[i].a_ipv6, ipstr, 585 INET6_ADDRSTRLEN)) { 586 syslog(LOG_DEBUG, "Found %s name server\n", ipstr); 587 } 588 } 589 res_ndestroy(&res_state); 590 return (i); 591 } 592 593 /* 594 * smb_gethostbyname 595 * 596 * Looks up a host by the given name. The host entry can come 597 * from any of the sources for hosts specified in the 598 * /etc/nsswitch.conf and the NetBIOS cache. 599 * 600 * XXX Invokes nbt_name_resolve API once the NBTD is integrated 601 * to look in the NetBIOS cache if getipnodebyname fails. 602 * 603 * Caller should invoke freehostent to free the returned hostent. 604 */ 605 struct hostent * 606 smb_gethostbyname(const char *name, int *err_num) 607 { 608 struct hostent *h; 609 610 h = getipnodebyname(name, AF_INET, 0, err_num); 611 if ((h == NULL) || h->h_length != INADDRSZ) 612 h = getipnodebyname(name, AF_INET6, AI_DEFAULT, err_num); 613 return (h); 614 } 615 616 /* 617 * smb_gethostbyaddr 618 * 619 * Looks up a host by the given IP address. The host entry can come 620 * from any of the sources for hosts specified in the 621 * /etc/nsswitch.conf and the NetBIOS cache. 622 * 623 * XXX Invokes nbt API to resolve name by IP once the NBTD is integrated 624 * to look in the NetBIOS cache if getipnodebyaddr fails. 625 * 626 * Caller should invoke freehostent to free the returned hostent. 627 */ 628 struct hostent * 629 smb_gethostbyaddr(const char *addr, int len, int type, int *err_num) 630 { 631 struct hostent *h; 632 633 h = getipnodebyaddr(addr, len, type, err_num); 634 635 return (h); 636 } 637