353d5c14f152ba8f746a115f06cfbcfc91f5c3e9
[jelmer/samba4-debian.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 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, 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 _PUBLIC_ NTSTATUS sidmap_sid_to_unixuid(struct sidmap_context *sidmap, 
127                                         struct dom_sid *sid, uid_t *uid)
128 {
129         const char *attrs[] = { "sAMAccountName", "uidNumber", 
130                                 "sAMAccountType", 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, samdb_base_dn(tmp_ctx), &res, attrs, 
141                            "objectSid=%s", ldap_encode_ndr_dom_sid(tmp_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(tmp_ctx, sid)));
150                 talloc_free(tmp_ctx);
151                 return NT_STATUS_INVALID_SID;
152         }
153
154         /* first try to get the uid directly */
155         s = samdb_result_string(res[0], "uidNumber", NULL);
156         if (s != NULL) {
157                 *uid = strtoul(s, NULL, 0);
158                 talloc_free(tmp_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, 
168                                  dom_sid_string(tmp_ctx, sid)));
169                         talloc_free(tmp_ctx);
170                         return NT_STATUS_NO_SUCH_USER;
171                 }
172                 *uid = pwd->pw_uid;
173                 talloc_free(tmp_ctx);
174                 return NT_STATUS_OK;
175         }
176
177         /* finally try via the sAMAccountName attribute */
178         s = samdb_result_string(res[0], "sAMAccountName", NULL);
179         if (s != NULL) {
180                 struct passwd *pwd = getpwnam(s);
181                 if (!pwd) {
182                         DEBUG(0,("sAMAccountName '%s' for sid %s does not exist as a local user\n", 
183                                  s, dom_sid_string(tmp_ctx, sid)));
184                         talloc_free(tmp_ctx);
185                         return NT_STATUS_NO_SUCH_USER;
186                 }
187                 *uid = pwd->pw_uid;
188                 talloc_free(tmp_ctx);
189                 return NT_STATUS_OK;
190         }
191
192
193 allocated_sid:
194         status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
195         if (!NT_STATUS_IS_OK(status)) {
196                 talloc_free(tmp_ctx);
197                 return NT_STATUS_NO_SUCH_DOMAIN;
198         }
199
200         if (dom_sid_in_domain(domain_sid, sid)) {
201                 uint32_t rid = sid->sub_auths[sid->num_auths-1];
202                 if (rid >= SIDMAP_LOCAL_USER_BASE && 
203                     rid <  SIDMAP_LOCAL_GROUP_BASE) {
204                         *uid = rid - SIDMAP_LOCAL_USER_BASE;
205                         talloc_free(tmp_ctx);
206                         return NT_STATUS_OK;
207                 }
208         }
209         
210
211         DEBUG(0,("sid_to_unixuid: no uidNumber, unixName or sAMAccountName for sid %s\n", 
212                  dom_sid_string(tmp_ctx, sid)));
213
214         talloc_free(tmp_ctx);
215         return NT_STATUS_INVALID_SID;
216 }
217
218
219 /*
220   map a sid to a unix gid
221 */
222 _PUBLIC_ NTSTATUS sidmap_sid_to_unixgid(struct sidmap_context *sidmap,
223                                         struct dom_sid *sid, gid_t *gid)
224 {
225         const char *attrs[] = { "sAMAccountName", "gidNumber", 
226                                 "unixName", "sAMAccountType", NULL };
227         int ret;
228         const char *s;
229         TALLOC_CTX *tmp_ctx;
230         struct ldb_message **res;
231         NTSTATUS status;
232         struct dom_sid *domain_sid;
233
234         tmp_ctx = talloc_new(sidmap);
235
236         ret = gendb_search(sidmap->samctx, tmp_ctx, samdb_base_dn(tmp_ctx), &res, attrs, 
237                            "objectSid=%s", ldap_encode_ndr_dom_sid(tmp_ctx, sid));
238         if (ret != 1) {
239                 goto allocated_sid;
240         }
241
242         /* make sure its not a user */
243         if (!is_group_account(res[0])) {
244                 DEBUG(0,("sid_to_unixgid: sid %s is a ATYPE_NORMAL_ACCOUNT\n", 
245                          dom_sid_string(tmp_ctx, sid)));
246                 talloc_free(tmp_ctx);
247                 return NT_STATUS_INVALID_SID;
248         }
249
250         /* first try to get the gid directly */
251         s = samdb_result_string(res[0], "gidNumber", NULL);
252         if (s != NULL) {
253                 *gid = strtoul(s, NULL, 0);
254                 talloc_free(tmp_ctx);
255                 return NT_STATUS_OK;
256         }
257
258         /* next try via the UnixName attribute */
259         s = samdb_result_string(res[0], "unixName", NULL);
260         if (s != NULL) {
261                 struct group *grp = getgrnam(s);
262                 if (!grp) {
263                         DEBUG(0,("unixName '%s' for sid %s does not exist as a local group\n", 
264                                  s, dom_sid_string(tmp_ctx, sid)));
265                         talloc_free(tmp_ctx);
266                         return NT_STATUS_NO_SUCH_GROUP;
267                 }
268                 *gid = grp->gr_gid;
269                 talloc_free(tmp_ctx);
270                 return NT_STATUS_OK;
271         }
272
273         /* finally try via the sAMAccountName attribute */
274         s = samdb_result_string(res[0], "sAMAccountName", NULL);
275         if (s != NULL) {
276                 struct group *grp = getgrnam(s);
277                 if (!grp) {
278                         DEBUG(0,("sAMAccountName '%s' for sid %s does not exist as a local group\n", s, dom_sid_string(tmp_ctx, sid)));
279                         talloc_free(tmp_ctx);
280                         return NT_STATUS_NO_SUCH_GROUP;
281                 }
282                 *gid = grp->gr_gid;
283                 talloc_free(tmp_ctx);
284                 return NT_STATUS_OK;
285         }
286
287 allocated_sid:
288         status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
289         if (!NT_STATUS_IS_OK(status)) {
290                 talloc_free(tmp_ctx);
291                 return NT_STATUS_NO_SUCH_DOMAIN;
292         }
293
294         if (dom_sid_in_domain(domain_sid, sid)) {
295                 uint32_t rid = sid->sub_auths[sid->num_auths-1];
296                 if (rid >= SIDMAP_LOCAL_GROUP_BASE) {
297                         *gid = rid - SIDMAP_LOCAL_GROUP_BASE;
298                         talloc_free(tmp_ctx);
299                         return NT_STATUS_OK;
300                 }
301         }
302
303         DEBUG(0,("sid_to_unixgid: no gidNumber, unixName or sAMAccountName for sid %s\n", 
304                  dom_sid_string(tmp_ctx, sid)));
305
306         talloc_free(tmp_ctx);
307         return NT_STATUS_INVALID_SID;
308 }
309
310
311 /*
312   map a unix uid to a dom_sid
313   the returned sid is allocated in the supplied mem_ctx
314 */
315 _PUBLIC_ NTSTATUS sidmap_uid_to_sid(struct sidmap_context *sidmap,
316                                     TALLOC_CTX *mem_ctx,
317                                     uid_t uid, struct dom_sid **sid)
318 {
319         const char *attrs[] = { "sAMAccountName", "objectSid", "sAMAccountType", NULL };
320         int ret, i;
321         TALLOC_CTX *tmp_ctx;
322         struct ldb_message **res;
323         struct passwd *pwd;
324         struct dom_sid *domain_sid;
325         NTSTATUS status;
326
327         /*
328           we search for the mapping in the following order:
329
330             - check if the uid is in the dynamic uid range assigned for winbindd
331               use. If it is, then look in winbindd sid mapping
332               database (not implemented yet)
333             - look for a user account in samdb that has uidNumber set to the
334               given uid
335             - look for a user account in samdb that has unixName or
336               sAMAccountName set to the name given by getpwuid()
337             - assign a SID by adding the uid to SIDMAP_LOCAL_USER_BASE in the local
338               domain
339         */
340
341
342         tmp_ctx = talloc_new(mem_ctx);
343
344
345         /*
346           step 2: look for a user account in samdb that has uidNumber set to the
347                   given uid
348         */
349
350         ret = gendb_search(sidmap->samctx, tmp_ctx, samdb_base_dn(tmp_ctx), &res, attrs, 
351                            "uidNumber=%u", (unsigned int)uid);
352         for (i=0;i<ret;i++) {
353                 if (!is_user_account(res[i])) continue;
354
355                 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
356                 talloc_free(tmp_ctx);
357                 NT_STATUS_HAVE_NO_MEMORY(*sid);
358                 return NT_STATUS_OK;
359         }
360
361         /*
362           step 3: look for a user account in samdb that has unixName
363                   or sAMAccountName set to the name given by getpwuid()
364         */
365         pwd = getpwuid(uid);
366         if (pwd == NULL) {
367                 goto allocate_sid;
368         }
369
370         ret = gendb_search(sidmap->samctx, tmp_ctx, NULL, &res, attrs, 
371                            "(|(unixName=%s)(sAMAccountName=%s))", 
372                            pwd->pw_name, pwd->pw_name);
373         for (i=0;i<ret;i++) {
374                 if (!is_user_account(res[i])) continue;
375
376                 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
377                 talloc_free(tmp_ctx);
378                 NT_STATUS_HAVE_NO_MEMORY(*sid);
379                 return NT_STATUS_OK;
380         }
381
382
383         /*
384             step 4: assign a SID by adding the uid to
385                     SIDMAP_LOCAL_USER_BASE in the local domain
386         */
387 allocate_sid:
388         if (uid > SIDMAP_MAX_LOCAL_UID) {
389                 return NT_STATUS_INVALID_SID;
390         }
391
392         status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
393         if (!NT_STATUS_IS_OK(status)) {
394                 talloc_free(tmp_ctx);
395                 return status;
396         }
397
398         *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_USER_BASE + uid);
399         talloc_free(tmp_ctx);
400
401         if (*sid == NULL) {
402                 return NT_STATUS_NO_MEMORY;
403         }
404
405         return NT_STATUS_OK;
406 }
407
408
409 /*
410   map a unix gid to a dom_sid
411   the returned sid is allocated in the supplied mem_ctx
412 */
413 _PUBLIC_ NTSTATUS sidmap_gid_to_sid(struct sidmap_context *sidmap,
414                                     TALLOC_CTX *mem_ctx,
415                                     gid_t gid, struct dom_sid **sid)
416 {
417         const char *attrs[] = { "sAMAccountName", "objectSid", "sAMAccountType", NULL };
418         int ret, i;
419         TALLOC_CTX *tmp_ctx;
420         struct ldb_message **res;
421         struct group *grp;
422         struct dom_sid *domain_sid;
423         NTSTATUS status;
424
425         /*
426           we search for the mapping in the following order:
427
428             - check if the gid is in the dynamic gid range assigned for winbindd
429               use. If it is, then look in winbindd sid mapping
430               database (not implemented yet)
431             - look for a group account in samdb that has gidNumber set to the
432               given gid
433             - look for a group account in samdb that has unixName or
434               sAMAccountName set to the name given by getgrgid()
435             - assign a SID by adding the gid to SIDMAP_LOCAL_GROUP_BASE in the local
436               domain
437         */
438
439
440         tmp_ctx = talloc_new(sidmap);
441
442
443         /*
444           step 2: look for a group account in samdb that has gidNumber set to the
445                   given gid
446         */
447
448         ret = gendb_search(sidmap->samctx, tmp_ctx, samdb_base_dn(tmp_ctx), &res, attrs, 
449                            "gidNumber=%u", (unsigned int)gid);
450         for (i=0;i<ret;i++) {
451                 if (!is_group_account(res[i])) continue;
452
453                 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
454                 talloc_free(tmp_ctx);
455                 NT_STATUS_HAVE_NO_MEMORY(*sid);
456                 return NT_STATUS_OK;
457         }
458
459         /*
460           step 3: look for a group account in samdb that has unixName
461                   or sAMAccountName set to the name given by getgrgid()
462         */
463         grp = getgrgid(gid);
464         if (grp == NULL) {
465                 goto allocate_sid;
466         }
467
468         ret = gendb_search(sidmap->samctx, tmp_ctx, samdb_base_dn(tmp_ctx), &res, attrs, 
469                            "(|(unixName=%s)(sAMAccountName=%s))", 
470                            grp->gr_name, grp->gr_name);
471         for (i=0;i<ret;i++) {
472                 if (!is_group_account(res[i])) continue;
473
474                 *sid = samdb_result_dom_sid(mem_ctx, res[i], "objectSid");
475                 talloc_free(tmp_ctx);
476                 NT_STATUS_HAVE_NO_MEMORY(*sid);
477                 return NT_STATUS_OK;
478         }
479
480
481         /*
482             step 4: assign a SID by adding the gid to
483                     SIDMAP_LOCAL_GROUP_BASE in the local domain
484         */
485 allocate_sid:
486         if (gid > SIDMAP_MAX_LOCAL_GID) {
487                 return NT_STATUS_INVALID_SID;
488         }
489
490         status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
491         if (!NT_STATUS_IS_OK(status)) {
492                 talloc_free(tmp_ctx);
493                 return status;
494         }
495
496         *sid = dom_sid_add_rid(mem_ctx, domain_sid, SIDMAP_LOCAL_GROUP_BASE + gid);
497         talloc_free(tmp_ctx);
498
499         if (*sid == NULL) {
500                 return NT_STATUS_NO_MEMORY;
501         }
502
503         return NT_STATUS_OK;
504 }
505
506 /*
507   check if a sid is in the range of auto-allocated SIDs from our primary domain,
508   and if it is, then return the name and atype
509 */
510 _PUBLIC_ NTSTATUS sidmap_allocated_sid_lookup(struct sidmap_context *sidmap, 
511                                               TALLOC_CTX *mem_ctx, 
512                                               const struct dom_sid *sid,
513                                               const char **name,
514                                               uint32_t *atype)
515 {
516         NTSTATUS status;
517         struct dom_sid *domain_sid;
518         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
519         uint32_t rid;
520
521         status = sidmap_primary_domain_sid(sidmap, tmp_ctx, &domain_sid);
522         if (!NT_STATUS_IS_OK(status)) {
523                 return NT_STATUS_NO_SUCH_DOMAIN;
524         }
525
526         if (!dom_sid_in_domain(domain_sid, sid)) {
527                 talloc_free(tmp_ctx);
528                 return NT_STATUS_INVALID_SID;
529         }
530
531         talloc_free(tmp_ctx);
532
533         rid = sid->sub_auths[sid->num_auths-1];
534         if (rid < SIDMAP_LOCAL_USER_BASE) {
535                 return NT_STATUS_INVALID_SID;
536         }
537
538         if (rid < SIDMAP_LOCAL_GROUP_BASE) {
539                 struct passwd *pwd;
540                 uid_t uid = rid - SIDMAP_LOCAL_USER_BASE;
541                 *atype = ATYPE_NORMAL_ACCOUNT;
542                 pwd = getpwuid(uid);
543                 if (pwd == NULL) {
544                         *name = talloc_asprintf(mem_ctx, "uid%u", uid);
545                 } else {
546                         *name = talloc_strdup(mem_ctx, pwd->pw_name);
547                 }
548         } else {
549                 struct group *grp;
550                 gid_t gid = rid - SIDMAP_LOCAL_GROUP_BASE;
551                 *atype = ATYPE_LOCAL_GROUP;
552                 grp = getgrgid(gid);
553                 if (grp == NULL) {
554                         *name = talloc_asprintf(mem_ctx, "gid%u", gid);
555                 } else {
556                         *name = talloc_strdup(mem_ctx, grp->gr_name);
557                 }
558         }
559
560         if (*name == NULL) {
561                 return NT_STATUS_NO_MEMORY;
562         }
563
564         return NT_STATUS_OK;
565 }