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 3 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, see <http://www.gnu.org/licenses/>.
23 #include "system/passwd.h"
24 #include "dsdb/common/flags.h"
25 #include "dsdb/samdb/samdb.h"
26 #include "auth/auth.h"
27 #include "libcli/ldap/ldap.h"
29 #include "libcli/security/security.h"
32 these are used for the fallback local uid/gid to sid mapping
35 #define SIDMAP_LOCAL_USER_BASE 0x80000000
36 #define SIDMAP_LOCAL_GROUP_BASE 0xC0000000
37 #define SIDMAP_MAX_LOCAL_UID 0x3fffffff
38 #define SIDMAP_MAX_LOCAL_GID 0x3fffffff
41 private context for sid mapping routines
43 struct sidmap_context {
44 struct ldb_context *samctx;
48 open a sidmap context - use talloc_free to close
50 _PUBLIC_ struct sidmap_context *sidmap_open(TALLOC_CTX *mem_ctx)
52 struct sidmap_context *sidmap;
53 sidmap = talloc(mem_ctx, struct sidmap_context);
57 sidmap->samctx = samdb_connect(sidmap, system_session(sidmap));
58 if (sidmap->samctx == NULL) {
68 check the sAMAccountType field of a search result to see if
69 the account is a user account
71 static BOOL is_user_account(struct ldb_message *res)
73 uint_t atype = samdb_result_uint(res, "sAMAccountType", 0);
74 if (atype && (!(atype & ATYPE_ACCOUNT))) {
81 check the sAMAccountType field of a search result to see if
82 the account is a group account
84 static BOOL is_group_account(struct ldb_message *res)
86 uint_t atype = samdb_result_uint(res, "sAMAccountType", 0);
87 if (atype && atype == ATYPE_NORMAL_ACCOUNT) {
96 return the dom_sid of our primary domain
98 static NTSTATUS sidmap_primary_domain_sid(struct sidmap_context *sidmap,
99 TALLOC_CTX *mem_ctx, struct dom_sid **sid)
101 const char *attrs[] = { "objectSid", NULL };
103 struct ldb_message **res = NULL;
105 ret = gendb_search_dn(sidmap->samctx, mem_ctx, NULL, &res, attrs);
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 _PUBLIC_ NTSTATUS sidmap_sid_to_unixuid(struct sidmap_context *sidmap,
125 struct dom_sid *sid, uid_t *uid)
127 const char *attrs[] = { "sAMAccountName", "uidNumber",
128 "sAMAccountType", "unixName", 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], "uidNumber", 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 uidNumber, unixName or sAMAccountName for sid %s\n",
210 dom_sid_string(tmp_ctx, sid)));
212 talloc_free(tmp_ctx);
213 return NT_STATUS_NONE_MAPPED;
218 see if a sid is a group - very inefficient!
220 _PUBLIC_ BOOL sidmap_sid_is_group(struct sidmap_context *sidmap, struct dom_sid *sid)
222 const char *attrs[] = { "sAMAccountType", NULL };
225 struct ldb_message **res;
227 struct dom_sid *domain_sid;
230 tmp_ctx = talloc_new(sidmap);
232 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
233 "objectSid=%s", ldap_encode_ndr_dom_sid(tmp_ctx, sid));
235 is_group = is_group_account(res[0]);
236 talloc_free(tmp_ctx);
240 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
241 if (!NT_STATUS_IS_OK(status)) {
242 talloc_free(tmp_ctx);
246 if (dom_sid_in_domain(domain_sid, sid)) {
247 uint32_t rid = sid->sub_auths[sid->num_auths-1];
248 if (rid >= SIDMAP_LOCAL_GROUP_BASE) {
249 talloc_free(tmp_ctx);
254 talloc_free(tmp_ctx);
259 map a sid to a unix gid
261 _PUBLIC_ NTSTATUS sidmap_sid_to_unixgid(struct sidmap_context *sidmap,
262 struct dom_sid *sid, gid_t *gid)
264 const char *attrs[] = { "sAMAccountName", "gidNumber",
265 "unixName", "sAMAccountType", NULL };
269 struct ldb_message **res;
271 struct dom_sid *domain_sid;
273 tmp_ctx = talloc_new(sidmap);
275 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
276 "objectSid=%s", ldap_encode_ndr_dom_sid(tmp_ctx, sid));
281 /* make sure its not a user */
282 if (!is_group_account(res[0])) {
283 DEBUG(0,("sid_to_unixgid: sid %s is a ATYPE_NORMAL_ACCOUNT\n",
284 dom_sid_string(tmp_ctx, sid)));
285 talloc_free(tmp_ctx);
286 return NT_STATUS_INVALID_SID;
289 /* first try to get the gid directly */
290 s = samdb_result_string(res[0], "gidNumber", NULL);
292 *gid = strtoul(s, NULL, 0);
293 talloc_free(tmp_ctx);
297 /* next try via the UnixName attribute */
298 s = samdb_result_string(res[0], "unixName", NULL);
300 struct group *grp = getgrnam(s);
302 DEBUG(0,("unixName '%s' for sid %s does not exist as a local group\n",
303 s, dom_sid_string(tmp_ctx, sid)));
304 talloc_free(tmp_ctx);
305 return NT_STATUS_NO_SUCH_GROUP;
308 talloc_free(tmp_ctx);
312 /* finally try via the sAMAccountName attribute */
313 s = samdb_result_string(res[0], "sAMAccountName", NULL);
315 struct group *grp = getgrnam(s);
317 DEBUG(0,("sAMAccountName '%s' for sid %s does not exist as a local group\n", s, dom_sid_string(tmp_ctx, sid)));
318 talloc_free(tmp_ctx);
319 return NT_STATUS_NO_SUCH_GROUP;
322 talloc_free(tmp_ctx);
327 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
328 if (!NT_STATUS_IS_OK(status)) {
329 talloc_free(tmp_ctx);
330 return NT_STATUS_NO_SUCH_DOMAIN;
333 if (dom_sid_in_domain(domain_sid, sid)) {
334 uint32_t rid = sid->sub_auths[sid->num_auths-1];
335 if (rid >= SIDMAP_LOCAL_GROUP_BASE) {
336 *gid = rid - SIDMAP_LOCAL_GROUP_BASE;
337 talloc_free(tmp_ctx);
342 DEBUG(0,("sid_to_unixgid: no gidNumber, unixName or sAMAccountName for sid %s\n",
343 dom_sid_string(tmp_ctx, sid)));
345 talloc_free(tmp_ctx);
346 return NT_STATUS_NONE_MAPPED;
351 map a unix uid to a dom_sid
352 the returned sid is allocated in the supplied mem_ctx
354 _PUBLIC_ NTSTATUS sidmap_uid_to_sid(struct sidmap_context *sidmap,
356 uid_t uid, struct dom_sid **sid)
358 const char *attrs[] = { "sAMAccountName", "objectSid", "sAMAccountType", NULL };
361 struct ldb_message **res;
363 struct dom_sid *domain_sid;
367 we search for the mapping in the following order:
369 - check if the uid is in the dynamic uid range assigned for winbindd
370 use. If it is, then look in winbindd sid mapping
371 database (not implemented yet)
372 - look for a user account in samdb that has uidNumber set to the
374 - look for a user account in samdb that has unixName or
375 sAMAccountName set to the name given by getpwuid()
376 - assign a SID by adding the uid to SIDMAP_LOCAL_USER_BASE in the local
381 tmp_ctx = talloc_new(mem_ctx);
385 step 2: look for a user account in samdb that has uidNumber set to the
389 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
390 "uidNumber=%u", (unsigned int)uid);
391 for (i=0;i<ret;i++) {
392 if (!is_user_account(res[i])) continue;
394 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
395 talloc_free(tmp_ctx);
396 NT_STATUS_HAVE_NO_MEMORY(*sid);
401 step 3: look for a user account in samdb that has unixName
402 or sAMAccountName set to the name given by getpwuid()
409 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
410 "(|(unixName=%s)(sAMAccountName=%s))",
411 pwd->pw_name, pwd->pw_name);
412 for (i=0;i<ret;i++) {
413 if (!is_user_account(res[i])) continue;
415 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
416 talloc_free(tmp_ctx);
417 NT_STATUS_HAVE_NO_MEMORY(*sid);
423 step 4: assign a SID by adding the uid to
424 SIDMAP_LOCAL_USER_BASE in the local domain
427 if (uid > SIDMAP_MAX_LOCAL_UID) {
428 return NT_STATUS_NONE_MAPPED;
431 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
432 if (!NT_STATUS_IS_OK(status)) {
433 talloc_free(tmp_ctx);
437 *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_USER_BASE + uid);
438 talloc_free(tmp_ctx);
441 return NT_STATUS_NO_MEMORY;
449 map a unix gid to a dom_sid
450 the returned sid is allocated in the supplied mem_ctx
452 _PUBLIC_ NTSTATUS sidmap_gid_to_sid(struct sidmap_context *sidmap,
454 gid_t gid, struct dom_sid **sid)
456 const char *attrs[] = { "sAMAccountName", "objectSid", "sAMAccountType", NULL };
459 struct ldb_message **res;
461 struct dom_sid *domain_sid;
465 we search for the mapping in the following order:
467 - check if the gid is in the dynamic gid range assigned for winbindd
468 use. If it is, then look in winbindd sid mapping
469 database (not implemented yet)
470 - look for a group account in samdb that has gidNumber set to the
472 - look for a group account in samdb that has unixName or
473 sAMAccountName set to the name given by getgrgid()
474 - assign a SID by adding the gid to SIDMAP_LOCAL_GROUP_BASE in the local
479 tmp_ctx = talloc_new(sidmap);
483 step 2: look for a group account in samdb that has gidNumber set to the
487 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
488 "gidNumber=%u", (unsigned int)gid);
489 for (i=0;i<ret;i++) {
490 if (!is_group_account(res[i])) continue;
492 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
493 talloc_free(tmp_ctx);
494 NT_STATUS_HAVE_NO_MEMORY(*sid);
499 step 3: look for a group account in samdb that has unixName
500 or sAMAccountName set to the name given by getgrgid()
507 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
508 "(|(unixName=%s)(sAMAccountName=%s))",
509 grp->gr_name, grp->gr_name);
510 for (i=0;i<ret;i++) {
511 if (!is_group_account(res[i])) continue;
513 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
514 talloc_free(tmp_ctx);
515 NT_STATUS_HAVE_NO_MEMORY(*sid);
521 step 4: assign a SID by adding the gid to
522 SIDMAP_LOCAL_GROUP_BASE in the local domain
525 if (gid > SIDMAP_MAX_LOCAL_GID) {
526 return NT_STATUS_NONE_MAPPED;
529 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
530 if (!NT_STATUS_IS_OK(status)) {
531 talloc_free(tmp_ctx);
535 *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_GROUP_BASE + gid);
536 talloc_free(tmp_ctx);
539 return NT_STATUS_NO_MEMORY;
546 check if a sid is in the range of auto-allocated SIDs from our primary domain,
547 and if it is, then return the name and atype
549 _PUBLIC_ NTSTATUS sidmap_allocated_sid_lookup(struct sidmap_context *sidmap,
551 const struct dom_sid *sid,
556 struct dom_sid *domain_sid;
557 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
560 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
561 if (!NT_STATUS_IS_OK(status)) {
562 return NT_STATUS_NO_SUCH_DOMAIN;
565 if (!dom_sid_in_domain(domain_sid, sid)) {
566 talloc_free(tmp_ctx);
567 return NT_STATUS_NONE_MAPPED;
570 talloc_free(tmp_ctx);
572 rid = sid->sub_auths[sid->num_auths-1];
573 if (rid < SIDMAP_LOCAL_USER_BASE) {
574 return NT_STATUS_NONE_MAPPED;
577 if (rid < SIDMAP_LOCAL_GROUP_BASE) {
579 uid_t uid = rid - SIDMAP_LOCAL_USER_BASE;
580 *atype = ATYPE_NORMAL_ACCOUNT;
583 *name = talloc_asprintf(mem_ctx, "uid%u", uid);
585 *name = talloc_strdup(mem_ctx, pwd->pw_name);
589 gid_t gid = rid - SIDMAP_LOCAL_GROUP_BASE;
590 *atype = ATYPE_LOCAL_GROUP;
593 *name = talloc_asprintf(mem_ctx, "gid%u", gid);
595 *name = talloc_strdup(mem_ctx, grp->gr_name);
600 return NT_STATUS_NO_MEMORY;