xref: /illumos-gate/usr/src/lib/smbsrv/libsmb/common/smb_sam.c (revision d656abb5804319b33c85955a73ee450ef7ff9739)
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  * Looks up the given name in local account databases:
37  *
38  * SMB Local users are looked up in /var/smb/smbpasswd
39  * SMB Local groups are looked up in /var/smb/smbgroup.db
40  *
41  * If the account is found, its information is populated
42  * in the passed smb_account_t structure. Caller must free
43  * allocated memories by calling smb_account_free() upon
44  * successful return.
45  *
46  * The type of account is specified by 'type', which can be user,
47  * alias (local group) or unknown. If the caller doesn't know
48  * whether the name is a user or group name then SidTypeUnknown
49  * should be passed.
50  *
51  * If a local user and group have the same name, the user will
52  * always be picked. Note that this situation cannot happen on
53  * Windows systems.
54  *
55  * If a SMB local user/group is found but it turns out that
56  * it'll be mapped to a domain user/group the lookup is considered
57  * failed and NT_STATUS_NONE_MAPPED is returned.
58  *
59  * Return status:
60  *
61  *   NT_STATUS_NOT_FOUND	This is not a local account
62  *   NT_STATUS_NONE_MAPPED	It's a local account but cannot be
63  *   				translated.
64  *   other error status codes.
65  */
66 uint32_t
67 smb_sam_lookup_name(char *domain, char *name, uint16_t type,
68     smb_account_t *account)
69 {
70 	char hostname[MAXHOSTNAMELEN];
71 	smb_sid_t *sid;
72 	uint32_t status;
73 
74 	bzero(account, sizeof (smb_account_t));
75 	(void) smb_getnetbiosname(hostname, sizeof (hostname));
76 
77 	if (domain != NULL) {
78 		if (!smb_ishostname(domain))
79 			return (NT_STATUS_NOT_FOUND);
80 
81 		/* Only Netbios hostname is accepted */
82 		if (utf8_strcasecmp(domain, hostname) != 0)
83 			return (NT_STATUS_NONE_MAPPED);
84 	}
85 
86 	switch (type) {
87 	case SidTypeUser:
88 		status = smb_sam_lookup_user(name, &sid);
89 		if (status != NT_STATUS_SUCCESS)
90 			return (status);
91 		break;
92 
93 	case SidTypeAlias:
94 		status = smb_sam_lookup_group(name, &sid);
95 		if (status != NT_STATUS_SUCCESS)
96 			return (status);
97 		break;
98 
99 	case SidTypeUnknown:
100 		type = SidTypeUser;
101 		status = smb_sam_lookup_user(name, &sid);
102 		if (status == NT_STATUS_SUCCESS)
103 			break;
104 
105 		if (status == NT_STATUS_NONE_MAPPED)
106 			return (status);
107 
108 		type = SidTypeAlias;
109 		status = smb_sam_lookup_group(name, &sid);
110 		if (status != NT_STATUS_SUCCESS)
111 			return (status);
112 		break;
113 
114 	default:
115 		return (NT_STATUS_INVALID_PARAMETER);
116 	}
117 
118 	account->a_name = strdup(name);
119 	account->a_sid = sid;
120 	account->a_domain = strdup(hostname);
121 	account->a_domsid = smb_sid_split(sid, &account->a_rid);
122 	account->a_type = type;
123 
124 	if (!smb_account_validate(account)) {
125 		smb_account_free(account);
126 		return (NT_STATUS_NO_MEMORY);
127 	}
128 
129 	return (NT_STATUS_SUCCESS);
130 }
131 
132 /*
133  * Looks up the given SID in local account databases:
134  *
135  * SMB Local users are looked up in /var/smb/smbpasswd
136  * SMB Local groups are looked up in /var/smb/smbgroup.db
137  *
138  * If the account is found, its information is populated
139  * in the passed smb_account_t structure. Caller must free
140  * allocated memories by calling smb_account_free() upon
141  * successful return.
142  *
143  * Return status:
144  *
145  *   NT_STATUS_NOT_FOUND	This is not a local account
146  *   NT_STATUS_NONE_MAPPED	It's a local account but cannot be
147  *   				translated.
148  *   other error status codes.
149  */
150 uint32_t
151 smb_sam_lookup_sid(smb_sid_t *sid, smb_account_t *account)
152 {
153 	char hostname[MAXHOSTNAMELEN];
154 	smb_passwd_t smbpw;
155 	smb_group_t grp;
156 	uint32_t rid;
157 	uid_t id;
158 	int id_type;
159 	int rc;
160 
161 	bzero(account, sizeof (smb_account_t));
162 
163 	if (!smb_sid_islocal(sid))
164 		return (NT_STATUS_NOT_FOUND);
165 
166 	id_type = SMB_IDMAP_UNKNOWN;
167 	if (smb_idmap_getid(sid, &id, &id_type) != IDMAP_SUCCESS)
168 		return (NT_STATUS_NONE_MAPPED);
169 
170 	switch (id_type) {
171 	case SMB_IDMAP_USER:
172 		account->a_type = SidTypeUser;
173 		if (smb_pwd_getpwuid(id, &smbpw) == NULL)
174 			return (NT_STATUS_NO_SUCH_USER);
175 
176 		account->a_name = strdup(smbpw.pw_name);
177 		break;
178 
179 	case SMB_IDMAP_GROUP:
180 		account->a_type = SidTypeAlias;
181 		(void) smb_sid_getrid(sid, &rid);
182 		rc = smb_lgrp_getbyrid(rid, SMB_LGRP_LOCAL, &grp);
183 		if (rc != SMB_LGRP_SUCCESS)
184 			return (NT_STATUS_NO_SUCH_ALIAS);
185 
186 		account->a_name = strdup(grp.sg_name);
187 		smb_lgrp_free(&grp);
188 		break;
189 
190 	default:
191 		return (NT_STATUS_NONE_MAPPED);
192 	}
193 
194 	if (smb_getnetbiosname(hostname, MAXHOSTNAMELEN) == 0)
195 		account->a_domain = strdup(hostname);
196 	account->a_sid = smb_sid_dup(sid);
197 	account->a_domsid = smb_sid_split(sid, &account->a_rid);
198 
199 	if (!smb_account_validate(account)) {
200 		smb_account_free(account);
201 		return (NT_STATUS_NO_MEMORY);
202 	}
203 
204 	return (NT_STATUS_SUCCESS);
205 }
206 
207 /*
208  * Returns number of SMB users, i.e. users who have entry
209  * in /var/smb/smbpasswd
210  */
211 int
212 smb_sam_usr_cnt(void)
213 {
214 	return (smb_pwd_num());
215 }
216 
217 /*
218  * Returns a list of local groups which the given user is
219  * their member. A pointer to an array of smb_ids_t
220  * structure is returned which must be freed by caller.
221  */
222 uint32_t
223 smb_sam_usr_groups(smb_sid_t *user_sid, smb_ids_t *gids)
224 {
225 	smb_id_t *ids;
226 	smb_giter_t gi;
227 	smb_group_t lgrp;
228 	int total_cnt, gcnt;
229 
230 	gcnt = 0;
231 	if (smb_lgrp_iteropen(&gi) != SMB_LGRP_SUCCESS)
232 		return (NT_STATUS_INTERNAL_ERROR);
233 
234 	while (smb_lgrp_iterate(&gi, &lgrp) == SMB_LGRP_SUCCESS) {
235 		if (smb_lgrp_is_member(&lgrp, user_sid))
236 			gcnt++;
237 		smb_lgrp_free(&lgrp);
238 	}
239 	smb_lgrp_iterclose(&gi);
240 
241 	if (gcnt == 0)
242 		return (NT_STATUS_SUCCESS);
243 
244 	total_cnt = gids->i_cnt + gcnt;
245 	gids->i_ids = realloc(gids->i_ids, total_cnt * sizeof (smb_id_t));
246 	if (gids->i_ids == NULL)
247 		return (NT_STATUS_NO_MEMORY);
248 
249 	if (smb_lgrp_iteropen(&gi) != SMB_LGRP_SUCCESS)
250 		return (NT_STATUS_INTERNAL_ERROR);
251 
252 	ids = gids->i_ids + gids->i_cnt;
253 	while (smb_lgrp_iterate(&gi, &lgrp) == SMB_LGRP_SUCCESS) {
254 		if (gcnt == 0) {
255 			smb_lgrp_free(&lgrp);
256 			break;
257 		}
258 		if (smb_lgrp_is_member(&lgrp, user_sid)) {
259 			ids->i_sid = smb_sid_dup(lgrp.sg_id.gs_sid);
260 			if (ids->i_sid == NULL) {
261 				smb_lgrp_free(&lgrp);
262 				return (NT_STATUS_NO_MEMORY);
263 			}
264 			ids->i_attrs = lgrp.sg_attr;
265 			gids->i_cnt++;
266 			gcnt--;
267 			ids++;
268 		}
269 		smb_lgrp_free(&lgrp);
270 	}
271 	smb_lgrp_iterclose(&gi);
272 
273 	return (NT_STATUS_SUCCESS);
274 }
275 
276 /*
277  * Returns the number of built-in or local groups stored
278  * in /var/smb/smbgroup.db
279  */
280 int
281 smb_sam_grp_cnt(nt_domain_type_t dtype)
282 {
283 	int grpcnt;
284 	int rc;
285 
286 	switch (dtype) {
287 	case NT_DOMAIN_BUILTIN:
288 		rc = smb_lgrp_numbydomain(SMB_LGRP_BUILTIN, &grpcnt);
289 		break;
290 
291 	case NT_DOMAIN_LOCAL:
292 		rc = smb_lgrp_numbydomain(SMB_LGRP_LOCAL, &grpcnt);
293 		break;
294 
295 	default:
296 		rc = SMB_LGRP_INVALID_ARG;
297 	}
298 
299 	return ((rc == SMB_LGRP_SUCCESS) ? grpcnt : 0);
300 }
301 
302 /*
303  * Determines whether the given SID is a member of the group
304  * specified by gname.
305  */
306 boolean_t
307 smb_sam_grp_ismember(const char *gname, smb_sid_t *sid)
308 {
309 	smb_group_t grp;
310 	boolean_t ismember = B_FALSE;
311 
312 	if (smb_lgrp_getbyname((char *)gname, &grp) == SMB_LGRP_SUCCESS) {
313 		ismember = smb_lgrp_is_member(&grp, sid);
314 		smb_lgrp_free(&grp);
315 	}
316 
317 	return (ismember);
318 }
319 
320 /*
321  * Frees memories allocated for the passed account fields.
322  */
323 void
324 smb_account_free(smb_account_t *account)
325 {
326 	free(account->a_name);
327 	free(account->a_domain);
328 	smb_sid_free(account->a_sid);
329 	smb_sid_free(account->a_domsid);
330 }
331 
332 /*
333  * Validates the given account.
334  */
335 boolean_t
336 smb_account_validate(smb_account_t *account)
337 {
338 	return ((account->a_name != NULL) && (account->a_sid != NULL) &&
339 	    (account->a_domain != NULL) && (account->a_domsid != NULL));
340 }
341 
342 /*
343  * Lookup local SMB user account database (/var/smb/smbpasswd)
344  * if there's a match query its SID from idmap service and make
345  * sure the SID is a local SID.
346  *
347  * The memory for the returned SID must be freed by the caller.
348  */
349 static uint32_t
350 smb_sam_lookup_user(char *name, smb_sid_t **sid)
351 {
352 	smb_passwd_t smbpw;
353 
354 	if (smb_pwd_getpwnam(name, &smbpw) == NULL)
355 		return (NT_STATUS_NO_SUCH_USER);
356 
357 	if (smb_idmap_getsid(smbpw.pw_uid, SMB_IDMAP_USER, sid)
358 	    != IDMAP_SUCCESS)
359 		return (NT_STATUS_NONE_MAPPED);
360 
361 	if (!smb_sid_islocal(*sid)) {
362 		smb_sid_free(*sid);
363 		return (NT_STATUS_NONE_MAPPED);
364 	}
365 
366 	return (NT_STATUS_SUCCESS);
367 }
368 
369 /*
370  * Lookup local SMB group account database (/var/smb/smbgroup.db)
371  * The memory for the returned SID must be freed by the caller.
372  */
373 static uint32_t
374 smb_sam_lookup_group(char *name, smb_sid_t **sid)
375 {
376 	smb_group_t grp;
377 
378 	if (smb_lgrp_getbyname(name, &grp) != SMB_LGRP_SUCCESS)
379 		return (NT_STATUS_NO_SUCH_ALIAS);
380 
381 	*sid = smb_sid_dup(grp.sg_id.gs_sid);
382 	smb_lgrp_free(&grp);
383 
384 	return ((*sid == NULL) ? NT_STATUS_NO_MEMORY : NT_STATUS_SUCCESS);
385 }
386