xref: /illumos-gate/usr/src/lib/smbsrv/libsmb/common/smb_domain.c (revision d656abb5804319b33c85955a73ee450ef7ff9739)
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 /*
27  * This file defines the NT domain environment values and the domain
28  * database interface. The database is a single linked list of
29  * structures containing domain type, name and SID information.
30  */
31 
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <stdio.h>
35 #include <strings.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <stdlib.h>
39 #include <netdb.h>
40 #include <syslog.h>
41 #include <synch.h>
42 #include <pwd.h>
43 #include <grp.h>
44 
45 #include <smbsrv/smbinfo.h>
46 #include <smbsrv/string.h>
47 #include <smbsrv/smb_sid.h>
48 #include <smbsrv/libsmb.h>
49 
50 #define	SMB_DOMAINS_FILE	"domains"
51 
52 static void nt_domain_unlist(nt_domain_t *);
53 
54 /*
55  * Valid domain type identifiers as text. This table must be kept
56  * in step with the nt_domain_type_t enum in ntdomain.h.
57  */
58 static char *nt_domain_type_name[NT_DOMAIN_NUM_TYPES] = {
59 	"null",
60 	"builtin",
61 	"local",
62 	"primary",
63 	"account",
64 	"trusted",
65 	"untrusted"
66 };
67 
68 
69 static rwlock_t		nt_domain_lock;
70 static nt_domain_t	*nt_domain_list;
71 
72 /*
73  * nt_domain_init
74  *
75  * NT domain database one time initialization. This function should
76  * be called during module installation.
77  *
78  * Returns 0 on successful domain initialization. Less than zero otherwise.
79  */
80 int
81 nt_domain_init(char *resource_domain, uint32_t secmode)
82 {
83 	nt_domain_t *domain;
84 	smb_sid_t *sid = NULL;
85 	char sidstr[128];
86 	char *lsidstr;
87 	char hostname[NETBIOS_NAME_SZ];
88 	int rc;
89 
90 	if (rwlock_init(&nt_domain_lock, USYNC_THREAD, NULL))
91 		return (SMB_DOMAIN_NODOMAIN_SID);
92 
93 	if (smb_getnetbiosname(hostname, NETBIOS_NAME_SZ) != 0) {
94 		(void) rwlock_destroy(&nt_domain_lock);
95 		return (SMB_DOMAIN_NOMACHINE_SID);
96 	}
97 
98 	lsidstr = smb_config_get_localsid();
99 
100 	if (lsidstr) {
101 		sid = smb_sid_fromstr(lsidstr);
102 
103 		if (sid) {
104 			domain = nt_domain_new(NT_DOMAIN_LOCAL, hostname, sid);
105 			(void) nt_domain_add(domain);
106 			free(sid);
107 		}
108 		free(lsidstr);
109 	} else {
110 		(void) rwlock_destroy(&nt_domain_lock);
111 		return (SMB_DOMAIN_NOMACHINE_SID);
112 	}
113 
114 	if ((sid = smb_sid_fromstr(NT_BUILTIN_DOMAIN_SIDSTR)) != NULL) {
115 		domain = nt_domain_new(NT_DOMAIN_BUILTIN, "BUILTIN", sid);
116 		(void) nt_domain_add(domain);
117 		free(sid);
118 	}
119 
120 	if (secmode == SMB_SECMODE_DOMAIN) {
121 		rc = smb_config_getstr(SMB_CI_DOMAIN_SID, sidstr,
122 		    sizeof (sidstr));
123 		sid = (rc == SMBD_SMF_OK) ? smb_sid_fromstr(sidstr) : NULL;
124 		if (smb_sid_isvalid(sid)) {
125 			domain = nt_domain_new(NT_DOMAIN_PRIMARY,
126 			    resource_domain, sid);
127 			(void) nt_domain_add(domain);
128 			free(sid);
129 		} else {
130 			free(sid);
131 			(void) rwlock_destroy(&nt_domain_lock);
132 			return (SMB_DOMAIN_NODOMAIN_SID);
133 		}
134 
135 	}
136 
137 	return (0);
138 }
139 
140 /*
141  * nt_domain_new
142  *
143  * Allocate and initialize a new domain structure. On success, a pointer to
144  * the new domain structure is returned. Otherwise a null pointer is returned.
145  */
146 nt_domain_t *
147 nt_domain_new(nt_domain_type_t type, char *name, smb_sid_t *sid)
148 {
149 	nt_domain_t *new_domain;
150 
151 	if ((name == NULL) || (sid == NULL))
152 		return (NULL);
153 
154 	if (type == NT_DOMAIN_NULL || type >= NT_DOMAIN_NUM_TYPES)
155 		return (NULL);
156 
157 	if ((new_domain = malloc(sizeof (nt_domain_t))) == NULL)
158 		return (NULL);
159 
160 	bzero(new_domain, sizeof (nt_domain_t));
161 	new_domain->type = type;
162 	new_domain->name = strdup(name);
163 	new_domain->sid = smb_sid_dup(sid);
164 
165 	return (new_domain);
166 }
167 
168 /*
169  * nt_domain_delete
170  *
171  * Free the memory used by the specified domain structure.
172  */
173 void
174 nt_domain_delete(nt_domain_t *domain)
175 {
176 	if (domain) {
177 		free(domain->name);
178 		free(domain->sid);
179 		free(domain);
180 	}
181 }
182 
183 
184 /*
185  * nt_domain_add
186  *
187  * Add a domain structure to the global list. There is no checking
188  * for duplicates. If it's the primary domain, we save the SID in the
189  * environment. Returns a pointer to the new domain entry on success.
190  * Otherwise a null pointer is returned.
191  */
192 nt_domain_t *
193 nt_domain_add(nt_domain_t *new_domain)
194 {
195 	char sidstr[SMB_SID_STRSZ];
196 
197 	if (new_domain == NULL)
198 		return (NULL);
199 
200 	(void) rw_wrlock(&nt_domain_lock);
201 
202 	new_domain->next = nt_domain_list;
203 	nt_domain_list = new_domain;
204 
205 	if (new_domain->type == NT_DOMAIN_PRIMARY) {
206 		smb_sid_tostr(new_domain->sid, sidstr);
207 		(void) smb_config_setstr(SMB_CI_DOMAIN_SID, sidstr);
208 	}
209 	(void) rw_unlock(&nt_domain_lock);
210 
211 	return (new_domain);
212 }
213 
214 
215 /*
216  * nt_domain_remove
217  *
218  * Remove a domain from the global list. The memory
219  * used by the structure is not freed.
220  */
221 void
222 nt_domain_remove(nt_domain_t *domain)
223 {
224 	(void) rw_wrlock(&nt_domain_lock);
225 	nt_domain_unlist(domain);
226 	(void) rw_unlock(&nt_domain_lock);
227 }
228 
229 
230 /*
231  * nt_domain_flush
232  *
233  * Flush all domains of the specified type from the list. This is
234  * useful for things like updating the list of trusted domains.
235  */
236 void
237 nt_domain_flush(nt_domain_type_t domain_type)
238 {
239 	nt_domain_t *domain = nt_domain_list;
240 
241 	(void) rw_wrlock(&nt_domain_lock);
242 	while (domain) {
243 		if (domain->type == domain_type) {
244 			nt_domain_unlist(domain);
245 			nt_domain_delete(domain);
246 			domain = nt_domain_list;
247 			continue;
248 		}
249 		domain = domain->next;
250 	}
251 	(void) rw_unlock(&nt_domain_lock);
252 }
253 
254 /*
255  * nt_domain_xlat_type
256  *
257  * Translate a domain type into a text string.
258  */
259 char *
260 nt_domain_xlat_type(nt_domain_type_t domain_type)
261 {
262 	if (domain_type < NT_DOMAIN_NUM_TYPES)
263 		return (nt_domain_type_name[domain_type]);
264 	else
265 		return ("unknown");
266 }
267 
268 
269 /*
270  * nt_domain_xlat_type_name
271  *
272  * Translate a domain type test string into a domain type.
273  */
274 nt_domain_type_t
275 nt_domain_xlat_type_name(char *type_name)
276 {
277 	int i;
278 
279 	for (i = 0; i < NT_DOMAIN_NUM_TYPES; ++i)
280 		if (utf8_strcasecmp(nt_domain_type_name[i], type_name) == 0)
281 			return (i);
282 
283 	return (NT_DOMAIN_NUM_TYPES);
284 }
285 
286 
287 /*
288  * nt_domain_lookup_name
289  *
290  * Lookup a domain by its domain name. If the domain is in the list,
291  * a pointer to it is returned. Otherwise a null pointer is returned.
292  */
293 nt_domain_t *
294 nt_domain_lookup_name(char *domain_name)
295 {
296 	char nbname[MAXHOSTNAMELEN];
297 	nt_domain_t *domain = nt_domain_list;
298 	char *p;
299 
300 	if (domain_name == NULL || *domain_name == '\0')
301 		return (NULL);
302 
303 	(void) strlcpy(nbname, domain_name, sizeof (nbname));
304 	if ((p = strchr(nbname, '.')) != NULL)
305 		*p = '\0';
306 
307 	(void) rw_rdlock(&nt_domain_lock);
308 	while (domain) {
309 		if (utf8_strcasecmp(domain->name, nbname) == 0)
310 			break;
311 
312 		domain = domain->next;
313 	}
314 	(void) rw_unlock(&nt_domain_lock);
315 
316 	return (domain);
317 }
318 
319 
320 /*
321  * nt_domain_lookup_sid
322  *
323  * Lookup a domain by its domain SID. If the domain is in the list,
324  * a pointer to it is returned. Otherwise a null pointer is returned.
325  */
326 nt_domain_t *
327 nt_domain_lookup_sid(smb_sid_t *domain_sid)
328 {
329 	nt_domain_t *domain = nt_domain_list;
330 
331 	(void) rw_rdlock(&nt_domain_lock);
332 	while (domain) {
333 		if (smb_sid_cmp(domain->sid, domain_sid))
334 			break;
335 
336 		domain = domain->next;
337 	}
338 	(void) rw_unlock(&nt_domain_lock);
339 
340 	return (domain);
341 }
342 
343 
344 /*
345  * nt_domain_lookupbytype
346  *
347  * Lookup a domain by its type. The first matching entry in the list
348  * is returned. Otherwise a null pointer is returned.
349  */
350 nt_domain_t *
351 nt_domain_lookupbytype(nt_domain_type_t type)
352 {
353 	nt_domain_t *domain = nt_domain_list;
354 
355 	(void) rw_rdlock(&nt_domain_lock);
356 	while (domain) {
357 		if (domain->type == type)
358 			break;
359 
360 		domain = domain->next;
361 	}
362 	(void) rw_unlock(&nt_domain_lock);
363 
364 	return (domain);
365 }
366 
367 
368 /*
369  * nt_domain_local_sid
370  *
371  * Return a pointer to the local domain SID. Each system has a SID that
372  * represents the local domain, which is named after the local hostname.
373  * The local domain SID must exist.
374  */
375 smb_sid_t *
376 nt_domain_local_sid(void)
377 {
378 	nt_domain_t *domain = nt_domain_list;
379 
380 	(void) rw_rdlock(&nt_domain_lock);
381 	while (domain) {
382 		if (domain->type == NT_DOMAIN_LOCAL)
383 			break;
384 
385 		domain = domain->next;
386 	}
387 	(void) rw_unlock(&nt_domain_lock);
388 
389 	return (domain->sid);
390 }
391 
392 
393 static void
394 nt_domain_unlist(nt_domain_t *domain)
395 {
396 	nt_domain_t **ppdomain = &nt_domain_list;
397 
398 	while (*ppdomain) {
399 		if (*ppdomain == domain) {
400 			*ppdomain = domain->next;
401 			domain->next = NULL;
402 			return;
403 		}
404 		ppdomain = &(*ppdomain)->next;
405 	}
406 }
407 
408 /*
409  * Write the list of domains to /var/run/smb/domains.
410  */
411 void
412 nt_domain_save(void)
413 {
414 	char		fname[MAXPATHLEN];
415 	char		sidstr[SMB_SID_STRSZ];
416 	char		tag;
417 	nt_domain_t	*domain;
418 	FILE		*fp;
419 	struct passwd	*pwd;
420 	struct group	*grp;
421 	uid_t		uid;
422 	gid_t		gid;
423 
424 	(void) snprintf(fname, MAXPATHLEN, "%s/%s",
425 	    SMB_VARRUN_DIR, SMB_DOMAINS_FILE);
426 
427 	if ((fp = fopen(fname, "w")) == NULL)
428 		return;
429 
430 	pwd = getpwnam("root");
431 	grp = getgrnam("sys");
432 	uid = (pwd == NULL) ? 0 : pwd->pw_uid;
433 	gid = (grp == NULL) ? 3 : grp->gr_gid;
434 
435 	(void) lockf(fileno(fp), F_LOCK, 0);
436 	(void) fchmod(fileno(fp), 0600);
437 	(void) fchown(fileno(fp), uid, gid);
438 
439 	(void) rw_rdlock(&nt_domain_lock);
440 
441 	domain = nt_domain_list;
442 	while (domain) {
443 		smb_sid_tostr(domain->sid, sidstr);
444 		switch (domain->type) {
445 		case NT_DOMAIN_PRIMARY:
446 			tag = '*';
447 			break;
448 
449 		case NT_DOMAIN_TRUSTED:
450 		case NT_DOMAIN_UNTRUSTED:
451 			tag = '-';
452 			break;
453 
454 		case NT_DOMAIN_LOCAL:
455 			tag = '.';
456 			break;
457 		default:
458 			domain = domain->next;
459 			continue;
460 		}
461 
462 		(void) fprintf(fp, "[%c] [%s] [%s]\n",
463 		    tag, domain->name, sidstr);
464 
465 		domain = domain->next;
466 	}
467 
468 	(void) rw_unlock(&nt_domain_lock);
469 	(void) lockf(fileno(fp), F_ULOCK, 0);
470 	(void) fclose(fp);
471 }
472 
473 /*
474  * List the domains in /var/run/smb/domains.
475  */
476 void
477 nt_domain_show(void)
478 {
479 	char buf[MAXPATHLEN];
480 	char *p;
481 	FILE *fp;
482 
483 	(void) snprintf(buf, MAXPATHLEN, "%s/%s",
484 	    SMB_VARRUN_DIR, SMB_DOMAINS_FILE);
485 
486 	if ((fp = fopen(buf, "r")) != NULL) {
487 		(void) lockf(fileno(fp), F_LOCK, 0);
488 
489 		while (fgets(buf, MAXPATHLEN, fp) != NULL) {
490 			if ((p = strchr(buf, '\n')) != NULL)
491 				*p = '\0';
492 			(void) printf("%s\n", buf);
493 		}
494 
495 		(void) lockf(fileno(fp), F_ULOCK, 0);
496 		(void) fclose(fp);
497 	}
498 }
499 
500 /*
501  * Remove the /var/run/smb/domains file.
502  */
503 void
504 nt_domain_unlink(void)
505 {
506 	char fname[MAXPATHLEN];
507 
508 	(void) snprintf(fname, MAXPATHLEN, "%s/%s",
509 	    SMB_VARRUN_DIR, SMB_DOMAINS_FILE);
510 	(void) unlink(fname);
511 }
512