r14964: - move sidmap code from ntvfs_common to SAMDB
[bbaumbach/samba-autobuild/.git] / source4 / dsdb / common / sidmap.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    mapping routines for SID <-> unix uid/gid
5
6    Copyright (C) Andrew Tridgell 2004
7    
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.
12    
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.
17    
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.
21 */
22
23 #include "includes.h"
24 #include "system/passwd.h"
25 #include "ads.h"
26 #include "dsdb/samdb/samdb.h"
27 #include "auth/auth.h"
28 #include "libcli/ldap/ldap.h"
29 #include "db_wrap.h"
30 #include "libcli/security/security.h"
31 #include "librpc/gen_ndr/ndr_security.h"
32
33 /*
34   these are used for the fallback local uid/gid to sid mapping
35   code.
36 */
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
41
42 /*
43   private context for sid mapping routines
44 */
45 struct sidmap_context {
46         struct ldb_wrap *samctx;
47 };
48
49 /*
50   open a sidmap context - use talloc_free to close
51 */
52 _PUBLIC_ struct sidmap_context *sidmap_open(TALLOC_CTX *mem_ctx)
53 {
54         struct sidmap_context *sidmap;
55         sidmap = talloc(mem_ctx, struct sidmap_context);
56         if (sidmap == NULL) {
57                 return NULL;
58         }
59         sidmap->samctx = samdb_connect(sidmap, system_session(sidmap));
60         if (sidmap->samctx == NULL) {
61                 talloc_free(sidmap);
62                 return NULL;
63         }
64
65         return sidmap;
66 }
67
68
69 /*
70   check the sAMAccountType field of a search result to see if
71   the account is a user account
72 */
73 static BOOL is_user_account(struct ldb_message *res)
74 {
75         uint_t atype = samdb_result_uint(res, "sAMAccountType", 0);
76         if (atype && (!(atype & ATYPE_ACCOUNT))) {
77                 return False;
78         }
79         return True;
80 }
81
82 /*
83   check the sAMAccountType field of a search result to see if
84   the account is a group account
85 */
86 static BOOL is_group_account(struct ldb_message *res)
87 {
88         uint_t atype = samdb_result_uint(res, "sAMAccountType", 0);
89         if (atype && atype == ATYPE_NORMAL_ACCOUNT) {
90                 return False;
91         }
92         return True;
93 }
94
95
96
97 /*
98   return the dom_sid of our primary domain
99 */
100 static NTSTATUS sidmap_primary_domain_sid(struct sidmap_context *sidmap, 
101                                           TALLOC_CTX *mem_ctx, struct dom_sid **sid)
102 {
103         const char *attrs[] = { "objectSid", NULL };
104         int ret;
105         struct ldb_message **res = NULL;
106
107         ret = gendb_search_dn(sidmap->samctx, mem_ctx, samdb_base_dn(mem_ctx), 
108                               &res, attrs);
109         if (ret != 1) {
110                 talloc_free(res);
111                 return NT_STATUS_NO_SUCH_DOMAIN;
112         }
113         
114         *sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
115         talloc_free(res);
116         if (*sid == NULL) {
117                 return NT_STATUS_NO_MEMORY;
118         }
119
120         return NT_STATUS_OK;
121 }
122
123
124 /*
125   map a sid to a unix uid
126 */
127 _PUBLIC_ NTSTATUS sidmap_sid_to_unixuid(struct sidmap_context *sidmap, 
128                                         struct dom_sid *sid, uid_t *uid)
129 {
130         const char *attrs[] = { "sAMAccountName", "unixID", 
131                                 "unixName", "sAMAccountType", NULL };
132         int ret;
133         const char *s;
134         TALLOC_CTX *tmp_ctx;
135         struct ldb_message **res;
136         struct dom_sid *domain_sid;
137         NTSTATUS status;
138
139         tmp_ctx = talloc_new(sidmap);
140
141         ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs, 
142                            "objectSid=%s", ldap_encode_ndr_dom_sid(tmp_ctx, sid));
143         if (ret != 1) {
144                 goto allocated_sid;
145         }
146
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;
153         }
154
155         /* first try to get the uid directly */
156         s = samdb_result_string(res[0], "unixID", NULL);
157         if (s != NULL) {
158                 *uid = strtoul(s, NULL, 0);
159                 talloc_free(tmp_ctx);
160                 return NT_STATUS_OK;
161         }
162
163         /* next try via the UnixName attribute */
164         s = samdb_result_string(res[0], "unixName", NULL);
165         if (s != NULL) {
166                 struct passwd *pwd = getpwnam(s);
167                 if (!pwd) {
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;
172                 }
173                 *uid = pwd->pw_uid;
174                 talloc_free(tmp_ctx);
175                 return NT_STATUS_OK;
176         }
177
178         /* finally try via the sAMAccountName attribute */
179         s = samdb_result_string(res[0], "sAMAccountName", NULL);
180         if (s != NULL) {
181                 struct passwd *pwd = getpwnam(s);
182                 if (!pwd) {
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;
187                 }
188                 *uid = pwd->pw_uid;
189                 talloc_free(tmp_ctx);
190                 return NT_STATUS_OK;
191         }
192
193
194 allocated_sid:
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;
199         }
200
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);
207                         return NT_STATUS_OK;
208                 }
209         }
210         
211
212         DEBUG(0,("sid_to_unixuid: no unixID, unixName or sAMAccountName for sid %s\n", 
213                  dom_sid_string(tmp_ctx, sid)));
214
215         talloc_free(tmp_ctx);
216         return NT_STATUS_INVALID_SID;
217 }
218
219
220 /*
221   map a sid to a unix gid
222 */
223 _PUBLIC_ NTSTATUS sidmap_sid_to_unixgid(struct sidmap_context *sidmap,
224                                         struct dom_sid *sid, gid_t *gid)
225 {
226         const char *attrs[] = { "sAMAccountName", "unixID", 
227                                 "unixName", "sAMAccountType", NULL };
228         int ret;
229         const char *s;
230         TALLOC_CTX *tmp_ctx;
231         struct ldb_message **res;
232         NTSTATUS status;
233         struct dom_sid *domain_sid;
234
235         tmp_ctx = talloc_new(sidmap);
236
237         ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs, 
238                            "objectSid=%s", ldap_encode_ndr_dom_sid(tmp_ctx, sid));
239         if (ret != 1) {
240                 goto allocated_sid;
241         }
242
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;
249         }
250
251         /* first try to get the gid directly */
252         s = samdb_result_string(res[0], "unixID", NULL);
253         if (s != NULL) {
254                 *gid = strtoul(s, NULL, 0);
255                 talloc_free(tmp_ctx);
256                 return NT_STATUS_OK;
257         }
258
259         /* next try via the UnixName attribute */
260         s = samdb_result_string(res[0], "unixName", NULL);
261         if (s != NULL) {
262                 struct group *grp = getgrnam(s);
263                 if (!grp) {
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;
268                 }
269                 *gid = grp->gr_gid;
270                 talloc_free(tmp_ctx);
271                 return NT_STATUS_OK;
272         }
273
274         /* finally try via the sAMAccountName attribute */
275         s = samdb_result_string(res[0], "sAMAccountName", NULL);
276         if (s != NULL) {
277                 struct group *grp = getgrnam(s);
278                 if (!grp) {
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;
282                 }
283                 *gid = grp->gr_gid;
284                 talloc_free(tmp_ctx);
285                 return NT_STATUS_OK;
286         }
287
288 allocated_sid:
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;
293         }
294
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);
300                         return NT_STATUS_OK;
301                 }
302         }
303
304         DEBUG(0,("sid_to_unixgid: no unixID, unixName or sAMAccountName for sid %s\n", 
305                  dom_sid_string(tmp_ctx, sid)));
306
307         talloc_free(tmp_ctx);
308         return NT_STATUS_INVALID_SID;
309 }
310
311
312 /*
313   map a unix uid to a dom_sid
314   the returned sid is allocated in the supplied mem_ctx
315 */
316 _PUBLIC_ NTSTATUS sidmap_uid_to_sid(struct sidmap_context *sidmap,
317                                     TALLOC_CTX *mem_ctx,
318                                     uid_t uid, struct dom_sid **sid)
319 {
320         const char *attrs[] = { "sAMAccountName", "objectSid", "sAMAccountType", NULL };
321         int ret, i;
322         TALLOC_CTX *tmp_ctx;
323         struct ldb_message **res;
324         struct passwd *pwd;
325         struct dom_sid *domain_sid;
326         NTSTATUS status;
327
328         /*
329           we search for the mapping in the following order:
330
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
335               given uid
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
339               domain
340         */
341
342
343         tmp_ctx = talloc_new(mem_ctx);
344
345
346         /*
347           step 2: look for a user account in samdb that has unixID set to the
348                   given uid
349         */
350
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;
355
356                 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
357                 talloc_free(tmp_ctx);
358                 NT_STATUS_HAVE_NO_MEMORY(*sid);
359                 return NT_STATUS_OK;
360         }
361
362         /*
363           step 3: look for a user account in samdb that has unixName
364                   or sAMAccountName set to the name given by getpwuid()
365         */
366         pwd = getpwuid(uid);
367         if (pwd == NULL) {
368                 goto allocate_sid;
369         }
370
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;
376
377                 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
378                 talloc_free(tmp_ctx);
379                 NT_STATUS_HAVE_NO_MEMORY(*sid);
380                 return NT_STATUS_OK;
381         }
382
383
384         /*
385             step 4: assign a SID by adding the uid to
386                     SIDMAP_LOCAL_USER_BASE in the local domain
387         */
388 allocate_sid:
389         if (uid > SIDMAP_MAX_LOCAL_UID) {
390                 return NT_STATUS_INVALID_SID;
391         }
392
393         status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
394         if (!NT_STATUS_IS_OK(status)) {
395                 talloc_free(tmp_ctx);
396                 return status;
397         }
398
399         *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_USER_BASE + uid);
400         talloc_free(tmp_ctx);
401
402         if (*sid == NULL) {
403                 return NT_STATUS_NO_MEMORY;
404         }
405
406         return NT_STATUS_OK;
407 }
408
409
410 /*
411   map a unix gid to a dom_sid
412   the returned sid is allocated in the supplied mem_ctx
413 */
414 _PUBLIC_ NTSTATUS sidmap_gid_to_sid(struct sidmap_context *sidmap,
415                                     TALLOC_CTX *mem_ctx,
416                                     gid_t gid, struct dom_sid **sid)
417 {
418         const char *attrs[] = { "sAMAccountName", "objectSid", "sAMAccountType", NULL };
419         int ret, i;
420         TALLOC_CTX *tmp_ctx;
421         struct ldb_message **res;
422         struct group *grp;
423         struct dom_sid *domain_sid;
424         NTSTATUS status;
425
426         /*
427           we search for the mapping in the following order:
428
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
433               given gid
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
437               domain
438         */
439
440
441         tmp_ctx = talloc_new(sidmap);
442
443
444         /*
445           step 2: look for a group account in samdb that has unixID set to the
446                   given gid
447         */
448
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;
453
454                 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
455                 talloc_free(tmp_ctx);
456                 NT_STATUS_HAVE_NO_MEMORY(*sid);
457                 return NT_STATUS_OK;
458         }
459
460         /*
461           step 3: look for a group account in samdb that has unixName
462                   or sAMAccountName set to the name given by getgrgid()
463         */
464         grp = getgrgid(gid);
465         if (grp == NULL) {
466                 goto allocate_sid;
467         }
468
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;
474
475                 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
476                 talloc_free(tmp_ctx);
477                 NT_STATUS_HAVE_NO_MEMORY(*sid);
478                 return NT_STATUS_OK;
479         }
480
481
482         /*
483             step 4: assign a SID by adding the gid to
484                     SIDMAP_LOCAL_GROUP_BASE in the local domain
485         */
486 allocate_sid:
487         if (gid > SIDMAP_MAX_LOCAL_GID) {
488                 return NT_STATUS_INVALID_SID;
489         }
490
491         status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
492         if (!NT_STATUS_IS_OK(status)) {
493                 talloc_free(tmp_ctx);
494                 return status;
495         }
496
497         *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_GROUP_BASE + gid);
498         talloc_free(tmp_ctx);
499
500         if (*sid == NULL) {
501                 return NT_STATUS_NO_MEMORY;
502         }
503
504         return NT_STATUS_OK;
505 }
506
507 /*
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
510 */
511 _PUBLIC_ NTSTATUS sidmap_allocated_sid_lookup(struct sidmap_context *sidmap, 
512                                               TALLOC_CTX *mem_ctx, 
513                                               const struct dom_sid *sid,
514                                               const char **name,
515                                               uint32_t *atype)
516 {
517         NTSTATUS status;
518         struct dom_sid *domain_sid;
519         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
520         uint32_t rid;
521
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;
525         }
526
527         if (!dom_sid_in_domain(domain_sid, sid)) {
528                 talloc_free(tmp_ctx);
529                 return NT_STATUS_INVALID_SID;
530         }
531
532         talloc_free(tmp_ctx);
533
534         rid = sid->sub_auths[sid->num_auths-1];
535         if (rid < SIDMAP_LOCAL_USER_BASE) {
536                 return NT_STATUS_INVALID_SID;
537         }
538
539         if (rid < SIDMAP_LOCAL_GROUP_BASE) {
540                 struct passwd *pwd;
541                 uid_t uid = rid - SIDMAP_LOCAL_USER_BASE;
542                 *atype = ATYPE_NORMAL_ACCOUNT;
543                 pwd = getpwuid(uid);
544                 if (pwd == NULL) {
545                         *name = talloc_asprintf(mem_ctx, "uid%u", uid);
546                 } else {
547                         *name = talloc_strdup(mem_ctx, pwd->pw_name);
548                 }
549         } else {
550                 struct group *grp;
551                 gid_t gid = rid - SIDMAP_LOCAL_GROUP_BASE;
552                 *atype = ATYPE_LOCAL_GROUP;
553                 grp = getgrgid(gid);
554                 if (grp == NULL) {
555                         *name = talloc_asprintf(mem_ctx, "gid%u", gid);
556                 } else {
557                         *name = talloc_strdup(mem_ctx, grp->gr_name);
558                 }
559         }
560
561         if (*name == NULL) {
562                 return NT_STATUS_NO_MEMORY;
563         }
564
565         return NT_STATUS_OK;
566 }