smb_info.c revision 11963:061945695ce1
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
48static mutex_t seqnum_mtx;
49
50/*
51 * IPC connection information that may be passed to the SMB Redirector.
52 */
53typedef struct {
54	char	user[SMB_USERNAME_MAXLEN];
55	uint8_t	passwd[SMBAUTH_HASH_SZ];
56} smb_ipc_t;
57
58static smb_ipc_t	ipc_info;
59static smb_ipc_t	ipc_orig_info;
60static 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 */
67void
68smb_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 */
115int
116smb_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 */
131int
132smb_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 */
151int
152smb_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 */
191int
192smb_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 */
226int
227smb_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 */
252int
253smb_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 */
292static int
293smb_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
310static int
311smb_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 */
332void
333smb_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 */
354void
355smb_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 */
369void
370smb_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 */
382void
383smb_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
393void
394smb_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
401void
402smb_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 */
423boolean_t
424smb_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 */
446int
447smb_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 */
485void
486smb_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 */
500void
501smb_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 */
516void
517smb_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 */
529void
530smb_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
556int
557smb_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 */
605struct hostent *
606smb_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 */
628struct hostent *
629smb_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