2 Unix SMB/CIFS implementation.
4 mapping routines for SID <-> unix uid/gid
6 Copyright (C) Andrew Tridgell 2004
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "librpc/gen_ndr/ndr_security.h"
27 these are used for the fallback local uid/gid to sid mapping
30 #define SIDMAP_LOCAL_USER_BASE 0x80000000
31 #define SIDMAP_LOCAL_GROUP_BASE 0xC0000000
32 #define SIDMAP_MAX_LOCAL_UID 0x3fffffff
33 #define SIDMAP_MAX_LOCAL_GID 0x3fffffff
36 private context for sid mapping routines
38 struct sidmap_context {
43 open a sidmap context - use talloc_free to close
45 struct sidmap_context *sidmap_open(TALLOC_CTX *mem_ctx)
47 struct sidmap_context *sidmap;
48 sidmap = talloc_p(mem_ctx, struct sidmap_context);
52 sidmap->samctx = samdb_connect(sidmap);
53 if (sidmap->samctx == NULL) {
63 check the sAMAccountType field of a search result to see if
64 the account is a user account
66 static BOOL is_user_account(struct ldb_message *res)
68 uint_t atype = samdb_result_uint(res, "sAMAccountType", 0);
69 if (atype && (!(atype & ATYPE_ACCOUNT))) {
76 check the sAMAccountType field of a search result to see if
77 the account is a group account
79 static BOOL is_group_account(struct ldb_message *res)
81 uint_t atype = samdb_result_uint(res, "sAMAccountType", 0);
82 if (atype && atype == ATYPE_NORMAL_ACCOUNT) {
91 return the dom_sid of our primary domain
93 static NTSTATUS sidmap_primary_domain_sid(struct sidmap_context *sidmap,
94 TALLOC_CTX *mem_ctx, struct dom_sid **sid)
96 const char *attrs[] = { "objectSid", NULL };
97 void *ctx = talloc_new(mem_ctx);
100 struct ldb_message **res;
102 ret = samdb_search(sidmap->samctx, ctx, NULL, &res, attrs,
103 "(&(objectClass=domain)(name=%s))", lp_workgroup());
106 return NT_STATUS_NO_SUCH_DOMAIN;
109 sidstr = samdb_result_string(res[0], "objectSid", NULL);
110 if (sidstr == NULL) {
112 return NT_STATUS_NO_SUCH_DOMAIN;
115 *sid = dom_sid_parse_talloc(mem_ctx, sidstr);
118 return NT_STATUS_NO_MEMORY;
126 map a sid to a unix uid
128 NTSTATUS sidmap_sid_to_unixuid(struct sidmap_context *sidmap,
129 struct dom_sid *sid, uid_t *uid)
131 const char *attrs[] = { "sAMAccountName", "unixID",
132 "unixName", "sAMAccountType", NULL };
136 struct ldb_message **res;
138 struct dom_sid *domain_sid;
141 ctx = talloc_new(sidmap);
142 sidstr = dom_sid_string(ctx, sid);
143 if (sidstr == NULL) {
145 return NT_STATUS_NO_MEMORY;
148 ret = samdb_search(sidmap->samctx, ctx, NULL, &res, attrs,
149 "objectSid=%s", sidstr);
154 /* make sure its a user, not a group */
155 if (!is_user_account(res[0])) {
156 DEBUG(0,("sid_to_unixuid: sid %s is not an account!\n", sidstr));
158 return NT_STATUS_INVALID_SID;
161 /* first try to get the uid directly */
162 s = samdb_result_string(res[0], "unixID", NULL);
164 *uid = strtoul(s, NULL, 0);
169 /* next try via the UnixName attribute */
170 s = samdb_result_string(res[0], "unixName", NULL);
172 struct passwd *pwd = getpwnam(s);
174 DEBUG(0,("unixName %s for sid %s does not exist as a local user\n", s, sidstr));
176 return NT_STATUS_NO_SUCH_USER;
183 /* finally try via the sAMAccountName attribute */
184 s = samdb_result_string(res[0], "sAMAccountName", NULL);
186 struct passwd *pwd = getpwnam(s);
188 DEBUG(0,("sAMAccountName '%s' for sid %s does not exist as a local user\n", s, sidstr));
190 return NT_STATUS_NO_SUCH_USER;
199 status = sidmap_primary_domain_sid(sidmap, ctx, &domain_sid);
200 if (!NT_STATUS_IS_OK(status)) {
202 return NT_STATUS_NO_SUCH_DOMAIN;
205 if (dom_sid_in_domain(domain_sid, sid)) {
206 uint32_t rid = sid->sub_auths[sid->num_auths-1];
207 if (rid >= SIDMAP_LOCAL_USER_BASE &&
208 rid < SIDMAP_LOCAL_GROUP_BASE) {
209 *uid = rid - SIDMAP_LOCAL_USER_BASE;
216 DEBUG(0,("sid_to_unixuid: no unixID, unixName or sAMAccountName for sid %s\n",
220 return NT_STATUS_INVALID_SID;
225 map a sid to a unix gid
227 NTSTATUS sidmap_sid_to_unixgid(struct sidmap_context *sidmap,
228 struct dom_sid *sid, gid_t *gid)
230 const char *attrs[] = { "sAMAccountName", "unixID",
231 "unixName", "sAMAccountType", NULL };
235 struct ldb_message **res;
238 struct dom_sid *domain_sid;
240 ctx = talloc_new(sidmap);
241 sidstr = dom_sid_string(ctx, sid);
242 if (sidstr == NULL) {
244 return NT_STATUS_NO_MEMORY;
247 ret = samdb_search(sidmap->samctx, ctx, NULL, &res, attrs,
248 "objectSid=%s", sidstr);
253 /* make sure its not a user */
254 if (!is_group_account(res[0])) {
255 DEBUG(0,("sid_to_unixgid: sid %s is a ATYPE_NORMAL_ACCOUNT\n", sidstr));
257 return NT_STATUS_INVALID_SID;
260 /* first try to get the gid directly */
261 s = samdb_result_string(res[0], "unixID", NULL);
263 *gid = strtoul(s, NULL, 0);
268 /* next try via the UnixName attribute */
269 s = samdb_result_string(res[0], "unixName", NULL);
271 struct group *grp = getgrnam(s);
273 DEBUG(0,("unixName '%s' for sid %s does not exist as a local group\n",
276 return NT_STATUS_NO_SUCH_USER;
283 /* finally try via the sAMAccountName attribute */
284 s = samdb_result_string(res[0], "sAMAccountName", NULL);
286 struct group *grp = getgrnam(s);
288 DEBUG(0,("sAMAccountName '%s' for sid %s does not exist as a local group\n", s, sidstr));
290 return NT_STATUS_NO_SUCH_USER;
298 status = sidmap_primary_domain_sid(sidmap, ctx, &domain_sid);
299 if (!NT_STATUS_IS_OK(status)) {
301 return NT_STATUS_NO_SUCH_DOMAIN;
304 if (dom_sid_in_domain(domain_sid, sid)) {
305 uint32_t rid = sid->sub_auths[sid->num_auths-1];
306 if (rid >= SIDMAP_LOCAL_GROUP_BASE) {
307 *gid = rid - SIDMAP_LOCAL_GROUP_BASE;
313 DEBUG(0,("sid_to_unixgid: no unixID, unixName or sAMAccountName for sid %s\n",
317 return NT_STATUS_INVALID_SID;
322 map a unix uid to a dom_sid
323 the returned sid is allocated in the supplied mem_ctx
325 NTSTATUS sidmap_uid_to_sid(struct sidmap_context *sidmap,
327 uid_t uid, struct dom_sid **sid)
329 const char *attrs[] = { "sAMAccountName", "objectSid", "sAMAccountType", NULL };
332 struct ldb_message **res;
334 struct dom_sid *domain_sid;
338 we search for the mapping in the following order:
340 - check if the uid is in the dynamic uid range assigned for winbindd
341 use. If it is, then look in winbindd sid mapping
342 database (not implemented yet)
343 - look for a user account in samdb that has unixID set to the
345 - look for a user account in samdb that has unixName or
346 sAMAccountName set to the name given by getpwuid()
347 - assign a SID by adding the uid to SIDMAP_LOCAL_USER_BASE in the local
352 ctx = talloc_new(sidmap);
356 step 2: look for a user account in samdb that has unixID set to the
360 ret = samdb_search(sidmap->samctx, ctx, NULL, &res, attrs,
361 "unixID=%u", (unsigned int)uid);
362 for (i=0;i<ret;i++) {
365 if (!is_user_account(res[i])) continue;
367 sidstr = samdb_result_string(res[i], "objectSid", NULL);
368 if (sidstr == NULL) continue;
370 *sid = dom_sid_parse_talloc(mem_ctx, sidstr);
373 return NT_STATUS_NO_MEMORY;
379 step 3: look for a user account in samdb that has unixName
380 or sAMAccountName set to the name given by getpwuid()
387 ret = samdb_search(sidmap->samctx, ctx, NULL, &res, attrs,
388 "(|(unixName=%s)(sAMAccountName=%s))",
389 pwd->pw_name, pwd->pw_name);
390 for (i=0;i<ret;i++) {
393 if (!is_user_account(res[i])) continue;
395 sidstr = samdb_result_string(res[i], "objectSid", NULL);
396 if (sidstr == NULL) continue;
398 *sid = dom_sid_parse_talloc(mem_ctx, sidstr);
401 return NT_STATUS_NO_MEMORY;
408 step 4: assign a SID by adding the uid to
409 SIDMAP_LOCAL_USER_BASE in the local domain
412 if (uid > SIDMAP_MAX_LOCAL_UID) {
413 return NT_STATUS_INVALID_SID;
416 status = sidmap_primary_domain_sid(sidmap, mem_ctx, &domain_sid);
417 if (!NT_STATUS_IS_OK(status)) {
422 *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_USER_BASE + uid);
426 return NT_STATUS_NO_MEMORY;
434 map a unix gid to a dom_sid
435 the returned sid is allocated in the supplied mem_ctx
437 NTSTATUS sidmap_gid_to_sid(struct sidmap_context *sidmap,
439 gid_t gid, struct dom_sid **sid)
441 const char *attrs[] = { "sAMAccountName", "objectSid", "sAMAccountType", NULL };
444 struct ldb_message **res;
446 struct dom_sid *domain_sid;
450 we search for the mapping in the following order:
452 - check if the gid is in the dynamic gid range assigned for winbindd
453 use. If it is, then look in winbindd sid mapping
454 database (not implemented yet)
455 - look for a group account in samdb that has unixID set to the
457 - look for a group account in samdb that has unixName or
458 sAMAccountName set to the name given by getgrgid()
459 - assign a SID by adding the gid to SIDMAP_LOCAL_GROUP_BASE in the local
464 ctx = talloc_new(sidmap);
468 step 2: look for a group account in samdb that has unixID set to the
472 ret = samdb_search(sidmap->samctx, ctx, NULL, &res, attrs,
473 "unixID=%u", (unsigned int)gid);
474 for (i=0;i<ret;i++) {
477 if (!is_group_account(res[i])) continue;
479 sidstr = samdb_result_string(res[i], "objectSid", NULL);
480 if (sidstr == NULL) continue;
482 *sid = dom_sid_parse_talloc(mem_ctx, sidstr);
485 return NT_STATUS_NO_MEMORY;
491 step 3: look for a group account in samdb that has unixName
492 or sAMAccountName set to the name given by getgrgid()
499 ret = samdb_search(sidmap->samctx, ctx, NULL, &res, attrs,
500 "(|(unixName=%s)(sAMAccountName=%s))",
501 grp->gr_name, grp->gr_name);
502 for (i=0;i<ret;i++) {
505 if (!is_group_account(res[i])) continue;
507 sidstr = samdb_result_string(res[i], "objectSid", NULL);
508 if (sidstr == NULL) continue;
510 *sid = dom_sid_parse_talloc(mem_ctx, sidstr);
513 return NT_STATUS_NO_MEMORY;
520 step 4: assign a SID by adding the gid to
521 SIDMAP_LOCAL_GROUP_BASE in the local domain
524 if (gid > SIDMAP_MAX_LOCAL_GID) {
525 return NT_STATUS_INVALID_SID;
528 status = sidmap_primary_domain_sid(sidmap, mem_ctx, &domain_sid);
529 if (!NT_STATUS_IS_OK(status)) {
534 *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_GROUP_BASE + gid);
538 return NT_STATUS_NO_MEMORY;
545 check if a sid is in the range of auto-allocated SIDs from our primary domain,
546 and if it is, then return the name and atype
548 NTSTATUS sidmap_allocated_sid_lookup(struct sidmap_context *sidmap,
550 const struct dom_sid *sid,
555 struct dom_sid *domain_sid;
556 void *ctx = talloc_new(mem_ctx);
559 status = sidmap_primary_domain_sid(sidmap, ctx, &domain_sid);
560 if (!NT_STATUS_IS_OK(status)) {
561 return NT_STATUS_NO_SUCH_DOMAIN;
564 if (!dom_sid_in_domain(domain_sid, sid)) {
566 return NT_STATUS_INVALID_SID;
571 rid = sid->sub_auths[sid->num_auths-1];
572 if (rid < SIDMAP_LOCAL_USER_BASE) {
573 return NT_STATUS_INVALID_SID;
576 if (rid < SIDMAP_LOCAL_GROUP_BASE) {
578 uid_t uid = rid - SIDMAP_LOCAL_USER_BASE;
579 *atype = ATYPE_NORMAL_ACCOUNT;
582 *name = talloc_asprintf(mem_ctx, "uid%u", uid);
584 *name = talloc_strdup(mem_ctx, pwd->pw_name);
588 gid_t gid = rid - SIDMAP_LOCAL_GROUP_BASE;
589 *atype = ATYPE_LOCAL_GROUP;
592 *name = talloc_asprintf(mem_ctx, "gid%u", gid);
594 *name = talloc_strdup(mem_ctx, grp->gr_name);
599 return NT_STATUS_NO_MEMORY;