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