xref: /illumos-gate/usr/src/lib/smbsrv/libsmb/common/smb_info.c (revision 7b59d02d2a384be9a08087b14defadd214b3c1dd)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/types.h>
29 #include <stdarg.h>
30 #include <unistd.h>
31 #include <stdlib.h>
32 #include <time.h>
33 #include <synch.h>
34 #include <syslog.h>
35 #include <string.h>
36 #include <strings.h>
37 #include <errno.h>
38 #include <net/if.h>
39 #include <netdb.h>
40 #include <netinet/in.h>
41 #include <arpa/nameser.h>
42 #include <resolv.h>
43 #include <sys/sockio.h>
44 #include <smbsrv/smbinfo.h>
45 #include <smbsrv/netbios.h>
46 #include <smbsrv/libsmb.h>
47 
48 static smb_ntdomain_t smbpdc_cache;
49 static mutex_t smbpdc_mtx;
50 static cond_t smbpdc_cv;
51 
52 extern int getdomainname(char *, int);
53 
54 /*
55  * smb_getdomaininfo
56  *
57  * Returns a pointer to the cached domain data. The caller can specify
58  * whether or not he is prepared to wait if the cache is not yet valid
59  * and for how long. The specified timeout is in seconds.
60  */
61 smb_ntdomain_t *
62 smb_getdomaininfo(uint32_t timeout)
63 {
64 	timestruc_t to;
65 	int err;
66 
67 	if (timeout != 0) {
68 		(void) mutex_lock(&smbpdc_mtx);
69 		while (smbpdc_cache.ipaddr == 0) {
70 			to.tv_sec = timeout;
71 			to.tv_nsec = 0;
72 			err = cond_reltimedwait(&smbpdc_cv, &smbpdc_mtx, &to);
73 			if (err == ETIME)
74 				break;
75 		}
76 		(void) mutex_unlock(&smbpdc_mtx);
77 	}
78 
79 	if (smbpdc_cache.ipaddr != 0)
80 		return (&smbpdc_cache);
81 	else
82 		return (0);
83 }
84 
85 void
86 smb_logdomaininfo(smb_ntdomain_t *di)
87 {
88 	char ipstr[16];
89 
90 	(void) inet_ntop(AF_INET, (const void *)&di->ipaddr, ipstr,
91 	    sizeof (ipstr));
92 	syslog(LOG_DEBUG, "smbd: %s (%s:%s)", di->domain, di->server, ipstr);
93 }
94 
95 /*
96  * smb_setdomaininfo
97  *
98  * Set the information for the specified domain. If the information is
99  * non-null, the notification event is raised to wakeup any threads
100  * blocking on the cache.
101  */
102 void
103 smb_setdomaininfo(char *domain, char *server, uint32_t ipaddr)
104 {
105 	char *p;
106 
107 	bzero(&smbpdc_cache, sizeof (smb_ntdomain_t));
108 
109 	if (domain && server && ipaddr) {
110 		(void) strlcpy(smbpdc_cache.domain, domain, SMB_PI_MAX_DOMAIN);
111 		(void) strlcpy(smbpdc_cache.server, server, SMB_PI_MAX_DOMAIN);
112 
113 		/*
114 		 * Remove DNS domain name extension
115 		 * to avoid confusing NetBIOS.
116 		 */
117 		if ((p = strchr(smbpdc_cache.domain, '.')) != 0)
118 			*p = '\0';
119 
120 		if ((p = strchr(smbpdc_cache.server, '.')) != 0)
121 			*p = '\0';
122 
123 		(void) mutex_lock(&smbpdc_mtx);
124 		smbpdc_cache.ipaddr = ipaddr;
125 		(void) cond_broadcast(&smbpdc_cv);
126 		(void) mutex_unlock(&smbpdc_mtx);
127 	}
128 }
129 
130 void
131 smb_load_kconfig(smb_kmod_cfg_t *kcfg)
132 {
133 	int64_t citem;
134 
135 	bzero(kcfg, sizeof (smb_kmod_cfg_t));
136 
137 	(void) smb_config_getnum(SMB_CI_MAX_BUFSIZE, &citem);
138 	kcfg->skc_maxbufsize = (uint32_t)citem;
139 	(void) smb_config_getnum(SMB_CI_MAX_WORKERS, &citem);
140 	kcfg->skc_maxworkers = (uint32_t)citem;
141 	(void) smb_config_getnum(SMB_CI_KEEPALIVE, &citem);
142 	kcfg->skc_keepalive = (uint32_t)citem;
143 	if ((kcfg->skc_keepalive != 0) &&
144 	    (kcfg->skc_keepalive < SMB_PI_KEEP_ALIVE_MIN))
145 		kcfg->skc_keepalive = SMB_PI_KEEP_ALIVE_MIN;
146 
147 	(void) smb_config_getnum(SMB_CI_OPLOCK_TIMEOUT, &citem);
148 	kcfg->skc_oplock_timeout = (uint32_t)citem;
149 	(void) smb_config_getnum(SMB_CI_MAX_CONNECTIONS, &citem);
150 	kcfg->skc_maxconnections = (uint32_t)citem;
151 	kcfg->skc_restrict_anon = smb_config_getbool(SMB_CI_RESTRICT_ANON);
152 	kcfg->skc_signing_enable = smb_config_getbool(SMB_CI_SIGNING_ENABLE);
153 	kcfg->skc_signing_required = smb_config_getbool(SMB_CI_SIGNING_REQD);
154 	kcfg->skc_signing_check = smb_config_getbool(SMB_CI_SIGNING_CHECK);
155 	kcfg->skc_oplock_enable = smb_config_getbool(SMB_CI_OPLOCK_ENABLE);
156 	kcfg->skc_flush_required = smb_config_getbool(SMB_CI_FLUSH_REQUIRED);
157 	kcfg->skc_sync_enable = smb_config_getbool(SMB_CI_SYNC_ENABLE);
158 	kcfg->skc_dirsymlink_enable =
159 	    !smb_config_getbool(SMB_CI_DIRSYMLINK_DISABLE);
160 	kcfg->skc_announce_quota = smb_config_getbool(SMB_CI_ANNONCE_QUOTA);
161 	kcfg->skc_secmode = smb_config_get_secmode();
162 	(void) smb_getdomainname(kcfg->skc_resource_domain,
163 	    sizeof (kcfg->skc_resource_domain));
164 	(void) smb_gethostname(kcfg->skc_hostname, sizeof (kcfg->skc_hostname),
165 	    1);
166 	(void) smb_config_getstr(SMB_CI_SYS_CMNT, kcfg->skc_system_comment,
167 	    sizeof (kcfg->skc_system_comment));
168 }
169 
170 /*
171  * Get the current system NetBIOS name.  The hostname is truncated at
172  * the first `.` or 15 bytes, whichever occurs first, and converted
173  * to uppercase (by smb_gethostname).  Text that appears after the
174  * first '.' is considered to be part of the NetBIOS scope.
175  *
176  * Returns 0 on success, otherwise -1 to indicate an error.
177  */
178 int
179 smb_getnetbiosname(char *buf, size_t buflen)
180 {
181 	if (smb_gethostname(buf, buflen, 1) != 0)
182 		return (-1);
183 
184 	if (buflen >= NETBIOS_NAME_SZ)
185 		buf[NETBIOS_NAME_SZ - 1] = '\0';
186 
187 	return (0);
188 }
189 
190 /*
191  * Get the current system node name.  The returned name is guaranteed
192  * to be null-terminated (gethostname may not null terminate the name).
193  * If the hostname has been fully-qualified for some reason, the domain
194  * part will be removed.  If the caller would like the name in upper
195  * case, it is folded to uppercase.
196  *
197  * If gethostname fails, the returned buffer will contain an empty
198  * string.
199  */
200 int
201 smb_gethostname(char *buf, size_t buflen, int upcase)
202 {
203 	char *p;
204 
205 	if (buf == NULL || buflen == 0)
206 		return (-1);
207 
208 	if (gethostname(buf, buflen) != 0) {
209 		*buf = '\0';
210 		return (-1);
211 	}
212 
213 	buf[buflen - 1] = '\0';
214 
215 	if ((p = strchr(buf, '.')) != NULL)
216 		*p = '\0';
217 
218 	if (upcase)
219 		(void) utf8_strupr(buf);
220 
221 	return (0);
222 }
223 
224 /*
225  * Obtain the fully-qualified name for this machine.  If the
226  * hostname is fully-qualified, accept it.  Otherwise, try to
227  * find an appropriate domain name to append to the hostname.
228  */
229 int
230 smb_getfqhostname(char *buf, size_t buflen)
231 {
232 	char hostname[MAXHOSTNAMELEN];
233 	char domain[MAXHOSTNAMELEN];
234 
235 	hostname[0] = '\0';
236 	domain[0] = '\0';
237 
238 	if (smb_gethostname(hostname, MAXHOSTNAMELEN, 0) != 0)
239 		return (-1);
240 
241 	if (smb_getfqdomainname(domain, MAXHOSTNAMELEN) != 0)
242 		return (-1);
243 
244 	if (hostname[0] == '\0')
245 		return (-1);
246 
247 	if (domain[0] == '\0') {
248 		(void) strlcpy(buf, hostname, buflen);
249 		return (0);
250 	}
251 
252 	(void) snprintf(buf, buflen, "%s.%s", hostname, domain);
253 	return (0);
254 }
255 
256 /*
257  * smb_resolve_netbiosname
258  *
259  * Convert the fully-qualified domain name (i.e. fqdn) to a NETBIOS name.
260  * Upon success, the NETBIOS name will be returned via buf parameter.
261  * Returns 0 upon success.  Otherwise, returns -1.
262  */
263 int
264 smb_resolve_netbiosname(char *fqdn, char *buf, size_t buflen)
265 {
266 	char *p;
267 
268 	if (!buf)
269 		return (-1);
270 
271 	*buf = '\0';
272 	if (!fqdn)
273 		return (-1);
274 
275 	(void) strlcpy(buf, fqdn, buflen);
276 	if ((p = strchr(buf, '.')) != NULL)
277 		*p = 0;
278 
279 	if (strlen(buf) >= NETBIOS_NAME_SZ)
280 		buf[NETBIOS_NAME_SZ - 1] = '\0';
281 
282 	return (0);
283 }
284 
285 /*
286  * smb_getdomainname
287  *
288  * Returns NETBIOS name of the domain if the system is in domain
289  * mode. Or returns workgroup name if the system is in workgroup
290  * mode.
291  */
292 int
293 smb_getdomainname(char *buf, size_t buflen)
294 {
295 	char domain[MAXHOSTNAMELEN];
296 	int rc;
297 
298 	if (buf == NULL || buflen == 0)
299 		return (-1);
300 
301 	*buf = '\0';
302 	rc = smb_config_getstr(SMB_CI_DOMAIN_NAME, domain,
303 	    sizeof (domain));
304 
305 	if ((rc != SMBD_SMF_OK) || (*domain == '\0'))
306 		return (-1);
307 
308 	(void) smb_resolve_netbiosname(domain, buf, buflen);
309 	return (0);
310 }
311 
312 /*
313  * smb_resolve_fqdn
314  *
315  * Converts the NETBIOS name of the domain (i.e. nbt_domain) to a fully
316  * qualified domain name. The domain from either the domain field or
317  * search list field of the /etc/resolv.conf will be returned via the
318  * buf parameter if the first label of the domain matches the given
319  * NETBIOS name.
320  *
321  * Returns -1 upon error. If a match is found, returns 1. Otherwise,
322  * returns 0.
323  */
324 int
325 smb_resolve_fqdn(char *nbt_domain, char *buf, size_t buflen)
326 {
327 	struct __res_state res_state;
328 	int i, found = 0;
329 	char *p;
330 	int dlen;
331 
332 	if (!buf)
333 		return (-1);
334 
335 	*buf = '\0';
336 	if (!nbt_domain)
337 		return (-1);
338 
339 	bzero(&res_state, sizeof (struct __res_state));
340 	if (res_ninit(&res_state))
341 		return (-1);
342 
343 	if (*nbt_domain == '\0') {
344 		if (*res_state.defdname == '\0') {
345 			res_ndestroy(&res_state);
346 			return (0);
347 		}
348 
349 		(void) strlcpy(buf, res_state.defdname, buflen);
350 		res_ndestroy(&res_state);
351 		return (1);
352 	}
353 
354 	dlen = strlen(nbt_domain);
355 	if (!strncasecmp(nbt_domain, res_state.defdname, dlen)) {
356 		(void) strlcpy(buf, res_state.defdname, buflen);
357 		res_ndestroy(&res_state);
358 		return (1);
359 	}
360 
361 	for (i = 0; (p = res_state.dnsrch[i]) != NULL; i++) {
362 		if (!strncasecmp(nbt_domain, p, dlen)) {
363 			(void) strlcpy(buf, p, buflen);
364 			found = 1;
365 			break;
366 		}
367 
368 	}
369 
370 	res_ndestroy(&res_state);
371 	return (found);
372 }
373 
374 /*
375  * smb_getfqdomainname
376  *
377  * If the domain_name property value is FQDN, it will be returned.
378  * In domain mode, the domain from either the domain field or
379  * search list field of the /etc/resolv.conf will be returned via the
380  * buf parameter if the first label of the domain matches the
381  * domain_name property. In workgroup mode, it returns the local
382  * domain.
383  *
384  * Returns 0 upon success.  Otherwise, returns -1.
385  */
386 int
387 smb_getfqdomainname(char *buf, size_t buflen)
388 {
389 	char domain[MAXHOSTNAMELEN];
390 	int rc = 0;
391 
392 	if (buf == NULL || buflen == 0)
393 		return (-1);
394 
395 	*buf = '\0';
396 	if (smb_config_get_secmode() == SMB_SECMODE_DOMAIN) {
397 		rc = smb_config_getstr(SMB_CI_DOMAIN_NAME, domain,
398 		    sizeof (domain));
399 
400 		if ((rc != SMBD_SMF_OK) || (*domain == '\0'))
401 			return (-1);
402 
403 		if (strchr(domain, '.') == NULL) {
404 			if (smb_resolve_fqdn(domain, buf, buflen) != 1)
405 				rc = -1;
406 		} else {
407 			(void) strlcpy(buf, domain, buflen);
408 		}
409 	} else {
410 		if (smb_resolve_fqdn("", buf, buflen) != 1)
411 			rc = -1;
412 	}
413 
414 	return (rc);
415 }
416 
417 
418 
419 /*
420  * Temporary fbt for dtrace until user space sdt enabled.
421  */
422 void
423 smb_tracef(const char *fmt, ...)
424 {
425 	va_list ap;
426 	char buf[128];
427 
428 	va_start(ap, fmt);
429 	(void) vsnprintf(buf, 128, fmt, ap);
430 	va_end(ap);
431 
432 	smb_trace(buf);
433 }
434 
435 /*
436  * Temporary fbt for dtrace until user space sdt enabled.
437  */
438 void
439 smb_trace(const char *s)
440 {
441 	syslog(LOG_DEBUG, "%s", s);
442 }
443 
444 /*
445  * smb_tonetbiosname
446  *
447  * Creates a NetBIOS name based on the given name and suffix.
448  * NetBIOS name is 15 capital characters, padded with space if needed
449  * and the 16th byte is the suffix.
450  */
451 void
452 smb_tonetbiosname(char *name, char *nb_name, char suffix)
453 {
454 	char tmp_name[NETBIOS_NAME_SZ];
455 	mts_wchar_t wtmp_name[NETBIOS_NAME_SZ];
456 	unsigned int cpid;
457 	int len;
458 	size_t rc;
459 
460 	len = 0;
461 	rc = mts_mbstowcs(wtmp_name, (const char *)name, NETBIOS_NAME_SZ);
462 
463 	if (rc != (size_t)-1) {
464 		wtmp_name[NETBIOS_NAME_SZ - 1] = 0;
465 		cpid = oem_get_smb_cpid();
466 		rc = unicodestooems(tmp_name, wtmp_name, NETBIOS_NAME_SZ, cpid);
467 		if (rc > 0)
468 			len = strlen(tmp_name);
469 	}
470 
471 	(void) memset(nb_name, ' ', NETBIOS_NAME_SZ - 1);
472 	if (len) {
473 		(void) utf8_strupr(tmp_name);
474 		(void) memcpy(nb_name, tmp_name, len);
475 	}
476 	nb_name[NETBIOS_NAME_SZ - 1] = suffix;
477 }
478 
479 int
480 smb_get_nameservers(struct in_addr *ips, int sz)
481 {
482 	union res_sockaddr_union set[MAXNS];
483 	int i, cnt;
484 	struct __res_state res_state;
485 
486 	if (ips == NULL)
487 		return (0);
488 
489 	bzero(&res_state, sizeof (struct __res_state));
490 	if (res_ninit(&res_state) < 0)
491 		return (0);
492 
493 	cnt = res_getservers(&res_state, set, MAXNS);
494 	for (i = 0; i < cnt; i++) {
495 		if (i >= sz)
496 			break;
497 		ips[i] = set[i].sin.sin_addr;
498 		syslog(LOG_DEBUG, "NS Found %s name server\n",
499 		    inet_ntoa(ips[i]));
500 	}
501 	syslog(LOG_DEBUG, "NS Found %d name servers\n", i);
502 	res_ndestroy(&res_state);
503 	return (i);
504 }
505