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"
30 #include "libcli/security/security.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, 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, samdb_base_dn(mem_ctx),
110 return NT_STATUS_NO_SUCH_DOMAIN;
113 *sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
116 return NT_STATUS_NO_MEMORY;
124 map a sid to a unix uid
126 _PUBLIC_ NTSTATUS sidmap_sid_to_unixuid(struct sidmap_context *sidmap,
127 struct dom_sid *sid, uid_t *uid)
129 const char *attrs[] = { "sAMAccountName", "uidNumber",
130 "sAMAccountType", NULL };
134 struct ldb_message **res;
135 struct dom_sid *domain_sid;
138 tmp_ctx = talloc_new(sidmap);
140 ret = gendb_search(sidmap->samctx, tmp_ctx, samdb_base_dn(tmp_ctx), &res, attrs,
141 "objectSid=%s", ldap_encode_ndr_dom_sid(tmp_ctx, sid));
146 /* make sure its a user, not a group */
147 if (!is_user_account(res[0])) {
148 DEBUG(0,("sid_to_unixuid: sid %s is not an account!\n",
149 dom_sid_string(tmp_ctx, sid)));
150 talloc_free(tmp_ctx);
151 return NT_STATUS_INVALID_SID;
154 /* first try to get the uid directly */
155 s = samdb_result_string(res[0], "uidNumber", NULL);
157 *uid = strtoul(s, NULL, 0);
158 talloc_free(tmp_ctx);
162 /* next try via the UnixName attribute */
163 s = samdb_result_string(res[0], "unixName", NULL);
165 struct passwd *pwd = getpwnam(s);
167 DEBUG(0,("unixName %s for sid %s does not exist as a local user\n", s,
168 dom_sid_string(tmp_ctx, sid)));
169 talloc_free(tmp_ctx);
170 return NT_STATUS_NO_SUCH_USER;
173 talloc_free(tmp_ctx);
177 /* finally try via the sAMAccountName attribute */
178 s = samdb_result_string(res[0], "sAMAccountName", NULL);
180 struct passwd *pwd = getpwnam(s);
182 DEBUG(0,("sAMAccountName '%s' for sid %s does not exist as a local user\n",
183 s, dom_sid_string(tmp_ctx, sid)));
184 talloc_free(tmp_ctx);
185 return NT_STATUS_NO_SUCH_USER;
188 talloc_free(tmp_ctx);
194 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
195 if (!NT_STATUS_IS_OK(status)) {
196 talloc_free(tmp_ctx);
197 return NT_STATUS_NO_SUCH_DOMAIN;
200 if (dom_sid_in_domain(domain_sid, sid)) {
201 uint32_t rid = sid->sub_auths[sid->num_auths-1];
202 if (rid >= SIDMAP_LOCAL_USER_BASE &&
203 rid < SIDMAP_LOCAL_GROUP_BASE) {
204 *uid = rid - SIDMAP_LOCAL_USER_BASE;
205 talloc_free(tmp_ctx);
211 DEBUG(0,("sid_to_unixuid: no uidNumber, unixName or sAMAccountName for sid %s\n",
212 dom_sid_string(tmp_ctx, sid)));
214 talloc_free(tmp_ctx);
215 return NT_STATUS_INVALID_SID;
220 map a sid to a unix gid
222 _PUBLIC_ NTSTATUS sidmap_sid_to_unixgid(struct sidmap_context *sidmap,
223 struct dom_sid *sid, gid_t *gid)
225 const char *attrs[] = { "sAMAccountName", "gidNumber",
226 "unixName", "sAMAccountType", NULL };
230 struct ldb_message **res;
232 struct dom_sid *domain_sid;
234 tmp_ctx = talloc_new(sidmap);
236 ret = gendb_search(sidmap->samctx, tmp_ctx, samdb_base_dn(tmp_ctx), &res, attrs,
237 "objectSid=%s", ldap_encode_ndr_dom_sid(tmp_ctx, sid));
242 /* make sure its not a user */
243 if (!is_group_account(res[0])) {
244 DEBUG(0,("sid_to_unixgid: sid %s is a ATYPE_NORMAL_ACCOUNT\n",
245 dom_sid_string(tmp_ctx, sid)));
246 talloc_free(tmp_ctx);
247 return NT_STATUS_INVALID_SID;
250 /* first try to get the gid directly */
251 s = samdb_result_string(res[0], "gidNumber", NULL);
253 *gid = strtoul(s, NULL, 0);
254 talloc_free(tmp_ctx);
258 /* next try via the UnixName attribute */
259 s = samdb_result_string(res[0], "unixName", NULL);
261 struct group *grp = getgrnam(s);
263 DEBUG(0,("unixName '%s' for sid %s does not exist as a local group\n",
264 s, dom_sid_string(tmp_ctx, sid)));
265 talloc_free(tmp_ctx);
266 return NT_STATUS_NO_SUCH_GROUP;
269 talloc_free(tmp_ctx);
273 /* finally try via the sAMAccountName attribute */
274 s = samdb_result_string(res[0], "sAMAccountName", NULL);
276 struct group *grp = getgrnam(s);
278 DEBUG(0,("sAMAccountName '%s' for sid %s does not exist as a local group\n", s, dom_sid_string(tmp_ctx, sid)));
279 talloc_free(tmp_ctx);
280 return NT_STATUS_NO_SUCH_GROUP;
283 talloc_free(tmp_ctx);
288 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
289 if (!NT_STATUS_IS_OK(status)) {
290 talloc_free(tmp_ctx);
291 return NT_STATUS_NO_SUCH_DOMAIN;
294 if (dom_sid_in_domain(domain_sid, sid)) {
295 uint32_t rid = sid->sub_auths[sid->num_auths-1];
296 if (rid >= SIDMAP_LOCAL_GROUP_BASE) {
297 *gid = rid - SIDMAP_LOCAL_GROUP_BASE;
298 talloc_free(tmp_ctx);
303 DEBUG(0,("sid_to_unixgid: no gidNumber, unixName or sAMAccountName for sid %s\n",
304 dom_sid_string(tmp_ctx, sid)));
306 talloc_free(tmp_ctx);
307 return NT_STATUS_INVALID_SID;
312 map a unix uid to a dom_sid
313 the returned sid is allocated in the supplied mem_ctx
315 _PUBLIC_ NTSTATUS sidmap_uid_to_sid(struct sidmap_context *sidmap,
317 uid_t uid, struct dom_sid **sid)
319 const char *attrs[] = { "sAMAccountName", "objectSid", "sAMAccountType", NULL };
322 struct ldb_message **res;
324 struct dom_sid *domain_sid;
328 we search for the mapping in the following order:
330 - check if the uid is in the dynamic uid range assigned for winbindd
331 use. If it is, then look in winbindd sid mapping
332 database (not implemented yet)
333 - look for a user account in samdb that has uidNumber set to the
335 - look for a user account in samdb that has unixName or
336 sAMAccountName set to the name given by getpwuid()
337 - assign a SID by adding the uid to SIDMAP_LOCAL_USER_BASE in the local
342 tmp_ctx = talloc_new(mem_ctx);
346 step 2: look for a user account in samdb that has uidNumber set to the
350 ret = gendb_search(sidmap->samctx, tmp_ctx, samdb_base_dn(tmp_ctx), &res, attrs,
351 "uidNumber=%u", (unsigned int)uid);
352 for (i=0;i<ret;i++) {
353 if (!is_user_account(res[i])) continue;
355 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
356 talloc_free(tmp_ctx);
357 NT_STATUS_HAVE_NO_MEMORY(*sid);
362 step 3: look for a user account in samdb that has unixName
363 or sAMAccountName set to the name given by getpwuid()
370 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
371 "(|(unixName=%s)(sAMAccountName=%s))",
372 pwd->pw_name, pwd->pw_name);
373 for (i=0;i<ret;i++) {
374 if (!is_user_account(res[i])) continue;
376 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
377 talloc_free(tmp_ctx);
378 NT_STATUS_HAVE_NO_MEMORY(*sid);
384 step 4: assign a SID by adding the uid to
385 SIDMAP_LOCAL_USER_BASE in the local domain
388 if (uid > SIDMAP_MAX_LOCAL_UID) {
389 return NT_STATUS_INVALID_SID;
392 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
393 if (!NT_STATUS_IS_OK(status)) {
394 talloc_free(tmp_ctx);
398 *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_USER_BASE + uid);
399 talloc_free(tmp_ctx);
402 return NT_STATUS_NO_MEMORY;
410 map a unix gid to a dom_sid
411 the returned sid is allocated in the supplied mem_ctx
413 _PUBLIC_ NTSTATUS sidmap_gid_to_sid(struct sidmap_context *sidmap,
415 gid_t gid, struct dom_sid **sid)
417 const char *attrs[] = { "sAMAccountName", "objectSid", "sAMAccountType", NULL };
420 struct ldb_message **res;
422 struct dom_sid *domain_sid;
426 we search for the mapping in the following order:
428 - check if the gid is in the dynamic gid range assigned for winbindd
429 use. If it is, then look in winbindd sid mapping
430 database (not implemented yet)
431 - look for a group account in samdb that has gidNumber set to the
433 - look for a group account in samdb that has unixName or
434 sAMAccountName set to the name given by getgrgid()
435 - assign a SID by adding the gid to SIDMAP_LOCAL_GROUP_BASE in the local
440 tmp_ctx = talloc_new(sidmap);
444 step 2: look for a group account in samdb that has gidNumber set to the
448 ret = gendb_search(sidmap->samctx, tmp_ctx, samdb_base_dn(tmp_ctx), &res, attrs,
449 "gidNumber=%u", (unsigned int)gid);
450 for (i=0;i<ret;i++) {
451 if (!is_group_account(res[i])) continue;
453 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
454 talloc_free(tmp_ctx);
455 NT_STATUS_HAVE_NO_MEMORY(*sid);
460 step 3: look for a group account in samdb that has unixName
461 or sAMAccountName set to the name given by getgrgid()
468 ret = gendb_search(sidmap->samctx, tmp_ctx, samdb_base_dn(tmp_ctx), &res, attrs,
469 "(|(unixName=%s)(sAMAccountName=%s))",
470 grp->gr_name, grp->gr_name);
471 for (i=0;i<ret;i++) {
472 if (!is_group_account(res[i])) continue;
474 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
475 talloc_free(tmp_ctx);
476 NT_STATUS_HAVE_NO_MEMORY(*sid);
482 step 4: assign a SID by adding the gid to
483 SIDMAP_LOCAL_GROUP_BASE in the local domain
486 if (gid > SIDMAP_MAX_LOCAL_GID) {
487 return NT_STATUS_INVALID_SID;
490 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
491 if (!NT_STATUS_IS_OK(status)) {
492 talloc_free(tmp_ctx);
496 *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_GROUP_BASE + gid);
497 talloc_free(tmp_ctx);
500 return NT_STATUS_NO_MEMORY;
507 check if a sid is in the range of auto-allocated SIDs from our primary domain,
508 and if it is, then return the name and atype
510 _PUBLIC_ NTSTATUS sidmap_allocated_sid_lookup(struct sidmap_context *sidmap,
512 const struct dom_sid *sid,
517 struct dom_sid *domain_sid;
518 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
521 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
522 if (!NT_STATUS_IS_OK(status)) {
523 return NT_STATUS_NO_SUCH_DOMAIN;
526 if (!dom_sid_in_domain(domain_sid, sid)) {
527 talloc_free(tmp_ctx);
528 return NT_STATUS_INVALID_SID;
531 talloc_free(tmp_ctx);
533 rid = sid->sub_auths[sid->num_auths-1];
534 if (rid < SIDMAP_LOCAL_USER_BASE) {
535 return NT_STATUS_INVALID_SID;
538 if (rid < SIDMAP_LOCAL_GROUP_BASE) {
540 uid_t uid = rid - SIDMAP_LOCAL_USER_BASE;
541 *atype = ATYPE_NORMAL_ACCOUNT;
544 *name = talloc_asprintf(mem_ctx, "uid%u", uid);
546 *name = talloc_strdup(mem_ctx, pwd->pw_name);
550 gid_t gid = rid - SIDMAP_LOCAL_GROUP_BASE;
551 *atype = ATYPE_LOCAL_GROUP;
554 *name = talloc_asprintf(mem_ctx, "gid%u", gid);
556 *name = talloc_strdup(mem_ctx, grp->gr_name);
561 return NT_STATUS_NO_MEMORY;