Merge branch 'v4-0-test' of ssh://git.samba.org/data/git/samba into openchange
[kai/samba.git] / source / 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 3 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, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
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_ndr.h"
28 #include "lib/ldb/include/ldb.h"
29 #include "util/util_ldb.h"
30 #include "libcli/security/security.h"
31 #include "param/param.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_context *samctx;
47 };
48
49 /*
50   open a sidmap context - use talloc_free to close
51 */
52 struct sidmap_context *sidmap_open(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_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, lp_ctx, system_session(sidmap, lp_ctx));
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, NULL, &res, attrs);
108         if (ret != 1) {
109                 talloc_free(res);
110                 return NT_STATUS_NO_SUCH_DOMAIN;
111         }
112         
113         *sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
114         talloc_free(res);
115         if (*sid == NULL) {
116                 return NT_STATUS_NO_MEMORY;
117         }
118
119         return NT_STATUS_OK;
120 }
121
122
123 /*
124   map a sid to a unix uid
125 */
126 NTSTATUS sidmap_sid_to_unixuid(struct sidmap_context *sidmap,
127                                         const struct dom_sid *sid, uid_t *uid)
128 {
129         const char *attrs[] = { "sAMAccountName", "uidNumber",
130                                 "sAMAccountType", "unixName", NULL };
131         int ret;
132         const char *s;
133         TALLOC_CTX *tmp_ctx;
134         struct ldb_message **res;
135         struct dom_sid *domain_sid;
136         NTSTATUS status;
137
138         tmp_ctx = talloc_new(sidmap);
139
140         ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs,
141                            "objectSid=%s",
142                            ldap_encode_ndr_dom_sid(tmp_ctx, sid));
143
144         if (ret != 1) {
145                 goto allocated_sid;
146         }
147
148         /* make sure its a user, not a group */
149         if (!is_user_account(res[0])) {
150                 DEBUG(0,("sid_to_unixuid: sid %s is not an account!\n",
151                          dom_sid_string(tmp_ctx, sid)));
152                 talloc_free(tmp_ctx);
153                 return NT_STATUS_INVALID_SID;
154         }
155
156         /* first try to get the uid directly */
157         s = samdb_result_string(res[0], "uidNumber", NULL);
158         if (s != NULL) {
159                 *uid = strtoul(s, NULL, 0);
160                 talloc_free(tmp_ctx);
161                 return NT_STATUS_OK;
162         }
163
164         /* next try via the UnixName attribute */
165         s = samdb_result_string(res[0], "unixName", NULL);
166         if (s != NULL) {
167                 struct passwd *pwd = getpwnam(s);
168                 if (!pwd) {
169                         DEBUG(0,("unixName %s for sid %s does not exist as a local user\n", s, 
170                                  dom_sid_string(tmp_ctx, sid)));
171                         talloc_free(tmp_ctx);
172                         return NT_STATUS_NO_SUCH_USER;
173                 }
174                 *uid = pwd->pw_uid;
175                 talloc_free(tmp_ctx);
176                 return NT_STATUS_OK;
177         }
178
179         /* finally try via the sAMAccountName attribute */
180         s = samdb_result_string(res[0], "sAMAccountName", NULL);
181         if (s != NULL) {
182                 struct passwd *pwd = getpwnam(s);
183                 if (!pwd) {
184                         DEBUG(0,("sAMAccountName '%s' for sid %s does not exist as a local user\n", 
185                                  s, dom_sid_string(tmp_ctx, sid)));
186                         talloc_free(tmp_ctx);
187                         return NT_STATUS_NO_SUCH_USER;
188                 }
189                 *uid = pwd->pw_uid;
190                 talloc_free(tmp_ctx);
191                 return NT_STATUS_OK;
192         }
193
194
195 allocated_sid:
196         status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
197         if (!NT_STATUS_IS_OK(status)) {
198                 talloc_free(tmp_ctx);
199                 return NT_STATUS_NO_SUCH_DOMAIN;
200         }
201
202         if (dom_sid_in_domain(domain_sid, sid)) {
203                 uint32_t rid = sid->sub_auths[sid->num_auths-1];
204                 if (rid >= SIDMAP_LOCAL_USER_BASE && 
205                     rid <  SIDMAP_LOCAL_GROUP_BASE) {
206                         *uid = rid - SIDMAP_LOCAL_USER_BASE;
207                         talloc_free(tmp_ctx);
208                         return NT_STATUS_OK;
209                 }
210         }
211         
212
213         DEBUG(0,("sid_to_unixuid: no uidNumber, unixName or sAMAccountName for sid %s\n", 
214                  dom_sid_string(tmp_ctx, sid)));
215
216         talloc_free(tmp_ctx);
217         return NT_STATUS_NONE_MAPPED;
218 }
219
220
221 /*
222   see if a sid is a group - very inefficient!
223 */
224 bool sidmap_sid_is_group(struct sidmap_context *sidmap, struct dom_sid *sid)
225 {
226         const char *attrs[] = { "sAMAccountType", NULL };
227         int ret;
228         TALLOC_CTX *tmp_ctx;
229         struct ldb_message **res;
230         NTSTATUS status;
231         struct dom_sid *domain_sid;
232         bool is_group;
233
234         tmp_ctx = talloc_new(sidmap);
235
236         ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs, 
237                            "objectSid=%s", ldap_encode_ndr_dom_sid(tmp_ctx, sid));
238         if (ret == 1) {
239                 is_group = is_group_account(res[0]);
240                 talloc_free(tmp_ctx);
241                 return is_group;
242         }
243
244         status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
245         if (!NT_STATUS_IS_OK(status)) {
246                 talloc_free(tmp_ctx);
247                 return false;
248         }
249
250         if (dom_sid_in_domain(domain_sid, sid)) {
251                 uint32_t rid = sid->sub_auths[sid->num_auths-1];
252                 if (rid >= SIDMAP_LOCAL_GROUP_BASE) {
253                         talloc_free(tmp_ctx);
254                         return true;
255                 }
256         }
257
258         talloc_free(tmp_ctx);
259         return false;
260 }
261
262 /*
263   map a sid to a unix gid
264 */
265 NTSTATUS sidmap_sid_to_unixgid(struct sidmap_context *sidmap,
266                                         const struct dom_sid *sid, gid_t *gid)
267 {
268         const char *attrs[] = { "sAMAccountName", "gidNumber",
269                                 "unixName", "sAMAccountType", NULL };
270         int ret;
271         const char *s;
272         TALLOC_CTX *tmp_ctx;
273         struct ldb_message **res;
274         NTSTATUS status;
275         struct dom_sid *domain_sid;
276
277         tmp_ctx = talloc_new(sidmap);
278
279         ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs, 
280                            "objectSid=%s", ldap_encode_ndr_dom_sid(tmp_ctx, sid));
281         if (ret != 1) {
282                 goto allocated_sid;
283         }
284
285         /* make sure its not a user */
286         if (!is_group_account(res[0])) {
287                 DEBUG(0,("sid_to_unixgid: sid %s is a ATYPE_NORMAL_ACCOUNT\n", 
288                          dom_sid_string(tmp_ctx, sid)));
289                 talloc_free(tmp_ctx);
290                 return NT_STATUS_INVALID_SID;
291         }
292
293         /* first try to get the gid directly */
294         s = samdb_result_string(res[0], "gidNumber", NULL);
295         if (s != NULL) {
296                 *gid = strtoul(s, NULL, 0);
297                 talloc_free(tmp_ctx);
298                 return NT_STATUS_OK;
299         }
300
301         /* next try via the UnixName attribute */
302         s = samdb_result_string(res[0], "unixName", NULL);
303         if (s != NULL) {
304                 struct group *grp = getgrnam(s);
305                 if (!grp) {
306                         DEBUG(0,("unixName '%s' for sid %s does not exist as a local group\n", 
307                                  s, dom_sid_string(tmp_ctx, sid)));
308                         talloc_free(tmp_ctx);
309                         return NT_STATUS_NO_SUCH_GROUP;
310                 }
311                 *gid = grp->gr_gid;
312                 talloc_free(tmp_ctx);
313                 return NT_STATUS_OK;
314         }
315
316         /* finally try via the sAMAccountName attribute */
317         s = samdb_result_string(res[0], "sAMAccountName", NULL);
318         if (s != NULL) {
319                 struct group *grp = getgrnam(s);
320                 if (!grp) {
321                         DEBUG(0,("sAMAccountName '%s' for sid %s does not exist as a local group\n", s, dom_sid_string(tmp_ctx, sid)));
322                         talloc_free(tmp_ctx);
323                         return NT_STATUS_NO_SUCH_GROUP;
324                 }
325                 *gid = grp->gr_gid;
326                 talloc_free(tmp_ctx);
327                 return NT_STATUS_OK;
328         }
329
330 allocated_sid:
331         status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
332         if (!NT_STATUS_IS_OK(status)) {
333                 talloc_free(tmp_ctx);
334                 return NT_STATUS_NO_SUCH_DOMAIN;
335         }
336
337         if (dom_sid_in_domain(domain_sid, sid)) {
338                 uint32_t rid = sid->sub_auths[sid->num_auths-1];
339                 if (rid >= SIDMAP_LOCAL_GROUP_BASE) {
340                         *gid = rid - SIDMAP_LOCAL_GROUP_BASE;
341                         talloc_free(tmp_ctx);
342                         return NT_STATUS_OK;
343                 }
344         }
345
346         DEBUG(0,("sid_to_unixgid: no gidNumber, unixName or sAMAccountName for sid %s\n", 
347                  dom_sid_string(tmp_ctx, sid)));
348
349         talloc_free(tmp_ctx);
350         return NT_STATUS_NONE_MAPPED;
351 }
352
353
354 /*
355   map a unix uid to a dom_sid
356   the returned sid is allocated in the supplied mem_ctx
357 */
358 NTSTATUS sidmap_uid_to_sid(struct sidmap_context *sidmap,
359                                     TALLOC_CTX *mem_ctx,
360                                     const uid_t uid, struct dom_sid **sid)
361 {
362         const char *attrs[] = { "sAMAccountName", "objectSid", "sAMAccountType", NULL };
363         int ret, i;
364         TALLOC_CTX *tmp_ctx;
365         struct ldb_message **res;
366         struct passwd *pwd;
367         struct dom_sid *domain_sid;
368         NTSTATUS status;
369
370         /*
371           we search for the mapping in the following order:
372
373             - check if the uid is in the dynamic uid range assigned for winbindd
374               use. If it is, then look in winbindd sid mapping
375               database (not implemented yet)
376             - look for a user account in samdb that has uidNumber set to the
377               given uid
378             - look for a user account in samdb that has unixName or
379               sAMAccountName set to the name given by getpwuid()
380             - assign a SID by adding the uid to SIDMAP_LOCAL_USER_BASE in the local
381               domain
382         */
383
384
385         tmp_ctx = talloc_new(mem_ctx);
386
387
388         /*
389           step 2: look for a user account in samdb that has uidNumber set to the
390                   given uid
391         */
392
393         ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs, 
394                            "uidNumber=%u", (unsigned int)uid);
395         for (i=0;i<ret;i++) {
396                 if (!is_user_account(res[i])) continue;
397
398                 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
399                 talloc_free(tmp_ctx);
400                 NT_STATUS_HAVE_NO_MEMORY(*sid);
401                 return NT_STATUS_OK;
402         }
403
404         /*
405           step 3: look for a user account in samdb that has unixName
406                   or sAMAccountName set to the name given by getpwuid()
407         */
408         pwd = getpwuid(uid);
409         if (pwd == NULL) {
410                 goto allocate_sid;
411         }
412
413         ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs, 
414                            "(|(unixName=%s)(sAMAccountName=%s))", 
415                            pwd->pw_name, pwd->pw_name);
416         for (i=0;i<ret;i++) {
417                 if (!is_user_account(res[i])) continue;
418
419                 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
420                 talloc_free(tmp_ctx);
421                 NT_STATUS_HAVE_NO_MEMORY(*sid);
422                 return NT_STATUS_OK;
423         }
424
425
426         /*
427             step 4: assign a SID by adding the uid to
428                     SIDMAP_LOCAL_USER_BASE in the local domain
429         */
430 allocate_sid:
431         if (uid > SIDMAP_MAX_LOCAL_UID) {
432                 return NT_STATUS_NONE_MAPPED;
433         }
434
435         status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
436         if (!NT_STATUS_IS_OK(status)) {
437                 talloc_free(tmp_ctx);
438                 return status;
439         }
440
441         *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_USER_BASE + uid);
442         talloc_free(tmp_ctx);
443
444         if (*sid == NULL) {
445                 return NT_STATUS_NO_MEMORY;
446         }
447
448         return NT_STATUS_OK;
449 }
450
451
452 /*
453   map a unix gid to a dom_sid
454   the returned sid is allocated in the supplied mem_ctx
455 */
456 NTSTATUS sidmap_gid_to_sid(struct sidmap_context *sidmap,
457                                     TALLOC_CTX *mem_ctx,
458                                     const gid_t gid, struct dom_sid **sid)
459 {
460         const char *attrs[] = { "sAMAccountName", "objectSid", "sAMAccountType", NULL };
461         int ret, i;
462         TALLOC_CTX *tmp_ctx;
463         struct ldb_message **res;
464         struct group *grp;
465         struct dom_sid *domain_sid;
466         NTSTATUS status;
467
468         /*
469           we search for the mapping in the following order:
470
471             - check if the gid is in the dynamic gid range assigned for winbindd
472               use. If it is, then look in winbindd sid mapping
473               database (not implemented yet)
474             - look for a group account in samdb that has gidNumber set to the
475               given gid
476             - look for a group account in samdb that has unixName or
477               sAMAccountName set to the name given by getgrgid()
478             - assign a SID by adding the gid to SIDMAP_LOCAL_GROUP_BASE in the local
479               domain
480         */
481
482
483         tmp_ctx = talloc_new(sidmap);
484
485
486         /*
487           step 2: look for a group account in samdb that has gidNumber set to the
488                   given gid
489         */
490
491         ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs, 
492                            "gidNumber=%u", (unsigned int)gid);
493         for (i=0;i<ret;i++) {
494                 if (!is_group_account(res[i])) continue;
495
496                 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
497                 talloc_free(tmp_ctx);
498                 NT_STATUS_HAVE_NO_MEMORY(*sid);
499                 return NT_STATUS_OK;
500         }
501
502         /*
503           step 3: look for a group account in samdb that has unixName
504                   or sAMAccountName set to the name given by getgrgid()
505         */
506         grp = getgrgid(gid);
507         if (grp == NULL) {
508                 goto allocate_sid;
509         }
510
511         ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs, 
512                            "(|(unixName=%s)(sAMAccountName=%s))", 
513                            grp->gr_name, grp->gr_name);
514         for (i=0;i<ret;i++) {
515                 if (!is_group_account(res[i])) continue;
516
517                 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
518                 talloc_free(tmp_ctx);
519                 NT_STATUS_HAVE_NO_MEMORY(*sid);
520                 return NT_STATUS_OK;
521         }
522
523
524         /*
525             step 4: assign a SID by adding the gid to
526                     SIDMAP_LOCAL_GROUP_BASE in the local domain
527         */
528 allocate_sid:
529         if (gid > SIDMAP_MAX_LOCAL_GID) {
530                 return NT_STATUS_NONE_MAPPED;
531         }
532
533         status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
534         if (!NT_STATUS_IS_OK(status)) {
535                 talloc_free(tmp_ctx);
536                 return status;
537         }
538
539         *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_GROUP_BASE + gid);
540         talloc_free(tmp_ctx);
541
542         if (*sid == NULL) {
543                 return NT_STATUS_NO_MEMORY;
544         }
545
546         return NT_STATUS_OK;
547 }
548
549 /*
550   check if a sid is in the range of auto-allocated SIDs from our primary domain,
551   and if it is, then return the name and atype
552 */
553 NTSTATUS sidmap_allocated_sid_lookup(struct sidmap_context *sidmap, 
554                                               TALLOC_CTX *mem_ctx, 
555                                               const struct dom_sid *sid,
556                                               const char **name,
557                                               enum lsa_SidType *rtype)
558 {
559         NTSTATUS status;
560         struct dom_sid *domain_sid;
561         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
562         uint32_t rid, atype;
563         
564         status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
565         if (!NT_STATUS_IS_OK(status)) {
566                 return NT_STATUS_NO_SUCH_DOMAIN;
567         }
568
569         if (!dom_sid_in_domain(domain_sid, sid)) {
570                 talloc_free(tmp_ctx);
571                 return NT_STATUS_NONE_MAPPED;
572         }
573
574         talloc_free(tmp_ctx);
575
576         rid = sid->sub_auths[sid->num_auths-1];
577         if (rid < SIDMAP_LOCAL_USER_BASE) {
578                 return NT_STATUS_NONE_MAPPED;
579         }
580
581         if (rid < SIDMAP_LOCAL_GROUP_BASE) {
582                 struct passwd *pwd;
583                 uid_t uid = rid - SIDMAP_LOCAL_USER_BASE;
584                 atype = ATYPE_NORMAL_ACCOUNT;
585                 *rtype = samdb_atype_map(atype);
586
587                 pwd = getpwuid(uid);
588                 if (pwd == NULL) {
589                         *name = talloc_asprintf(mem_ctx, "uid%u", uid);
590                 } else {
591                         *name = talloc_strdup(mem_ctx, pwd->pw_name);
592                 }
593         } else {
594                 struct group *grp;
595                 gid_t gid = rid - SIDMAP_LOCAL_GROUP_BASE;
596                 atype = ATYPE_LOCAL_GROUP;
597                 *rtype = samdb_atype_map(atype);
598                 grp = getgrgid(gid);
599                 if (grp == NULL) {
600                         *name = talloc_asprintf(mem_ctx, "gid%u", gid);
601                 } else {
602                         *name = talloc_strdup(mem_ctx, grp->gr_name);
603                 }
604         }
605
606         if (*name == NULL) {
607                 return NT_STATUS_NO_MEMORY;
608         }
609
610         return NT_STATUS_OK;
611 }