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