xref: /illumos-gate/usr/src/lib/smbsrv/libsmb/common/smb_domain.c (revision 89dc44ce9705974a8bc4a39f1e878a0491a5be61)
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 	nt_domain_t *domain = nt_domain_list;
297 
298 	(void) rw_rdlock(&nt_domain_lock);
299 	while (domain) {
300 		if (utf8_strcasecmp(domain->name, domain_name) == 0)
301 			break;
302 
303 		domain = domain->next;
304 	}
305 	(void) rw_unlock(&nt_domain_lock);
306 
307 	return (domain);
308 }
309 
310 
311 /*
312  * nt_domain_lookup_sid
313  *
314  * Lookup a domain by its domain SID. If the domain is in the list,
315  * a pointer to it is returned. Otherwise a null pointer is returned.
316  */
317 nt_domain_t *
318 nt_domain_lookup_sid(smb_sid_t *domain_sid)
319 {
320 	nt_domain_t *domain = nt_domain_list;
321 
322 	(void) rw_rdlock(&nt_domain_lock);
323 	while (domain) {
324 		if (smb_sid_cmp(domain->sid, domain_sid))
325 			break;
326 
327 		domain = domain->next;
328 	}
329 	(void) rw_unlock(&nt_domain_lock);
330 
331 	return (domain);
332 }
333 
334 
335 /*
336  * nt_domain_lookupbytype
337  *
338  * Lookup a domain by its type. The first matching entry in the list
339  * is returned. Otherwise a null pointer is returned.
340  */
341 nt_domain_t *
342 nt_domain_lookupbytype(nt_domain_type_t type)
343 {
344 	nt_domain_t *domain = nt_domain_list;
345 
346 	(void) rw_rdlock(&nt_domain_lock);
347 	while (domain) {
348 		if (domain->type == type)
349 			break;
350 
351 		domain = domain->next;
352 	}
353 	(void) rw_unlock(&nt_domain_lock);
354 
355 	return (domain);
356 }
357 
358 
359 /*
360  * nt_domain_local_sid
361  *
362  * Return a pointer to the local domain SID. Each system has a SID that
363  * represents the local domain, which is named after the local hostname.
364  * The local domain SID must exist.
365  */
366 smb_sid_t *
367 nt_domain_local_sid(void)
368 {
369 	nt_domain_t *domain = nt_domain_list;
370 
371 	(void) rw_rdlock(&nt_domain_lock);
372 	while (domain) {
373 		if (domain->type == NT_DOMAIN_LOCAL)
374 			break;
375 
376 		domain = domain->next;
377 	}
378 	(void) rw_unlock(&nt_domain_lock);
379 
380 	return (domain->sid);
381 }
382 
383 
384 static void
385 nt_domain_unlist(nt_domain_t *domain)
386 {
387 	nt_domain_t **ppdomain = &nt_domain_list;
388 
389 	while (*ppdomain) {
390 		if (*ppdomain == domain) {
391 			*ppdomain = domain->next;
392 			domain->next = NULL;
393 			return;
394 		}
395 		ppdomain = &(*ppdomain)->next;
396 	}
397 }
398 
399 /*
400  * Write the list of domains to /var/run/smb/domains.
401  */
402 void
403 nt_domain_save(void)
404 {
405 	char		fname[MAXPATHLEN];
406 	char		sidstr[SMB_SID_STRSZ];
407 	char		tag;
408 	nt_domain_t	*domain;
409 	FILE		*fp;
410 	struct passwd	*pwd;
411 	struct group	*grp;
412 	uid_t		uid;
413 	gid_t		gid;
414 
415 	(void) snprintf(fname, MAXPATHLEN, "%s/%s",
416 	    SMB_VARRUN_DIR, SMB_DOMAINS_FILE);
417 
418 	if ((fp = fopen(fname, "w")) == NULL)
419 		return;
420 
421 	pwd = getpwnam("root");
422 	grp = getgrnam("sys");
423 	uid = (pwd == NULL) ? 0 : pwd->pw_uid;
424 	gid = (grp == NULL) ? 3 : grp->gr_gid;
425 
426 	(void) lockf(fileno(fp), F_LOCK, 0);
427 	(void) fchmod(fileno(fp), 0600);
428 	(void) fchown(fileno(fp), uid, gid);
429 
430 	(void) rw_rdlock(&nt_domain_lock);
431 
432 	domain = nt_domain_list;
433 	while (domain) {
434 		smb_sid_tostr(domain->sid, sidstr);
435 		switch (domain->type) {
436 		case NT_DOMAIN_PRIMARY:
437 			tag = '*';
438 			break;
439 
440 		case NT_DOMAIN_TRUSTED:
441 		case NT_DOMAIN_UNTRUSTED:
442 			tag = '-';
443 			break;
444 
445 		case NT_DOMAIN_LOCAL:
446 			tag = '.';
447 			break;
448 		default:
449 			domain = domain->next;
450 			continue;
451 		}
452 
453 		(void) fprintf(fp, "[%c] [%s] [%s]\n",
454 		    tag, domain->name, sidstr);
455 
456 		domain = domain->next;
457 	}
458 
459 	(void) rw_unlock(&nt_domain_lock);
460 	(void) lockf(fileno(fp), F_ULOCK, 0);
461 	(void) fclose(fp);
462 }
463 
464 /*
465  * List the domains in /var/run/smb/domains.
466  */
467 void
468 nt_domain_show(void)
469 {
470 	char buf[MAXPATHLEN];
471 	char *p;
472 	FILE *fp;
473 
474 	(void) snprintf(buf, MAXPATHLEN, "%s/%s",
475 	    SMB_VARRUN_DIR, SMB_DOMAINS_FILE);
476 
477 	if ((fp = fopen(buf, "r")) != NULL) {
478 		(void) lockf(fileno(fp), F_LOCK, 0);
479 
480 		while (fgets(buf, MAXPATHLEN, fp) != NULL) {
481 			if ((p = strchr(buf, '\n')) != NULL)
482 				*p = '\0';
483 			(void) printf("%s\n", buf);
484 		}
485 
486 		(void) lockf(fileno(fp), F_ULOCK, 0);
487 		(void) fclose(fp);
488 	}
489 }
490 
491 /*
492  * Remove the /var/run/smb/domains file.
493  */
494 void
495 nt_domain_unlink(void)
496 {
497 	char fname[MAXPATHLEN];
498 
499 	(void) snprintf(fname, MAXPATHLEN, "%s/%s",
500 	    SMB_VARRUN_DIR, SMB_DOMAINS_FILE);
501 	(void) unlink(fname);
502 }
503