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