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
26 #include <strings.h>
27 #include <smbsrv/libsmb.h>
28
29 extern int smb_pwd_num(void);
30 extern int smb_lgrp_numbydomain(smb_domain_type_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
smb_sam_lookup_name(char * domain,char * name,uint16_t type,smb_account_t * account)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 (smb_strcasecmp(domain, di.di_nbname, 0) != 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 (smb_strcasecmp(name, di.di_nbname, 0) == 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
smb_sam_lookup_sid(smb_sid_t * sid,smb_account_t * account)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_DOMAIN_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
smb_sam_usr_cnt(void)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
smb_sam_usr_groups(smb_sid_t * user_sid,smb_ids_t * gids)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
smb_sam_grp_cnt(smb_domain_type_t dtype)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_DOMAIN_BUILTIN, &grpcnt);
373 break;
374
375 case SMB_DOMAIN_LOCAL:
376 rc = smb_lgrp_numbydomain(SMB_DOMAIN_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
smb_sam_grp_ismember(const char * gname,smb_sid_t * sid)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
smb_account_free(smb_account_t * account)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
smb_account_validate(smb_account_t * account)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
smb_sam_lookup_user(char * name,smb_sid_t ** sid)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
smb_sam_lookup_group(char * name,smb_sid_t ** sid)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 *
smb_lwka_lookup_name(char * name)472 smb_lwka_lookup_name(char *name)
473 {
474 int i;
475
476 for (i = 0; i < SMB_LWKA_NUM; i++) {
477 if (smb_strcasecmp(name, lwka_tbl[i].lwka_name, 0) == 0)
478 return (&lwka_tbl[i]);
479 }
480
481 return (NULL);
482 }
483
484 static smb_lwka_t *
smb_lwka_lookup_sid(smb_sid_t * sid)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