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"
28 #include "util/util_ldb.h"
29 #include "libcli/security/security.h"
30 #include "param/param.h"
33 these are used for the fallback local uid/gid to sid mapping
36 #define SIDMAP_LOCAL_USER_BASE 0x80000000
37 #define SIDMAP_LOCAL_GROUP_BASE 0xC0000000
38 #define SIDMAP_MAX_LOCAL_UID 0x3fffffff
39 #define SIDMAP_MAX_LOCAL_GID 0x3fffffff
42 private context for sid mapping routines
44 struct sidmap_context {
45 struct ldb_context *samctx;
49 open a sidmap context - use talloc_free to close
51 _PUBLIC_ struct sidmap_context *sidmap_open(TALLOC_CTX *mem_ctx)
53 struct sidmap_context *sidmap;
54 sidmap = talloc(mem_ctx, struct sidmap_context);
58 sidmap->samctx = samdb_connect(sidmap, global_loadparm, system_session(sidmap));
59 if (sidmap->samctx == NULL) {
69 check the sAMAccountType field of a search result to see if
70 the account is a user account
72 static bool is_user_account(struct ldb_message *res)
74 uint_t atype = samdb_result_uint(res, "sAMAccountType", 0);
75 if (atype && (!(atype & ATYPE_ACCOUNT))) {
82 check the sAMAccountType field of a search result to see if
83 the account is a group account
85 static bool is_group_account(struct ldb_message *res)
87 uint_t atype = samdb_result_uint(res, "sAMAccountType", 0);
88 if (atype && atype == ATYPE_NORMAL_ACCOUNT) {
97 return the dom_sid of our primary domain
99 static NTSTATUS sidmap_primary_domain_sid(struct sidmap_context *sidmap,
100 TALLOC_CTX *mem_ctx, struct dom_sid **sid)
102 const char *attrs[] = { "objectSid", NULL };
104 struct ldb_message **res = NULL;
106 ret = gendb_search_dn(sidmap->samctx, mem_ctx, NULL, &res, attrs);
109 return NT_STATUS_NO_SUCH_DOMAIN;
112 *sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
115 return NT_STATUS_NO_MEMORY;
123 map a sid to a unix uid
125 _PUBLIC_ NTSTATUS sidmap_sid_to_unixuid(struct sidmap_context *sidmap,
126 struct dom_sid *sid, uid_t *uid)
128 const char *attrs[] = { "sAMAccountName", "uidNumber",
129 "sAMAccountType", "unixName", NULL };
133 struct ldb_message **res;
134 struct dom_sid *domain_sid;
137 tmp_ctx = talloc_new(sidmap);
139 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
140 "objectSid=%s", ldap_encode_ndr_dom_sid(tmp_ctx, sid));
145 /* make sure its a user, not a group */
146 if (!is_user_account(res[0])) {
147 DEBUG(0,("sid_to_unixuid: sid %s is not an account!\n",
148 dom_sid_string(tmp_ctx, sid)));
149 talloc_free(tmp_ctx);
150 return NT_STATUS_INVALID_SID;
153 /* first try to get the uid directly */
154 s = samdb_result_string(res[0], "uidNumber", NULL);
156 *uid = strtoul(s, NULL, 0);
157 talloc_free(tmp_ctx);
161 /* next try via the UnixName attribute */
162 s = samdb_result_string(res[0], "unixName", NULL);
164 struct passwd *pwd = getpwnam(s);
166 DEBUG(0,("unixName %s for sid %s does not exist as a local user\n", s,
167 dom_sid_string(tmp_ctx, sid)));
168 talloc_free(tmp_ctx);
169 return NT_STATUS_NO_SUCH_USER;
172 talloc_free(tmp_ctx);
176 /* finally try via the sAMAccountName attribute */
177 s = samdb_result_string(res[0], "sAMAccountName", NULL);
179 struct passwd *pwd = getpwnam(s);
181 DEBUG(0,("sAMAccountName '%s' for sid %s does not exist as a local user\n",
182 s, dom_sid_string(tmp_ctx, sid)));
183 talloc_free(tmp_ctx);
184 return NT_STATUS_NO_SUCH_USER;
187 talloc_free(tmp_ctx);
193 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
194 if (!NT_STATUS_IS_OK(status)) {
195 talloc_free(tmp_ctx);
196 return NT_STATUS_NO_SUCH_DOMAIN;
199 if (dom_sid_in_domain(domain_sid, sid)) {
200 uint32_t rid = sid->sub_auths[sid->num_auths-1];
201 if (rid >= SIDMAP_LOCAL_USER_BASE &&
202 rid < SIDMAP_LOCAL_GROUP_BASE) {
203 *uid = rid - SIDMAP_LOCAL_USER_BASE;
204 talloc_free(tmp_ctx);
210 DEBUG(0,("sid_to_unixuid: no uidNumber, unixName or sAMAccountName for sid %s\n",
211 dom_sid_string(tmp_ctx, sid)));
213 talloc_free(tmp_ctx);
214 return NT_STATUS_NONE_MAPPED;
219 see if a sid is a group - very inefficient!
221 _PUBLIC_ bool sidmap_sid_is_group(struct sidmap_context *sidmap, struct dom_sid *sid)
223 const char *attrs[] = { "sAMAccountType", NULL };
226 struct ldb_message **res;
228 struct dom_sid *domain_sid;
231 tmp_ctx = talloc_new(sidmap);
233 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
234 "objectSid=%s", ldap_encode_ndr_dom_sid(tmp_ctx, sid));
236 is_group = is_group_account(res[0]);
237 talloc_free(tmp_ctx);
241 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
242 if (!NT_STATUS_IS_OK(status)) {
243 talloc_free(tmp_ctx);
247 if (dom_sid_in_domain(domain_sid, sid)) {
248 uint32_t rid = sid->sub_auths[sid->num_auths-1];
249 if (rid >= SIDMAP_LOCAL_GROUP_BASE) {
250 talloc_free(tmp_ctx);
255 talloc_free(tmp_ctx);
260 map a sid to a unix gid
262 _PUBLIC_ NTSTATUS sidmap_sid_to_unixgid(struct sidmap_context *sidmap,
263 struct dom_sid *sid, gid_t *gid)
265 const char *attrs[] = { "sAMAccountName", "gidNumber",
266 "unixName", "sAMAccountType", NULL };
270 struct ldb_message **res;
272 struct dom_sid *domain_sid;
274 tmp_ctx = talloc_new(sidmap);
276 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
277 "objectSid=%s", ldap_encode_ndr_dom_sid(tmp_ctx, sid));
282 /* make sure its not a user */
283 if (!is_group_account(res[0])) {
284 DEBUG(0,("sid_to_unixgid: sid %s is a ATYPE_NORMAL_ACCOUNT\n",
285 dom_sid_string(tmp_ctx, sid)));
286 talloc_free(tmp_ctx);
287 return NT_STATUS_INVALID_SID;
290 /* first try to get the gid directly */
291 s = samdb_result_string(res[0], "gidNumber", NULL);
293 *gid = strtoul(s, NULL, 0);
294 talloc_free(tmp_ctx);
298 /* next try via the UnixName attribute */
299 s = samdb_result_string(res[0], "unixName", NULL);
301 struct group *grp = getgrnam(s);
303 DEBUG(0,("unixName '%s' for sid %s does not exist as a local group\n",
304 s, dom_sid_string(tmp_ctx, sid)));
305 talloc_free(tmp_ctx);
306 return NT_STATUS_NO_SUCH_GROUP;
309 talloc_free(tmp_ctx);
313 /* finally try via the sAMAccountName attribute */
314 s = samdb_result_string(res[0], "sAMAccountName", NULL);
316 struct group *grp = getgrnam(s);
318 DEBUG(0,("sAMAccountName '%s' for sid %s does not exist as a local group\n", s, dom_sid_string(tmp_ctx, sid)));
319 talloc_free(tmp_ctx);
320 return NT_STATUS_NO_SUCH_GROUP;
323 talloc_free(tmp_ctx);
328 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
329 if (!NT_STATUS_IS_OK(status)) {
330 talloc_free(tmp_ctx);
331 return NT_STATUS_NO_SUCH_DOMAIN;
334 if (dom_sid_in_domain(domain_sid, sid)) {
335 uint32_t rid = sid->sub_auths[sid->num_auths-1];
336 if (rid >= SIDMAP_LOCAL_GROUP_BASE) {
337 *gid = rid - SIDMAP_LOCAL_GROUP_BASE;
338 talloc_free(tmp_ctx);
343 DEBUG(0,("sid_to_unixgid: no gidNumber, unixName or sAMAccountName for sid %s\n",
344 dom_sid_string(tmp_ctx, sid)));
346 talloc_free(tmp_ctx);
347 return NT_STATUS_NONE_MAPPED;
352 map a unix uid to a dom_sid
353 the returned sid is allocated in the supplied mem_ctx
355 _PUBLIC_ NTSTATUS sidmap_uid_to_sid(struct sidmap_context *sidmap,
357 uid_t uid, struct dom_sid **sid)
359 const char *attrs[] = { "sAMAccountName", "objectSid", "sAMAccountType", NULL };
362 struct ldb_message **res;
364 struct dom_sid *domain_sid;
368 we search for the mapping in the following order:
370 - check if the uid is in the dynamic uid range assigned for winbindd
371 use. If it is, then look in winbindd sid mapping
372 database (not implemented yet)
373 - look for a user account in samdb that has uidNumber set to the
375 - look for a user account in samdb that has unixName or
376 sAMAccountName set to the name given by getpwuid()
377 - assign a SID by adding the uid to SIDMAP_LOCAL_USER_BASE in the local
382 tmp_ctx = talloc_new(mem_ctx);
386 step 2: look for a user account in samdb that has uidNumber set to the
390 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
391 "uidNumber=%u", (unsigned int)uid);
392 for (i=0;i<ret;i++) {
393 if (!is_user_account(res[i])) continue;
395 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
396 talloc_free(tmp_ctx);
397 NT_STATUS_HAVE_NO_MEMORY(*sid);
402 step 3: look for a user account in samdb that has unixName
403 or sAMAccountName set to the name given by getpwuid()
410 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
411 "(|(unixName=%s)(sAMAccountName=%s))",
412 pwd->pw_name, pwd->pw_name);
413 for (i=0;i<ret;i++) {
414 if (!is_user_account(res[i])) continue;
416 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
417 talloc_free(tmp_ctx);
418 NT_STATUS_HAVE_NO_MEMORY(*sid);
424 step 4: assign a SID by adding the uid to
425 SIDMAP_LOCAL_USER_BASE in the local domain
428 if (uid > SIDMAP_MAX_LOCAL_UID) {
429 return NT_STATUS_NONE_MAPPED;
432 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
433 if (!NT_STATUS_IS_OK(status)) {
434 talloc_free(tmp_ctx);
438 *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_USER_BASE + uid);
439 talloc_free(tmp_ctx);
442 return NT_STATUS_NO_MEMORY;
450 map a unix gid to a dom_sid
451 the returned sid is allocated in the supplied mem_ctx
453 _PUBLIC_ NTSTATUS sidmap_gid_to_sid(struct sidmap_context *sidmap,
455 gid_t gid, struct dom_sid **sid)
457 const char *attrs[] = { "sAMAccountName", "objectSid", "sAMAccountType", NULL };
460 struct ldb_message **res;
462 struct dom_sid *domain_sid;
466 we search for the mapping in the following order:
468 - check if the gid is in the dynamic gid range assigned for winbindd
469 use. If it is, then look in winbindd sid mapping
470 database (not implemented yet)
471 - look for a group account in samdb that has gidNumber set to the
473 - look for a group account in samdb that has unixName or
474 sAMAccountName set to the name given by getgrgid()
475 - assign a SID by adding the gid to SIDMAP_LOCAL_GROUP_BASE in the local
480 tmp_ctx = talloc_new(sidmap);
484 step 2: look for a group account in samdb that has gidNumber set to the
488 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
489 "gidNumber=%u", (unsigned int)gid);
490 for (i=0;i<ret;i++) {
491 if (!is_group_account(res[i])) continue;
493 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
494 talloc_free(tmp_ctx);
495 NT_STATUS_HAVE_NO_MEMORY(*sid);
500 step 3: look for a group account in samdb that has unixName
501 or sAMAccountName set to the name given by getgrgid()
508 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
509 "(|(unixName=%s)(sAMAccountName=%s))",
510 grp->gr_name, grp->gr_name);
511 for (i=0;i<ret;i++) {
512 if (!is_group_account(res[i])) continue;
514 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
515 talloc_free(tmp_ctx);
516 NT_STATUS_HAVE_NO_MEMORY(*sid);
522 step 4: assign a SID by adding the gid to
523 SIDMAP_LOCAL_GROUP_BASE in the local domain
526 if (gid > SIDMAP_MAX_LOCAL_GID) {
527 return NT_STATUS_NONE_MAPPED;
530 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
531 if (!NT_STATUS_IS_OK(status)) {
532 talloc_free(tmp_ctx);
536 *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_GROUP_BASE + gid);
537 talloc_free(tmp_ctx);
540 return NT_STATUS_NO_MEMORY;
547 check if a sid is in the range of auto-allocated SIDs from our primary domain,
548 and if it is, then return the name and atype
550 _PUBLIC_ NTSTATUS sidmap_allocated_sid_lookup(struct sidmap_context *sidmap,
552 const struct dom_sid *sid,
554 enum lsa_SidType *rtype)
557 struct dom_sid *domain_sid;
558 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
561 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
562 if (!NT_STATUS_IS_OK(status)) {
563 return NT_STATUS_NO_SUCH_DOMAIN;
566 if (!dom_sid_in_domain(domain_sid, sid)) {
567 talloc_free(tmp_ctx);
568 return NT_STATUS_NONE_MAPPED;
571 talloc_free(tmp_ctx);
573 rid = sid->sub_auths[sid->num_auths-1];
574 if (rid < SIDMAP_LOCAL_USER_BASE) {
575 return NT_STATUS_NONE_MAPPED;
578 if (rid < SIDMAP_LOCAL_GROUP_BASE) {
580 uid_t uid = rid - SIDMAP_LOCAL_USER_BASE;
581 atype = ATYPE_NORMAL_ACCOUNT;
582 *rtype = samdb_atype_map(atype);
586 *name = talloc_asprintf(mem_ctx, "uid%u", uid);
588 *name = talloc_strdup(mem_ctx, pwd->pw_name);
592 gid_t gid = rid - SIDMAP_LOCAL_GROUP_BASE;
593 atype = ATYPE_LOCAL_GROUP;
594 *rtype = samdb_atype_map(atype);
597 *name = talloc_asprintf(mem_ctx, "gid%u", gid);
599 *name = talloc_strdup(mem_ctx, grp->gr_name);
604 return NT_STATUS_NO_MEMORY;