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