1f19ba171d2f1518584c7698fc4375851cfde0ee
[bbaumbach/samba-autobuild/.git] / source4 / ntvfs / 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/filesys.h"
25 #include "system/passwd.h"
26 #include "librpc/gen_ndr/ndr_security.h"
27 #include "ads.h"
28 #include "dsdb/samdb/samdb.h"
29 #include "auth/auth.h"
30 #include "libcli/ldap/ldap.h"
31
32 /*
33   these are used for the fallback local uid/gid to sid mapping
34   code.
35 */
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
40
41 /*
42   private context for sid mapping routines
43 */
44 struct sidmap_context {
45         void *samctx;
46 };
47
48 /*
49   open a sidmap context - use talloc_free to close
50 */
51 struct sidmap_context *sidmap_open(TALLOC_CTX *mem_ctx)
52 {
53         struct sidmap_context *sidmap;
54         sidmap = talloc(mem_ctx, struct sidmap_context);
55         if (sidmap == NULL) {
56                 return NULL;
57         }
58         sidmap->samctx = samdb_connect(sidmap, system_session(sidmap));
59         if (sidmap->samctx == NULL) {
60                 talloc_free(sidmap);
61                 return NULL;
62         }
63
64         return sidmap;
65 }
66
67
68 /*
69   check the sAMAccountType field of a search result to see if
70   the account is a user account
71 */
72 static BOOL is_user_account(struct ldb_message *res)
73 {
74         uint_t atype = samdb_result_uint(res, "sAMAccountType", 0);
75         if (atype && (!(atype & ATYPE_ACCOUNT))) {
76                 return False;
77         }
78         return True;
79 }
80
81 /*
82   check the sAMAccountType field of a search result to see if
83   the account is a group account
84 */
85 static BOOL is_group_account(struct ldb_message *res)
86 {
87         uint_t atype = samdb_result_uint(res, "sAMAccountType", 0);
88         if (atype && atype == ATYPE_NORMAL_ACCOUNT) {
89                 return False;
90         }
91         return True;
92 }
93
94
95
96 /*
97   return the dom_sid of our primary domain
98 */
99 static NTSTATUS sidmap_primary_domain_sid(struct sidmap_context *sidmap, 
100                                           TALLOC_CTX *mem_ctx, struct dom_sid **sid)
101 {
102         const char *attrs[] = { "objectSid", NULL };
103         int ret;
104         struct ldb_message **res = NULL;
105
106         ret = gendb_search_dn(sidmap->samctx, mem_ctx, samdb_base_dn(mem_ctx), 
107                               &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                                struct dom_sid *sid, uid_t *uid)
128 {
129         const char *attrs[] = { "sAMAccountName", "unixID", 
130                                 "unixName", "sAMAccountType", NULL };
131         int ret;
132         const char *s;
133         void *ctx;
134         struct ldb_message **res;
135         struct dom_sid *domain_sid;
136         NTSTATUS status;
137
138         ctx = talloc_new(sidmap);
139
140         ret = gendb_search(sidmap->samctx, ctx, NULL, &res, attrs, 
141                            "objectSid=%s", ldap_encode_ndr_dom_sid(ctx, sid));
142         if (ret != 1) {
143                 goto allocated_sid;
144         }
145
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(ctx, sid)));
150                 talloc_free(ctx);
151                 return NT_STATUS_INVALID_SID;
152         }
153
154         /* first try to get the uid directly */
155         s = samdb_result_string(res[0], "unixID", NULL);
156         if (s != NULL) {
157                 *uid = strtoul(s, NULL, 0);
158                 talloc_free(ctx);
159                 return NT_STATUS_OK;
160         }
161
162         /* next try via the UnixName attribute */
163         s = samdb_result_string(res[0], "unixName", NULL);
164         if (s != NULL) {
165                 struct passwd *pwd = getpwnam(s);
166                 if (!pwd) {
167                         DEBUG(0,("unixName %s for sid %s does not exist as a local user\n", s, dom_sid_string(ctx, sid)));
168                         talloc_free(ctx);
169                         return NT_STATUS_NO_SUCH_USER;
170                 }
171                 *uid = pwd->pw_uid;
172                 talloc_free(ctx);
173                 return NT_STATUS_OK;
174         }
175
176         /* finally try via the sAMAccountName attribute */
177         s = samdb_result_string(res[0], "sAMAccountName", NULL);
178         if (s != NULL) {
179                 struct passwd *pwd = getpwnam(s);
180                 if (!pwd) {
181                         DEBUG(0,("sAMAccountName '%s' for sid %s does not exist as a local user\n", 
182                                  s, dom_sid_string(ctx, sid)));
183                         talloc_free(ctx);
184                         return NT_STATUS_NO_SUCH_USER;
185                 }
186                 *uid = pwd->pw_uid;
187                 talloc_free(ctx);
188                 return NT_STATUS_OK;
189         }
190
191
192 allocated_sid:
193         status = sidmap_primary_domain_sid(sidmap, ctx, &domain_sid);
194         if (!NT_STATUS_IS_OK(status)) {
195                 talloc_free(ctx);
196                 return NT_STATUS_NO_SUCH_DOMAIN;
197         }
198
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(ctx);
205                         return NT_STATUS_OK;
206                 }
207         }
208         
209
210         DEBUG(0,("sid_to_unixuid: no unixID, unixName or sAMAccountName for sid %s\n", 
211                  dom_sid_string(ctx, sid)));
212
213         talloc_free(ctx);
214         return NT_STATUS_INVALID_SID;
215 }
216
217
218 /*
219   map a sid to a unix gid
220 */
221 NTSTATUS sidmap_sid_to_unixgid(struct sidmap_context *sidmap,
222                                struct dom_sid *sid, gid_t *gid)
223 {
224         const char *attrs[] = { "sAMAccountName", "unixID", 
225                                 "unixName", "sAMAccountType", NULL };
226         int ret;
227         const char *s;
228         void *ctx;
229         struct ldb_message **res;
230         NTSTATUS status;
231         struct dom_sid *domain_sid;
232
233         ctx = talloc_new(sidmap);
234
235         ret = gendb_search(sidmap->samctx, ctx, NULL, &res, attrs, 
236                            "objectSid=%s", ldap_encode_ndr_dom_sid(ctx, sid));
237         if (ret != 1) {
238                 goto allocated_sid;
239         }
240
241         /* make sure its not a user */
242         if (!is_group_account(res[0])) {
243                 DEBUG(0,("sid_to_unixgid: sid %s is a ATYPE_NORMAL_ACCOUNT\n", 
244                          dom_sid_string(ctx, sid)));
245                 talloc_free(ctx);
246                 return NT_STATUS_INVALID_SID;
247         }
248
249         /* first try to get the gid directly */
250         s = samdb_result_string(res[0], "unixID", NULL);
251         if (s != NULL) {
252                 *gid = strtoul(s, NULL, 0);
253                 talloc_free(ctx);
254                 return NT_STATUS_OK;
255         }
256
257         /* next try via the UnixName attribute */
258         s = samdb_result_string(res[0], "unixName", NULL);
259         if (s != NULL) {
260                 struct group *grp = getgrnam(s);
261                 if (!grp) {
262                         DEBUG(0,("unixName '%s' for sid %s does not exist as a local group\n", 
263                                  s, dom_sid_string(ctx, sid)));
264                         talloc_free(ctx);
265                         return NT_STATUS_NO_SUCH_USER;
266                 }
267                 *gid = grp->gr_gid;
268                 talloc_free(ctx);
269                 return NT_STATUS_OK;
270         }
271
272         /* finally try via the sAMAccountName attribute */
273         s = samdb_result_string(res[0], "sAMAccountName", NULL);
274         if (s != NULL) {
275                 struct group *grp = getgrnam(s);
276                 if (!grp) {
277                         DEBUG(0,("sAMAccountName '%s' for sid %s does not exist as a local group\n", s, dom_sid_string(ctx, sid)));
278                         talloc_free(ctx);
279                         return NT_STATUS_NO_SUCH_USER;
280                 }
281                 *gid = grp->gr_gid;
282                 talloc_free(ctx);
283                 return NT_STATUS_OK;
284         }
285
286 allocated_sid:
287         status = sidmap_primary_domain_sid(sidmap, ctx, &domain_sid);
288         if (!NT_STATUS_IS_OK(status)) {
289                 talloc_free(ctx);
290                 return NT_STATUS_NO_SUCH_DOMAIN;
291         }
292
293         if (dom_sid_in_domain(domain_sid, sid)) {
294                 uint32_t rid = sid->sub_auths[sid->num_auths-1];
295                 if (rid >= SIDMAP_LOCAL_GROUP_BASE) {
296                         *gid = rid - SIDMAP_LOCAL_GROUP_BASE;
297                         talloc_free(ctx);
298                         return NT_STATUS_OK;
299                 }
300         }
301
302         DEBUG(0,("sid_to_unixgid: no unixID, unixName or sAMAccountName for sid %s\n", 
303                  dom_sid_string(ctx, sid)));
304
305         talloc_free(ctx);
306         return NT_STATUS_INVALID_SID;
307 }
308
309
310 /*
311   map a unix uid to a dom_sid
312   the returned sid is allocated in the supplied mem_ctx
313 */
314 NTSTATUS sidmap_uid_to_sid(struct sidmap_context *sidmap,
315                            TALLOC_CTX *mem_ctx,
316                            uid_t uid, struct dom_sid **sid)
317 {
318         const char *attrs[] = { "sAMAccountName", "objectSid", "sAMAccountType", NULL };
319         int ret, i;
320         void *ctx;
321         struct ldb_message **res;
322         struct passwd *pwd;
323         struct dom_sid *domain_sid;
324         NTSTATUS status;
325
326         /*
327           we search for the mapping in the following order:
328
329             - check if the uid is in the dynamic uid range assigned for winbindd
330               use. If it is, then look in winbindd sid mapping
331               database (not implemented yet)
332             - look for a user account in samdb that has unixID set to the
333               given uid
334             - look for a user account in samdb that has unixName or
335               sAMAccountName set to the name given by getpwuid()
336             - assign a SID by adding the uid to SIDMAP_LOCAL_USER_BASE in the local
337               domain
338         */
339
340
341         ctx = talloc_new(sidmap);
342
343
344         /*
345           step 2: look for a user account in samdb that has unixID set to the
346                   given uid
347         */
348
349         ret = gendb_search(sidmap->samctx, ctx, NULL, &res, attrs, 
350                            "unixID=%u", (unsigned int)uid);
351         for (i=0;i<ret;i++) {
352                 if (!is_user_account(res[i])) continue;
353
354                 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
355                 talloc_free(ctx);
356                 NT_STATUS_HAVE_NO_MEMORY(*sid);
357                 return NT_STATUS_OK;
358         }
359
360         /*
361           step 3: look for a user account in samdb that has unixName
362                   or sAMAccountName set to the name given by getpwuid()
363         */
364         pwd = getpwuid(uid);
365         if (pwd == NULL) {
366                 goto allocate_sid;
367         }
368
369         ret = gendb_search(sidmap->samctx, ctx, NULL, &res, attrs, 
370                            "(|(unixName=%s)(sAMAccountName=%s))", 
371                            pwd->pw_name, pwd->pw_name);
372         for (i=0;i<ret;i++) {
373                 if (!is_user_account(res[i])) continue;
374
375                 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
376                 talloc_free(ctx);
377                 NT_STATUS_HAVE_NO_MEMORY(*sid);
378                 return NT_STATUS_OK;
379         }
380
381
382         /*
383             step 4: assign a SID by adding the uid to
384                     SIDMAP_LOCAL_USER_BASE in the local domain
385         */
386 allocate_sid:
387         if (uid > SIDMAP_MAX_LOCAL_UID) {
388                 return NT_STATUS_INVALID_SID;
389         }
390
391         status = sidmap_primary_domain_sid(sidmap, mem_ctx, &domain_sid);
392         if (!NT_STATUS_IS_OK(status)) {
393                 talloc_free(ctx);
394                 return status;
395         }
396
397         *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_USER_BASE + uid);
398         talloc_free(ctx);
399
400         if (*sid == NULL) {
401                 return NT_STATUS_NO_MEMORY;
402         }
403
404         return NT_STATUS_OK;
405 }
406
407
408 /*
409   map a unix gid to a dom_sid
410   the returned sid is allocated in the supplied mem_ctx
411 */
412 NTSTATUS sidmap_gid_to_sid(struct sidmap_context *sidmap,
413                            TALLOC_CTX *mem_ctx,
414                            gid_t gid, struct dom_sid **sid)
415 {
416         const char *attrs[] = { "sAMAccountName", "objectSid", "sAMAccountType", NULL };
417         int ret, i;
418         void *ctx;
419         struct ldb_message **res;
420         struct group *grp;
421         struct dom_sid *domain_sid;
422         NTSTATUS status;
423
424         /*
425           we search for the mapping in the following order:
426
427             - check if the gid is in the dynamic gid range assigned for winbindd
428               use. If it is, then look in winbindd sid mapping
429               database (not implemented yet)
430             - look for a group account in samdb that has unixID set to the
431               given gid
432             - look for a group account in samdb that has unixName or
433               sAMAccountName set to the name given by getgrgid()
434             - assign a SID by adding the gid to SIDMAP_LOCAL_GROUP_BASE in the local
435               domain
436         */
437
438
439         ctx = talloc_new(sidmap);
440
441
442         /*
443           step 2: look for a group account in samdb that has unixID set to the
444                   given gid
445         */
446
447         ret = gendb_search(sidmap->samctx, ctx, NULL, &res, attrs, 
448                            "unixID=%u", (unsigned int)gid);
449         for (i=0;i<ret;i++) {
450                 if (!is_group_account(res[i])) continue;
451
452                 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
453                 talloc_free(ctx);
454                 NT_STATUS_HAVE_NO_MEMORY(*sid);
455                 return NT_STATUS_OK;
456         }
457
458         /*
459           step 3: look for a group account in samdb that has unixName
460                   or sAMAccountName set to the name given by getgrgid()
461         */
462         grp = getgrgid(gid);
463         if (grp == NULL) {
464                 goto allocate_sid;
465         }
466
467         ret = gendb_search(sidmap->samctx, ctx, NULL, &res, attrs, 
468                            "(|(unixName=%s)(sAMAccountName=%s))", 
469                            grp->gr_name, grp->gr_name);
470         for (i=0;i<ret;i++) {
471                 if (!is_group_account(res[i])) continue;
472
473                 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
474                 talloc_free(ctx);
475                 NT_STATUS_HAVE_NO_MEMORY(*sid);
476                 return NT_STATUS_OK;
477         }
478
479
480         /*
481             step 4: assign a SID by adding the gid to
482                     SIDMAP_LOCAL_GROUP_BASE in the local domain
483         */
484 allocate_sid:
485         if (gid > SIDMAP_MAX_LOCAL_GID) {
486                 return NT_STATUS_INVALID_SID;
487         }
488
489         status = sidmap_primary_domain_sid(sidmap, mem_ctx, &domain_sid);
490         if (!NT_STATUS_IS_OK(status)) {
491                 talloc_free(ctx);
492                 return status;
493         }
494
495         *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_GROUP_BASE + gid);
496         talloc_free(ctx);
497
498         if (*sid == NULL) {
499                 return NT_STATUS_NO_MEMORY;
500         }
501
502         return NT_STATUS_OK;
503 }
504
505 /*
506   check if a sid is in the range of auto-allocated SIDs from our primary domain,
507   and if it is, then return the name and atype
508 */
509 NTSTATUS sidmap_allocated_sid_lookup(struct sidmap_context *sidmap, 
510                                      TALLOC_CTX *mem_ctx, 
511                                      const struct dom_sid *sid,
512                                      const char **name,
513                                      uint32_t *atype)
514 {
515         NTSTATUS status;
516         struct dom_sid *domain_sid;
517         void *ctx = talloc_new(mem_ctx);
518         uint32_t rid;
519
520         status = sidmap_primary_domain_sid(sidmap, ctx, &domain_sid);
521         if (!NT_STATUS_IS_OK(status)) {
522                 return NT_STATUS_NO_SUCH_DOMAIN;
523         }
524
525         if (!dom_sid_in_domain(domain_sid, sid)) {
526                 talloc_free(ctx);
527                 return NT_STATUS_INVALID_SID;
528         }
529
530         talloc_free(ctx);
531
532         rid = sid->sub_auths[sid->num_auths-1];
533         if (rid < SIDMAP_LOCAL_USER_BASE) {
534                 return NT_STATUS_INVALID_SID;
535         }
536
537         if (rid < SIDMAP_LOCAL_GROUP_BASE) {
538                 struct passwd *pwd;
539                 uid_t uid = rid - SIDMAP_LOCAL_USER_BASE;
540                 *atype = ATYPE_NORMAL_ACCOUNT;
541                 pwd = getpwuid(uid);
542                 if (pwd == NULL) {
543                         *name = talloc_asprintf(mem_ctx, "uid%u", uid);
544                 } else {
545                         *name = talloc_strdup(mem_ctx, pwd->pw_name);
546                 }
547         } else {
548                 struct group *grp;
549                 gid_t gid = rid - SIDMAP_LOCAL_GROUP_BASE;
550                 *atype = ATYPE_LOCAL_GROUP;
551                 grp = getgrgid(gid);
552                 if (grp == NULL) {
553                         *name = talloc_asprintf(mem_ctx, "gid%u", gid);
554                 } else {
555                         *name = talloc_strdup(mem_ctx, grp->gr_name);
556                 }
557         }
558
559         if (*name == NULL) {
560                 return NT_STATUS_NO_MEMORY;
561         }
562
563         return NT_STATUS_OK;
564 }