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 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 *
25 * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
26 * Copyright 2018 RackTop Systems.
27 */
28
29 #include <strings.h>
30 #include <smbsrv/libsmb.h>
31
32 extern int smb_pwd_num(void);
33 extern int smb_lgrp_numbydomain(smb_domain_type_t, int *);
34
35 static uint32_t smb_sam_lookup_user(char *, smb_sid_t **);
36 static uint32_t smb_sam_lookup_group(char *, smb_sid_t **);
37
38 /*
39 * Local well-known accounts data structure table and prototypes
40 */
41 typedef struct smb_lwka {
42 uint32_t lwka_rid;
43 char *lwka_name;
44 uint16_t lwka_type;
45 } smb_lwka_t;
46
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 }
60 };
61
62 #define SMB_LWKA_NUM (sizeof (lwka_tbl)/sizeof (lwka_tbl[0]))
63
64 static smb_lwka_t *smb_lwka_lookup_name(char *);
65 static smb_lwka_t *smb_lwka_lookup_sid(smb_sid_t *);
66
67 /*
68 * Looks up the given name in local account databases:
69 *
70 * SMB Local users are looked up in /var/smb/smbpasswd
71 * SMB Local groups are looked up in /var/smb/smbgroup.db
72 *
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
76 * successful return.
77 *
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
81 * should be passed.
82 *
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
85 * Windows systems.
86 *
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.
90 *
91 * Return status:
92 *
93 * NT_STATUS_NOT_FOUND This is not a local account
94 * NT_STATUS_NONE_MAPPED It's a local account but cannot be
95 * translated.
96 * other error status codes.
97 */
98 uint32_t
smb_sam_lookup_name(char * domain,char * name,uint16_t type,smb_account_t * account)99 smb_sam_lookup_name(char *domain, char *name, uint16_t type,
100 smb_account_t *account)
101 {
102 smb_domain_t di;
103 smb_sid_t *sid;
104 uint32_t status;
105 smb_lwka_t *lwka;
106
107 bzero(account, sizeof (smb_account_t));
108
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);
113
114 /* Only Netbios hostname is accepted */
115 if (smb_strcasecmp(domain, di.di_nbname, 0) != 0)
116 return (NT_STATUS_NONE_MAPPED);
117 } else {
118 if (!smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di))
119 return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
120 }
121
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;
130
131 if (!smb_account_validate(account)) {
132 smb_account_free(account);
133 return (NT_STATUS_NO_MEMORY);
134 }
135
136 return (NT_STATUS_SUCCESS);
137 }
138
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;
142 } else {
143 switch (type) {
144 case SidTypeUser:
145 status = smb_sam_lookup_user(name, &sid);
146 if (status != NT_STATUS_SUCCESS)
147 return (status);
148 break;
149
150 case SidTypeAlias:
151 status = smb_sam_lookup_group(name, &sid);
152 if (status != NT_STATUS_SUCCESS)
153 return (status);
154 break;
155
156 case SidTypeUnknown:
157 type = SidTypeUser;
158 status = smb_sam_lookup_user(name, &sid);
159 if (status == NT_STATUS_SUCCESS)
160 break;
161
162 if (status == NT_STATUS_NONE_MAPPED)
163 return (status);
164
165 type = SidTypeAlias;
166 status = smb_sam_lookup_group(name, &sid);
167 if (status != NT_STATUS_SUCCESS)
168 return (status);
169 break;
170
171 default:
172 return (NT_STATUS_INVALID_PARAMETER);
173 }
174 }
175
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;
181
182 if (!smb_account_validate(account)) {
183 smb_account_free(account);
184 return (NT_STATUS_NO_MEMORY);
185 }
186
187 return (NT_STATUS_SUCCESS);
188 }
189
190 /*
191 * Looks up the given SID in local account databases:
192 *
193 * SMB Local users are looked up in /var/smb/smbpasswd
194 * SMB Local groups are looked up in /var/smb/smbgroup.db
195 *
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
199 * successful return.
200 *
201 * Return status:
202 *
203 * NT_STATUS_NOT_FOUND This is not a local account
204 * NT_STATUS_NONE_MAPPED It's a local account but cannot be
205 * translated.
206 * other error status codes.
207 */
208 uint32_t
smb_sam_lookup_sid(smb_sid_t * sid,smb_account_t * account)209 smb_sam_lookup_sid(smb_sid_t *sid, smb_account_t *account)
210 {
211 char hostname[MAXHOSTNAMELEN];
212 smb_passwd_t smbpw;
213 smb_group_t grp;
214 smb_lwka_t *lwka;
215 smb_domain_t di;
216 uint32_t rid;
217 uid_t id;
218 int id_type;
219 int rc;
220
221 bzero(account, sizeof (smb_account_t));
222
223 if (!smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di))
224 return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
225
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;
234
235 if (!smb_account_validate(account)) {
236 smb_account_free(account);
237 return (NT_STATUS_NO_MEMORY);
238 }
239
240 return (NT_STATUS_SUCCESS);
241 }
242
243 if (!smb_sid_indomain(di.di_binsid, sid)) {
244 /* This is not a local SID */
245 return (NT_STATUS_NOT_FOUND);
246 }
247
248 if ((lwka = smb_lwka_lookup_sid(sid)) != NULL) {
249 account->a_type = lwka->lwka_type;
250 account->a_name = strdup(lwka->lwka_name);
251 } else {
252 id_type = SMB_IDMAP_UNKNOWN;
253 if (smb_idmap_getid(sid, &id, &id_type) != IDMAP_SUCCESS)
254 return (NT_STATUS_NONE_MAPPED);
255
256 switch (id_type) {
257 case SMB_IDMAP_USER:
258 account->a_type = SidTypeUser;
259 if (smb_pwd_getpwuid(id, &smbpw) == NULL)
260 return (NT_STATUS_NO_SUCH_USER);
261
262 account->a_name = strdup(smbpw.pw_name);
263 account->a_flags = smbpw.pw_flags;
264 break;
265
266 case SMB_IDMAP_GROUP:
267 account->a_type = SidTypeAlias;
268 (void) smb_sid_getrid(sid, &rid);
269 rc = smb_lgrp_getbyrid(rid, SMB_DOMAIN_LOCAL, &grp);
270 if (rc != SMB_LGRP_SUCCESS)
271 return (NT_STATUS_NO_SUCH_ALIAS);
272
273 account->a_name = strdup(grp.sg_name);
274 smb_lgrp_free(&grp);
275 break;
276
277 default:
278 return (NT_STATUS_NONE_MAPPED);
279 }
280 }
281
282 if (smb_getnetbiosname(hostname, MAXHOSTNAMELEN) == 0)
283 account->a_domain = strdup(hostname);
284 account->a_sid = smb_sid_dup(sid);
285 account->a_domsid = smb_sid_split(sid, &account->a_rid);
286
287 if (!smb_account_validate(account)) {
288 smb_account_free(account);
289 return (NT_STATUS_NO_MEMORY);
290 }
291
292 return (NT_STATUS_SUCCESS);
293 }
294
295 /*
296 * Returns number of SMB users, i.e. users who have entry
297 * in /var/smb/smbpasswd
298 */
299 int
smb_sam_usr_cnt(void)300 smb_sam_usr_cnt(void)
301 {
302 return (smb_pwd_num());
303 }
304
305 /*
306 * Updates a list of groups in which the given user is a member
307 * by adding any local (SAM) groups.
308 *
309 * We are a member of local groups where the local group
310 * contains either the user's primary SID, or any of their
311 * other SIDs such as from domain groups, SID history, etc.
312 * We can have indirect membership via domain groups.
313 */
314 uint32_t
smb_sam_usr_groups(smb_sid_t * user_sid,smb_ids_t * gids)315 smb_sam_usr_groups(smb_sid_t *user_sid, smb_ids_t *gids)
316 {
317 smb_ids_t new_gids;
318 smb_id_t *ids, *new_ids;
319 smb_giter_t gi;
320 smb_group_t lgrp;
321 int i, gcnt, total_cnt;
322 uint32_t ret;
323 boolean_t member;
324
325 /*
326 * First pass: count groups to be added (gcnt)
327 */
328 gcnt = 0;
329 if (smb_lgrp_iteropen(&gi) != SMB_LGRP_SUCCESS)
330 return (NT_STATUS_INTERNAL_ERROR);
331
332 while (smb_lgrp_iterate(&gi, &lgrp) == SMB_LGRP_SUCCESS) {
333 member = B_FALSE;
334 if (smb_lgrp_is_member(&lgrp, user_sid))
335 member = B_TRUE;
336 else for (i = 0, ids = gids->i_ids;
337 i < gids->i_cnt; i++, ids++) {
338 if (smb_lgrp_is_member(&lgrp, ids->i_sid)) {
339 member = B_TRUE;
340 break;
341 }
342 }
343 /* Careful: only count lgrp once */
344 if (member)
345 gcnt++;
346 smb_lgrp_free(&lgrp);
347 }
348 smb_lgrp_iterclose(&gi);
349
350 if (gcnt == 0)
351 return (NT_STATUS_SUCCESS);
352
353 /*
354 * Second pass: add to groups list.
355 * Do not modify gcnt after here.
356 */
357 if (smb_lgrp_iteropen(&gi) != SMB_LGRP_SUCCESS)
358 return (NT_STATUS_INTERNAL_ERROR);
359
360 /*
361 * Expand the list (copy to a new, larger one)
362 * Note: were're copying pointers from the old
363 * array to the new (larger) array, and then
364 * adding new pointers after what we copied.
365 */
366 ret = 0;
367 new_gids.i_cnt = gids->i_cnt;
368 total_cnt = gids->i_cnt + gcnt;
369 new_gids.i_ids = malloc(total_cnt * sizeof (smb_id_t));
370 if (new_gids.i_ids == NULL) {
371 ret = NT_STATUS_NO_MEMORY;
372 goto out;
373 }
374 (void) memcpy(new_gids.i_ids, gids->i_ids,
375 gids->i_cnt * sizeof (smb_id_t));
376 new_ids = new_gids.i_ids + gids->i_cnt;
377 (void) memset(new_ids, 0, gcnt * sizeof (smb_id_t));
378
379 /*
380 * Add group SIDs starting at the end of the
381 * previous list. (new_ids)
382 */
383 while (smb_lgrp_iterate(&gi, &lgrp) == SMB_LGRP_SUCCESS) {
384 member = B_FALSE;
385 if (smb_lgrp_is_member(&lgrp, user_sid))
386 member = B_TRUE;
387 else for (i = 0, ids = gids->i_ids;
388 i < gids->i_cnt; i++, ids++) {
389 if (smb_lgrp_is_member(&lgrp, ids->i_sid)) {
390 member = B_TRUE;
391 break;
392 }
393 }
394 if (member && (new_gids.i_cnt < (gids->i_cnt + gcnt))) {
395 new_ids->i_sid = smb_sid_dup(lgrp.sg_id.gs_sid);
396 if (new_ids->i_sid == NULL) {
397 smb_lgrp_free(&lgrp);
398 ret = NT_STATUS_NO_MEMORY;
399 goto out;
400 }
401 new_ids->i_attrs = lgrp.sg_attr;
402 new_ids++;
403 new_gids.i_cnt++;
404 }
405 smb_lgrp_free(&lgrp);
406 }
407
408 out:
409 smb_lgrp_iterclose(&gi);
410
411 if (ret != 0) {
412 if (new_gids.i_ids != NULL) {
413 /*
414 * Free only the new sids we added.
415 * The old ones were copied ptrs.
416 */
417 ids = new_gids.i_ids + gids->i_cnt;
418 for (i = 0; i < gcnt; i++, ids++) {
419 smb_sid_free(ids->i_sid);
420 }
421 free(new_gids.i_ids);
422 }
423 return (ret);
424 }
425
426 /*
427 * Success! Update passed gids and
428 * free the old array.
429 */
430 free(gids->i_ids);
431 *gids = new_gids;
432
433 return (NT_STATUS_SUCCESS);
434 }
435
436 /*
437 * Returns the number of built-in or local groups stored
438 * in /var/smb/smbgroup.db
439 */
440 int
smb_sam_grp_cnt(smb_domain_type_t dtype)441 smb_sam_grp_cnt(smb_domain_type_t dtype)
442 {
443 int grpcnt;
444 int rc;
445
446 switch (dtype) {
447 case SMB_DOMAIN_BUILTIN:
448 rc = smb_lgrp_numbydomain(SMB_DOMAIN_BUILTIN, &grpcnt);
449 break;
450
451 case SMB_DOMAIN_LOCAL:
452 rc = smb_lgrp_numbydomain(SMB_DOMAIN_LOCAL, &grpcnt);
453 break;
454
455 default:
456 rc = SMB_LGRP_INVALID_ARG;
457 }
458
459 return ((rc == SMB_LGRP_SUCCESS) ? grpcnt : 0);
460 }
461
462 /*
463 * Determines whether the given SID is a member of the group
464 * specified by gname.
465 */
466 boolean_t
smb_sam_grp_ismember(const char * gname,smb_sid_t * sid)467 smb_sam_grp_ismember(const char *gname, smb_sid_t *sid)
468 {
469 smb_group_t grp;
470 boolean_t ismember = B_FALSE;
471
472 if (smb_lgrp_getbyname((char *)gname, &grp) == SMB_LGRP_SUCCESS) {
473 ismember = smb_lgrp_is_member(&grp, sid);
474 smb_lgrp_free(&grp);
475 }
476
477 return (ismember);
478 }
479
480 /*
481 * Frees memories allocated for the passed account fields.
482 * Initializes @account after all.
483 */
484 void
smb_account_free(smb_account_t * account)485 smb_account_free(smb_account_t *account)
486 {
487 free(account->a_name);
488 free(account->a_domain);
489 smb_sid_free(account->a_sid);
490 smb_sid_free(account->a_domsid);
491
492 bzero(account, sizeof (smb_account_t));
493 }
494
495 /*
496 * Validates the given account.
497 */
498 boolean_t
smb_account_validate(smb_account_t * account)499 smb_account_validate(smb_account_t *account)
500 {
501 return ((account->a_name != NULL) && (account->a_sid != NULL) &&
502 (account->a_domain != NULL) && (account->a_domsid != NULL));
503 }
504
505 /*
506 * Lookup local SMB user account database (/var/smb/smbpasswd)
507 * if there's a match query its SID from idmap service and make
508 * sure the SID is a local SID.
509 *
510 * The memory for the returned SID must be freed by the caller.
511 */
512 static uint32_t
smb_sam_lookup_user(char * name,smb_sid_t ** sid)513 smb_sam_lookup_user(char *name, smb_sid_t **sid)
514 {
515 smb_passwd_t smbpw;
516
517 if (smb_pwd_getpwnam(name, &smbpw) == NULL)
518 return (NT_STATUS_NO_SUCH_USER);
519
520 if (smb_idmap_getsid(smbpw.pw_uid, SMB_IDMAP_USER, sid)
521 != IDMAP_SUCCESS)
522 return (NT_STATUS_NONE_MAPPED);
523
524 if (!smb_sid_islocal(*sid)) {
525 smb_sid_free(*sid);
526 return (NT_STATUS_NONE_MAPPED);
527 }
528
529 return (NT_STATUS_SUCCESS);
530 }
531
532 /*
533 * Lookup local SMB group account database (/var/smb/smbgroup.db)
534 * The memory for the returned SID must be freed by the caller.
535 */
536 static uint32_t
smb_sam_lookup_group(char * name,smb_sid_t ** sid)537 smb_sam_lookup_group(char *name, smb_sid_t **sid)
538 {
539 smb_group_t grp;
540
541 if (smb_lgrp_getbyname(name, &grp) != SMB_LGRP_SUCCESS)
542 return (NT_STATUS_NO_SUCH_ALIAS);
543
544 *sid = smb_sid_dup(grp.sg_id.gs_sid);
545 smb_lgrp_free(&grp);
546
547 return ((*sid == NULL) ? NT_STATUS_NO_MEMORY : NT_STATUS_SUCCESS);
548 }
549
550 static smb_lwka_t *
smb_lwka_lookup_name(char * name)551 smb_lwka_lookup_name(char *name)
552 {
553 int i;
554
555 for (i = 0; i < SMB_LWKA_NUM; i++) {
556 if (smb_strcasecmp(name, lwka_tbl[i].lwka_name, 0) == 0)
557 return (&lwka_tbl[i]);
558 }
559
560 return (NULL);
561 }
562
563 static smb_lwka_t *
smb_lwka_lookup_sid(smb_sid_t * sid)564 smb_lwka_lookup_sid(smb_sid_t *sid)
565 {
566 uint32_t rid;
567 int i;
568
569 (void) smb_sid_getrid(sid, &rid);
570 if (rid > 999)
571 return (NULL);
572
573 for (i = 0; i < SMB_LWKA_NUM; i++) {
574 if (rid == lwka_tbl[i].lwka_rid)
575 return (&lwka_tbl[i]);
576 }
577
578 return (NULL);
579 }
580
581 /*
582 * smb_sid_islocal
583 *
584 * Check a SID to see if it belongs to the local domain.
585 */
586 boolean_t
smb_sid_islocal(smb_sid_t * sid)587 smb_sid_islocal(smb_sid_t *sid)
588 {
589 smb_domain_t di;
590 boolean_t islocal = B_FALSE;
591
592 if (smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di))
593 islocal = smb_sid_indomain(di.di_binsid, sid);
594
595 return (islocal);
596 }
597
598 void
smb_ids_free(smb_ids_t * ids)599 smb_ids_free(smb_ids_t *ids)
600 {
601 smb_id_t *id;
602 int i;
603
604 if ((ids != NULL) && (ids->i_ids != NULL)) {
605 id = ids->i_ids;
606 for (i = 0; i < ids->i_cnt; i++, id++)
607 smb_sid_free(id->i_sid);
608
609 free(ids->i_ids);
610 }
611 }
612