xref: /illumos-gate/usr/src/lib/smbsrv/libmlsvc/common/lsalib.c (revision 6bb6b5762ca4b17cd5fb3c6c123f17489d5635aa)
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 2015 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_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 uint32_t lsa_list_accounts(mlsvc_handle_t *);
48 static uint32_t lsa_map_status(uint32_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. (Raw, not LSA-ized)
139  */
140 DWORD
141 lsa_query_primary_domain_info(char *server, char *domain,
142     smb_domain_t *info)
143 {
144 	mlsvc_handle_t domain_handle;
145 	char user[SMB_USERNAME_MAXLEN];
146 	DWORD status;
147 
148 	smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
149 
150 	status = lsar_open(server, domain, user, &domain_handle);
151 	if (status != 0)
152 		return (status);
153 
154 	status = lsar_query_info_policy(&domain_handle,
155 	    MSLSA_POLICY_PRIMARY_DOMAIN_INFO, info);
156 
157 	(void) lsar_close(&domain_handle);
158 	return (status);
159 }
160 
161 /*
162  * Obtains the account domain SID and name from the current server
163  * (domain controller).
164  *
165  * The requested information will be returned via 'info' argument.
166  *
167  * Returns NT status codes. (Raw, not LSA-ized)
168  */
169 DWORD
170 lsa_query_account_domain_info(char *server, char *domain,
171     smb_domain_t *info)
172 {
173 	mlsvc_handle_t domain_handle;
174 	char user[SMB_USERNAME_MAXLEN];
175 	DWORD status;
176 
177 	smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
178 
179 	status = lsar_open(server, domain, user, &domain_handle);
180 	if (status != 0)
181 		return (status);
182 
183 	status = lsar_query_info_policy(&domain_handle,
184 	    MSLSA_POLICY_ACCOUNT_DOMAIN_INFO, info);
185 
186 	(void) lsar_close(&domain_handle);
187 	return (status);
188 }
189 
190 /*
191  * lsa_query_dns_domain_info
192  *
193  * Obtains the DNS domain info from the specified server
194  * (domain controller).
195  *
196  * The requested information will be returned via 'info' argument.
197  *
198  * Returns NT status codes. (Raw, not LSA-ized)
199  */
200 DWORD
201 lsa_query_dns_domain_info(char *server, char *domain, smb_domain_t *info)
202 {
203 	mlsvc_handle_t domain_handle;
204 	char user[SMB_USERNAME_MAXLEN];
205 	DWORD status;
206 
207 	smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
208 
209 	status = lsar_open(server, domain, user, &domain_handle);
210 	if (status != 0)
211 		return (status);
212 
213 	status = lsar_query_info_policy(&domain_handle,
214 	    MSLSA_POLICY_DNS_DOMAIN_INFO, info);
215 
216 	(void) lsar_close(&domain_handle);
217 	return (status);
218 }
219 
220 /*
221  * Enumerate the trusted domains of  primary domain.
222  * This is the basic enumaration call which only returns the
223  * NetBIOS name of the domain and its SID.
224  *
225  * The requested information will be returned via 'info' argument.
226  *
227  * Returns NT status codes.  (Raw, not LSA-ized)
228  */
229 DWORD
230 lsa_enum_trusted_domains(char *server, char *domain,
231     smb_trusted_domains_t *info)
232 {
233 	mlsvc_handle_t domain_handle;
234 	char user[SMB_USERNAME_MAXLEN];
235 	DWORD enum_context;
236 	DWORD status;
237 
238 	smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
239 
240 	status = lsar_open(server, domain, user, &domain_handle);
241 	if (status != 0)
242 		return (status);
243 
244 	enum_context = 0;
245 
246 	status = lsar_enum_trusted_domains(&domain_handle, &enum_context, info);
247 	if (status == NT_STATUS_NO_MORE_ENTRIES) {
248 		/*
249 		 * STATUS_NO_MORE_ENTRIES indicates that we
250 		 * have all of the available information.
251 		 */
252 		status = NT_STATUS_SUCCESS;
253 	}
254 
255 	(void) lsar_close(&domain_handle);
256 	return (status);
257 }
258 
259 /*
260  * Enumerate the trusted domains of the primary domain.
261  * This is the extended enumaration call which besides
262  * NetBIOS name of the domain and its SID, it will return
263  * the FQDN plus some trust information which is not used.
264  *
265  * The requested information will be returned via 'info' argument.
266  *
267  * Returns NT status codes. (Raw, not LSA-ized)
268  */
269 DWORD
270 lsa_enum_trusted_domains_ex(char *server, char *domain,
271     smb_trusted_domains_t *info)
272 {
273 	mlsvc_handle_t domain_handle;
274 	char user[SMB_USERNAME_MAXLEN];
275 	DWORD enum_context;
276 	DWORD status;
277 
278 	smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
279 
280 	status = lsar_open(server, domain, user, &domain_handle);
281 	if (status != 0)
282 		return (status);
283 
284 	enum_context = 0;
285 
286 	status = lsar_enum_trusted_domains_ex(&domain_handle, &enum_context,
287 	    info);
288 	if (status == NT_STATUS_NO_MORE_ENTRIES) {
289 		/*
290 		 * STATUS_NO_MORE_ENTRIES indicates that we
291 		 * have all of the available information.
292 		 */
293 		status = NT_STATUS_SUCCESS;
294 	}
295 
296 	(void) lsar_close(&domain_handle);
297 	return (status);
298 }
299 
300 /*
301  * Lookup well known accounts table
302  *
303  * Return status:
304  *
305  *   NT_STATUS_SUCCESS		Account is translated successfully
306  *   NT_STATUS_NOT_FOUND	This is not a well known account
307  *   NT_STATUS_NONE_MAPPED	Account is found but domains don't match
308  *   NT_STATUS_NO_MEMORY	Memory shortage
309  *   NT_STATUS_INTERNAL_ERROR	Internal error/unexpected failure
310  */
311 static uint32_t
312 lsa_lookup_name_builtin(char *domain, char *name, smb_account_t *info)
313 {
314 	smb_wka_t *wka;
315 	char *wkadom;
316 
317 	bzero(info, sizeof (smb_account_t));
318 
319 	if ((wka = smb_wka_lookup_name(name)) == NULL)
320 		return (NT_STATUS_NOT_FOUND);
321 
322 	if ((wkadom = smb_wka_get_domain(wka->wka_domidx)) == NULL)
323 		return (NT_STATUS_INTERNAL_ERROR);
324 
325 	if ((domain != NULL) && (smb_strcasecmp(domain, wkadom, 0) != 0))
326 		return (NT_STATUS_NONE_MAPPED);
327 
328 	info->a_name = strdup(name);
329 	info->a_sid = smb_sid_dup(wka->wka_binsid);
330 	info->a_domain = strdup(wkadom);
331 	info->a_domsid = smb_sid_split(wka->wka_binsid, &info->a_rid);
332 	info->a_type = wka->wka_type;
333 
334 	if (!smb_account_validate(info)) {
335 		smb_account_free(info);
336 		return (NT_STATUS_NO_MEMORY);
337 	}
338 
339 	return (NT_STATUS_SUCCESS);
340 }
341 
342 /*
343  * Lookup a domain account by its name.
344  *
345  * The information is returned in the user_info structure.
346  * The caller is responsible for allocating and releasing
347  * this structure.
348  *
349  * Returns NT status codes. (LSA-ized)
350  */
351 static uint32_t
352 lsa_lookup_name_domain(char *account_name, smb_account_t *info)
353 {
354 	mlsvc_handle_t domain_handle;
355 	smb_domainex_t dinfo;
356 	char user[SMB_USERNAME_MAXLEN];
357 	uint32_t status;
358 
359 	smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
360 
361 	if (!smb_domain_getinfo(&dinfo))
362 		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
363 
364 	status = lsar_open(dinfo.d_dci.dc_name, dinfo.d_primary.di_nbname,
365 	    user, &domain_handle);
366 	if (status != 0)
367 		return (lsa_map_status(status));
368 
369 	status = lsar_lookup_names(&domain_handle, account_name, info);
370 
371 	(void) lsar_close(&domain_handle);
372 	return (status);
373 }
374 
375 /*
376  * lsa_lookup_privs
377  *
378  * Request the privileges associated with the specified account. In
379  * order to get the privileges, we first have to lookup the name on
380  * the specified domain controller and obtain the appropriate SID.
381  * The SID can then be used to open the account and obtain the
382  * account privileges. The results from both the name lookup and the
383  * privileges are returned in the user_info structure. The caller is
384  * responsible for allocating and releasing this structure.
385  *
386  * Returns NT status codes. (LSA-ized)
387  */
388 /*ARGSUSED*/
389 DWORD
390 lsa_lookup_privs(char *account_name, char *target_name, smb_account_t *ainfo)
391 {
392 	mlsvc_handle_t domain_handle;
393 	smb_domainex_t dinfo;
394 	char user[SMB_USERNAME_MAXLEN];
395 	DWORD status;
396 
397 	smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
398 
399 	if (!smb_domain_getinfo(&dinfo))
400 		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
401 
402 	status = lsar_open(dinfo.d_dci.dc_name, dinfo.d_primary.di_nbname,
403 	    user, &domain_handle);
404 	if (status != 0)
405 		return (lsa_map_status(status));
406 
407 	status = lsa_list_accounts(&domain_handle);
408 	(void) lsar_close(&domain_handle);
409 	return (status);
410 }
411 
412 /*
413  * lsa_list_privs
414  *
415  * List the privileges supported by the specified server.
416  * This function is only intended for diagnostics.
417  *
418  * Returns NT status codes. (LSA-ized)
419  */
420 DWORD
421 lsa_list_privs(char *server, char *domain)
422 {
423 	static char name[128];
424 	static struct ms_luid luid;
425 	mlsvc_handle_t domain_handle;
426 	char user[SMB_USERNAME_MAXLEN];
427 	DWORD status;
428 	int rc;
429 	int i;
430 
431 	smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
432 
433 	status = lsar_open(server, domain, user, &domain_handle);
434 	if (status != 0)
435 		return (lsa_map_status(status));
436 
437 	for (i = 0; i < 30; ++i) {
438 		luid.low_part = i;
439 		rc = lsar_lookup_priv_name(&domain_handle, &luid, name, 128);
440 		if (rc != 0)
441 			continue;
442 
443 		(void) lsar_lookup_priv_value(&domain_handle, name, &luid);
444 		(void) lsar_lookup_priv_display_name(&domain_handle, name,
445 		    name, 128);
446 	}
447 
448 	(void) lsar_close(&domain_handle);
449 	return (NT_STATUS_SUCCESS);
450 }
451 
452 /*
453  * lsa_list_accounts
454  *
455  * This function can be used to list the accounts in the specified
456  * domain. For now the SIDs are just listed in the system log.
457  *
458  * Returns NT status
459  */
460 static DWORD
461 lsa_list_accounts(mlsvc_handle_t *domain_handle)
462 {
463 	mlsvc_handle_t account_handle;
464 	struct mslsa_EnumAccountBuf accounts;
465 	struct mslsa_sid *sid;
466 	smb_account_t ainfo;
467 	DWORD enum_context = 0;
468 	DWORD status;
469 	int i;
470 
471 	bzero(&accounts, sizeof (struct mslsa_EnumAccountBuf));
472 
473 	do {
474 		status = lsar_enum_accounts(domain_handle, &enum_context,
475 		    &accounts);
476 		if (status != 0)
477 			return (status);
478 
479 		for (i = 0; i < accounts.entries_read; ++i) {
480 			sid = accounts.info[i].sid;
481 
482 			if (lsar_open_account(domain_handle, sid,
483 			    &account_handle) == 0) {
484 				(void) lsar_enum_privs_account(&account_handle,
485 				    &ainfo);
486 				(void) lsar_close(&account_handle);
487 			}
488 
489 			free(accounts.info[i].sid);
490 		}
491 
492 		if (accounts.info)
493 			free(accounts.info);
494 	} while (status == 0 && accounts.entries_read != 0);
495 
496 	return (0);
497 }
498 
499 /*
500  * Lookup well known accounts table for the given SID
501  *
502  * Return status:
503  *
504  *   NT_STATUS_SUCCESS		Account is translated successfully
505  *   NT_STATUS_NOT_FOUND	This is not a well known account
506  *   NT_STATUS_NO_MEMORY	Memory shortage
507  *   NT_STATUS_INTERNAL_ERROR	Internal error/unexpected failure
508  */
509 static uint32_t
510 lsa_lookup_sid_builtin(smb_sid_t *sid, smb_account_t *ainfo)
511 {
512 	smb_wka_t *wka;
513 	char *wkadom;
514 
515 	bzero(ainfo, sizeof (smb_account_t));
516 
517 	if ((wka = smb_wka_lookup_sid(sid)) == NULL)
518 		return (NT_STATUS_NOT_FOUND);
519 
520 	if ((wkadom = smb_wka_get_domain(wka->wka_domidx)) == NULL)
521 		return (NT_STATUS_INTERNAL_ERROR);
522 
523 	ainfo->a_name = strdup(wka->wka_name);
524 	ainfo->a_sid = smb_sid_dup(wka->wka_binsid);
525 	ainfo->a_domain = strdup(wkadom);
526 	ainfo->a_domsid = smb_sid_split(ainfo->a_sid, &ainfo->a_rid);
527 	ainfo->a_type = wka->wka_type;
528 
529 	if (!smb_account_validate(ainfo)) {
530 		smb_account_free(ainfo);
531 		return (NT_STATUS_NO_MEMORY);
532 	}
533 
534 	return (NT_STATUS_SUCCESS);
535 }
536 
537 /*
538  * Lookup a domain account by its SID.
539  *
540  * The information is returned in the user_info structure.
541  * The caller is responsible for allocating and releasing
542  * this structure.
543  *
544  * Returns NT status codes. (LSA-ized)
545  */
546 static uint32_t
547 lsa_lookup_sid_domain(smb_sid_t *sid, smb_account_t *ainfo)
548 {
549 	mlsvc_handle_t domain_handle;
550 	smb_domainex_t dinfo;
551 	char user[SMB_USERNAME_MAXLEN];
552 	uint32_t status;
553 
554 	smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
555 
556 	if (!smb_domain_getinfo(&dinfo))
557 		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
558 
559 	status = lsar_open(dinfo.d_dci.dc_name, dinfo.d_primary.di_nbname,
560 	    user, &domain_handle);
561 	if (status != 0)
562 		return (lsa_map_status(status));
563 
564 	status = lsar_lookup_sids(&domain_handle, sid, ainfo);
565 
566 	(void) lsar_close(&domain_handle);
567 	return (status);
568 }
569 
570 /*
571  * Most functions that call the local security authority expect
572  * only a limited set of status returns.  This function maps the
573  * status we get from talking to our domain controller into one
574  * that LSA functions can return.  Most common errors become:
575  * NT_STATUS_CANT_ACCESS_DOMAIN_INFO (when no DC etc.)
576  */
577 static uint32_t
578 lsa_map_status(uint32_t status)
579 {
580 	switch (status) {
581 	case NT_STATUS_SUCCESS:
582 		break;
583 	case NT_STATUS_INVALID_PARAMETER:	/* rpc bind */
584 		break;
585 	case NT_STATUS_NO_MEMORY:
586 		break;
587 	case NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND:
588 	case NT_STATUS_BAD_NETWORK_PATH:	/* get server addr */
589 	case NT_STATUS_NETWORK_ACCESS_DENIED:	/* authentication */
590 	case NT_STATUS_BAD_NETWORK_NAME:	/* tree connect */
591 	case NT_STATUS_ACCESS_DENIED:		/* open pipe */
592 		status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
593 		break;
594 	default:
595 		status = NT_STATUS_UNSUCCESSFUL;
596 		break;
597 	}
598 	return (status);
599 }
600