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