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