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