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 2007 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 /*
29  * This module provides the high level interface to the LSA RPC functions.
30  */
31 
32 #include <strings.h>
33 #include <unistd.h>
34 #include <netdb.h>
35 
36 #include <smbsrv/libsmb.h>
37 #include <smbsrv/libsmbns.h>
38 #include <smbsrv/libmlsvc.h>
39 #include <smbsrv/lsalib.h>
40 #include <smbsrv/ntstatus.h>
41 #include <smbsrv/smbinfo.h>
42 #include <smbsrv/ntsid.h>
43 #include <smbsrv/smb_token.h>
44 
45 static int lsa_list_accounts(mlsvc_handle_t *);
46 
47 /*
48  * lsa_query_primary_domain_info
49  *
50  * Obtains the primary domain SID and name from the specified server
51  * (domain controller). The information is stored in the NT domain
52  * database by the lower level lsar_query_info_policy call. The caller
53  * should query the database to obtain a reference to the primary
54  * domain information.
55  *
56  * Returns NT status codes.
57  */
58 DWORD
59 lsa_query_primary_domain_info(void)
60 {
61 	mlsvc_handle_t domain_handle;
62 	DWORD status;
63 
64 	if ((lsar_open(MLSVC_IPC_ANON, 0, 0, 0, 0, &domain_handle)) != 0)
65 		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
66 
67 	status = lsar_query_info_policy(&domain_handle,
68 	    MSLSA_POLICY_PRIMARY_DOMAIN_INFO);
69 
70 	(void) lsar_close(&domain_handle);
71 	return (status);
72 }
73 
74 /*
75  * lsa_query_account_domain_info
76  *
77  * Obtains the account domain SID and name from the current server
78  * (domain controller). The information is stored in the NT domain
79  * database by the lower level lsar_query_info_policy call. The caller
80  * should query the database to obtain a reference to the account
81  * domain information.
82  *
83  * Returns NT status codes.
84  */
85 DWORD
86 lsa_query_account_domain_info(void)
87 {
88 	mlsvc_handle_t domain_handle;
89 	DWORD status;
90 
91 	if ((lsar_open(MLSVC_IPC_ANON, 0, 0, 0, 0, &domain_handle)) != 0)
92 		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
93 
94 	status = lsar_query_info_policy(&domain_handle,
95 	    MSLSA_POLICY_ACCOUNT_DOMAIN_INFO);
96 
97 	(void) lsar_close(&domain_handle);
98 	return (status);
99 }
100 
101 /*
102  * lsa_enum_trusted_domains
103  *
104  * Enumerate the trusted domains in our primary domain. The information
105  * is stored in the NT domain database by the lower level
106  * lsar_enum_trusted_domains call. The caller should query the database
107  * to obtain a reference to the trusted domain information.
108  *
109  * Returns NT status codes.
110  */
111 DWORD
112 lsa_enum_trusted_domains(void)
113 {
114 	mlsvc_handle_t domain_handle;
115 	DWORD enum_context;
116 	DWORD status;
117 
118 	if ((lsar_open(MLSVC_IPC_ANON, 0, 0, 0, 0, &domain_handle)) != 0)
119 		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
120 
121 	enum_context = 0;
122 
123 	status = lsar_enum_trusted_domains(&domain_handle, &enum_context);
124 	if (status == MLSVC_NO_MORE_DATA) {
125 		/*
126 		 * MLSVC_NO_MORE_DATA indicates that we
127 		 * have all of the available information.
128 		 */
129 		status = NT_STATUS_SUCCESS;
130 	}
131 
132 	(void) lsar_close(&domain_handle);
133 	return (status);
134 }
135 
136 /*
137  * lsa_test_lookup
138  *
139  * Test routine for lsa_lookup_name and lsa_lookup_sid.
140  */
141 void
142 lsa_test_lookup(char *name)
143 {
144 	smb_userinfo_t *user_info;
145 	nt_sid_t *sid;
146 	DWORD status;
147 	smb_ntdomain_t *di;
148 
149 	if ((di = smb_getdomaininfo(0)) == 0)
150 		return;
151 
152 	user_info = mlsvc_alloc_user_info();
153 
154 	if (lsa_lookup_builtin_name(name, user_info) != 0) {
155 		status = lsa_lookup_name(di->server, di->domain, name,
156 		    user_info);
157 
158 		if (status == 0) {
159 			sid = nt_sid_splice(user_info->domain_sid,
160 			    user_info->rid);
161 
162 			(void) lsa_lookup_sid(sid, user_info);
163 			free(sid);
164 		}
165 	}
166 
167 	mlsvc_free_user_info(user_info);
168 }
169 
170 /*
171  * lsa_lookup_builtin_name
172  *
173  * lookup builtin account table to see if account_name is
174  * there. If it is there, set sid_name_use, domain_sid,
175  * domain_name, and rid fields of the passed user_info
176  * structure and return 0. If lookup fails return 1.
177  */
178 int
179 lsa_lookup_builtin_name(char *account_name, smb_userinfo_t *user_info)
180 {
181 	char *domain;
182 	int res;
183 
184 	user_info->domain_sid = nt_builtin_lookup_name(account_name,
185 	    &user_info->sid_name_use);
186 
187 	if (user_info->domain_sid == 0)
188 		return (1);
189 
190 	res = nt_sid_split(user_info->domain_sid, &user_info->rid);
191 	if (res < 0)
192 		return (1);
193 
194 	domain = nt_builtin_lookup_domain(account_name);
195 	if (domain) {
196 		user_info->domain_name = strdup(domain);
197 		return (0);
198 	}
199 
200 	return (1);
201 }
202 
203 /*
204  * lsa_lookup_local_sam
205  *
206  * lookup for the given account name in the local SAM database.
207  * Returns 0 on success. If lookup fails return 1.
208  */
209 int
210 lsa_lookup_local_sam(char *domain, char *account_name,
211     smb_userinfo_t *user_info)
212 {
213 	nt_group_t *grp;
214 
215 	if (*domain == '\0' || *account_name == '\0')
216 		return (1);
217 
218 	grp = nt_group_getinfo(account_name, RWLOCK_READER);
219 	if (grp == 0)
220 		return (1);
221 
222 	user_info->sid_name_use = *grp->sid_name_use;
223 	user_info->domain_sid = nt_sid_dup(grp->sid);
224 	nt_group_putinfo(grp);
225 
226 	if (user_info->domain_sid == 0)
227 		return (1);
228 
229 	(void) nt_sid_split(user_info->domain_sid, &user_info->rid);
230 	user_info->domain_name = strdup(domain);
231 
232 	if (user_info->domain_name == 0) {
233 		free(user_info->domain_sid);
234 		user_info->domain_sid = 0;
235 		return (1);
236 	}
237 
238 	return (0);
239 }
240 
241 /*
242  * lsa_lookup_local
243  *
244  * if given account name has domain part, check to see if
245  * it matches with host name or any of host's primary addresses.
246  * if any match found first lookup in builtin accounts table and
247  * then in local SAM table.
248  *
249  * if account name doesn't have domain part, first do local lookups
250  * if nothing is found return 1. This means that caller function should
251  * do domain lookup.
252  * if any error happened return -1, if name is found return 0.
253  */
254 int
255 lsa_lookup_local(char *name, smb_userinfo_t *user_info)
256 {
257 	char hostname[MAXHOSTNAMELEN];
258 	int res = 0;
259 	int local_lookup = 0;
260 	char *tmp;
261 	net_cfg_t cfg;
262 	uint32_t addr;
263 
264 	if (smb_gethostname(hostname, MAXHOSTNAMELEN, 1) != 0)
265 		return (-1);
266 
267 	tmp = strchr(name, '\\');
268 	if (tmp != 0) {
269 		*tmp = 0;
270 		if (strcasecmp(name, hostname) == 0)
271 			local_lookup = 1;
272 
273 		if (!local_lookup) {
274 			addr = inet_addr(name);
275 			if (smb_nic_get_byip(addr, &cfg) != NULL) {
276 				local_lookup = 1;
277 			}
278 		}
279 
280 		if (!local_lookup) {
281 			/* do domain lookup */
282 			*tmp = '\\';
283 			return (1);
284 		}
285 
286 		name = tmp + 1;
287 		local_lookup = 1;
288 	}
289 
290 	res = lsa_lookup_builtin_name(name, user_info);
291 	if (res != 0)
292 		res = lsa_lookup_local_sam(hostname, name, user_info);
293 
294 	if (res == 0)
295 		return (0);
296 
297 	if (local_lookup)
298 		return (-1);
299 
300 	return (1);
301 }
302 
303 /*
304  * lsa_lookup_name
305  *
306  * Lookup a name on the specified server (domain controller) and obtain
307  * the appropriate SID. The information is returned in the user_info
308  * structure. The caller is responsible for allocating and releasing
309  * this structure. On success sid_name_use will be set to indicate the
310  * type of SID. If the name is the domain name, this function will be
311  * identical to lsa_domain_info. Otherwise the rid and name fields will
312  * also be valid. On failure sid_name_use will be set to SidTypeUnknown.
313  *
314  * On success 0 is returned. Otherwise a -ve error code.
315  */
316 int lsa_lookup_name(char *server, char *domain, char *account_name,
317     smb_userinfo_t *user_info)
318 {
319 	mlsvc_handle_t domain_handle;
320 	int rc;
321 
322 	rc = lsar_open(MLSVC_IPC_ANON, server, domain, 0, 0, &domain_handle);
323 	if (rc != 0)
324 		return (-1);
325 
326 	rc = lsar_lookup_names(&domain_handle, account_name, user_info);
327 
328 	(void) lsar_close(&domain_handle);
329 	return (rc);
330 }
331 
332 /*
333  * lsa_lookup_name2
334  *
335  * Returns NT status codes.
336  */
337 DWORD lsa_lookup_name2(char *server, char *domain, char *account_name,
338     smb_userinfo_t *user_info)
339 {
340 	mlsvc_handle_t domain_handle;
341 	DWORD status;
342 	int rc;
343 
344 	rc = lsar_open(MLSVC_IPC_ANON, server, domain, 0, 0, &domain_handle);
345 	if (rc != 0)
346 		return (NT_STATUS_INVALID_PARAMETER);
347 
348 	status = lsar_lookup_names2(&domain_handle, account_name, user_info);
349 	if (status == NT_STATUS_REVISION_MISMATCH) {
350 		/*
351 		 * Not a Windows 2000 domain controller:
352 		 * use the NT compatible call.
353 		 */
354 		if (lsar_lookup_names(&domain_handle, account_name,
355 		    user_info) != 0)
356 			status = NT_STATUS_NONE_MAPPED;
357 		else
358 			status = 0;
359 	}
360 
361 	(void) lsar_close(&domain_handle);
362 	return (status);
363 }
364 
365 /*
366  * lsa_lookup_sid
367  *
368  * Lookup a SID on the specified server (domain controller) and obtain
369  * the appropriate name. The information is returned in the user_info
370  * structure. The caller is responsible for allocating and releasing
371  * this structure. On success sid_name_use will be set to indicate the
372  * type of SID. On failure sid_name_use will be set to SidTypeUnknown.
373  *
374  * On success 0 is returned. Otherwise a -ve error code.
375  */
376 int
377 lsa_lookup_sid(nt_sid_t *sid, smb_userinfo_t *user_info)
378 {
379 	mlsvc_handle_t domain_handle;
380 	int rc;
381 
382 	rc = lsar_open(MLSVC_IPC_ANON, 0, 0, 0, 0, &domain_handle);
383 	if (rc != 0)
384 		return (-1);
385 
386 	rc = lsar_lookup_sids(&domain_handle,
387 	    (struct mslsa_sid *)sid, user_info);
388 
389 	(void) lsar_close(&domain_handle);
390 	return (rc);
391 }
392 
393 /*
394  * lsa_lookup_sid2
395  *
396  * Returns NT status codes.
397  */
398 DWORD
399 lsa_lookup_sid2(nt_sid_t *sid, smb_userinfo_t *user_info)
400 {
401 	mlsvc_handle_t domain_handle;
402 	DWORD status;
403 	int rc;
404 
405 	rc = lsar_open(MLSVC_IPC_ANON, 0, 0, 0, 0, &domain_handle);
406 	if (rc != 0)
407 		return (NT_STATUS_INVALID_PARAMETER);
408 
409 	status = lsar_lookup_sids2(&domain_handle,
410 	    (struct mslsa_sid *)sid, user_info);
411 
412 	if (status == NT_STATUS_REVISION_MISMATCH) {
413 		/*
414 		 * Not a Windows 2000 domain controller:
415 		 * use the NT compatible call.
416 		 */
417 		if (lsar_lookup_sids(&domain_handle, (struct mslsa_sid *)sid,
418 		    user_info) != 0)
419 			status = NT_STATUS_NONE_MAPPED;
420 		else
421 			status = 0;
422 	}
423 
424 	(void) lsar_close(&domain_handle);
425 	return (status);
426 }
427 
428 /*
429  * lsa_test_lookup2
430  *
431  * Test routine for lsa_lookup_name2 and lsa_lookup_sid2.
432  */
433 void
434 lsa_test_lookup2(char *name)
435 {
436 	smb_userinfo_t *user_info;
437 	nt_sid_t *sid;
438 	DWORD status;
439 	smb_ntdomain_t *di;
440 
441 	if ((di = smb_getdomaininfo(0)) == 0)
442 		return;
443 
444 	user_info = mlsvc_alloc_user_info();
445 
446 	if (lsa_lookup_builtin_name(name, user_info) != 0) {
447 		status = lsa_lookup_name2(di->server, di->domain, name,
448 		    user_info);
449 
450 		if (status == 0) {
451 			sid = nt_sid_splice(user_info->domain_sid,
452 			    user_info->rid);
453 
454 			(void) lsa_lookup_sid2(sid, user_info);
455 			free(sid);
456 		}
457 	}
458 
459 	mlsvc_free_user_info(user_info);
460 }
461 
462 /*
463  * lsa_lookup_privs
464  *
465  * Request the privileges associated with the specified account. In
466  * order to get the privileges, we first have to lookup the name on
467  * the specified domain controller and obtain the appropriate SID.
468  * The SID can then be used to open the account and obtain the
469  * account privileges. The results from both the name lookup and the
470  * privileges are returned in the user_info structure. The caller is
471  * responsible for allocating and releasing this structure.
472  *
473  * On success 0 is returned. Otherwise a -ve error code.
474  */
475 /*ARGSUSED*/
476 int
477 lsa_lookup_privs(char *server, char *account_name, char *target_name,
478     smb_userinfo_t *user_info)
479 {
480 	mlsvc_handle_t domain_handle;
481 	int rc;
482 #if 0
483 	mlsvc_handle_t account_handle;
484 	struct mslsa_sid *sid;
485 
486 	lsa_lookup_name(0, 0, target_name, user_info);
487 
488 	sid = (struct mslsa_sid *)
489 	    nt_sid_splice(user_info->domain_sid, user_info->rid);
490 
491 	lsa_lookup_sid(server, account_name, (nt_sid_t *)sid, user_info);
492 #endif
493 	if ((lsar_open(MLSVC_IPC_ANON, 0, 0, 0, 0, &domain_handle)) != 0)
494 		return (-1);
495 
496 	rc = lsa_list_accounts(&domain_handle);
497 #if 0
498 	rc = lsar_open_account(&domain_handle, sid, &account_handle);
499 	if (rc == 0) {
500 		(void) lsar_enum_privs_account(&account_handle, user_info);
501 		(void) lsar_close(&account_handle);
502 	}
503 
504 	free(sid);
505 #endif
506 	(void) lsar_close(&domain_handle);
507 	return (rc);
508 }
509 
510 /*
511  * lsa_list_privs
512  *
513  * List the privileges supported by the specified server.
514  * This function is only intended for diagnostics.
515  *
516  * Returns NT status codes.
517  */
518 DWORD
519 lsa_list_privs(char *server, char *domain)
520 {
521 	static char name[128];
522 	static struct ms_luid luid;
523 	mlsvc_handle_t domain_handle;
524 	int rc;
525 	int i;
526 
527 	rc = lsar_open(MLSVC_IPC_ANON, server, domain, 0, 0, &domain_handle);
528 	if (rc != 0)
529 		return (NT_STATUS_INVALID_PARAMETER);
530 
531 	for (i = 0; i < 30; ++i) {
532 		luid.low_part = i;
533 		rc = lsar_lookup_priv_name(&domain_handle, &luid, name, 128);
534 		if (rc != 0)
535 			continue;
536 
537 		(void) lsar_lookup_priv_value(&domain_handle, name, &luid);
538 		(void) lsar_lookup_priv_display_name(&domain_handle, name,
539 		    name, 128);
540 	}
541 
542 	(void) lsar_close(&domain_handle);
543 	return (NT_STATUS_SUCCESS);
544 }
545 
546 /*
547  * lsa_test
548  *
549  * LSA test routine: open and close the LSA interface.
550  * TBD: the parameters should be server and domain.
551  *
552  * On success 0 is returned. Otherwise a -ve error code.
553  */
554 /*ARGSUSED*/
555 int
556 lsa_test(char *server, char *account_name)
557 {
558 	mlsvc_handle_t domain_handle;
559 	int rc;
560 
561 	rc = lsar_open(MLSVC_IPC_ANON, 0, 0, 0, 0, &domain_handle);
562 	if (rc != 0)
563 		return (-1);
564 
565 	if (lsar_close(&domain_handle) != 0)
566 		return (-1);
567 
568 	return (0);
569 }
570 
571 /*
572  * lsa_list_accounts
573  *
574  * This function can be used to list the accounts in the specified
575  * domain. For now the SIDs are just listed in the system log.
576  *
577  * On success 0 is returned. Otherwise a -ve error code.
578  */
579 static int
580 lsa_list_accounts(mlsvc_handle_t *domain_handle)
581 {
582 	mlsvc_handle_t account_handle;
583 	struct mslsa_EnumAccountBuf accounts;
584 	struct mslsa_sid *sid;
585 	char *name;
586 	WORD sid_name_use;
587 	smb_userinfo_t *user_info;
588 	DWORD enum_context = 0;
589 	int rc;
590 	int i;
591 
592 	user_info = mlsvc_alloc_user_info();
593 	bzero(&accounts, sizeof (struct mslsa_EnumAccountBuf));
594 
595 	do {
596 		rc = lsar_enum_accounts(domain_handle, &enum_context,
597 		    &accounts);
598 		if (rc != 0)
599 			return (rc);
600 
601 		for (i = 0; i < accounts.entries_read; ++i) {
602 			sid = accounts.info[i].sid;
603 
604 			name = nt_builtin_lookup_sid((nt_sid_t *)sid,
605 			    &sid_name_use);
606 
607 			if (name == 0) {
608 				if (lsar_lookup_sids(domain_handle, sid,
609 				    user_info) == 0) {
610 					name = user_info->name;
611 					sid_name_use = user_info->sid_name_use;
612 				} else {
613 					name = "unknown";
614 					sid_name_use = SidTypeUnknown;
615 				}
616 			}
617 
618 			nt_sid_logf((nt_sid_t *)sid);
619 
620 			if (lsar_open_account(domain_handle, sid,
621 			    &account_handle) == 0) {
622 				(void) lsar_enum_privs_account(&account_handle,
623 				    user_info);
624 				(void) lsar_close(&account_handle);
625 			}
626 
627 			free(accounts.info[i].sid);
628 			mlsvc_release_user_info(user_info);
629 		}
630 
631 		if (accounts.info)
632 			free(accounts.info);
633 	} while (rc == 0 && accounts.entries_read != 0);
634 
635 	mlsvc_free_user_info(user_info);
636 	return (0);
637 }
638