Home | History | Annotate | Download | only in common
      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