Remove a number of NT_STATUS_HAVE_NO_MEMORY_AND_FREE macros from the codebase.
[mat/samba.git] / source4 / dsdb / common / util_groups.c
1 /*
2    Unix SMB/CIFS implementation.
3    Password and authentication handling
4    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2010
5    Copyright (C) Stefan Metzmacher                         2005
6    Copyright (C) Matthias Dieter Wallnöfer                 2009
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 3 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, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "auth/auth.h"
24 #include <ldb.h>
25 #include "dsdb/samdb/samdb.h"
26 #include "libcli/security/security.h"
27 #include "dsdb/common/util.h"
28
29 /* This function tests if a SID structure "sids" contains the SID "sid" */
30 static bool sids_contains_sid(const struct dom_sid *sids,
31                               const unsigned int num_sids,
32                               const struct dom_sid *sid)
33 {
34         unsigned int i;
35
36         for (i = 0; i < num_sids; i++) {
37                 if (dom_sid_equal(&sids[i], sid))
38                         return true;
39         }
40         return false;
41 }
42
43 /*
44  * This function generates the transitive closure of a given SAM object "dn_val"
45  * (it basically expands nested memberships).
46  * If the object isn't located in the "res_sids" structure yet and the
47  * "only_childs" flag is false, we add it to "res_sids".
48  * Then we've always to consider the "memberOf" attributes. We invoke the
49  * function recursively on each of it with the "only_childs" flag set to
50  * "false".
51  * The "only_childs" flag is particularly useful if you have a user object and
52  * want to include all it's groups (referenced with "memberOf") but not itself
53  * or considering if that object matches the filter.
54  *
55  * At the beginning "res_sids" should reference to a NULL pointer.
56  */
57 NTSTATUS dsdb_expand_nested_groups(struct ldb_context *sam_ctx,
58                                    struct ldb_val *dn_val, const bool only_childs, const char *filter,
59                                    TALLOC_CTX *res_sids_ctx, struct dom_sid **res_sids,
60                                    unsigned int *num_res_sids)
61 {
62         const char * const attrs[] = { "memberOf", NULL };
63         unsigned int i;
64         int ret;
65         bool already_there;
66         struct ldb_dn *dn;
67         struct dom_sid sid;
68         TALLOC_CTX *tmp_ctx;
69         struct ldb_result *res;
70         NTSTATUS status;
71         const struct ldb_message_element *el;
72
73         if (*res_sids == NULL) {
74                 *num_res_sids = 0;
75         }
76
77         if (!sam_ctx) {
78                 DEBUG(0, ("No SAM available, cannot determine local groups\n"));
79                 return NT_STATUS_INVALID_SYSTEM_SERVICE;
80         }
81
82         tmp_ctx = talloc_new(res_sids_ctx);
83
84         dn = ldb_dn_from_ldb_val(tmp_ctx, sam_ctx, dn_val);
85         if (dn == NULL) {
86                 talloc_free(tmp_ctx);
87                 DEBUG(0, (__location__ ": we failed parsing DN %.*s, so we cannot calculate the group token\n",
88                           (int)dn_val->length, dn_val->data));
89                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
90         }
91
92         status = dsdb_get_extended_dn_sid(dn, &sid, "SID");
93         if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
94                 /* If we fail finding a SID then this is no error since it could
95                  * be a non SAM object - e.g. a group with object class
96                  * "groupOfNames" */
97                 talloc_free(tmp_ctx);
98                 return NT_STATUS_OK;
99         } else if (!NT_STATUS_IS_OK(status)) {
100                 DEBUG(0, (__location__ ": when parsing DN '%s' we failed to parse it's SID component, so we cannot calculate the group token: %s\n",
101                           ldb_dn_get_extended_linearized(tmp_ctx, dn, 1),
102                           nt_errstr(status)));
103                 talloc_free(tmp_ctx);
104                 return status;
105         }
106
107         if (!ldb_dn_minimise(dn)) {
108                 talloc_free(tmp_ctx);
109                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
110         }
111
112         if (only_childs) {
113                 ret = dsdb_search_dn(sam_ctx, tmp_ctx, &res, dn, attrs,
114                                      DSDB_SEARCH_SHOW_EXTENDED_DN);
115         } else {
116                 /* This is an O(n^2) linear search */
117                 already_there = sids_contains_sid(*res_sids,
118                                                   *num_res_sids, &sid);
119                 if (already_there) {
120                         talloc_free(tmp_ctx);
121                         return NT_STATUS_OK;
122                 }
123
124                 ret = dsdb_search(sam_ctx, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
125                                   attrs, DSDB_SEARCH_SHOW_EXTENDED_DN, "%s",
126                                   filter);
127         }
128
129         /*
130          * We have the problem with the caller creating a <SID=S-....>
131          * DN for ForeignSecurityPrincipals as they also have
132          * duplicate objects with the SAME SID under CN=Configuration.
133          * This causes a SID= DN to fail with NO_SUCH_OBJECT on Samba
134          * and on Windows.  So, we allow this to fail, and
135          * double-check if we can find it with a search in the main
136          * domain partition.
137          */
138         if (ret == LDB_ERR_NO_SUCH_OBJECT && only_childs) {
139                 char *sid_string = dom_sid_string(tmp_ctx,
140                                                   &sid);
141                 if (!sid_string) {
142                         talloc_free(tmp_ctx);
143                         return NT_STATUS_OK;
144                 }
145
146                 ret = dsdb_search(sam_ctx, tmp_ctx, &res,
147                                   ldb_get_default_basedn(sam_ctx),
148                                   LDB_SCOPE_SUBTREE,
149                                   attrs, DSDB_SEARCH_SHOW_EXTENDED_DN,
150                                   "(&(objectClass=foreignSecurityPrincipal)(objectSID=%s))",
151                                   sid_string);
152         }
153
154         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
155                 talloc_free(tmp_ctx);
156                 return NT_STATUS_OK;
157         }
158
159         if (ret != LDB_SUCCESS) {
160                 DEBUG(1, (__location__ ": dsdb_search for %s failed: %s\n",
161                           ldb_dn_get_extended_linearized(tmp_ctx, dn, 1),
162                           ldb_errstring(sam_ctx)));
163                 talloc_free(tmp_ctx);
164                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
165         }
166
167         /* We may get back 0 results, if the SID didn't match the filter - such as it wasn't a domain group, for example */
168         if (res->count != 1) {
169                 talloc_free(tmp_ctx);
170                 return NT_STATUS_OK;
171         }
172
173         /* We only apply this test once we know the SID matches the filter */
174         if (!only_childs) {
175                 *res_sids = talloc_realloc(res_sids_ctx, *res_sids,
176                         struct dom_sid, *num_res_sids + 1);
177                 if (*res_sids == NULL) {
178                         TALLOC_FREE(tmp_ctx);
179                         return NT_STATUS_NO_MEMORY;
180                 }
181                 (*res_sids)[*num_res_sids] = sid;
182                 ++(*num_res_sids);
183         }
184
185         el = ldb_msg_find_element(res->msgs[0], "memberOf");
186
187         for (i = 0; el && i < el->num_values; i++) {
188                 status = dsdb_expand_nested_groups(sam_ctx, &el->values[i],
189                                                    false, filter, res_sids_ctx, res_sids, num_res_sids);
190                 if (!NT_STATUS_IS_OK(status)) {
191                         talloc_free(tmp_ctx);
192                         return status;
193                 }
194         }
195
196         talloc_free(tmp_ctx);
197
198         return NT_STATUS_OK;
199 }