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