xref: /illumos-gate/usr/src/lib/smbsrv/libmlsvc/common/lsalib.c (revision d656abb5804319b33c85955a73ee450ef7ff9739)
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/libsmbrdr.h>
36 #include <smbsrv/ntstatus.h>
37 #include <smbsrv/smbinfo.h>
38 #include <smbsrv/smb_token.h>
39 
40 #include <lsalib.h>
41 
42 static uint32_t lsa_lookup_name_builtin(char *, char *, smb_account_t *);
43 static uint32_t lsa_lookup_name_domain(char *, smb_account_t *);
44 
45 static uint32_t lsa_lookup_sid_builtin(smb_sid_t *, smb_account_t *);
46 static uint32_t lsa_lookup_sid_domain(smb_sid_t *, smb_account_t *);
47 
48 static int lsa_list_accounts(mlsvc_handle_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  * lsa_query_primary_domain_info
134  *
135  * Obtains the primary domain SID and name from the specified server
136  * (domain controller). The information is stored in the NT domain
137  * database by the lower level lsar_query_info_policy call. The caller
138  * should query the database to obtain a reference to the primary
139  * domain information.
140  *
141  * The requested information will be returned via 'info' argument.
142  * Caller must call lsa_free_info() when done.
143  *
144  * Returns NT status codes.
145  */
146 DWORD
147 lsa_query_primary_domain_info(char *server, char *domain, lsa_info_t *info)
148 {
149 	mlsvc_handle_t domain_handle;
150 	DWORD status;
151 	char *user = smbrdr_ipc_get_user();
152 
153 	if ((lsar_open(server, domain, user, &domain_handle)) != 0)
154 		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
155 
156 	status = lsar_query_info_policy(&domain_handle,
157 	    MSLSA_POLICY_PRIMARY_DOMAIN_INFO, info);
158 
159 	(void) lsar_close(&domain_handle);
160 	return (status);
161 }
162 
163 /*
164  * lsa_query_account_domain_info
165  *
166  * Obtains the account domain SID and name from the current server
167  * (domain controller). The information is stored in the NT domain
168  * database by the lower level lsar_query_info_policy call. The caller
169  * should query the database to obtain a reference to the account
170  * domain information.
171  *
172  * The requested information will be returned via 'info' argument.
173  * Caller must invoke lsa_free_info() to when done.
174  *
175  * Returns NT status codes.
176  */
177 DWORD
178 lsa_query_account_domain_info(char *server, char *domain, lsa_info_t *info)
179 {
180 	mlsvc_handle_t domain_handle;
181 	DWORD status;
182 	char *user = smbrdr_ipc_get_user();
183 
184 	if ((lsar_open(server, domain, user, &domain_handle)) != 0)
185 		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
186 
187 	status = lsar_query_info_policy(&domain_handle,
188 	    MSLSA_POLICY_ACCOUNT_DOMAIN_INFO, info);
189 
190 	(void) lsar_close(&domain_handle);
191 	return (status);
192 }
193 
194 /*
195  * lsa_query_dns_domain_info
196  *
197  * Obtains the DNS domain info from the specified server
198  * (domain controller).
199  *
200  * The requested information will be returned via 'info' argument.
201  * Caller must call lsa_free_info() when done.
202  *
203  * Returns NT status codes.
204  */
205 DWORD
206 lsa_query_dns_domain_info(char *server, char *domain, lsa_info_t *info)
207 {
208 	mlsvc_handle_t domain_handle;
209 	DWORD status;
210 	char *user = smbrdr_ipc_get_user();
211 
212 	if ((lsar_open(server, domain, user, &domain_handle)) != 0)
213 		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
214 
215 	status = lsar_query_info_policy(&domain_handle,
216 	    MSLSA_POLICY_DNS_DOMAIN_INFO, info);
217 
218 	(void) lsar_close(&domain_handle);
219 	return (status);
220 }
221 
222 /*
223  * lsa_enum_trusted_domains
224  *
225  * Enumerate the trusted domains in our primary domain. The information
226  * is stored in the NT domain database by the lower level
227  * lsar_enum_trusted_domains call. The caller should query the database
228  * to obtain a reference to the trusted domain information.
229  *
230  * The requested information will be returned via 'info' argument.
231  * Caller must call lsa_free_info() when done.
232  *
233  * Returns NT status codes.
234  */
235 DWORD
236 lsa_enum_trusted_domains(char *server, char *domain, lsa_info_t *info)
237 {
238 	mlsvc_handle_t domain_handle;
239 	DWORD enum_context;
240 	DWORD status;
241 	char *user = smbrdr_ipc_get_user();
242 
243 	if ((lsar_open(server, domain, user, &domain_handle)) != 0)
244 		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
245 
246 	enum_context = 0;
247 
248 	status = lsar_enum_trusted_domains(&domain_handle, &enum_context, info);
249 	if (status == MLSVC_NO_MORE_DATA) {
250 		/*
251 		 * MLSVC_NO_MORE_DATA indicates that we
252 		 * have all of the available information.
253 		 */
254 		status = NT_STATUS_SUCCESS;
255 	}
256 
257 	(void) lsar_close(&domain_handle);
258 	return (status);
259 }
260 
261 /*
262  * lsa_free_info
263  */
264 void
265 lsa_free_info(lsa_info_t *info)
266 {
267 	lsa_trusted_domainlist_t *list;
268 	int i;
269 
270 	if (!info)
271 		return;
272 
273 	switch (info->i_type) {
274 	case LSA_INFO_PRIMARY_DOMAIN:
275 		smb_sid_free(info->i_domain.di_primary.n_sid);
276 		break;
277 
278 	case LSA_INFO_ACCOUNT_DOMAIN:
279 		smb_sid_free(info->i_domain.di_account.n_sid);
280 		break;
281 
282 	case LSA_INFO_DNS_DOMAIN:
283 		smb_sid_free(info->i_domain.di_dns.d_sid);
284 		break;
285 
286 	case LSA_INFO_TRUSTED_DOMAINS:
287 		list = &info->i_domain.di_trust;
288 		for (i = 0; i < list->t_num; i++)
289 			smb_sid_free(list->t_domains[i].n_sid);
290 		free(list->t_domains);
291 		break;
292 
293 	case LSA_INFO_NONE:
294 		break;
295 	}
296 }
297 
298 /*
299  * Lookup well known accounts table
300  *
301  * Return status:
302  *
303  *   NT_STATUS_SUCCESS		Account is translated successfully
304  *   NT_STATUS_NOT_FOUND	This is not a well known account
305  *   NT_STATUS_NONE_MAPPED	Account is found but domains don't match
306  *   NT_STATUS_NO_MEMORY	Memory shortage
307  *   NT_STATUS_INTERNAL_ERROR	Internal error/unexpected failure
308  */
309 static uint32_t
310 lsa_lookup_name_builtin(char *domain, char *name, smb_account_t *info)
311 {
312 	smb_wka_t *wka;
313 	char *wkadom;
314 
315 	bzero(info, sizeof (smb_account_t));
316 
317 	if ((wka = smb_wka_lookup_name(name)) == NULL)
318 		return (NT_STATUS_NOT_FOUND);
319 
320 	if ((wkadom = smb_wka_get_domain(wka->wka_domidx)) == NULL)
321 		return (NT_STATUS_INTERNAL_ERROR);
322 
323 	if ((domain != NULL) && (utf8_strcasecmp(domain, wkadom) != 0))
324 		return (NT_STATUS_NONE_MAPPED);
325 
326 	info->a_name = strdup(name);
327 	info->a_sid = smb_sid_dup(wka->wka_binsid);
328 	info->a_domain = strdup(wkadom);
329 	info->a_domsid = smb_sid_split(wka->wka_binsid, &info->a_rid);
330 	info->a_type = wka->wka_type;
331 
332 	if (!smb_account_validate(info)) {
333 		smb_account_free(info);
334 		return (NT_STATUS_NO_MEMORY);
335 	}
336 
337 	return (NT_STATUS_SUCCESS);
338 }
339 
340 /*
341  * Lookup the given account in domain.
342  *
343  * The information is returned in the user_info structure.
344  * The caller is responsible for allocating and releasing
345  * this structure.
346  */
347 static uint32_t
348 lsa_lookup_name_domain(char *account_name, smb_account_t *info)
349 {
350 	mlsvc_handle_t domain_handle;
351 	smb_domain_t dinfo;
352 	char *user = smbrdr_ipc_get_user();
353 	uint32_t status;
354 
355 	if (!smb_domain_getinfo(&dinfo))
356 		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
357 
358 	if (lsar_open(dinfo.d_dc, dinfo.d_nbdomain, user, &domain_handle) != 0)
359 		return (NT_STATUS_INVALID_PARAMETER);
360 
361 	status = lsar_lookup_names2(&domain_handle, account_name, info);
362 	if (status == NT_STATUS_REVISION_MISMATCH) {
363 		/*
364 		 * Not a Windows 2000 domain controller:
365 		 * use the NT compatible call.
366 		 */
367 		status = lsar_lookup_names(&domain_handle, account_name, info);
368 	}
369 
370 	(void) lsar_close(&domain_handle);
371 	return (status);
372 }
373 
374 /*
375  * lsa_lookup_privs
376  *
377  * Request the privileges associated with the specified account. In
378  * order to get the privileges, we first have to lookup the name on
379  * the specified domain controller and obtain the appropriate SID.
380  * The SID can then be used to open the account and obtain the
381  * account privileges. The results from both the name lookup and the
382  * privileges are returned in the user_info structure. The caller is
383  * responsible for allocating and releasing this structure.
384  *
385  * On success 0 is returned. Otherwise a -ve error code.
386  */
387 /*ARGSUSED*/
388 int
389 lsa_lookup_privs(char *account_name, char *target_name, smb_account_t *ainfo)
390 {
391 	mlsvc_handle_t domain_handle;
392 	int rc;
393 	char *user = smbrdr_ipc_get_user();
394 	smb_domain_t dinfo;
395 
396 	if (!smb_domain_getinfo(&dinfo))
397 		return (-1);
398 
399 	if ((lsar_open(dinfo.d_dc, dinfo.d_nbdomain, user,
400 	    &domain_handle)) != 0)
401 		return (-1);
402 
403 	rc = lsa_list_accounts(&domain_handle);
404 	(void) lsar_close(&domain_handle);
405 	return (rc);
406 }
407 
408 /*
409  * lsa_list_privs
410  *
411  * List the privileges supported by the specified server.
412  * This function is only intended for diagnostics.
413  *
414  * Returns NT status codes.
415  */
416 DWORD
417 lsa_list_privs(char *server, char *domain)
418 {
419 	static char name[128];
420 	static struct ms_luid luid;
421 	mlsvc_handle_t domain_handle;
422 	int rc;
423 	int i;
424 	char *user = smbrdr_ipc_get_user();
425 
426 	rc = lsar_open(server, domain, user, &domain_handle);
427 	if (rc != 0)
428 		return (NT_STATUS_INVALID_PARAMETER);
429 
430 	for (i = 0; i < 30; ++i) {
431 		luid.low_part = i;
432 		rc = lsar_lookup_priv_name(&domain_handle, &luid, name, 128);
433 		if (rc != 0)
434 			continue;
435 
436 		(void) lsar_lookup_priv_value(&domain_handle, name, &luid);
437 		(void) lsar_lookup_priv_display_name(&domain_handle, name,
438 		    name, 128);
439 	}
440 
441 	(void) lsar_close(&domain_handle);
442 	return (NT_STATUS_SUCCESS);
443 }
444 
445 /*
446  * lsa_list_accounts
447  *
448  * This function can be used to list the accounts in the specified
449  * domain. For now the SIDs are just listed in the system log.
450  *
451  * On success 0 is returned. Otherwise a -ve error code.
452  */
453 static int
454 lsa_list_accounts(mlsvc_handle_t *domain_handle)
455 {
456 	mlsvc_handle_t account_handle;
457 	struct mslsa_EnumAccountBuf accounts;
458 	struct mslsa_sid *sid;
459 	smb_account_t ainfo;
460 	DWORD enum_context = 0;
461 	int rc;
462 	int i;
463 
464 	bzero(&accounts, sizeof (struct mslsa_EnumAccountBuf));
465 
466 	do {
467 		rc = lsar_enum_accounts(domain_handle, &enum_context,
468 		    &accounts);
469 		if (rc != 0)
470 			return (rc);
471 
472 		for (i = 0; i < accounts.entries_read; ++i) {
473 			sid = accounts.info[i].sid;
474 
475 			if (lsar_open_account(domain_handle, sid,
476 			    &account_handle) == 0) {
477 				(void) lsar_enum_privs_account(&account_handle,
478 				    &ainfo);
479 				(void) lsar_close(&account_handle);
480 			}
481 
482 			free(accounts.info[i].sid);
483 		}
484 
485 		if (accounts.info)
486 			free(accounts.info);
487 	} while (rc == 0 && accounts.entries_read != 0);
488 
489 	return (0);
490 }
491 
492 /*
493  * Lookup well known accounts table for the given SID
494  *
495  * Return status:
496  *
497  *   NT_STATUS_SUCCESS		Account is translated successfully
498  *   NT_STATUS_NOT_FOUND	This is not a well known account
499  *   NT_STATUS_NO_MEMORY	Memory shortage
500  *   NT_STATUS_INTERNAL_ERROR	Internal error/unexpected failure
501  */
502 static uint32_t
503 lsa_lookup_sid_builtin(smb_sid_t *sid, smb_account_t *ainfo)
504 {
505 	smb_wka_t *wka;
506 	char *wkadom;
507 
508 	bzero(ainfo, sizeof (smb_account_t));
509 
510 	if ((wka = smb_wka_lookup_sid(sid)) == NULL)
511 		return (NT_STATUS_NOT_FOUND);
512 
513 	if ((wkadom = smb_wka_get_domain(wka->wka_domidx)) == NULL)
514 		return (NT_STATUS_INTERNAL_ERROR);
515 
516 	ainfo->a_name = strdup(wka->wka_name);
517 	ainfo->a_sid = smb_sid_dup(wka->wka_binsid);
518 	ainfo->a_domain = strdup(wkadom);
519 	ainfo->a_domsid = smb_sid_split(ainfo->a_sid, &ainfo->a_rid);
520 	ainfo->a_type = wka->wka_type;
521 
522 	if (!smb_account_validate(ainfo)) {
523 		smb_account_free(ainfo);
524 		return (NT_STATUS_NO_MEMORY);
525 	}
526 
527 	return (NT_STATUS_SUCCESS);
528 }
529 
530 static uint32_t
531 lsa_lookup_sid_domain(smb_sid_t *sid, smb_account_t *ainfo)
532 {
533 	mlsvc_handle_t domain_handle;
534 	char *user = smbrdr_ipc_get_user();
535 	uint32_t status;
536 	smb_domain_t dinfo;
537 
538 	if (!smb_domain_getinfo(&dinfo))
539 		return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
540 
541 	if (lsar_open(dinfo.d_dc, dinfo.d_nbdomain, user, &domain_handle) != 0)
542 		return (NT_STATUS_INVALID_PARAMETER);
543 
544 	status = lsar_lookup_sids2(&domain_handle, (struct mslsa_sid *)sid,
545 	    ainfo);
546 
547 	if (status == NT_STATUS_REVISION_MISMATCH) {
548 		/*
549 		 * Not a Windows 2000 domain controller:
550 		 * use the NT compatible call.
551 		 */
552 		status = lsar_lookup_sids(&domain_handle,
553 		    (struct mslsa_sid *)sid, ainfo);
554 	}
555 
556 	(void) lsar_close(&domain_handle);
557 	return (status);
558 }
559