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