xref: /illumos-gate/usr/src/lib/smbsrv/libsmb/common/smb_sam.c (revision 635216b673cf196ac523ff2a7ab715717e553292)
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 	smb_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 (!smb_domain_lookup_name(domain, &di) ||
108 		    (di.di_type != SMB_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 (!smb_domain_lookup_type(SMB_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 	smb_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 (!smb_domain_lookup_type(SMB_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(smb_domain_type_t dtype)
366 {
367 	int grpcnt;
368 	int rc;
369 
370 	switch (dtype) {
371 	case SMB_DOMAIN_BUILTIN:
372 		rc = smb_lgrp_numbydomain(SMB_LGRP_BUILTIN, &grpcnt);
373 		break;
374 
375 	case SMB_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