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 module provides the high level interface to the LSA RPC functions.
28  */
29 
30 #include <strings.h>
31 #include <unistd.h>
32 
33 #include <smbsrv/libsmb.h>
34 #include <smbsrv/libmlsvc.h>
35 #include <smbsrv/libsmbrdr.h>
36 #include <smbsrv/ntstatus.h>
37 #include <smbsrv/smbinfo.h>
38 #include <smbsrv/smb_token.h>
39 
40 #include <lsalib.h>
41 
42 static uint32_t lsa_lookup_name_builtin(char *, char *, smb_account_t *);
43 static uint32_t lsa_lookup_name_domain(char *, smb_account_t *);
44 
45 static uint32_t lsa_lookup_sid_builtin(smb_sid_t *, smb_account_t *);
46 static uint32_t lsa_lookup_sid_domain(smb_sid_t *, smb_account_t *);
47 
48 static int lsa_list_accounts(mlsvc_handle_t *);
49 
50 /*
51  * Lookup the given account and returns the account information
52  * in the passed smb_account_t structure.
53  *
54  * The lookup is performed in the following order:
55  *    well known accounts
56  *    local accounts
57  *    domain accounts
58  *
59  * If it's established the given account is well know or local
60  * but the lookup fails for some reason, the next step(s) won't be
61  * performed.
62  *
63  * If the name is a domain account, it may refer to a user, group or
64  * alias. If it is a local account, its type should be specified
65  * in the sid_type parameter. In case the account type is unknown
66  * sid_type should be set to SidTypeUnknown.
67  *
68  * account argument could be either [domain\]name or [domain/]name.
69  *
70  * Return status:
71  *
72  *   NT_STATUS_SUCCESS		Account is successfully translated
73  *   NT_STATUS_NONE_MAPPED	Couldn't translate the account
74  */
75 uint32_t
76 lsa_lookup_name(char *account, uint16_t type, smb_account_t *info)
77 {
78 	char nambuf[SMB_USERNAME_MAXLEN];
79 	char dombuf[SMB_PI_MAX_DOMAIN];
80 	char *name, *domain;
81 	uint32_t status;
82 	char *slash;
83 
84 	(void) strsubst(account, '/', '\\');
85 	(void) strcanon(account, "\\");
86 	/* \john -> john */
87 	account += strspn(account, "\\");
88 
89 	if ((slash = strchr(account, '\\')) != NULL) {
90 		*slash = '\0';
91 		(void) strlcpy(dombuf, account, sizeof (dombuf));
92 		(void) strlcpy(nambuf, slash + 1, sizeof (nambuf));
93 		*slash = '\\';
94 		name = nambuf;
95 		domain = dombuf;
96 	} else {
97 		name = account;
98 		domain = NULL;
99 	}
100 
101 	status = lsa_lookup_name_builtin(domain, name, info);
102 	if (status == NT_STATUS_NOT_FOUND) {
103 		status = smb_sam_lookup_name(domain, name, type, info);
104 		if (status == NT_STATUS_SUCCESS)
105 			return (status);
106 
107 		if ((domain == NULL) || (status == NT_STATUS_NOT_FOUND))
108 			status = lsa_lookup_name_domain(account, info);
109 	}
110 
111 	return ((status == NT_STATUS_SUCCESS) ? status : NT_STATUS_NONE_MAPPED);
112 }
113 
114 uint32_t
115 lsa_lookup_sid(smb_sid_t *sid, smb_account_t *info)
116 {
117 	uint32_t status;
118 
119 	if (!smb_sid_isvalid(sid))
120 		return (NT_STATUS_INVALID_SID);
121 
122 	status = lsa_lookup_sid_builtin(sid, info);
123 	if (status == NT_STATUS_NOT_FOUND) {
124 		status = smb_sam_lookup_sid(sid, info);
125 		if (status == NT_STATUS_NOT_FOUND)
126 			status = lsa_lookup_sid_domain(sid, info);
127 	}
128 
129 	return ((status == NT_STATUS_SUCCESS) ? status : NT_STATUS_NONE_MAPPED);
130 }
131 
132 /*
133  * Obtains the primary domain SID and name from the specified server
134  * (domain controller).
135  *
136  * The requested information will be returned via 'info' argument.
137  *
138  * Returns NT status codes.
139  */
140 DWORD
141 lsa_query_primary_domain_info(char *server, char *domain,
142     nt_domain_t *info)
143 {
144 	mlsvc_handle_t domain_handle;
145 	DWORD status;
146 	char *user = smbrdr_ipc_get_user();
147 
148 	if ((lsar_open(server, domain, user, &domain_handle)) != 0)
149 		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
150 
151 	status = lsar_query_info_policy(&domain_handle,
152 	    MSLSA_POLICY_PRIMARY_DOMAIN_INFO, info);
153 
154 	(void) lsar_close(&domain_handle);
155 	return (status);
156 }
157 
158 /*
159  * Obtains the account domain SID and name from the current server
160  * (domain controller).
161  *
162  * The requested information will be returned via 'info' argument.
163  *
164  * Returns NT status codes.
165  */
166 DWORD
167 lsa_query_account_domain_info(char *server, char *domain,
168     nt_domain_t *info)
169 {
170 	mlsvc_handle_t domain_handle;
171 	DWORD status;
172 	char *user = smbrdr_ipc_get_user();
173 
174 	if ((lsar_open(server, domain, user, &domain_handle)) != 0)
175 		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
176 
177 	status = lsar_query_info_policy(&domain_handle,
178 	    MSLSA_POLICY_ACCOUNT_DOMAIN_INFO, info);
179 
180 	(void) lsar_close(&domain_handle);
181 	return (status);
182 }
183 
184 /*
185  * lsa_query_dns_domain_info
186  *
187  * Obtains the DNS domain info from the specified server
188  * (domain controller).
189  *
190  * The requested information will be returned via 'info' argument.
191  *
192  * Returns NT status codes.
193  */
194 DWORD
195 lsa_query_dns_domain_info(char *server, char *domain, nt_domain_t *info)
196 {
197 	mlsvc_handle_t domain_handle;
198 	DWORD status;
199 	char *user = smbrdr_ipc_get_user();
200 
201 	if ((lsar_open(server, domain, user, &domain_handle)) != 0)
202 		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
203 
204 	status = lsar_query_info_policy(&domain_handle,
205 	    MSLSA_POLICY_DNS_DOMAIN_INFO, info);
206 
207 	(void) lsar_close(&domain_handle);
208 	return (status);
209 }
210 
211 /*
212  * Enumerate the trusted domains of  primary domain.
213  * This is the basic enumaration call which only returns the
214  * NetBIOS name of the domain and its SID.
215  *
216  * The requested information will be returned via 'info' argument.
217  *
218  * Returns NT status codes.
219  */
220 DWORD
221 lsa_enum_trusted_domains(char *server, char *domain,
222     smb_trusted_domains_t *info)
223 {
224 	mlsvc_handle_t domain_handle;
225 	DWORD enum_context;
226 	DWORD status;
227 	char *user = smbrdr_ipc_get_user();
228 
229 	if ((lsar_open(server, domain, user, &domain_handle)) != 0)
230 		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
231 
232 	enum_context = 0;
233 
234 	status = lsar_enum_trusted_domains(&domain_handle, &enum_context, info);
235 	if (status == MLSVC_NO_MORE_DATA) {
236 		/*
237 		 * MLSVC_NO_MORE_DATA indicates that we
238 		 * have all of the available information.
239 		 */
240 		status = NT_STATUS_SUCCESS;
241 	}
242 
243 	(void) lsar_close(&domain_handle);
244 	return (status);
245 }
246 
247 /*
248  * Enumerate the trusted domains of the primary domain.
249  * This is the extended enumaration call which besides
250  * NetBIOS name of the domain and its SID, it will return
251  * the FQDN plus some trust information which is not used.
252  *
253  * The requested information will be returned via 'info' argument.
254  *
255  * Returns NT status codes.
256  */
257 DWORD
258 lsa_enum_trusted_domains_ex(char *server, char *domain,
259     smb_trusted_domains_t *info)
260 {
261 	mlsvc_handle_t domain_handle;
262 	DWORD enum_context;
263 	DWORD status;
264 	char *user = smbrdr_ipc_get_user();
265 
266 	if ((lsar_open(server, domain, user, &domain_handle)) != 0)
267 		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
268 
269 	enum_context = 0;
270 
271 	status = lsar_enum_trusted_domains_ex(&domain_handle, &enum_context,
272 	    info);
273 	if (status == MLSVC_NO_MORE_DATA) {
274 		/*
275 		 * MLSVC_NO_MORE_DATA indicates that we
276 		 * have all of the available information.
277 		 */
278 		status = NT_STATUS_SUCCESS;
279 	}
280 
281 	(void) lsar_close(&domain_handle);
282 	return (status);
283 }
284 
285 /*
286  * Lookup well known accounts table
287  *
288  * Return status:
289  *
290  *   NT_STATUS_SUCCESS		Account is translated successfully
291  *   NT_STATUS_NOT_FOUND	This is not a well known account
292  *   NT_STATUS_NONE_MAPPED	Account is found but domains don't match
293  *   NT_STATUS_NO_MEMORY	Memory shortage
294  *   NT_STATUS_INTERNAL_ERROR	Internal error/unexpected failure
295  */
296 static uint32_t
297 lsa_lookup_name_builtin(char *domain, char *name, smb_account_t *info)
298 {
299 	smb_wka_t *wka;
300 	char *wkadom;
301 
302 	bzero(info, sizeof (smb_account_t));
303 
304 	if ((wka = smb_wka_lookup_name(name)) == NULL)
305 		return (NT_STATUS_NOT_FOUND);
306 
307 	if ((wkadom = smb_wka_get_domain(wka->wka_domidx)) == NULL)
308 		return (NT_STATUS_INTERNAL_ERROR);
309 
310 	if ((domain != NULL) && (utf8_strcasecmp(domain, wkadom) != 0))
311 		return (NT_STATUS_NONE_MAPPED);
312 
313 	info->a_name = strdup(name);
314 	info->a_sid = smb_sid_dup(wka->wka_binsid);
315 	info->a_domain = strdup(wkadom);
316 	info->a_domsid = smb_sid_split(wka->wka_binsid, &info->a_rid);
317 	info->a_type = wka->wka_type;
318 
319 	if (!smb_account_validate(info)) {
320 		smb_account_free(info);
321 		return (NT_STATUS_NO_MEMORY);
322 	}
323 
324 	return (NT_STATUS_SUCCESS);
325 }
326 
327 /*
328  * Lookup the given account in domain.
329  *
330  * The information is returned in the user_info structure.
331  * The caller is responsible for allocating and releasing
332  * this structure.
333  */
334 static uint32_t
335 lsa_lookup_name_domain(char *account_name, smb_account_t *info)
336 {
337 	mlsvc_handle_t domain_handle;
338 	smb_domain_t dinfo;
339 	char *user = smbrdr_ipc_get_user();
340 	uint32_t status;
341 
342 	if (!smb_domain_getinfo(&dinfo))
343 		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
344 
345 	if (lsar_open(dinfo.d_dc, dinfo.d_info.di_nbname, user,
346 	    &domain_handle) != 0)
347 		return (NT_STATUS_INVALID_PARAMETER);
348 
349 	status = lsar_lookup_names2(&domain_handle, account_name, info);
350 	if (status == NT_STATUS_REVISION_MISMATCH) {
351 		/*
352 		 * Not a Windows 2000 domain controller:
353 		 * use the NT compatible call.
354 		 */
355 		status = lsar_lookup_names(&domain_handle, account_name, info);
356 	}
357 
358 	(void) lsar_close(&domain_handle);
359 	return (status);
360 }
361 
362 /*
363  * lsa_lookup_privs
364  *
365  * Request the privileges associated with the specified account. In
366  * order to get the privileges, we first have to lookup the name on
367  * the specified domain controller and obtain the appropriate SID.
368  * The SID can then be used to open the account and obtain the
369  * account privileges. The results from both the name lookup and the
370  * privileges are returned in the user_info structure. The caller is
371  * responsible for allocating and releasing this structure.
372  *
373  * On success 0 is returned. Otherwise a -ve error code.
374  */
375 /*ARGSUSED*/
376 int
377 lsa_lookup_privs(char *account_name, char *target_name, smb_account_t *ainfo)
378 {
379 	mlsvc_handle_t domain_handle;
380 	int rc;
381 	char *user = smbrdr_ipc_get_user();
382 	smb_domain_t dinfo;
383 
384 	if (!smb_domain_getinfo(&dinfo))
385 		return (-1);
386 
387 	if ((lsar_open(dinfo.d_dc, dinfo.d_info.di_nbname, user,
388 	    &domain_handle)) != 0)
389 		return (-1);
390 
391 	rc = lsa_list_accounts(&domain_handle);
392 	(void) lsar_close(&domain_handle);
393 	return (rc);
394 }
395 
396 /*
397  * lsa_list_privs
398  *
399  * List the privileges supported by the specified server.
400  * This function is only intended for diagnostics.
401  *
402  * Returns NT status codes.
403  */
404 DWORD
405 lsa_list_privs(char *server, char *domain)
406 {
407 	static char name[128];
408 	static struct ms_luid luid;
409 	mlsvc_handle_t domain_handle;
410 	int rc;
411 	int i;
412 	char *user = smbrdr_ipc_get_user();
413 
414 	rc = lsar_open(server, domain, user, &domain_handle);
415 	if (rc != 0)
416 		return (NT_STATUS_INVALID_PARAMETER);
417 
418 	for (i = 0; i < 30; ++i) {
419 		luid.low_part = i;
420 		rc = lsar_lookup_priv_name(&domain_handle, &luid, name, 128);
421 		if (rc != 0)
422 			continue;
423 
424 		(void) lsar_lookup_priv_value(&domain_handle, name, &luid);
425 		(void) lsar_lookup_priv_display_name(&domain_handle, name,
426 		    name, 128);
427 	}
428 
429 	(void) lsar_close(&domain_handle);
430 	return (NT_STATUS_SUCCESS);
431 }
432 
433 /*
434  * lsa_list_accounts
435  *
436  * This function can be used to list the accounts in the specified
437  * domain. For now the SIDs are just listed in the system log.
438  *
439  * On success 0 is returned. Otherwise a -ve error code.
440  */
441 static int
442 lsa_list_accounts(mlsvc_handle_t *domain_handle)
443 {
444 	mlsvc_handle_t account_handle;
445 	struct mslsa_EnumAccountBuf accounts;
446 	struct mslsa_sid *sid;
447 	smb_account_t ainfo;
448 	DWORD enum_context = 0;
449 	int rc;
450 	int i;
451 
452 	bzero(&accounts, sizeof (struct mslsa_EnumAccountBuf));
453 
454 	do {
455 		rc = lsar_enum_accounts(domain_handle, &enum_context,
456 		    &accounts);
457 		if (rc != 0)
458 			return (rc);
459 
460 		for (i = 0; i < accounts.entries_read; ++i) {
461 			sid = accounts.info[i].sid;
462 
463 			if (lsar_open_account(domain_handle, sid,
464 			    &account_handle) == 0) {
465 				(void) lsar_enum_privs_account(&account_handle,
466 				    &ainfo);
467 				(void) lsar_close(&account_handle);
468 			}
469 
470 			free(accounts.info[i].sid);
471 		}
472 
473 		if (accounts.info)
474 			free(accounts.info);
475 	} while (rc == 0 && accounts.entries_read != 0);
476 
477 	return (0);
478 }
479 
480 /*
481  * Lookup well known accounts table for the given SID
482  *
483  * Return status:
484  *
485  *   NT_STATUS_SUCCESS		Account is translated successfully
486  *   NT_STATUS_NOT_FOUND	This is not a well known account
487  *   NT_STATUS_NO_MEMORY	Memory shortage
488  *   NT_STATUS_INTERNAL_ERROR	Internal error/unexpected failure
489  */
490 static uint32_t
491 lsa_lookup_sid_builtin(smb_sid_t *sid, smb_account_t *ainfo)
492 {
493 	smb_wka_t *wka;
494 	char *wkadom;
495 
496 	bzero(ainfo, sizeof (smb_account_t));
497 
498 	if ((wka = smb_wka_lookup_sid(sid)) == NULL)
499 		return (NT_STATUS_NOT_FOUND);
500 
501 	if ((wkadom = smb_wka_get_domain(wka->wka_domidx)) == NULL)
502 		return (NT_STATUS_INTERNAL_ERROR);
503 
504 	ainfo->a_name = strdup(wka->wka_name);
505 	ainfo->a_sid = smb_sid_dup(wka->wka_binsid);
506 	ainfo->a_domain = strdup(wkadom);
507 	ainfo->a_domsid = smb_sid_split(ainfo->a_sid, &ainfo->a_rid);
508 	ainfo->a_type = wka->wka_type;
509 
510 	if (!smb_account_validate(ainfo)) {
511 		smb_account_free(ainfo);
512 		return (NT_STATUS_NO_MEMORY);
513 	}
514 
515 	return (NT_STATUS_SUCCESS);
516 }
517 
518 static uint32_t
519 lsa_lookup_sid_domain(smb_sid_t *sid, smb_account_t *ainfo)
520 {
521 	mlsvc_handle_t domain_handle;
522 	char *user = smbrdr_ipc_get_user();
523 	uint32_t status;
524 	smb_domain_t dinfo;
525 
526 	if (!smb_domain_getinfo(&dinfo))
527 		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
528 
529 	if (lsar_open(dinfo.d_dc, dinfo.d_info.di_nbname, user,
530 	    &domain_handle) != 0)
531 		return (NT_STATUS_INVALID_PARAMETER);
532 
533 	status = lsar_lookup_sids2(&domain_handle, (struct mslsa_sid *)sid,
534 	    ainfo);
535 
536 	if (status == NT_STATUS_REVISION_MISMATCH) {
537 		/*
538 		 * Not a Windows 2000 domain controller:
539 		 * use the NT compatible call.
540 		 */
541 		status = lsar_lookup_sids(&domain_handle,
542 		    (struct mslsa_sid *)sid, ainfo);
543 	}
544 
545 	(void) lsar_close(&domain_handle);
546 	return (status);
547 }
548