xref: /illumos-gate/usr/src/lib/smbsrv/libsmb/common/smb_sam.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
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
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
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
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
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
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
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
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
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
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
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 *
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 *
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
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
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