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/proto.h"
31 #include "librpc/gen_ndr/ndr_security.h"
34 these are used for the fallback local uid/gid to sid mapping
37 #define SIDMAP_LOCAL_USER_BASE 0x80000000
38 #define SIDMAP_LOCAL_GROUP_BASE 0xC0000000
39 #define SIDMAP_MAX_LOCAL_UID 0x3fffffff
40 #define SIDMAP_MAX_LOCAL_GID 0x3fffffff
43 private context for sid mapping routines
45 struct sidmap_context {
50 open a sidmap context - use talloc_free to close
52 struct sidmap_context *sidmap_open(TALLOC_CTX *mem_ctx)
54 struct sidmap_context *sidmap;
55 sidmap = talloc(mem_ctx, struct sidmap_context);
59 sidmap->samctx = samdb_connect(sidmap, system_session(sidmap));
60 if (sidmap->samctx == NULL) {
70 check the sAMAccountType field of a search result to see if
71 the account is a user account
73 static BOOL is_user_account(struct ldb_message *res)
75 uint_t atype = samdb_result_uint(res, "sAMAccountType", 0);
76 if (atype && (!(atype & ATYPE_ACCOUNT))) {
83 check the sAMAccountType field of a search result to see if
84 the account is a group account
86 static BOOL is_group_account(struct ldb_message *res)
88 uint_t atype = samdb_result_uint(res, "sAMAccountType", 0);
89 if (atype && atype == ATYPE_NORMAL_ACCOUNT) {
98 return the dom_sid of our primary domain
100 static NTSTATUS sidmap_primary_domain_sid(struct sidmap_context *sidmap,
101 TALLOC_CTX *mem_ctx, struct dom_sid **sid)
103 const char *attrs[] = { "objectSid", NULL };
105 struct ldb_message **res = NULL;
107 ret = gendb_search_dn(sidmap->samctx, mem_ctx, samdb_base_dn(mem_ctx),
111 return NT_STATUS_NO_SUCH_DOMAIN;
114 *sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
117 return NT_STATUS_NO_MEMORY;
125 map a sid to a unix uid
127 NTSTATUS sidmap_sid_to_unixuid(struct sidmap_context *sidmap,
128 struct dom_sid *sid, uid_t *uid)
130 const char *attrs[] = { "sAMAccountName", "unixID",
131 "unixName", "sAMAccountType", NULL };
135 struct ldb_message **res;
136 struct dom_sid *domain_sid;
139 tmp_ctx = talloc_new(sidmap);
141 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
142 "objectSid=%s", ldap_encode_ndr_dom_sid(tmp_ctx, sid));
147 /* make sure its a user, not a group */
148 if (!is_user_account(res[0])) {
149 DEBUG(0,("sid_to_unixuid: sid %s is not an account!\n",
150 dom_sid_string(tmp_ctx, sid)));
151 talloc_free(tmp_ctx);
152 return NT_STATUS_INVALID_SID;
155 /* first try to get the uid directly */
156 s = samdb_result_string(res[0], "unixID", NULL);
158 *uid = strtoul(s, NULL, 0);
159 talloc_free(tmp_ctx);
163 /* next try via the UnixName attribute */
164 s = samdb_result_string(res[0], "unixName", NULL);
166 struct passwd *pwd = getpwnam(s);
168 DEBUG(0,("unixName %s for sid %s does not exist as a local user\n", s,
169 dom_sid_string(tmp_ctx, sid)));
170 talloc_free(tmp_ctx);
171 return NT_STATUS_NO_SUCH_USER;
174 talloc_free(tmp_ctx);
178 /* finally try via the sAMAccountName attribute */
179 s = samdb_result_string(res[0], "sAMAccountName", NULL);
181 struct passwd *pwd = getpwnam(s);
183 DEBUG(0,("sAMAccountName '%s' for sid %s does not exist as a local user\n",
184 s, dom_sid_string(tmp_ctx, sid)));
185 talloc_free(tmp_ctx);
186 return NT_STATUS_NO_SUCH_USER;
189 talloc_free(tmp_ctx);
195 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
196 if (!NT_STATUS_IS_OK(status)) {
197 talloc_free(tmp_ctx);
198 return NT_STATUS_NO_SUCH_DOMAIN;
201 if (dom_sid_in_domain(domain_sid, sid)) {
202 uint32_t rid = sid->sub_auths[sid->num_auths-1];
203 if (rid >= SIDMAP_LOCAL_USER_BASE &&
204 rid < SIDMAP_LOCAL_GROUP_BASE) {
205 *uid = rid - SIDMAP_LOCAL_USER_BASE;
206 talloc_free(tmp_ctx);
212 DEBUG(0,("sid_to_unixuid: no unixID, unixName or sAMAccountName for sid %s\n",
213 dom_sid_string(tmp_ctx, sid)));
215 talloc_free(tmp_ctx);
216 return NT_STATUS_INVALID_SID;
221 map a sid to a unix gid
223 NTSTATUS sidmap_sid_to_unixgid(struct sidmap_context *sidmap,
224 struct dom_sid *sid, gid_t *gid)
226 const char *attrs[] = { "sAMAccountName", "unixID",
227 "unixName", "sAMAccountType", NULL };
231 struct ldb_message **res;
233 struct dom_sid *domain_sid;
235 tmp_ctx = talloc_new(sidmap);
237 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
238 "objectSid=%s", ldap_encode_ndr_dom_sid(tmp_ctx, sid));
243 /* make sure its not a user */
244 if (!is_group_account(res[0])) {
245 DEBUG(0,("sid_to_unixgid: sid %s is a ATYPE_NORMAL_ACCOUNT\n",
246 dom_sid_string(tmp_ctx, sid)));
247 talloc_free(tmp_ctx);
248 return NT_STATUS_INVALID_SID;
251 /* first try to get the gid directly */
252 s = samdb_result_string(res[0], "unixID", NULL);
254 *gid = strtoul(s, NULL, 0);
255 talloc_free(tmp_ctx);
259 /* next try via the UnixName attribute */
260 s = samdb_result_string(res[0], "unixName", NULL);
262 struct group *grp = getgrnam(s);
264 DEBUG(0,("unixName '%s' for sid %s does not exist as a local group\n",
265 s, dom_sid_string(tmp_ctx, sid)));
266 talloc_free(tmp_ctx);
267 return NT_STATUS_NO_SUCH_USER;
270 talloc_free(tmp_ctx);
274 /* finally try via the sAMAccountName attribute */
275 s = samdb_result_string(res[0], "sAMAccountName", NULL);
277 struct group *grp = getgrnam(s);
279 DEBUG(0,("sAMAccountName '%s' for sid %s does not exist as a local group\n", s, dom_sid_string(tmp_ctx, sid)));
280 talloc_free(tmp_ctx);
281 return NT_STATUS_NO_SUCH_USER;
284 talloc_free(tmp_ctx);
289 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
290 if (!NT_STATUS_IS_OK(status)) {
291 talloc_free(tmp_ctx);
292 return NT_STATUS_NO_SUCH_DOMAIN;
295 if (dom_sid_in_domain(domain_sid, sid)) {
296 uint32_t rid = sid->sub_auths[sid->num_auths-1];
297 if (rid >= SIDMAP_LOCAL_GROUP_BASE) {
298 *gid = rid - SIDMAP_LOCAL_GROUP_BASE;
299 talloc_free(tmp_ctx);
304 DEBUG(0,("sid_to_unixgid: no unixID, unixName or sAMAccountName for sid %s\n",
305 dom_sid_string(tmp_ctx, sid)));
307 talloc_free(tmp_ctx);
308 return NT_STATUS_INVALID_SID;
313 map a unix uid to a dom_sid
314 the returned sid is allocated in the supplied mem_ctx
316 NTSTATUS sidmap_uid_to_sid(struct sidmap_context *sidmap,
318 uid_t uid, struct dom_sid **sid)
320 const char *attrs[] = { "sAMAccountName", "objectSid", "sAMAccountType", NULL };
323 struct ldb_message **res;
325 struct dom_sid *domain_sid;
329 we search for the mapping in the following order:
331 - check if the uid is in the dynamic uid range assigned for winbindd
332 use. If it is, then look in winbindd sid mapping
333 database (not implemented yet)
334 - look for a user account in samdb that has unixID set to the
336 - look for a user account in samdb that has unixName or
337 sAMAccountName set to the name given by getpwuid()
338 - assign a SID by adding the uid to SIDMAP_LOCAL_USER_BASE in the local
343 tmp_ctx = talloc_new(mem_ctx);
347 step 2: look for a user account in samdb that has unixID set to the
351 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
352 "unixID=%u", (unsigned int)uid);
353 for (i=0;i<ret;i++) {
354 if (!is_user_account(res[i])) continue;
356 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
357 talloc_free(tmp_ctx);
358 NT_STATUS_HAVE_NO_MEMORY(*sid);
363 step 3: look for a user account in samdb that has unixName
364 or sAMAccountName set to the name given by getpwuid()
371 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
372 "(|(unixName=%s)(sAMAccountName=%s))",
373 pwd->pw_name, pwd->pw_name);
374 for (i=0;i<ret;i++) {
375 if (!is_user_account(res[i])) continue;
377 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
378 talloc_free(tmp_ctx);
379 NT_STATUS_HAVE_NO_MEMORY(*sid);
385 step 4: assign a SID by adding the uid to
386 SIDMAP_LOCAL_USER_BASE in the local domain
389 if (uid > SIDMAP_MAX_LOCAL_UID) {
390 return NT_STATUS_INVALID_SID;
393 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
394 if (!NT_STATUS_IS_OK(status)) {
395 talloc_free(tmp_ctx);
399 *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_USER_BASE + uid);
400 talloc_free(tmp_ctx);
403 return NT_STATUS_NO_MEMORY;
411 map a unix gid to a dom_sid
412 the returned sid is allocated in the supplied mem_ctx
414 NTSTATUS sidmap_gid_to_sid(struct sidmap_context *sidmap,
416 gid_t gid, struct dom_sid **sid)
418 const char *attrs[] = { "sAMAccountName", "objectSid", "sAMAccountType", NULL };
421 struct ldb_message **res;
423 struct dom_sid *domain_sid;
427 we search for the mapping in the following order:
429 - check if the gid is in the dynamic gid range assigned for winbindd
430 use. If it is, then look in winbindd sid mapping
431 database (not implemented yet)
432 - look for a group account in samdb that has unixID set to the
434 - look for a group account in samdb that has unixName or
435 sAMAccountName set to the name given by getgrgid()
436 - assign a SID by adding the gid to SIDMAP_LOCAL_GROUP_BASE in the local
441 tmp_ctx = talloc_new(sidmap);
445 step 2: look for a group account in samdb that has unixID set to the
449 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
450 "unixID=%u", (unsigned int)gid);
451 for (i=0;i<ret;i++) {
452 if (!is_group_account(res[i])) continue;
454 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
455 talloc_free(tmp_ctx);
456 NT_STATUS_HAVE_NO_MEMORY(*sid);
461 step 3: look for a group account in samdb that has unixName
462 or sAMAccountName set to the name given by getgrgid()
469 ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
470 "(|(unixName=%s)(sAMAccountName=%s))",
471 grp->gr_name, grp->gr_name);
472 for (i=0;i<ret;i++) {
473 if (!is_group_account(res[i])) continue;
475 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
476 talloc_free(tmp_ctx);
477 NT_STATUS_HAVE_NO_MEMORY(*sid);
483 step 4: assign a SID by adding the gid to
484 SIDMAP_LOCAL_GROUP_BASE in the local domain
487 if (gid > SIDMAP_MAX_LOCAL_GID) {
488 return NT_STATUS_INVALID_SID;
491 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
492 if (!NT_STATUS_IS_OK(status)) {
493 talloc_free(tmp_ctx);
497 *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_GROUP_BASE + gid);
498 talloc_free(tmp_ctx);
501 return NT_STATUS_NO_MEMORY;
508 check if a sid is in the range of auto-allocated SIDs from our primary domain,
509 and if it is, then return the name and atype
511 NTSTATUS sidmap_allocated_sid_lookup(struct sidmap_context *sidmap,
513 const struct dom_sid *sid,
518 struct dom_sid *domain_sid;
519 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
522 status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
523 if (!NT_STATUS_IS_OK(status)) {
524 return NT_STATUS_NO_SUCH_DOMAIN;
527 if (!dom_sid_in_domain(domain_sid, sid)) {
528 talloc_free(tmp_ctx);
529 return NT_STATUS_INVALID_SID;
532 talloc_free(tmp_ctx);
534 rid = sid->sub_auths[sid->num_auths-1];
535 if (rid < SIDMAP_LOCAL_USER_BASE) {
536 return NT_STATUS_INVALID_SID;
539 if (rid < SIDMAP_LOCAL_GROUP_BASE) {
541 uid_t uid = rid - SIDMAP_LOCAL_USER_BASE;
542 *atype = ATYPE_NORMAL_ACCOUNT;
545 *name = talloc_asprintf(mem_ctx, "uid%u", uid);
547 *name = talloc_strdup(mem_ctx, pwd->pw_name);
551 gid_t gid = rid - SIDMAP_LOCAL_GROUP_BASE;
552 *atype = ATYPE_LOCAL_GROUP;
555 *name = talloc_asprintf(mem_ctx, "gid%u", gid);
557 *name = talloc_strdup(mem_ctx, grp->gr_name);
562 return NT_STATUS_NO_MEMORY;