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