dde01fba72ec5d6e1bda06dbe2f893269c83f234
[tprouty/samba.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
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         struct ldb_context *samctx;
46 };
47
48 /*
49   open a sidmap context - use talloc_free to close
50 */
51 _PUBLIC_ 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, NULL, &res, attrs);
107         if (ret != 1) {
108                 talloc_free(res);
109                 return NT_STATUS_NO_SUCH_DOMAIN;
110         }
111         
112         *sid = samdb_result_dom_sid(mem_ctx, res[0], "objectSid");
113         talloc_free(res);
114         if (*sid == NULL) {
115                 return NT_STATUS_NO_MEMORY;
116         }
117
118         return NT_STATUS_OK;
119 }
120
121
122 /*
123   map a sid to a unix uid
124 */
125 _PUBLIC_ NTSTATUS sidmap_sid_to_unixuid(struct sidmap_context *sidmap, 
126                                         struct dom_sid *sid, uid_t *uid)
127 {
128         const char *attrs[] = { "sAMAccountName", "uidNumber", 
129                                 "sAMAccountType", NULL };
130         int ret;
131         const char *s;
132         TALLOC_CTX *tmp_ctx;
133         struct ldb_message **res;
134         struct dom_sid *domain_sid;
135         NTSTATUS status;
136
137         tmp_ctx = talloc_new(sidmap);
138
139         ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs, 
140                            "objectSid=%s", ldap_encode_ndr_dom_sid(tmp_ctx, sid));
141         if (ret != 1) {
142                 goto allocated_sid;
143         }
144
145         /* make sure its a user, not a group */
146         if (!is_user_account(res[0])) {
147                 DEBUG(0,("sid_to_unixuid: sid %s is not an account!\n", 
148                          dom_sid_string(tmp_ctx, sid)));
149                 talloc_free(tmp_ctx);
150                 return NT_STATUS_INVALID_SID;
151         }
152
153         /* first try to get the uid directly */
154         s = samdb_result_string(res[0], "uidNumber", NULL);
155         if (s != NULL) {
156                 *uid = strtoul(s, NULL, 0);
157                 talloc_free(tmp_ctx);
158                 return NT_STATUS_OK;
159         }
160
161         /* next try via the UnixName attribute */
162         s = samdb_result_string(res[0], "unixName", NULL);
163         if (s != NULL) {
164                 struct passwd *pwd = getpwnam(s);
165                 if (!pwd) {
166                         DEBUG(0,("unixName %s for sid %s does not exist as a local user\n", s, 
167                                  dom_sid_string(tmp_ctx, sid)));
168                         talloc_free(tmp_ctx);
169                         return NT_STATUS_NO_SUCH_USER;
170                 }
171                 *uid = pwd->pw_uid;
172                 talloc_free(tmp_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(tmp_ctx, sid)));
183                         talloc_free(tmp_ctx);
184                         return NT_STATUS_NO_SUCH_USER;
185                 }
186                 *uid = pwd->pw_uid;
187                 talloc_free(tmp_ctx);
188                 return NT_STATUS_OK;
189         }
190
191
192 allocated_sid:
193         status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
194         if (!NT_STATUS_IS_OK(status)) {
195                 talloc_free(tmp_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(tmp_ctx);
205                         return NT_STATUS_OK;
206                 }
207         }
208         
209
210         DEBUG(0,("sid_to_unixuid: no uidNumber, unixName or sAMAccountName for sid %s\n", 
211                  dom_sid_string(tmp_ctx, sid)));
212
213         talloc_free(tmp_ctx);
214         return NT_STATUS_INVALID_SID;
215 }
216
217
218 /*
219   map a sid to a unix gid
220 */
221 _PUBLIC_ NTSTATUS sidmap_sid_to_unixgid(struct sidmap_context *sidmap,
222                                         struct dom_sid *sid, gid_t *gid)
223 {
224         const char *attrs[] = { "sAMAccountName", "gidNumber", 
225                                 "unixName", "sAMAccountType", NULL };
226         int ret;
227         const char *s;
228         TALLOC_CTX *tmp_ctx;
229         struct ldb_message **res;
230         NTSTATUS status;
231         struct dom_sid *domain_sid;
232
233         tmp_ctx = talloc_new(sidmap);
234
235         ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs, 
236                            "objectSid=%s", ldap_encode_ndr_dom_sid(tmp_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(tmp_ctx, sid)));
245                 talloc_free(tmp_ctx);
246                 return NT_STATUS_INVALID_SID;
247         }
248
249         /* first try to get the gid directly */
250         s = samdb_result_string(res[0], "gidNumber", NULL);
251         if (s != NULL) {
252                 *gid = strtoul(s, NULL, 0);
253                 talloc_free(tmp_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(tmp_ctx, sid)));
264                         talloc_free(tmp_ctx);
265                         return NT_STATUS_NO_SUCH_GROUP;
266                 }
267                 *gid = grp->gr_gid;
268                 talloc_free(tmp_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(tmp_ctx, sid)));
278                         talloc_free(tmp_ctx);
279                         return NT_STATUS_NO_SUCH_GROUP;
280                 }
281                 *gid = grp->gr_gid;
282                 talloc_free(tmp_ctx);
283                 return NT_STATUS_OK;
284         }
285
286 allocated_sid:
287         status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
288         if (!NT_STATUS_IS_OK(status)) {
289                 talloc_free(tmp_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(tmp_ctx);
298                         return NT_STATUS_OK;
299                 }
300         }
301
302         DEBUG(0,("sid_to_unixgid: no gidNumber, unixName or sAMAccountName for sid %s\n", 
303                  dom_sid_string(tmp_ctx, sid)));
304
305         talloc_free(tmp_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 _PUBLIC_ 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         TALLOC_CTX *tmp_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 uidNumber 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         tmp_ctx = talloc_new(mem_ctx);
342
343
344         /*
345           step 2: look for a user account in samdb that has uidNumber set to the
346                   given uid
347         */
348
349         ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs, 
350                            "uidNumber=%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(tmp_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, tmp_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(tmp_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, tmp_ctx, &domain_sid);
392         if (!NT_STATUS_IS_OK(status)) {
393                 talloc_free(tmp_ctx);
394                 return status;
395         }
396
397         *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_USER_BASE + uid);
398         talloc_free(tmp_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 _PUBLIC_ 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         TALLOC_CTX *tmp_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 gidNumber 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         tmp_ctx = talloc_new(sidmap);
440
441
442         /*
443           step 2: look for a group account in samdb that has gidNumber set to the
444                   given gid
445         */
446
447         ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs, 
448                            "gidNumber=%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(tmp_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, tmp_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(tmp_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, tmp_ctx, &domain_sid);
490         if (!NT_STATUS_IS_OK(status)) {
491                 talloc_free(tmp_ctx);
492                 return status;
493         }
494
495         *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_GROUP_BASE + gid);
496         talloc_free(tmp_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 _PUBLIC_ 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         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
518         uint32_t rid;
519
520         status = sidmap_primary_domain_sid(sidmap, tmp_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(tmp_ctx);
527                 return NT_STATUS_INVALID_SID;
528         }
529
530         talloc_free(tmp_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 }