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