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 == MLSVC_NO_MORE_DATA) { 243 /* 244 * MLSVC_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 == MLSVC_NO_MORE_DATA) { 283 /* 284 * MLSVC_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) && (utf8_strcasecmp(domain, wkadom) != 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_names2(&domain_handle, account_name, info); 361 if (status == NT_STATUS_REVISION_MISMATCH) { 362 /* 363 * Not a Windows 2000 domain controller: 364 * use the NT compatible call. 365 */ 366 status = lsar_lookup_names(&domain_handle, account_name, info); 367 } 368 369 (void) lsar_close(&domain_handle); 370 return (status); 371 } 372 373 /* 374 * lsa_lookup_privs 375 * 376 * Request the privileges associated with the specified account. In 377 * order to get the privileges, we first have to lookup the name on 378 * the specified domain controller and obtain the appropriate SID. 379 * The SID can then be used to open the account and obtain the 380 * account privileges. The results from both the name lookup and the 381 * privileges are returned in the user_info structure. The caller is 382 * responsible for allocating and releasing this structure. 383 * 384 * On success 0 is returned. Otherwise a -ve error code. 385 */ 386 /*ARGSUSED*/ 387 int 388 lsa_lookup_privs(char *account_name, char *target_name, smb_account_t *ainfo) 389 { 390 mlsvc_handle_t domain_handle; 391 int rc; 392 smb_domainex_t dinfo; 393 char user[SMB_USERNAME_MAXLEN]; 394 395 smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); 396 397 if (!smb_domain_getinfo(&dinfo)) 398 return (-1); 399 400 if ((lsar_open(dinfo.d_dc, dinfo.d_primary.di_nbname, user, 401 &domain_handle)) != 0) 402 return (-1); 403 404 rc = lsa_list_accounts(&domain_handle); 405 (void) lsar_close(&domain_handle); 406 return (rc); 407 } 408 409 /* 410 * lsa_list_privs 411 * 412 * List the privileges supported by the specified server. 413 * This function is only intended for diagnostics. 414 * 415 * Returns NT status codes. 416 */ 417 DWORD 418 lsa_list_privs(char *server, char *domain) 419 { 420 static char name[128]; 421 static struct ms_luid luid; 422 mlsvc_handle_t domain_handle; 423 int rc; 424 int i; 425 char user[SMB_USERNAME_MAXLEN]; 426 427 smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); 428 429 rc = lsar_open(server, domain, user, &domain_handle); 430 if (rc != 0) 431 return (NT_STATUS_INVALID_PARAMETER); 432 433 for (i = 0; i < 30; ++i) { 434 luid.low_part = i; 435 rc = lsar_lookup_priv_name(&domain_handle, &luid, name, 128); 436 if (rc != 0) 437 continue; 438 439 (void) lsar_lookup_priv_value(&domain_handle, name, &luid); 440 (void) lsar_lookup_priv_display_name(&domain_handle, name, 441 name, 128); 442 } 443 444 (void) lsar_close(&domain_handle); 445 return (NT_STATUS_SUCCESS); 446 } 447 448 /* 449 * lsa_list_accounts 450 * 451 * This function can be used to list the accounts in the specified 452 * domain. For now the SIDs are just listed in the system log. 453 * 454 * On success 0 is returned. Otherwise a -ve error code. 455 */ 456 static int 457 lsa_list_accounts(mlsvc_handle_t *domain_handle) 458 { 459 mlsvc_handle_t account_handle; 460 struct mslsa_EnumAccountBuf accounts; 461 struct mslsa_sid *sid; 462 smb_account_t ainfo; 463 DWORD enum_context = 0; 464 int rc; 465 int i; 466 467 bzero(&accounts, sizeof (struct mslsa_EnumAccountBuf)); 468 469 do { 470 rc = lsar_enum_accounts(domain_handle, &enum_context, 471 &accounts); 472 if (rc != 0) 473 return (rc); 474 475 for (i = 0; i < accounts.entries_read; ++i) { 476 sid = accounts.info[i].sid; 477 478 if (lsar_open_account(domain_handle, sid, 479 &account_handle) == 0) { 480 (void) lsar_enum_privs_account(&account_handle, 481 &ainfo); 482 (void) lsar_close(&account_handle); 483 } 484 485 free(accounts.info[i].sid); 486 } 487 488 if (accounts.info) 489 free(accounts.info); 490 } while (rc == 0 && accounts.entries_read != 0); 491 492 return (0); 493 } 494 495 /* 496 * Lookup well known accounts table for the given SID 497 * 498 * Return status: 499 * 500 * NT_STATUS_SUCCESS Account is translated successfully 501 * NT_STATUS_NOT_FOUND This is not a well known account 502 * NT_STATUS_NO_MEMORY Memory shortage 503 * NT_STATUS_INTERNAL_ERROR Internal error/unexpected failure 504 */ 505 static uint32_t 506 lsa_lookup_sid_builtin(smb_sid_t *sid, smb_account_t *ainfo) 507 { 508 smb_wka_t *wka; 509 char *wkadom; 510 511 bzero(ainfo, sizeof (smb_account_t)); 512 513 if ((wka = smb_wka_lookup_sid(sid)) == NULL) 514 return (NT_STATUS_NOT_FOUND); 515 516 if ((wkadom = smb_wka_get_domain(wka->wka_domidx)) == NULL) 517 return (NT_STATUS_INTERNAL_ERROR); 518 519 ainfo->a_name = strdup(wka->wka_name); 520 ainfo->a_sid = smb_sid_dup(wka->wka_binsid); 521 ainfo->a_domain = strdup(wkadom); 522 ainfo->a_domsid = smb_sid_split(ainfo->a_sid, &ainfo->a_rid); 523 ainfo->a_type = wka->wka_type; 524 525 if (!smb_account_validate(ainfo)) { 526 smb_account_free(ainfo); 527 return (NT_STATUS_NO_MEMORY); 528 } 529 530 return (NT_STATUS_SUCCESS); 531 } 532 533 static uint32_t 534 lsa_lookup_sid_domain(smb_sid_t *sid, smb_account_t *ainfo) 535 { 536 mlsvc_handle_t domain_handle; 537 uint32_t status; 538 smb_domainex_t dinfo; 539 char user[SMB_USERNAME_MAXLEN]; 540 541 smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); 542 543 if (!smb_domain_getinfo(&dinfo)) 544 return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); 545 546 if (lsar_open(dinfo.d_dc, dinfo.d_primary.di_nbname, user, 547 &domain_handle) != 0) 548 return (NT_STATUS_INVALID_PARAMETER); 549 550 status = lsar_lookup_sids2(&domain_handle, (struct mslsa_sid *)sid, 551 ainfo); 552 553 if (status == NT_STATUS_REVISION_MISMATCH) { 554 /* 555 * Not a Windows 2000 domain controller: 556 * use the NT compatible call. 557 */ 558 status = lsar_lookup_sids(&domain_handle, 559 (struct mslsa_sid *)sid, ainfo); 560 } 561 562 (void) lsar_close(&domain_handle); 563 return (status); 564 } 565