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
lsa_lookup_name(char * account,uint16_t type,smb_account_t * info)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
lsa_lookup_sid(smb_sid_t * sid,smb_account_t * info)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
lsa_query_primary_domain_info(char * server,char * domain,smb_domain_t * info)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
lsa_query_account_domain_info(char * server,char * domain,smb_domain_t * info)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
lsa_query_dns_domain_info(char * server,char * domain,smb_domain_t * info)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
lsa_enum_trusted_domains(char * server,char * domain,smb_trusted_domains_t * info)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
lsa_enum_trusted_domains_ex(char * server,char * domain,smb_trusted_domains_t * info)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
lsa_lookup_name_builtin(char * domain,char * name,smb_account_t * info)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
lsa_lookup_name_domain(char * account_name,smb_account_t * info)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
lsa_lookup_privs(char * account_name,char * target_name,smb_account_t * ainfo)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
lsa_list_privs(char * server,char * domain)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
lsa_list_accounts(mlsvc_handle_t * domain_handle)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
lsa_lookup_sid_builtin(smb_sid_t * sid,smb_account_t * ainfo)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
lsa_lookup_sid_domain(smb_sid_t * sid,smb_account_t * ainfo)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
lsa_map_status(uint32_t status)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