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