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 #include <strings.h> 27 #include <smbsrv/libsmb.h> 28 29 extern int smb_pwd_num(void); 30 extern int smb_lgrp_numbydomain(smb_gdomain_t, int *); 31 32 static uint32_t smb_sam_lookup_user(char *, smb_sid_t **); 33 static uint32_t smb_sam_lookup_group(char *, smb_sid_t **); 34 35 /* 36 * Local well-known accounts data structure table and prototypes 37 */ 38 typedef struct smb_lwka { 39 uint32_t lwka_rid; 40 char *lwka_name; 41 uint16_t lwka_type; 42 } smb_lwka_t; 43 44 static smb_lwka_t lwka_tbl[] = { 45 { 500, "Administrator", SidTypeUser }, 46 { 501, "Guest", SidTypeUser }, 47 { 502, "KRBTGT", SidTypeUser }, 48 { 512, "Domain Admins", SidTypeGroup }, 49 { 513, "Domain Users", SidTypeGroup }, 50 { 514, "Domain Guests", SidTypeGroup }, 51 { 516, "Domain Controllers", SidTypeGroup }, 52 { 517, "Cert Publishers", SidTypeGroup }, 53 { 518, "Schema Admins", SidTypeGroup }, 54 { 519, "Enterprise Admins", SidTypeGroup }, 55 { 520, "Global Policy Creator Owners", SidTypeGroup }, 56 { 533, "RAS and IAS Servers", SidTypeGroup } 57 }; 58 59 #define SMB_LWKA_NUM (sizeof (lwka_tbl)/sizeof (lwka_tbl[0])) 60 61 static smb_lwka_t *smb_lwka_lookup_name(char *); 62 static smb_lwka_t *smb_lwka_lookup_sid(smb_sid_t *); 63 64 /* 65 * Looks up the given name in local account databases: 66 * 67 * SMB Local users are looked up in /var/smb/smbpasswd 68 * SMB Local groups are looked up in /var/smb/smbgroup.db 69 * 70 * If the account is found, its information is populated 71 * in the passed smb_account_t structure. Caller must free 72 * allocated memories by calling smb_account_free() upon 73 * successful return. 74 * 75 * The type of account is specified by 'type', which can be user, 76 * alias (local group) or unknown. If the caller doesn't know 77 * whether the name is a user or group name then SidTypeUnknown 78 * should be passed. 79 * 80 * If a local user and group have the same name, the user will 81 * always be picked. Note that this situation cannot happen on 82 * Windows systems. 83 * 84 * If a SMB local user/group is found but it turns out that 85 * it'll be mapped to a domain user/group the lookup is considered 86 * failed and NT_STATUS_NONE_MAPPED is returned. 87 * 88 * Return status: 89 * 90 * NT_STATUS_NOT_FOUND This is not a local account 91 * NT_STATUS_NONE_MAPPED It's a local account but cannot be 92 * translated. 93 * other error status codes. 94 */ 95 uint32_t 96 smb_sam_lookup_name(char *domain, char *name, uint16_t type, 97 smb_account_t *account) 98 { 99 nt_domain_t di; 100 smb_sid_t *sid; 101 uint32_t status; 102 smb_lwka_t *lwka; 103 104 bzero(account, sizeof (smb_account_t)); 105 106 if (domain != NULL) { 107 if (!nt_domain_lookup_name(domain, &di) || 108 (di.di_type != NT_DOMAIN_LOCAL)) 109 return (NT_STATUS_NOT_FOUND); 110 111 /* Only Netbios hostname is accepted */ 112 if (utf8_strcasecmp(domain, di.di_nbname) != 0) 113 return (NT_STATUS_NONE_MAPPED); 114 } else { 115 if (!nt_domain_lookup_type(NT_DOMAIN_LOCAL, &di)) 116 return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); 117 } 118 119 if (utf8_strcasecmp(name, di.di_nbname) == 0) { 120 /* This is the local domain name */ 121 account->a_type = SidTypeDomain; 122 account->a_name = strdup(""); 123 account->a_domain = strdup(di.di_nbname); 124 account->a_sid = smb_sid_dup(di.di_binsid); 125 account->a_domsid = smb_sid_dup(di.di_binsid); 126 account->a_rid = (uint32_t)-1; 127 128 if (!smb_account_validate(account)) { 129 smb_account_free(account); 130 return (NT_STATUS_NO_MEMORY); 131 } 132 133 return (NT_STATUS_SUCCESS); 134 } 135 136 if ((lwka = smb_lwka_lookup_name(name)) != NULL) { 137 sid = smb_sid_splice(di.di_binsid, lwka->lwka_rid); 138 type = lwka->lwka_type; 139 } else { 140 switch (type) { 141 case SidTypeUser: 142 status = smb_sam_lookup_user(name, &sid); 143 if (status != NT_STATUS_SUCCESS) 144 return (status); 145 break; 146 147 case SidTypeAlias: 148 status = smb_sam_lookup_group(name, &sid); 149 if (status != NT_STATUS_SUCCESS) 150 return (status); 151 break; 152 153 case SidTypeUnknown: 154 type = SidTypeUser; 155 status = smb_sam_lookup_user(name, &sid); 156 if (status == NT_STATUS_SUCCESS) 157 break; 158 159 if (status == NT_STATUS_NONE_MAPPED) 160 return (status); 161 162 type = SidTypeAlias; 163 status = smb_sam_lookup_group(name, &sid); 164 if (status != NT_STATUS_SUCCESS) 165 return (status); 166 break; 167 168 default: 169 return (NT_STATUS_INVALID_PARAMETER); 170 } 171 } 172 173 account->a_name = strdup(name); 174 account->a_sid = sid; 175 account->a_domain = strdup(di.di_nbname); 176 account->a_domsid = smb_sid_split(sid, &account->a_rid); 177 account->a_type = type; 178 179 if (!smb_account_validate(account)) { 180 smb_account_free(account); 181 return (NT_STATUS_NO_MEMORY); 182 } 183 184 return (NT_STATUS_SUCCESS); 185 } 186 187 /* 188 * Looks up the given SID in local account databases: 189 * 190 * SMB Local users are looked up in /var/smb/smbpasswd 191 * SMB Local groups are looked up in /var/smb/smbgroup.db 192 * 193 * If the account is found, its information is populated 194 * in the passed smb_account_t structure. Caller must free 195 * allocated memories by calling smb_account_free() upon 196 * successful return. 197 * 198 * Return status: 199 * 200 * NT_STATUS_NOT_FOUND This is not a local account 201 * NT_STATUS_NONE_MAPPED It's a local account but cannot be 202 * translated. 203 * other error status codes. 204 */ 205 uint32_t 206 smb_sam_lookup_sid(smb_sid_t *sid, smb_account_t *account) 207 { 208 char hostname[MAXHOSTNAMELEN]; 209 smb_passwd_t smbpw; 210 smb_group_t grp; 211 smb_lwka_t *lwka; 212 nt_domain_t di; 213 uint32_t rid; 214 uid_t id; 215 int id_type; 216 int rc; 217 218 bzero(account, sizeof (smb_account_t)); 219 220 if (!nt_domain_lookup_type(NT_DOMAIN_LOCAL, &di)) 221 return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); 222 223 if (smb_sid_cmp(sid, di.di_binsid)) { 224 /* This is the local domain SID */ 225 account->a_type = SidTypeDomain; 226 account->a_name = strdup(""); 227 account->a_domain = strdup(di.di_nbname); 228 account->a_sid = smb_sid_dup(sid); 229 account->a_domsid = smb_sid_dup(sid); 230 account->a_rid = (uint32_t)-1; 231 232 if (!smb_account_validate(account)) { 233 smb_account_free(account); 234 return (NT_STATUS_NO_MEMORY); 235 } 236 237 return (NT_STATUS_SUCCESS); 238 } 239 240 if (!smb_sid_indomain(di.di_binsid, sid)) { 241 /* This is not a local SID */ 242 return (NT_STATUS_NOT_FOUND); 243 } 244 245 if ((lwka = smb_lwka_lookup_sid(sid)) != NULL) { 246 account->a_type = lwka->lwka_type; 247 account->a_name = strdup(lwka->lwka_name); 248 } else { 249 id_type = SMB_IDMAP_UNKNOWN; 250 if (smb_idmap_getid(sid, &id, &id_type) != IDMAP_SUCCESS) 251 return (NT_STATUS_NONE_MAPPED); 252 253 switch (id_type) { 254 case SMB_IDMAP_USER: 255 account->a_type = SidTypeUser; 256 if (smb_pwd_getpwuid(id, &smbpw) == NULL) 257 return (NT_STATUS_NO_SUCH_USER); 258 259 account->a_name = strdup(smbpw.pw_name); 260 break; 261 262 case SMB_IDMAP_GROUP: 263 account->a_type = SidTypeAlias; 264 (void) smb_sid_getrid(sid, &rid); 265 rc = smb_lgrp_getbyrid(rid, SMB_LGRP_LOCAL, &grp); 266 if (rc != SMB_LGRP_SUCCESS) 267 return (NT_STATUS_NO_SUCH_ALIAS); 268 269 account->a_name = strdup(grp.sg_name); 270 smb_lgrp_free(&grp); 271 break; 272 273 default: 274 return (NT_STATUS_NONE_MAPPED); 275 } 276 } 277 278 if (smb_getnetbiosname(hostname, MAXHOSTNAMELEN) == 0) 279 account->a_domain = strdup(hostname); 280 account->a_sid = smb_sid_dup(sid); 281 account->a_domsid = smb_sid_split(sid, &account->a_rid); 282 283 if (!smb_account_validate(account)) { 284 smb_account_free(account); 285 return (NT_STATUS_NO_MEMORY); 286 } 287 288 return (NT_STATUS_SUCCESS); 289 } 290 291 /* 292 * Returns number of SMB users, i.e. users who have entry 293 * in /var/smb/smbpasswd 294 */ 295 int 296 smb_sam_usr_cnt(void) 297 { 298 return (smb_pwd_num()); 299 } 300 301 /* 302 * Returns a list of local groups which the given user is 303 * their member. A pointer to an array of smb_ids_t 304 * structure is returned which must be freed by caller. 305 */ 306 uint32_t 307 smb_sam_usr_groups(smb_sid_t *user_sid, smb_ids_t *gids) 308 { 309 smb_id_t *ids; 310 smb_giter_t gi; 311 smb_group_t lgrp; 312 int total_cnt, gcnt; 313 314 gcnt = 0; 315 if (smb_lgrp_iteropen(&gi) != SMB_LGRP_SUCCESS) 316 return (NT_STATUS_INTERNAL_ERROR); 317 318 while (smb_lgrp_iterate(&gi, &lgrp) == SMB_LGRP_SUCCESS) { 319 if (smb_lgrp_is_member(&lgrp, user_sid)) 320 gcnt++; 321 smb_lgrp_free(&lgrp); 322 } 323 smb_lgrp_iterclose(&gi); 324 325 if (gcnt == 0) 326 return (NT_STATUS_SUCCESS); 327 328 total_cnt = gids->i_cnt + gcnt; 329 gids->i_ids = realloc(gids->i_ids, total_cnt * sizeof (smb_id_t)); 330 if (gids->i_ids == NULL) 331 return (NT_STATUS_NO_MEMORY); 332 333 if (smb_lgrp_iteropen(&gi) != SMB_LGRP_SUCCESS) 334 return (NT_STATUS_INTERNAL_ERROR); 335 336 ids = gids->i_ids + gids->i_cnt; 337 while (smb_lgrp_iterate(&gi, &lgrp) == SMB_LGRP_SUCCESS) { 338 if (gcnt == 0) { 339 smb_lgrp_free(&lgrp); 340 break; 341 } 342 if (smb_lgrp_is_member(&lgrp, user_sid)) { 343 ids->i_sid = smb_sid_dup(lgrp.sg_id.gs_sid); 344 if (ids->i_sid == NULL) { 345 smb_lgrp_free(&lgrp); 346 return (NT_STATUS_NO_MEMORY); 347 } 348 ids->i_attrs = lgrp.sg_attr; 349 gids->i_cnt++; 350 gcnt--; 351 ids++; 352 } 353 smb_lgrp_free(&lgrp); 354 } 355 smb_lgrp_iterclose(&gi); 356 357 return (NT_STATUS_SUCCESS); 358 } 359 360 /* 361 * Returns the number of built-in or local groups stored 362 * in /var/smb/smbgroup.db 363 */ 364 int 365 smb_sam_grp_cnt(nt_domain_type_t dtype) 366 { 367 int grpcnt; 368 int rc; 369 370 switch (dtype) { 371 case NT_DOMAIN_BUILTIN: 372 rc = smb_lgrp_numbydomain(SMB_LGRP_BUILTIN, &grpcnt); 373 break; 374 375 case NT_DOMAIN_LOCAL: 376 rc = smb_lgrp_numbydomain(SMB_LGRP_LOCAL, &grpcnt); 377 break; 378 379 default: 380 rc = SMB_LGRP_INVALID_ARG; 381 } 382 383 return ((rc == SMB_LGRP_SUCCESS) ? grpcnt : 0); 384 } 385 386 /* 387 * Determines whether the given SID is a member of the group 388 * specified by gname. 389 */ 390 boolean_t 391 smb_sam_grp_ismember(const char *gname, smb_sid_t *sid) 392 { 393 smb_group_t grp; 394 boolean_t ismember = B_FALSE; 395 396 if (smb_lgrp_getbyname((char *)gname, &grp) == SMB_LGRP_SUCCESS) { 397 ismember = smb_lgrp_is_member(&grp, sid); 398 smb_lgrp_free(&grp); 399 } 400 401 return (ismember); 402 } 403 404 /* 405 * Frees memories allocated for the passed account fields. 406 */ 407 void 408 smb_account_free(smb_account_t *account) 409 { 410 free(account->a_name); 411 free(account->a_domain); 412 smb_sid_free(account->a_sid); 413 smb_sid_free(account->a_domsid); 414 } 415 416 /* 417 * Validates the given account. 418 */ 419 boolean_t 420 smb_account_validate(smb_account_t *account) 421 { 422 return ((account->a_name != NULL) && (account->a_sid != NULL) && 423 (account->a_domain != NULL) && (account->a_domsid != NULL)); 424 } 425 426 /* 427 * Lookup local SMB user account database (/var/smb/smbpasswd) 428 * if there's a match query its SID from idmap service and make 429 * sure the SID is a local SID. 430 * 431 * The memory for the returned SID must be freed by the caller. 432 */ 433 static uint32_t 434 smb_sam_lookup_user(char *name, smb_sid_t **sid) 435 { 436 smb_passwd_t smbpw; 437 438 if (smb_pwd_getpwnam(name, &smbpw) == NULL) 439 return (NT_STATUS_NO_SUCH_USER); 440 441 if (smb_idmap_getsid(smbpw.pw_uid, SMB_IDMAP_USER, sid) 442 != IDMAP_SUCCESS) 443 return (NT_STATUS_NONE_MAPPED); 444 445 if (!smb_sid_islocal(*sid)) { 446 smb_sid_free(*sid); 447 return (NT_STATUS_NONE_MAPPED); 448 } 449 450 return (NT_STATUS_SUCCESS); 451 } 452 453 /* 454 * Lookup local SMB group account database (/var/smb/smbgroup.db) 455 * The memory for the returned SID must be freed by the caller. 456 */ 457 static uint32_t 458 smb_sam_lookup_group(char *name, smb_sid_t **sid) 459 { 460 smb_group_t grp; 461 462 if (smb_lgrp_getbyname(name, &grp) != SMB_LGRP_SUCCESS) 463 return (NT_STATUS_NO_SUCH_ALIAS); 464 465 *sid = smb_sid_dup(grp.sg_id.gs_sid); 466 smb_lgrp_free(&grp); 467 468 return ((*sid == NULL) ? NT_STATUS_NO_MEMORY : NT_STATUS_SUCCESS); 469 } 470 471 static smb_lwka_t * 472 smb_lwka_lookup_name(char *name) 473 { 474 int i; 475 476 for (i = 0; i < SMB_LWKA_NUM; i++) { 477 if (utf8_strcasecmp(name, lwka_tbl[i].lwka_name) == 0) 478 return (&lwka_tbl[i]); 479 } 480 481 return (NULL); 482 } 483 484 static smb_lwka_t * 485 smb_lwka_lookup_sid(smb_sid_t *sid) 486 { 487 uint32_t rid; 488 int i; 489 490 (void) smb_sid_getrid(sid, &rid); 491 if (rid > 999) 492 return (NULL); 493 494 for (i = 0; i < SMB_LWKA_NUM; i++) { 495 if (rid == lwka_tbl[i].lwka_rid) 496 return (&lwka_tbl[i]); 497 } 498 499 return (NULL); 500 } 501