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.
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.
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]
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
26 * Copyright 2018 RackTop Systems.
30 #include <smbsrv/libsmb.h>
32 extern int smb_pwd_num(void);
33 extern int smb_lgrp_numbydomain(smb_domain_type_t
, int *);
35 static uint32_t smb_sam_lookup_user(char *, smb_sid_t
**);
36 static uint32_t smb_sam_lookup_group(char *, smb_sid_t
**);
39 * Local well-known accounts data structure table and prototypes
41 typedef struct smb_lwka
{
47 static smb_lwka_t lwka_tbl
[] = {
48 { 500, "Administrator", SidTypeUser
},
49 { 501, "Guest", SidTypeUser
},
50 { 502, "KRBTGT", SidTypeUser
},
51 { 512, "Domain Admins", SidTypeGroup
},
52 { 513, "Domain Users", SidTypeGroup
},
53 { 514, "Domain Guests", SidTypeGroup
},
54 { 516, "Domain Controllers", SidTypeGroup
},
55 { 517, "Cert Publishers", SidTypeGroup
},
56 { 518, "Schema Admins", SidTypeGroup
},
57 { 519, "Enterprise Admins", SidTypeGroup
},
58 { 520, "Global Policy Creator Owners", SidTypeGroup
},
59 { 533, "RAS and IAS Servers", SidTypeGroup
}
62 #define SMB_LWKA_NUM (sizeof (lwka_tbl)/sizeof (lwka_tbl[0]))
64 static smb_lwka_t
*smb_lwka_lookup_name(char *);
65 static smb_lwka_t
*smb_lwka_lookup_sid(smb_sid_t
*);
68 * Looks up the given name in local account databases:
70 * SMB Local users are looked up in /var/smb/smbpasswd
71 * SMB Local groups are looked up in /var/smb/smbgroup.db
73 * If the account is found, its information is populated
74 * in the passed smb_account_t structure. Caller must free
75 * allocated memories by calling smb_account_free() upon
78 * The type of account is specified by 'type', which can be user,
79 * alias (local group) or unknown. If the caller doesn't know
80 * whether the name is a user or group name then SidTypeUnknown
83 * If a local user and group have the same name, the user will
84 * always be picked. Note that this situation cannot happen on
87 * If a SMB local user/group is found but it turns out that
88 * it'll be mapped to a domain user/group the lookup is considered
89 * failed and NT_STATUS_NONE_MAPPED is returned.
93 * NT_STATUS_NOT_FOUND This is not a local account
94 * NT_STATUS_NONE_MAPPED It's a local account but cannot be
96 * other error status codes.
99 smb_sam_lookup_name(char *domain
, char *name
, uint16_t type
,
100 smb_account_t
*account
)
107 bzero(account
, sizeof (smb_account_t
));
109 if (domain
!= NULL
) {
110 if (!smb_domain_lookup_name(domain
, &di
) ||
111 (di
.di_type
!= SMB_DOMAIN_LOCAL
))
112 return (NT_STATUS_NOT_FOUND
);
114 /* Only Netbios hostname is accepted */
115 if (smb_strcasecmp(domain
, di
.di_nbname
, 0) != 0)
116 return (NT_STATUS_NONE_MAPPED
);
118 if (!smb_domain_lookup_type(SMB_DOMAIN_LOCAL
, &di
))
119 return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO
);
122 if (smb_strcasecmp(name
, di
.di_nbname
, 0) == 0) {
123 /* This is the local domain name */
124 account
->a_type
= SidTypeDomain
;
125 account
->a_name
= strdup("");
126 account
->a_domain
= strdup(di
.di_nbname
);
127 account
->a_sid
= smb_sid_dup(di
.di_binsid
);
128 account
->a_domsid
= smb_sid_dup(di
.di_binsid
);
129 account
->a_rid
= (uint32_t)-1;
131 if (!smb_account_validate(account
)) {
132 smb_account_free(account
);
133 return (NT_STATUS_NO_MEMORY
);
136 return (NT_STATUS_SUCCESS
);
139 if ((lwka
= smb_lwka_lookup_name(name
)) != NULL
) {
140 sid
= smb_sid_splice(di
.di_binsid
, lwka
->lwka_rid
);
141 type
= lwka
->lwka_type
;
145 status
= smb_sam_lookup_user(name
, &sid
);
146 if (status
!= NT_STATUS_SUCCESS
)
151 status
= smb_sam_lookup_group(name
, &sid
);
152 if (status
!= NT_STATUS_SUCCESS
)
158 status
= smb_sam_lookup_user(name
, &sid
);
159 if (status
== NT_STATUS_SUCCESS
)
162 if (status
== NT_STATUS_NONE_MAPPED
)
166 status
= smb_sam_lookup_group(name
, &sid
);
167 if (status
!= NT_STATUS_SUCCESS
)
172 return (NT_STATUS_INVALID_PARAMETER
);
176 account
->a_name
= strdup(name
);
177 account
->a_sid
= sid
;
178 account
->a_domain
= strdup(di
.di_nbname
);
179 account
->a_domsid
= smb_sid_split(sid
, &account
->a_rid
);
180 account
->a_type
= type
;
182 if (!smb_account_validate(account
)) {
183 smb_account_free(account
);
184 return (NT_STATUS_NO_MEMORY
);
187 return (NT_STATUS_SUCCESS
);
191 * Looks up the given SID in local account databases:
193 * SMB Local users are looked up in /var/smb/smbpasswd
194 * SMB Local groups are looked up in /var/smb/smbgroup.db
196 * If the account is found, its information is populated
197 * in the passed smb_account_t structure. Caller must free
198 * allocated memories by calling smb_account_free() upon
203 * NT_STATUS_NOT_FOUND This is not a local account
204 * NT_STATUS_NONE_MAPPED It's a local account but cannot be
206 * other error status codes.
209 smb_sam_lookup_sid(smb_sid_t
*sid
, smb_account_t
*account
)
211 char hostname
[MAXHOSTNAMELEN
];
221 bzero(account
, sizeof (smb_account_t
));
223 if (!smb_domain_lookup_type(SMB_DOMAIN_LOCAL
, &di
))
224 return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO
);
226 if (smb_sid_cmp(sid
, di
.di_binsid
)) {
227 /* This is the local domain SID */
228 account
->a_type
= SidTypeDomain
;
229 account
->a_name
= strdup("");
230 account
->a_domain
= strdup(di
.di_nbname
);
231 account
->a_sid
= smb_sid_dup(sid
);
232 account
->a_domsid
= smb_sid_dup(sid
);
233 account
->a_rid
= (uint32_t)-1;
235 if (!smb_account_validate(account
)) {
236 smb_account_free(account
);
237 return (NT_STATUS_NO_MEMORY
);
240 return (NT_STATUS_SUCCESS
);
243 if (!smb_sid_indomain(di
.di_binsid
, sid
)) {
244 /* This is not a local SID */
245 return (NT_STATUS_NOT_FOUND
);
248 if ((lwka
= smb_lwka_lookup_sid(sid
)) != NULL
) {
249 account
->a_type
= lwka
->lwka_type
;
250 account
->a_name
= strdup(lwka
->lwka_name
);
252 id_type
= SMB_IDMAP_UNKNOWN
;
253 if (smb_idmap_getid(sid
, &id
, &id_type
) != IDMAP_SUCCESS
)
254 return (NT_STATUS_NONE_MAPPED
);
258 account
->a_type
= SidTypeUser
;
259 if (smb_pwd_getpwuid(id
, &smbpw
) == NULL
)
260 return (NT_STATUS_NO_SUCH_USER
);
262 account
->a_name
= strdup(smbpw
.pw_name
);
265 case SMB_IDMAP_GROUP
:
266 account
->a_type
= SidTypeAlias
;
267 (void) smb_sid_getrid(sid
, &rid
);
268 rc
= smb_lgrp_getbyrid(rid
, SMB_DOMAIN_LOCAL
, &grp
);
269 if (rc
!= SMB_LGRP_SUCCESS
)
270 return (NT_STATUS_NO_SUCH_ALIAS
);
272 account
->a_name
= strdup(grp
.sg_name
);
277 return (NT_STATUS_NONE_MAPPED
);
281 if (smb_getnetbiosname(hostname
, MAXHOSTNAMELEN
) == 0)
282 account
->a_domain
= strdup(hostname
);
283 account
->a_sid
= smb_sid_dup(sid
);
284 account
->a_domsid
= smb_sid_split(sid
, &account
->a_rid
);
286 if (!smb_account_validate(account
)) {
287 smb_account_free(account
);
288 return (NT_STATUS_NO_MEMORY
);
291 return (NT_STATUS_SUCCESS
);
295 * Returns number of SMB users, i.e. users who have entry
296 * in /var/smb/smbpasswd
299 smb_sam_usr_cnt(void)
301 return (smb_pwd_num());
305 * Updates a list of groups in which the given user is a member
306 * by adding any local (SAM) groups.
308 * We are a member of local groups where the local group
309 * contains either the user's primary SID, or any of their
310 * other SIDs such as from domain groups, SID history, etc.
311 * We can have indirect membership via domain groups.
314 smb_sam_usr_groups(smb_sid_t
*user_sid
, smb_ids_t
*gids
)
317 smb_id_t
*ids
, *new_ids
;
320 int i
, gcnt
, total_cnt
;
325 * First pass: count groups to be added (gcnt)
328 if (smb_lgrp_iteropen(&gi
) != SMB_LGRP_SUCCESS
)
329 return (NT_STATUS_INTERNAL_ERROR
);
331 while (smb_lgrp_iterate(&gi
, &lgrp
) == SMB_LGRP_SUCCESS
) {
333 if (smb_lgrp_is_member(&lgrp
, user_sid
))
335 else for (i
= 0, ids
= gids
->i_ids
;
336 i
< gids
->i_cnt
; i
++, ids
++) {
337 if (smb_lgrp_is_member(&lgrp
, ids
->i_sid
)) {
342 /* Careful: only count lgrp once */
345 smb_lgrp_free(&lgrp
);
347 smb_lgrp_iterclose(&gi
);
350 return (NT_STATUS_SUCCESS
);
353 * Second pass: add to groups list.
354 * Do not modify gcnt after here.
356 if (smb_lgrp_iteropen(&gi
) != SMB_LGRP_SUCCESS
)
357 return (NT_STATUS_INTERNAL_ERROR
);
360 * Expand the list (copy to a new, larger one)
361 * Note: were're copying pointers from the old
362 * array to the new (larger) array, and then
363 * adding new pointers after what we copied.
366 new_gids
.i_cnt
= gids
->i_cnt
;
367 total_cnt
= gids
->i_cnt
+ gcnt
;
368 new_gids
.i_ids
= malloc(total_cnt
* sizeof (smb_id_t
));
369 if (new_gids
.i_ids
== NULL
) {
370 ret
= NT_STATUS_NO_MEMORY
;
373 (void) memcpy(new_gids
.i_ids
, gids
->i_ids
,
374 gids
->i_cnt
* sizeof (smb_id_t
));
375 new_ids
= new_gids
.i_ids
+ gids
->i_cnt
;
376 (void) memset(new_ids
, 0, gcnt
* sizeof (smb_id_t
));
379 * Add group SIDs starting at the end of the
380 * previous list. (new_ids)
382 while (smb_lgrp_iterate(&gi
, &lgrp
) == SMB_LGRP_SUCCESS
) {
384 if (smb_lgrp_is_member(&lgrp
, user_sid
))
386 else for (i
= 0, ids
= gids
->i_ids
;
387 i
< gids
->i_cnt
; i
++, ids
++) {
388 if (smb_lgrp_is_member(&lgrp
, ids
->i_sid
)) {
393 if (member
&& (new_gids
.i_cnt
< (gids
->i_cnt
+ gcnt
))) {
394 new_ids
->i_sid
= smb_sid_dup(lgrp
.sg_id
.gs_sid
);
395 if (new_ids
->i_sid
== NULL
) {
396 smb_lgrp_free(&lgrp
);
397 ret
= NT_STATUS_NO_MEMORY
;
400 new_ids
->i_attrs
= lgrp
.sg_attr
;
404 smb_lgrp_free(&lgrp
);
408 smb_lgrp_iterclose(&gi
);
411 if (new_gids
.i_ids
!= NULL
) {
413 * Free only the new sids we added.
414 * The old ones were copied ptrs.
416 ids
= new_gids
.i_ids
+ gids
->i_cnt
;
417 for (i
= 0; i
< gcnt
; i
++, ids
++) {
418 smb_sid_free(ids
->i_sid
);
420 free(new_gids
.i_ids
);
426 * Success! Update passed gids and
427 * free the old array.
432 return (NT_STATUS_SUCCESS
);
436 * Returns the number of built-in or local groups stored
437 * in /var/smb/smbgroup.db
440 smb_sam_grp_cnt(smb_domain_type_t dtype
)
446 case SMB_DOMAIN_BUILTIN
:
447 rc
= smb_lgrp_numbydomain(SMB_DOMAIN_BUILTIN
, &grpcnt
);
450 case SMB_DOMAIN_LOCAL
:
451 rc
= smb_lgrp_numbydomain(SMB_DOMAIN_LOCAL
, &grpcnt
);
455 rc
= SMB_LGRP_INVALID_ARG
;
458 return ((rc
== SMB_LGRP_SUCCESS
) ? grpcnt
: 0);
462 * Determines whether the given SID is a member of the group
463 * specified by gname.
466 smb_sam_grp_ismember(const char *gname
, smb_sid_t
*sid
)
469 boolean_t ismember
= B_FALSE
;
471 if (smb_lgrp_getbyname((char *)gname
, &grp
) == SMB_LGRP_SUCCESS
) {
472 ismember
= smb_lgrp_is_member(&grp
, sid
);
480 * Frees memories allocated for the passed account fields.
481 * Initializes @account after all.
484 smb_account_free(smb_account_t
*account
)
486 free(account
->a_name
);
487 free(account
->a_domain
);
488 smb_sid_free(account
->a_sid
);
489 smb_sid_free(account
->a_domsid
);
491 bzero(account
, sizeof (smb_account_t
));
495 * Validates the given account.
498 smb_account_validate(smb_account_t
*account
)
500 return ((account
->a_name
!= NULL
) && (account
->a_sid
!= NULL
) &&
501 (account
->a_domain
!= NULL
) && (account
->a_domsid
!= NULL
));
505 * Lookup local SMB user account database (/var/smb/smbpasswd)
506 * if there's a match query its SID from idmap service and make
507 * sure the SID is a local SID.
509 * The memory for the returned SID must be freed by the caller.
512 smb_sam_lookup_user(char *name
, smb_sid_t
**sid
)
516 if (smb_pwd_getpwnam(name
, &smbpw
) == NULL
)
517 return (NT_STATUS_NO_SUCH_USER
);
519 if (smb_idmap_getsid(smbpw
.pw_uid
, SMB_IDMAP_USER
, sid
)
521 return (NT_STATUS_NONE_MAPPED
);
523 if (!smb_sid_islocal(*sid
)) {
525 return (NT_STATUS_NONE_MAPPED
);
528 return (NT_STATUS_SUCCESS
);
532 * Lookup local SMB group account database (/var/smb/smbgroup.db)
533 * The memory for the returned SID must be freed by the caller.
536 smb_sam_lookup_group(char *name
, smb_sid_t
**sid
)
540 if (smb_lgrp_getbyname(name
, &grp
) != SMB_LGRP_SUCCESS
)
541 return (NT_STATUS_NO_SUCH_ALIAS
);
543 *sid
= smb_sid_dup(grp
.sg_id
.gs_sid
);
546 return ((*sid
== NULL
) ? NT_STATUS_NO_MEMORY
: NT_STATUS_SUCCESS
);
550 smb_lwka_lookup_name(char *name
)
554 for (i
= 0; i
< SMB_LWKA_NUM
; i
++) {
555 if (smb_strcasecmp(name
, lwka_tbl
[i
].lwka_name
, 0) == 0)
556 return (&lwka_tbl
[i
]);
563 smb_lwka_lookup_sid(smb_sid_t
*sid
)
568 (void) smb_sid_getrid(sid
, &rid
);
572 for (i
= 0; i
< SMB_LWKA_NUM
; i
++) {
573 if (rid
== lwka_tbl
[i
].lwka_rid
)
574 return (&lwka_tbl
[i
]);
583 * Check a SID to see if it belongs to the local domain.
586 smb_sid_islocal(smb_sid_t
*sid
)
589 boolean_t islocal
= B_FALSE
;
591 if (smb_domain_lookup_type(SMB_DOMAIN_LOCAL
, &di
))
592 islocal
= smb_sid_indomain(di
.di_binsid
, sid
);
598 smb_ids_free(smb_ids_t
*ids
)
603 if ((ids
!= NULL
) && (ids
->i_ids
!= NULL
)) {
605 for (i
= 0; i
< ids
->i_cnt
; i
++, id
++)
606 smb_sid_free(id
->i_sid
);