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 "system/filesys.h"
25 #include "system/passwd.h"
26 #include "librpc/gen_ndr/ndr_security.h"
30 these are used for the fallback local uid/gid to sid mapping
33 #define SIDMAP_LOCAL_USER_BASE 0x80000000
34 #define SIDMAP_LOCAL_GROUP_BASE 0xC0000000
35 #define SIDMAP_MAX_LOCAL_UID 0x3fffffff
36 #define SIDMAP_MAX_LOCAL_GID 0x3fffffff
39 private context for sid mapping routines
41 struct sidmap_context {
46 open a sidmap context - use talloc_free to close
48 struct sidmap_context *sidmap_open(TALLOC_CTX *mem_ctx)
50 struct sidmap_context *sidmap;
51 sidmap = talloc(mem_ctx, struct sidmap_context);
55 sidmap->samctx = samdb_connect(sidmap);
56 if (sidmap->samctx == NULL) {
66 check the sAMAccountType field of a search result to see if
67 the account is a user account
69 static BOOL is_user_account(struct ldb_message *res)
71 uint_t atype = samdb_result_uint(res, "sAMAccountType", 0);
72 if (atype && (!(atype & ATYPE_ACCOUNT))) {
79 check the sAMAccountType field of a search result to see if
80 the account is a group account
82 static BOOL is_group_account(struct ldb_message *res)
84 uint_t atype = samdb_result_uint(res, "sAMAccountType", 0);
85 if (atype && atype == ATYPE_NORMAL_ACCOUNT) {
94 return the dom_sid of our primary domain
96 static NTSTATUS sidmap_primary_domain_sid(struct sidmap_context *sidmap,
97 TALLOC_CTX *mem_ctx, struct dom_sid **sid)
99 const char *attrs[] = { "objectSid", NULL };
101 struct ldb_message **res = NULL;
103 ret = gendb_search(sidmap->samctx, mem_ctx, NULL, &res, attrs,
104 "(&(objectClass=domain)(name=%s))", lp_workgroup());
107 return NT_STATUS_NO_SUCH_DOMAIN;
110 *sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
113 return NT_STATUS_NO_MEMORY;
121 map a sid to a unix uid
123 NTSTATUS sidmap_sid_to_unixuid(struct sidmap_context *sidmap,
124 struct dom_sid *sid, uid_t *uid)
126 const char *attrs[] = { "sAMAccountName", "unixID",
127 "unixName", "sAMAccountType", NULL };
131 struct ldb_message **res;
132 struct dom_sid *domain_sid;
135 ctx = talloc_new(sidmap);
137 ret = gendb_search(sidmap->samctx, ctx, NULL, &res, attrs,
138 "objectSid=%s", ldap_encode_ndr_dom_sid(ctx, sid));
143 /* make sure its a user, not a group */
144 if (!is_user_account(res[0])) {
145 DEBUG(0,("sid_to_unixuid: sid %s is not an account!\n",
146 dom_sid_string(ctx, sid)));
148 return NT_STATUS_INVALID_SID;
151 /* first try to get the uid directly */
152 s = samdb_result_string(res[0], "unixID", NULL);
154 *uid = strtoul(s, NULL, 0);
159 /* next try via the UnixName attribute */
160 s = samdb_result_string(res[0], "unixName", NULL);
162 struct passwd *pwd = getpwnam(s);
164 DEBUG(0,("unixName %s for sid %s does not exist as a local user\n", s, dom_sid_string(ctx, sid)));
166 return NT_STATUS_NO_SUCH_USER;
173 /* finally try via the sAMAccountName attribute */
174 s = samdb_result_string(res[0], "sAMAccountName", NULL);
176 struct passwd *pwd = getpwnam(s);
178 DEBUG(0,("sAMAccountName '%s' for sid %s does not exist as a local user\n",
179 s, dom_sid_string(ctx, sid)));
181 return NT_STATUS_NO_SUCH_USER;
190 status = sidmap_primary_domain_sid(sidmap, ctx, &domain_sid);
191 if (!NT_STATUS_IS_OK(status)) {
193 return NT_STATUS_NO_SUCH_DOMAIN;
196 if (dom_sid_in_domain(domain_sid, sid)) {
197 uint32_t rid = sid->sub_auths[sid->num_auths-1];
198 if (rid >= SIDMAP_LOCAL_USER_BASE &&
199 rid < SIDMAP_LOCAL_GROUP_BASE) {
200 *uid = rid - SIDMAP_LOCAL_USER_BASE;
207 DEBUG(0,("sid_to_unixuid: no unixID, unixName or sAMAccountName for sid %s\n",
208 dom_sid_string(ctx, sid)));
211 return NT_STATUS_INVALID_SID;
216 map a sid to a unix gid
218 NTSTATUS sidmap_sid_to_unixgid(struct sidmap_context *sidmap,
219 struct dom_sid *sid, gid_t *gid)
221 const char *attrs[] = { "sAMAccountName", "unixID",
222 "unixName", "sAMAccountType", NULL };
226 struct ldb_message **res;
228 struct dom_sid *domain_sid;
230 ctx = talloc_new(sidmap);
232 ret = gendb_search(sidmap->samctx, ctx, NULL, &res, attrs,
233 "objectSid=%s", ldap_encode_ndr_dom_sid(ctx, sid));
238 /* make sure its not a user */
239 if (!is_group_account(res[0])) {
240 DEBUG(0,("sid_to_unixgid: sid %s is a ATYPE_NORMAL_ACCOUNT\n",
241 dom_sid_string(ctx, sid)));
243 return NT_STATUS_INVALID_SID;
246 /* first try to get the gid directly */
247 s = samdb_result_string(res[0], "unixID", NULL);
249 *gid = strtoul(s, NULL, 0);
254 /* next try via the UnixName attribute */
255 s = samdb_result_string(res[0], "unixName", NULL);
257 struct group *grp = getgrnam(s);
259 DEBUG(0,("unixName '%s' for sid %s does not exist as a local group\n",
260 s, dom_sid_string(ctx, sid)));
262 return NT_STATUS_NO_SUCH_USER;
269 /* finally try via the sAMAccountName attribute */
270 s = samdb_result_string(res[0], "sAMAccountName", NULL);
272 struct group *grp = getgrnam(s);
274 DEBUG(0,("sAMAccountName '%s' for sid %s does not exist as a local group\n", s, dom_sid_string(ctx, sid)));
276 return NT_STATUS_NO_SUCH_USER;
284 status = sidmap_primary_domain_sid(sidmap, ctx, &domain_sid);
285 if (!NT_STATUS_IS_OK(status)) {
287 return NT_STATUS_NO_SUCH_DOMAIN;
290 if (dom_sid_in_domain(domain_sid, sid)) {
291 uint32_t rid = sid->sub_auths[sid->num_auths-1];
292 if (rid >= SIDMAP_LOCAL_GROUP_BASE) {
293 *gid = rid - SIDMAP_LOCAL_GROUP_BASE;
299 DEBUG(0,("sid_to_unixgid: no unixID, unixName or sAMAccountName for sid %s\n",
300 dom_sid_string(ctx, sid)));
303 return NT_STATUS_INVALID_SID;
308 map a unix uid to a dom_sid
309 the returned sid is allocated in the supplied mem_ctx
311 NTSTATUS sidmap_uid_to_sid(struct sidmap_context *sidmap,
313 uid_t uid, struct dom_sid **sid)
315 const char *attrs[] = { "sAMAccountName", "objectSid", "sAMAccountType", NULL };
318 struct ldb_message **res;
320 struct dom_sid *domain_sid;
324 we search for the mapping in the following order:
326 - check if the uid is in the dynamic uid range assigned for winbindd
327 use. If it is, then look in winbindd sid mapping
328 database (not implemented yet)
329 - look for a user account in samdb that has unixID set to the
331 - look for a user account in samdb that has unixName or
332 sAMAccountName set to the name given by getpwuid()
333 - assign a SID by adding the uid to SIDMAP_LOCAL_USER_BASE in the local
338 ctx = talloc_new(sidmap);
342 step 2: look for a user account in samdb that has unixID set to the
346 ret = gendb_search(sidmap->samctx, ctx, NULL, &res, attrs,
347 "unixID=%u", (unsigned int)uid);
348 for (i=0;i<ret;i++) {
349 if (!is_user_account(res[i])) continue;
351 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
353 NT_STATUS_HAVE_NO_MEMORY(*sid);
358 step 3: look for a user account in samdb that has unixName
359 or sAMAccountName set to the name given by getpwuid()
366 ret = gendb_search(sidmap->samctx, ctx, NULL, &res, attrs,
367 "(|(unixName=%s)(sAMAccountName=%s))",
368 pwd->pw_name, pwd->pw_name);
369 for (i=0;i<ret;i++) {
370 if (!is_user_account(res[i])) continue;
372 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
374 NT_STATUS_HAVE_NO_MEMORY(*sid);
380 step 4: assign a SID by adding the uid to
381 SIDMAP_LOCAL_USER_BASE in the local domain
384 if (uid > SIDMAP_MAX_LOCAL_UID) {
385 return NT_STATUS_INVALID_SID;
388 status = sidmap_primary_domain_sid(sidmap, mem_ctx, &domain_sid);
389 if (!NT_STATUS_IS_OK(status)) {
394 *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_USER_BASE + uid);
398 return NT_STATUS_NO_MEMORY;
406 map a unix gid to a dom_sid
407 the returned sid is allocated in the supplied mem_ctx
409 NTSTATUS sidmap_gid_to_sid(struct sidmap_context *sidmap,
411 gid_t gid, struct dom_sid **sid)
413 const char *attrs[] = { "sAMAccountName", "objectSid", "sAMAccountType", NULL };
416 struct ldb_message **res;
418 struct dom_sid *domain_sid;
422 we search for the mapping in the following order:
424 - check if the gid is in the dynamic gid range assigned for winbindd
425 use. If it is, then look in winbindd sid mapping
426 database (not implemented yet)
427 - look for a group account in samdb that has unixID set to the
429 - look for a group account in samdb that has unixName or
430 sAMAccountName set to the name given by getgrgid()
431 - assign a SID by adding the gid to SIDMAP_LOCAL_GROUP_BASE in the local
436 ctx = talloc_new(sidmap);
440 step 2: look for a group account in samdb that has unixID set to the
444 ret = gendb_search(sidmap->samctx, ctx, NULL, &res, attrs,
445 "unixID=%u", (unsigned int)gid);
446 for (i=0;i<ret;i++) {
447 if (!is_group_account(res[i])) continue;
449 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
451 NT_STATUS_HAVE_NO_MEMORY(*sid);
456 step 3: look for a group account in samdb that has unixName
457 or sAMAccountName set to the name given by getgrgid()
464 ret = gendb_search(sidmap->samctx, ctx, NULL, &res, attrs,
465 "(|(unixName=%s)(sAMAccountName=%s))",
466 grp->gr_name, grp->gr_name);
467 for (i=0;i<ret;i++) {
468 if (!is_group_account(res[i])) continue;
470 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
472 NT_STATUS_HAVE_NO_MEMORY(*sid);
478 step 4: assign a SID by adding the gid to
479 SIDMAP_LOCAL_GROUP_BASE in the local domain
482 if (gid > SIDMAP_MAX_LOCAL_GID) {
483 return NT_STATUS_INVALID_SID;
486 status = sidmap_primary_domain_sid(sidmap, mem_ctx, &domain_sid);
487 if (!NT_STATUS_IS_OK(status)) {
492 *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_GROUP_BASE + gid);
496 return NT_STATUS_NO_MEMORY;
503 check if a sid is in the range of auto-allocated SIDs from our primary domain,
504 and if it is, then return the name and atype
506 NTSTATUS sidmap_allocated_sid_lookup(struct sidmap_context *sidmap,
508 const struct dom_sid *sid,
513 struct dom_sid *domain_sid;
514 void *ctx = talloc_new(mem_ctx);
517 status = sidmap_primary_domain_sid(sidmap, ctx, &domain_sid);
518 if (!NT_STATUS_IS_OK(status)) {
519 return NT_STATUS_NO_SUCH_DOMAIN;
522 if (!dom_sid_in_domain(domain_sid, sid)) {
524 return NT_STATUS_INVALID_SID;
529 rid = sid->sub_auths[sid->num_auths-1];
530 if (rid < SIDMAP_LOCAL_USER_BASE) {
531 return NT_STATUS_INVALID_SID;
534 if (rid < SIDMAP_LOCAL_GROUP_BASE) {
536 uid_t uid = rid - SIDMAP_LOCAL_USER_BASE;
537 *atype = ATYPE_NORMAL_ACCOUNT;
540 *name = talloc_asprintf(mem_ctx, "uid%u", uid);
542 *name = talloc_strdup(mem_ctx, pwd->pw_name);
546 gid_t gid = rid - SIDMAP_LOCAL_GROUP_BASE;
547 *atype = ATYPE_LOCAL_GROUP;
550 *name = talloc_asprintf(mem_ctx, "gid%u", gid);
552 *name = talloc_strdup(mem_ctx, grp->gr_name);
557 return NT_STATUS_NO_MEMORY;