xref: /illumos-gate/usr/src/lib/smbsrv/libsmb/common/smb_info.c (revision faa1795a28a5c712eed6d0a3f84d98c368a316c6)
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 static mutex_t seqnum_mtx;
52 
53 extern int getdomainname(char *, int);
54 
55 /*
56  * smb_getdomaininfo
57  *
58  * Returns a pointer to the cached domain data. The caller can specify
59  * whether or not he is prepared to wait if the cache is not yet valid
60  * and for how long. The specified timeout is in seconds.
61  */
62 smb_ntdomain_t *
63 smb_getdomaininfo(uint32_t timeout)
64 {
65 	timestruc_t to;
66 	int err;
67 
68 	if (timeout != 0) {
69 		(void) mutex_lock(&smbpdc_mtx);
70 		while (smbpdc_cache.ipaddr == 0) {
71 			to.tv_sec = timeout;
72 			to.tv_nsec = 0;
73 			err = cond_reltimedwait(&smbpdc_cv, &smbpdc_mtx, &to);
74 			if (err == ETIME)
75 				break;
76 		}
77 		(void) mutex_unlock(&smbpdc_mtx);
78 	}
79 
80 	if (smbpdc_cache.ipaddr != 0)
81 		return (&smbpdc_cache);
82 	else
83 		return (0);
84 }
85 
86 void
87 smb_logdomaininfo(smb_ntdomain_t *di)
88 {
89 	char ipstr[16];
90 
91 	(void) inet_ntop(AF_INET, (const void *)&di->ipaddr, ipstr,
92 	    sizeof (ipstr));
93 	syslog(LOG_DEBUG, "smbd: %s (%s:%s)", di->domain, di->server, ipstr);
94 }
95 
96 /*
97  * smb_setdomaininfo
98  *
99  * Set the information for the specified domain. If the information is
100  * non-null, the notification event is raised to wakeup any threads
101  * blocking on the cache.
102  */
103 void
104 smb_setdomaininfo(char *domain, char *server, uint32_t ipaddr)
105 {
106 	char *p;
107 
108 	bzero(&smbpdc_cache, sizeof (smb_ntdomain_t));
109 
110 	if (domain && server && ipaddr) {
111 		(void) strlcpy(smbpdc_cache.domain, domain, SMB_PI_MAX_DOMAIN);
112 		(void) strlcpy(smbpdc_cache.server, server, SMB_PI_MAX_DOMAIN);
113 
114 		/*
115 		 * Remove DNS domain name extension
116 		 * to avoid confusing NetBIOS.
117 		 */
118 		if ((p = strchr(smbpdc_cache.domain, '.')) != 0)
119 			*p = '\0';
120 
121 		if ((p = strchr(smbpdc_cache.server, '.')) != 0)
122 			*p = '\0';
123 
124 		(void) mutex_lock(&smbpdc_mtx);
125 		smbpdc_cache.ipaddr = ipaddr;
126 		(void) cond_broadcast(&smbpdc_cv);
127 		(void) mutex_unlock(&smbpdc_mtx);
128 	}
129 }
130 
131 void
132 smb_load_kconfig(smb_kmod_cfg_t *kcfg)
133 {
134 	int64_t citem;
135 
136 	bzero(kcfg, sizeof (smb_kmod_cfg_t));
137 
138 	(void) smb_config_getnum(SMB_CI_MAX_WORKERS, &citem);
139 	kcfg->skc_maxworkers = (uint32_t)citem;
140 	(void) smb_config_getnum(SMB_CI_KEEPALIVE, &citem);
141 	kcfg->skc_keepalive = (uint32_t)citem;
142 	if ((kcfg->skc_keepalive != 0) &&
143 	    (kcfg->skc_keepalive < SMB_PI_KEEP_ALIVE_MIN))
144 		kcfg->skc_keepalive = SMB_PI_KEEP_ALIVE_MIN;
145 
146 	(void) smb_config_getnum(SMB_CI_MAX_CONNECTIONS, &citem);
147 	kcfg->skc_maxconnections = (uint32_t)citem;
148 	kcfg->skc_restrict_anon = smb_config_getbool(SMB_CI_RESTRICT_ANON);
149 	kcfg->skc_signing_enable = smb_config_getbool(SMB_CI_SIGNING_ENABLE);
150 	kcfg->skc_signing_required = smb_config_getbool(SMB_CI_SIGNING_REQD);
151 	kcfg->skc_signing_check = smb_config_getbool(SMB_CI_SIGNING_CHECK);
152 	kcfg->skc_oplock_enable = smb_config_getbool(SMB_CI_OPLOCK_ENABLE);
153 	kcfg->skc_sync_enable = smb_config_getbool(SMB_CI_SYNC_ENABLE);
154 	kcfg->skc_secmode = smb_config_get_secmode();
155 	(void) smb_getdomainname(kcfg->skc_resource_domain,
156 	    sizeof (kcfg->skc_resource_domain));
157 	(void) smb_gethostname(kcfg->skc_hostname, sizeof (kcfg->skc_hostname),
158 	    1);
159 	(void) smb_config_getstr(SMB_CI_SYS_CMNT, kcfg->skc_system_comment,
160 	    sizeof (kcfg->skc_system_comment));
161 }
162 
163 /*
164  * Get the current system NetBIOS name.  The hostname is truncated at
165  * the first `.` or 15 bytes, whichever occurs first, and converted
166  * to uppercase (by smb_gethostname).  Text that appears after the
167  * first '.' is considered to be part of the NetBIOS scope.
168  *
169  * Returns 0 on success, otherwise -1 to indicate an error.
170  */
171 int
172 smb_getnetbiosname(char *buf, size_t buflen)
173 {
174 	if (smb_gethostname(buf, buflen, 1) != 0)
175 		return (-1);
176 
177 	if (buflen >= NETBIOS_NAME_SZ)
178 		buf[NETBIOS_NAME_SZ - 1] = '\0';
179 
180 	return (0);
181 }
182 
183 /*
184  * Get the current system node name.  The returned name is guaranteed
185  * to be null-terminated (gethostname may not null terminate the name).
186  * If the hostname has been fully-qualified for some reason, the domain
187  * part will be removed.  If the caller would like the name in upper
188  * case, it is folded to uppercase.
189  *
190  * If gethostname fails, the returned buffer will contain an empty
191  * string.
192  */
193 int
194 smb_gethostname(char *buf, size_t buflen, int upcase)
195 {
196 	char *p;
197 
198 	if (buf == NULL || buflen == 0)
199 		return (-1);
200 
201 	if (gethostname(buf, buflen) != 0) {
202 		*buf = '\0';
203 		return (-1);
204 	}
205 
206 	buf[buflen - 1] = '\0';
207 
208 	if ((p = strchr(buf, '.')) != NULL)
209 		*p = '\0';
210 
211 	if (upcase)
212 		(void) utf8_strupr(buf);
213 
214 	return (0);
215 }
216 
217 /*
218  * Obtain the fully-qualified name for this machine.  If the
219  * hostname is fully-qualified, accept it.  Otherwise, try to
220  * find an appropriate domain name to append to the hostname.
221  */
222 int
223 smb_getfqhostname(char *buf, size_t buflen)
224 {
225 	char hostname[MAXHOSTNAMELEN];
226 	char domain[MAXHOSTNAMELEN];
227 
228 	hostname[0] = '\0';
229 	domain[0] = '\0';
230 
231 	if (smb_gethostname(hostname, MAXHOSTNAMELEN, 0) != 0)
232 		return (-1);
233 
234 	if (smb_getfqdomainname(domain, MAXHOSTNAMELEN) != 0)
235 		return (-1);
236 
237 	if (hostname[0] == '\0')
238 		return (-1);
239 
240 	if (domain[0] == '\0') {
241 		(void) strlcpy(buf, hostname, buflen);
242 		return (0);
243 	}
244 
245 	(void) snprintf(buf, buflen, "%s.%s", hostname, domain);
246 	return (0);
247 }
248 
249 /*
250  * smb_resolve_netbiosname
251  *
252  * Convert the fully-qualified domain name (i.e. fqdn) to a NETBIOS name.
253  * Upon success, the NETBIOS name will be returned via buf parameter.
254  * Returns 0 upon success.  Otherwise, returns -1.
255  */
256 int
257 smb_resolve_netbiosname(char *fqdn, char *buf, size_t buflen)
258 {
259 	char *p;
260 
261 	if (!buf)
262 		return (-1);
263 
264 	*buf = '\0';
265 	if (!fqdn)
266 		return (-1);
267 
268 	(void) strlcpy(buf, fqdn, buflen);
269 	if ((p = strchr(buf, '.')) != NULL)
270 		*p = 0;
271 
272 	if (strlen(buf) >= NETBIOS_NAME_SZ)
273 		buf[NETBIOS_NAME_SZ - 1] = '\0';
274 
275 	return (0);
276 }
277 
278 /*
279  * smb_getdomainname
280  *
281  * Returns NETBIOS name of the domain if the system is in domain
282  * mode. Or returns workgroup name if the system is in workgroup
283  * mode.
284  */
285 int
286 smb_getdomainname(char *buf, size_t buflen)
287 {
288 	char domain[MAXHOSTNAMELEN];
289 	int rc;
290 
291 	if (buf == NULL || buflen == 0)
292 		return (-1);
293 
294 	*buf = '\0';
295 	rc = smb_config_getstr(SMB_CI_DOMAIN_NAME, domain,
296 	    sizeof (domain));
297 
298 	if ((rc != SMBD_SMF_OK) || (*domain == '\0'))
299 		return (-1);
300 
301 	(void) smb_resolve_netbiosname(domain, buf, buflen);
302 	return (0);
303 }
304 
305 /*
306  * smb_getdomainsid
307  *
308  * Returns the domain SID if the system is in domain mode.
309  * Otherwise returns NULL.
310  *
311  * Note: Callers are responsible for freeing a returned SID.
312  */
313 nt_sid_t *
314 smb_getdomainsid(void)
315 {
316 	char buf[MAXHOSTNAMELEN];
317 	nt_sid_t *sid;
318 	int security_mode;
319 	int rc;
320 
321 	security_mode = smb_config_get_secmode();
322 	if (security_mode != SMB_SECMODE_DOMAIN)
323 		return (NULL);
324 
325 	*buf = '\0';
326 	rc = smb_config_getstr(SMB_CI_DOMAIN_SID, buf, MAXHOSTNAMELEN);
327 	if ((rc != SMBD_SMF_OK) || (*buf == '\0'))
328 		return (NULL);
329 
330 	if ((sid = nt_sid_strtosid(buf)) == NULL)
331 		return (NULL);
332 
333 	return (sid);
334 }
335 
336 /*
337  * smb_resolve_fqdn
338  *
339  * Converts the NETBIOS name of the domain (i.e. nbt_domain) to a fully
340  * qualified domain name. The domain from either the domain field or
341  * search list field of the /etc/resolv.conf will be returned via the
342  * buf parameter if the first label of the domain matches the given
343  * NETBIOS name.
344  *
345  * Returns -1 upon error. If a match is found, returns 1. Otherwise,
346  * returns 0.
347  */
348 int
349 smb_resolve_fqdn(char *nbt_domain, char *buf, size_t buflen)
350 {
351 	struct __res_state res_state;
352 	int i, found = 0;
353 	char *p;
354 	int dlen;
355 
356 	if (!buf)
357 		return (-1);
358 
359 	*buf = '\0';
360 	if (!nbt_domain)
361 		return (-1);
362 
363 	bzero(&res_state, sizeof (struct __res_state));
364 	if (res_ninit(&res_state))
365 		return (-1);
366 
367 	if (*nbt_domain == '\0') {
368 		if (*res_state.defdname == '\0') {
369 			res_ndestroy(&res_state);
370 			return (0);
371 		}
372 
373 		(void) strlcpy(buf, res_state.defdname, buflen);
374 		res_ndestroy(&res_state);
375 		return (1);
376 	}
377 
378 	dlen = strlen(nbt_domain);
379 	if (!strncasecmp(nbt_domain, res_state.defdname, dlen)) {
380 		(void) strlcpy(buf, res_state.defdname, buflen);
381 		res_ndestroy(&res_state);
382 		return (1);
383 	}
384 
385 	for (i = 0; (p = res_state.dnsrch[i]) != NULL; i++) {
386 		if (!strncasecmp(nbt_domain, p, dlen)) {
387 			(void) strlcpy(buf, p, buflen);
388 			found = 1;
389 			break;
390 		}
391 
392 	}
393 
394 	res_ndestroy(&res_state);
395 	return (found);
396 }
397 
398 /*
399  * smb_getfqdomainname
400  *
401  * If the domain_name property value is FQDN, it will be returned.
402  * In domain mode, the domain from either the domain field or
403  * search list field of the /etc/resolv.conf will be returned via the
404  * buf parameter if the first label of the domain matches the
405  * domain_name property. In workgroup mode, it returns the local
406  * domain.
407  *
408  * Returns 0 upon success.  Otherwise, returns -1.
409  */
410 int
411 smb_getfqdomainname(char *buf, size_t buflen)
412 {
413 	char domain[MAXHOSTNAMELEN];
414 	int rc = 0;
415 
416 	if (buf == NULL || buflen == 0)
417 		return (-1);
418 
419 	*buf = '\0';
420 	if (smb_config_get_secmode() == SMB_SECMODE_DOMAIN) {
421 		rc = smb_config_getstr(SMB_CI_DOMAIN_NAME, domain,
422 		    sizeof (domain));
423 
424 		if ((rc != SMBD_SMF_OK) || (*domain == '\0'))
425 			return (-1);
426 
427 		if (strchr(domain, '.') == NULL) {
428 			if (smb_resolve_fqdn(domain, buf, buflen) != 1)
429 				rc = -1;
430 		} else {
431 			(void) strlcpy(buf, domain, buflen);
432 		}
433 	} else {
434 		if (smb_resolve_fqdn("", buf, buflen) != 1)
435 			rc = -1;
436 	}
437 
438 	return (rc);
439 }
440 
441 
442 /*
443  * smb_set_machine_passwd
444  *
445  * This function should be used when setting the machine password property.
446  * The associated sequence number is incremented.
447  */
448 static int
449 smb_set_machine_passwd(char *passwd)
450 {
451 	int64_t num;
452 	int rc = -1;
453 
454 	if (smb_config_set(SMB_CI_MACHINE_PASSWD, passwd) != SMBD_SMF_OK)
455 		return (-1);
456 
457 	(void) mutex_lock(&seqnum_mtx);
458 	(void) smb_config_getnum(SMB_CI_KPASSWD_SEQNUM, &num);
459 	if (smb_config_setnum(SMB_CI_KPASSWD_SEQNUM, ++num)
460 	    == SMBD_SMF_OK)
461 		rc = 0;
462 	(void) mutex_unlock(&seqnum_mtx);
463 	return (rc);
464 }
465 
466 /*
467  * smb_match_netlogon_seqnum
468  *
469  * A sequence number is associated with each machine password property
470  * update and the netlogon credential chain setup. If the
471  * sequence numbers don't match, a NETLOGON credential chain
472  * establishment is required.
473  *
474  * Returns 0 if kpasswd_seqnum equals to netlogon_seqnum. Otherwise,
475  * returns -1.
476  */
477 boolean_t
478 smb_match_netlogon_seqnum(void)
479 {
480 	int64_t setpasswd_seqnum;
481 	int64_t netlogon_seqnum;
482 
483 	(void) mutex_lock(&seqnum_mtx);
484 	(void) smb_config_getnum(SMB_CI_KPASSWD_SEQNUM, &setpasswd_seqnum);
485 	(void) smb_config_getnum(SMB_CI_NETLOGON_SEQNUM, &netlogon_seqnum);
486 	(void) mutex_unlock(&seqnum_mtx);
487 	return (setpasswd_seqnum == netlogon_seqnum);
488 }
489 
490 /*
491  * smb_getjoineddomain
492  *
493  * Returns the NETBIOS name of the domain to which the system is joined
494  * via smbadm CLI. If the system is in workgroup mode or any error has
495  * been encountered, an empty string will be returned via buf.
496  *
497  * Returns:
498  *   -1 upon errors.
499  *    0 if in workgroup mode.
500  *    1 if in domain mode.
501  */
502 int
503 smb_getjoineddomain(char *buf, size_t buflen)
504 {
505 	if (buf == NULL || buflen == 0)
506 		return (-1);
507 
508 	*buf = '\0';
509 	if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN)
510 		return (0);
511 
512 	return (smb_getdomainname(buf, buflen) ? -1 : 1);
513 }
514 
515 /*
516  * smb_setdomainprops
517  *
518  * This function should be called after joining an AD to
519  * set all the domain related SMF properties.
520  *
521  * The kpasswd_domain property is the AD domain to which the system
522  * is joined via kclient. If this function is invoked by the SMB
523  * daemon, fqdn should be set to NULL.
524  */
525 int
526 smb_setdomainprops(char *fqdn, char *server, char *passwd)
527 {
528 	if (server == NULL || passwd == NULL)
529 		return (-1);
530 
531 	if ((*server == '\0') || (*passwd == '\0'))
532 		return (-1);
533 
534 	if (fqdn && (smb_config_set(SMB_CI_KPASSWD_DOMAIN, fqdn) != 0))
535 		return (-1);
536 
537 	if (smb_config_set(SMB_CI_KPASSWD_SRV, server) != 0)
538 		return (-1);
539 
540 	if (smb_set_machine_passwd(passwd) != 0) {
541 		syslog(LOG_ERR, "smb_setdomainprops: failed to set"
542 		    " machine account password");
543 		return (-1);
544 	}
545 
546 	/*
547 	 * If we successfully create a trust account, we mark
548 	 * ourselves as a domain member in the environment so
549 	 * that we use the SAMLOGON version of the NETLOGON
550 	 * PDC location protocol.
551 	 */
552 	(void) smb_config_setbool(SMB_CI_DOMAIN_MEMB, B_TRUE);
553 
554 	return (0);
555 }
556 
557 /*
558  * smb_update_netlogon_seqnum
559  *
560  * This function should only be called upon a successful netlogon
561  * credential chain establishment to set the sequence number of the
562  * netlogon to match with that of the kpasswd.
563  */
564 void
565 smb_update_netlogon_seqnum(void)
566 {
567 	int64_t num;
568 
569 	(void) mutex_lock(&seqnum_mtx);
570 	(void) smb_config_getnum(SMB_CI_KPASSWD_SEQNUM, &num);
571 	(void) smb_config_setnum(SMB_CI_NETLOGON_SEQNUM, num);
572 	(void) mutex_unlock(&seqnum_mtx);
573 }
574 
575 
576 /*
577  * Temporary fbt for dtrace until user space sdt enabled.
578  */
579 void
580 smb_tracef(const char *fmt, ...)
581 {
582 	va_list ap;
583 	char buf[128];
584 
585 	va_start(ap, fmt);
586 	(void) vsnprintf(buf, 128, fmt, ap);
587 	va_end(ap);
588 
589 	smb_trace(buf);
590 }
591 
592 /*
593  * Temporary fbt for dtrace until user space sdt enabled.
594  */
595 void
596 smb_trace(const char *s)
597 {
598 	syslog(LOG_DEBUG, "%s", s);
599 }
600 
601 /*
602  * smb_tonetbiosname
603  *
604  * Creates a NetBIOS name based on the given name and suffix.
605  * NetBIOS name is 15 capital characters, padded with space if needed
606  * and the 16th byte is the suffix.
607  */
608 void
609 smb_tonetbiosname(char *name, char *nb_name, char suffix)
610 {
611 	char tmp_name[NETBIOS_NAME_SZ];
612 	mts_wchar_t wtmp_name[NETBIOS_NAME_SZ];
613 	unsigned int cpid;
614 	int len;
615 	size_t rc;
616 
617 	len = 0;
618 	rc = mts_mbstowcs(wtmp_name, (const char *)name, NETBIOS_NAME_SZ);
619 
620 	if (rc != (size_t)-1) {
621 		wtmp_name[NETBIOS_NAME_SZ - 1] = 0;
622 		cpid = oem_get_smb_cpid();
623 		rc = unicodestooems(tmp_name, wtmp_name, NETBIOS_NAME_SZ, cpid);
624 		if (rc > 0)
625 			len = strlen(tmp_name);
626 	}
627 
628 	(void) memset(nb_name, ' ', NETBIOS_NAME_SZ - 1);
629 	if (len) {
630 		(void) utf8_strupr(tmp_name);
631 		(void) memcpy(nb_name, tmp_name, len);
632 	}
633 	nb_name[NETBIOS_NAME_SZ - 1] = suffix;
634 }
635 
636 int
637 smb_get_nameservers(struct in_addr *ips, int sz)
638 {
639 	union res_sockaddr_union set[MAXNS];
640 	int i, cnt;
641 	struct __res_state res_state;
642 
643 	if (ips == NULL)
644 		return (0);
645 
646 	bzero(&res_state, sizeof (struct __res_state));
647 	if (res_ninit(&res_state) < 0)
648 		return (0);
649 
650 	cnt = res_getservers(&res_state, set, MAXNS);
651 	for (i = 0; i < cnt; i++) {
652 		if (i >= sz)
653 			break;
654 		ips[i] = set[i].sin.sin_addr;
655 		syslog(LOG_DEBUG, "NS Found %s name server\n",
656 		    inet_ntoa(ips[i]));
657 	}
658 	syslog(LOG_DEBUG, "NS Found %d name servers\n", i);
659 	res_ndestroy(&res_state);
660 	return (i);
661 }
662