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/passwd.h"
26 #include "dsdb/samdb/samdb.h"
27 #include "auth/auth.h"
28 #include "libcli/ldap/ldap.h"
31 these are used for the fallback local uid/gid to sid mapping
34 #define SIDMAP_LOCAL_USER_BASE 0x80000000
35 #define SIDMAP_LOCAL_GROUP_BASE 0xC0000000
36 #define SIDMAP_MAX_LOCAL_UID 0x3fffffff
37 #define SIDMAP_MAX_LOCAL_GID 0x3fffffff
40 private context for sid mapping routines
42 struct sidmap_context {
47 open a sidmap context - use talloc_free to close
49 struct sidmap_context *sidmap_open(TALLOC_CTX *mem_ctx)
51 struct sidmap_context *sidmap;
52 sidmap = talloc(mem_ctx, struct sidmap_context);
56 sidmap->samctx = samdb_connect(sidmap, system_session(sidmap));
57 if (sidmap->samctx == NULL) {
67 check the sAMAccountType field of a search result to see if
68 the account is a user account
70 static BOOL is_user_account(struct ldb_message *res)
72 uint_t atype = samdb_result_uint(res, "sAMAccountType", 0);
73 if (atype && (!(atype & ATYPE_ACCOUNT))) {
80 check the sAMAccountType field of a search result to see if
81 the account is a group account
83 static BOOL is_group_account(struct ldb_message *res)
85 uint_t atype = samdb_result_uint(res, "sAMAccountType", 0);
86 if (atype && atype == ATYPE_NORMAL_ACCOUNT) {
95 return the dom_sid of our primary domain
97 static NTSTATUS sidmap_primary_domain_sid(struct sidmap_context *sidmap,
98 TALLOC_CTX *mem_ctx, struct dom_sid **sid)
100 const char *attrs[] = { "objectSid", NULL };
102 struct ldb_message **res = NULL;
104 ret = gendb_search_dn(sidmap->samctx, mem_ctx, samdb_base_dn(mem_ctx),
108 return NT_STATUS_NO_SUCH_DOMAIN;
111 *sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
114 return NT_STATUS_NO_MEMORY;
122 map a sid to a unix uid
124 NTSTATUS sidmap_sid_to_unixuid(struct sidmap_context *sidmap,
125 struct dom_sid *sid, uid_t *uid)
127 const char *attrs[] = { "sAMAccountName", "unixID",
128 "unixName", "sAMAccountType", NULL };
132 struct ldb_message **res;
133 struct dom_sid *domain_sid;
136 tmp_ctx = talloc_new(sidmap);
138 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
139 "objectSid=%s", ldap_encode_ndr_dom_sid(tmp_ctx, sid));
144 /* make sure its a user, not a group */
145 if (!is_user_account(res[0])) {
146 DEBUG(0,("sid_to_unixuid: sid %s is not an account!\n",
147 dom_sid_string(tmp_ctx, sid)));
148 talloc_free(tmp_ctx);
149 return NT_STATUS_INVALID_SID;
152 /* first try to get the uid directly */
153 s = samdb_result_string(res[0], "unixID", NULL);
155 *uid = strtoul(s, NULL, 0);
156 talloc_free(tmp_ctx);
160 /* next try via the UnixName attribute */
161 s = samdb_result_string(res[0], "unixName", NULL);
163 struct passwd *pwd = getpwnam(s);
165 DEBUG(0,("unixName %s for sid %s does not exist as a local user\n", s,
166 dom_sid_string(tmp_ctx, sid)));
167 talloc_free(tmp_ctx);
168 return NT_STATUS_NO_SUCH_USER;
171 talloc_free(tmp_ctx);
175 /* finally try via the sAMAccountName attribute */
176 s = samdb_result_string(res[0], "sAMAccountName", NULL);
178 struct passwd *pwd = getpwnam(s);
180 DEBUG(0,("sAMAccountName '%s' for sid %s does not exist as a local user\n",
181 s, dom_sid_string(tmp_ctx, sid)));
182 talloc_free(tmp_ctx);
183 return NT_STATUS_NO_SUCH_USER;
186 talloc_free(tmp_ctx);
192 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
193 if (!NT_STATUS_IS_OK(status)) {
194 talloc_free(tmp_ctx);
195 return NT_STATUS_NO_SUCH_DOMAIN;
198 if (dom_sid_in_domain(domain_sid, sid)) {
199 uint32_t rid = sid->sub_auths[sid->num_auths-1];
200 if (rid >= SIDMAP_LOCAL_USER_BASE &&
201 rid < SIDMAP_LOCAL_GROUP_BASE) {
202 *uid = rid - SIDMAP_LOCAL_USER_BASE;
203 talloc_free(tmp_ctx);
209 DEBUG(0,("sid_to_unixuid: no unixID, unixName or sAMAccountName for sid %s\n",
210 dom_sid_string(tmp_ctx, sid)));
212 talloc_free(tmp_ctx);
213 return NT_STATUS_INVALID_SID;
218 map a sid to a unix gid
220 NTSTATUS sidmap_sid_to_unixgid(struct sidmap_context *sidmap,
221 struct dom_sid *sid, gid_t *gid)
223 const char *attrs[] = { "sAMAccountName", "unixID",
224 "unixName", "sAMAccountType", NULL };
228 struct ldb_message **res;
230 struct dom_sid *domain_sid;
232 tmp_ctx = talloc_new(sidmap);
234 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
235 "objectSid=%s", ldap_encode_ndr_dom_sid(tmp_ctx, sid));
240 /* make sure its not a user */
241 if (!is_group_account(res[0])) {
242 DEBUG(0,("sid_to_unixgid: sid %s is a ATYPE_NORMAL_ACCOUNT\n",
243 dom_sid_string(tmp_ctx, sid)));
244 talloc_free(tmp_ctx);
245 return NT_STATUS_INVALID_SID;
248 /* first try to get the gid directly */
249 s = samdb_result_string(res[0], "unixID", NULL);
251 *gid = strtoul(s, NULL, 0);
252 talloc_free(tmp_ctx);
256 /* next try via the UnixName attribute */
257 s = samdb_result_string(res[0], "unixName", NULL);
259 struct group *grp = getgrnam(s);
261 DEBUG(0,("unixName '%s' for sid %s does not exist as a local group\n",
262 s, dom_sid_string(tmp_ctx, sid)));
263 talloc_free(tmp_ctx);
264 return NT_STATUS_NO_SUCH_USER;
267 talloc_free(tmp_ctx);
271 /* finally try via the sAMAccountName attribute */
272 s = samdb_result_string(res[0], "sAMAccountName", NULL);
274 struct group *grp = getgrnam(s);
276 DEBUG(0,("sAMAccountName '%s' for sid %s does not exist as a local group\n", s, dom_sid_string(tmp_ctx, sid)));
277 talloc_free(tmp_ctx);
278 return NT_STATUS_NO_SUCH_USER;
281 talloc_free(tmp_ctx);
286 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
287 if (!NT_STATUS_IS_OK(status)) {
288 talloc_free(tmp_ctx);
289 return NT_STATUS_NO_SUCH_DOMAIN;
292 if (dom_sid_in_domain(domain_sid, sid)) {
293 uint32_t rid = sid->sub_auths[sid->num_auths-1];
294 if (rid >= SIDMAP_LOCAL_GROUP_BASE) {
295 *gid = rid - SIDMAP_LOCAL_GROUP_BASE;
296 talloc_free(tmp_ctx);
301 DEBUG(0,("sid_to_unixgid: no unixID, unixName or sAMAccountName for sid %s\n",
302 dom_sid_string(tmp_ctx, sid)));
304 talloc_free(tmp_ctx);
305 return NT_STATUS_INVALID_SID;
310 map a unix uid to a dom_sid
311 the returned sid is allocated in the supplied mem_ctx
313 NTSTATUS sidmap_uid_to_sid(struct sidmap_context *sidmap,
315 uid_t uid, struct dom_sid **sid)
317 const char *attrs[] = { "sAMAccountName", "objectSid", "sAMAccountType", NULL };
320 struct ldb_message **res;
322 struct dom_sid *domain_sid;
326 we search for the mapping in the following order:
328 - check if the uid is in the dynamic uid range assigned for winbindd
329 use. If it is, then look in winbindd sid mapping
330 database (not implemented yet)
331 - look for a user account in samdb that has unixID set to the
333 - look for a user account in samdb that has unixName or
334 sAMAccountName set to the name given by getpwuid()
335 - assign a SID by adding the uid to SIDMAP_LOCAL_USER_BASE in the local
340 tmp_ctx = talloc_new(mem_ctx);
344 step 2: look for a user account in samdb that has unixID set to the
348 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
349 "unixID=%u", (unsigned int)uid);
350 for (i=0;i<ret;i++) {
351 if (!is_user_account(res[i])) continue;
353 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
354 talloc_free(tmp_ctx);
355 NT_STATUS_HAVE_NO_MEMORY(*sid);
360 step 3: look for a user account in samdb that has unixName
361 or sAMAccountName set to the name given by getpwuid()
368 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
369 "(|(unixName=%s)(sAMAccountName=%s))",
370 pwd->pw_name, pwd->pw_name);
371 for (i=0;i<ret;i++) {
372 if (!is_user_account(res[i])) continue;
374 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
375 talloc_free(tmp_ctx);
376 NT_STATUS_HAVE_NO_MEMORY(*sid);
382 step 4: assign a SID by adding the uid to
383 SIDMAP_LOCAL_USER_BASE in the local domain
386 if (uid > SIDMAP_MAX_LOCAL_UID) {
387 return NT_STATUS_INVALID_SID;
390 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
391 if (!NT_STATUS_IS_OK(status)) {
392 talloc_free(tmp_ctx);
396 *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_USER_BASE + uid);
397 talloc_free(tmp_ctx);
400 return NT_STATUS_NO_MEMORY;
408 map a unix gid to a dom_sid
409 the returned sid is allocated in the supplied mem_ctx
411 NTSTATUS sidmap_gid_to_sid(struct sidmap_context *sidmap,
413 gid_t gid, struct dom_sid **sid)
415 const char *attrs[] = { "sAMAccountName", "objectSid", "sAMAccountType", NULL };
418 struct ldb_message **res;
420 struct dom_sid *domain_sid;
424 we search for the mapping in the following order:
426 - check if the gid is in the dynamic gid range assigned for winbindd
427 use. If it is, then look in winbindd sid mapping
428 database (not implemented yet)
429 - look for a group account in samdb that has unixID set to the
431 - look for a group account in samdb that has unixName or
432 sAMAccountName set to the name given by getgrgid()
433 - assign a SID by adding the gid to SIDMAP_LOCAL_GROUP_BASE in the local
438 tmp_ctx = talloc_new(sidmap);
442 step 2: look for a group account in samdb that has unixID set to the
446 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
447 "unixID=%u", (unsigned int)gid);
448 for (i=0;i<ret;i++) {
449 if (!is_group_account(res[i])) continue;
451 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
452 talloc_free(tmp_ctx);
453 NT_STATUS_HAVE_NO_MEMORY(*sid);
458 step 3: look for a group account in samdb that has unixName
459 or sAMAccountName set to the name given by getgrgid()
466 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
467 "(|(unixName=%s)(sAMAccountName=%s))",
468 grp->gr_name, grp->gr_name);
469 for (i=0;i<ret;i++) {
470 if (!is_group_account(res[i])) continue;
472 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
473 talloc_free(tmp_ctx);
474 NT_STATUS_HAVE_NO_MEMORY(*sid);
480 step 4: assign a SID by adding the gid to
481 SIDMAP_LOCAL_GROUP_BASE in the local domain
484 if (gid > SIDMAP_MAX_LOCAL_GID) {
485 return NT_STATUS_INVALID_SID;
488 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
489 if (!NT_STATUS_IS_OK(status)) {
490 talloc_free(tmp_ctx);
494 *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_GROUP_BASE + gid);
495 talloc_free(tmp_ctx);
498 return NT_STATUS_NO_MEMORY;
505 check if a sid is in the range of auto-allocated SIDs from our primary domain,
506 and if it is, then return the name and atype
508 NTSTATUS sidmap_allocated_sid_lookup(struct sidmap_context *sidmap,
510 const struct dom_sid *sid,
515 struct dom_sid *domain_sid;
516 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
519 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
520 if (!NT_STATUS_IS_OK(status)) {
521 return NT_STATUS_NO_SUCH_DOMAIN;
524 if (!dom_sid_in_domain(domain_sid, sid)) {
525 talloc_free(tmp_ctx);
526 return NT_STATUS_INVALID_SID;
529 talloc_free(tmp_ctx);
531 rid = sid->sub_auths[sid->num_auths-1];
532 if (rid < SIDMAP_LOCAL_USER_BASE) {
533 return NT_STATUS_INVALID_SID;
536 if (rid < SIDMAP_LOCAL_GROUP_BASE) {
538 uid_t uid = rid - SIDMAP_LOCAL_USER_BASE;
539 *atype = ATYPE_NORMAL_ACCOUNT;
542 *name = talloc_asprintf(mem_ctx, "uid%u", uid);
544 *name = talloc_strdup(mem_ctx, pwd->pw_name);
548 gid_t gid = rid - SIDMAP_LOCAL_GROUP_BASE;
549 *atype = ATYPE_LOCAL_GROUP;
552 *name = talloc_asprintf(mem_ctx, "gid%u", gid);
554 *name = talloc_strdup(mem_ctx, grp->gr_name);
559 return NT_STATUS_NO_MEMORY;