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