09aa3f5e710de385f766760019655535403c7e6b
[samba.git] / source4 / dsdb / common / rodc_helper.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    common sid helper functions
5
6    Copyright (C) Catalyst.NET Ltd 2017
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 "rpc_server/dcerpc_server.h"
24 #include "librpc/gen_ndr/ndr_security.h"
25 #include "source4/dsdb/samdb/samdb.h"
26 #include "libcli/security/security.h"
27
28 /*
29   see if any SIDs in list1 are in list2
30  */
31 bool sid_list_match(uint32_t num_sids1,
32                     const struct dom_sid *list1,
33                     uint32_t num_sids2,
34                     const struct dom_sid *list2)
35 {
36         unsigned int i, j;
37         /* do we ever have enough SIDs here to worry about O(n^2) ? */
38         for (i=0; i < num_sids1; i++) {
39                 for (j=0; j < num_sids2; j++) {
40                         if (dom_sid_equal(&list1[i], &list2[j])) {
41                                 return true;
42                         }
43                 }
44         }
45         return false;
46 }
47
48 /*
49  * Return an array of SIDs from a ldb_message given an attribute name assumes
50  * the SIDs are in NDR form (with additional sids applied on the end).
51  */
52 WERROR samdb_result_sid_array_ndr(struct ldb_context *sam_ctx,
53                                   struct ldb_message *msg,
54                                   TALLOC_CTX *mem_ctx,
55                                   const char *attr,
56                                   uint32_t *num_sids,
57                                   struct dom_sid **sids,
58                                   const struct dom_sid *additional_sids,
59                                   unsigned int num_additional)
60 {
61         struct ldb_message_element *el;
62         unsigned int i, j;
63
64         el = ldb_msg_find_element(msg, attr);
65         if (!el) {
66                 *sids = NULL;
67                 return WERR_OK;
68         }
69
70         /* Make array long enough for NULL and additional SID */
71         (*sids) = talloc_array(mem_ctx, struct dom_sid,
72                                el->num_values + num_additional);
73         W_ERROR_HAVE_NO_MEMORY(*sids);
74
75         for (i=0; i<el->num_values; i++) {
76                 enum ndr_err_code ndr_err;
77
78                 ndr_err = ndr_pull_struct_blob_all_noalloc(&el->values[i], &(*sids)[i],
79                                                (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
80                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
81                         return WERR_INTERNAL_DB_CORRUPTION;
82                 }
83         }
84
85         for (j = 0; j < num_additional; j++) {
86                 (*sids)[i++] = additional_sids[j];
87         }
88
89         *num_sids = i;
90
91         return WERR_OK;
92 }
93
94 /*
95   return an array of SIDs from a ldb_message given an attribute name
96   assumes the SIDs are in extended DN format
97  */
98 WERROR samdb_result_sid_array_dn(struct ldb_context *sam_ctx,
99                                  struct ldb_message *msg,
100                                  TALLOC_CTX *mem_ctx,
101                                  const char *attr,
102                                  uint32_t *num_sids,
103                                  struct dom_sid **sids)
104 {
105         struct ldb_message_element *el;
106         unsigned int i;
107
108         el = ldb_msg_find_element(msg, attr);
109         if (!el) {
110                 *sids = NULL;
111                 return WERR_OK;
112         }
113
114         (*sids) = talloc_array(mem_ctx, struct dom_sid, el->num_values + 1);
115         W_ERROR_HAVE_NO_MEMORY(*sids);
116
117         for (i=0; i<el->num_values; i++) {
118                 struct ldb_dn *dn = ldb_dn_from_ldb_val(mem_ctx, sam_ctx, &el->values[i]);
119                 NTSTATUS status;
120                 struct dom_sid sid = { 0, };
121
122                 status = dsdb_get_extended_dn_sid(dn, &sid, "SID");
123                 if (!NT_STATUS_IS_OK(status)) {
124                         return WERR_INTERNAL_DB_CORRUPTION;
125                 }
126                 (*sids)[i] = sid;
127         }
128         *num_sids = i;
129
130         return WERR_OK;
131 }
132
133 WERROR samdb_confirm_rodc_allowed_to_repl_to_sid_list(struct ldb_context *sam_ctx,
134                                                       struct ldb_message *rodc_msg,
135                                                       struct ldb_message *obj_msg,
136                                                       uint32_t num_token_sids,
137                                                       struct dom_sid *token_sids)
138 {
139         uint32_t num_never_reveal_sids, num_reveal_sids;
140         struct dom_sid *never_reveal_sids, *reveal_sids;
141         TALLOC_CTX *frame = talloc_stackframe();
142         WERROR werr;
143         uint32_t rodc_uac;
144         
145         /*
146          * We are not allowed to get anyone elses krbtgt secrets (and
147          * in callers that don't shortcut before this, the RODC should
148          * not deal with any krbtgt)
149          */
150         if (samdb_result_dn(sam_ctx, frame,
151                             obj_msg, "msDS-KrbTgtLinkBL", NULL)) {
152                 TALLOC_FREE(frame);
153                 DBG_INFO("Denied attempt to replicate to/act as a RODC krbtgt trust account %s using RODC: %s\n",
154                          ldb_dn_get_linearized(obj_msg->dn),
155                          ldb_dn_get_linearized(rodc_msg->dn));
156                 return WERR_DS_DRA_SECRETS_DENIED;
157         }
158
159         if (ldb_msg_find_attr_as_uint(obj_msg,
160                                       "userAccountControl", 0) &
161             UF_INTERDOMAIN_TRUST_ACCOUNT) {
162                 DBG_INFO("Denied attempt to replicate to/act as a inter-domain trust account %s using RODC: %s\n",
163                          ldb_dn_get_linearized(obj_msg->dn),
164                          ldb_dn_get_linearized(rodc_msg->dn));
165                 TALLOC_FREE(frame);
166                 return WERR_DS_DRA_SECRETS_DENIED;
167         }
168
169         /* Be very sure the RODC is really an RODC */
170         rodc_uac = ldb_msg_find_attr_as_uint(rodc_msg,
171                                              "userAccountControl",
172                                              0);
173         if ((rodc_uac & UF_PARTIAL_SECRETS_ACCOUNT)
174             != UF_PARTIAL_SECRETS_ACCOUNT) {
175                 DBG_ERR("Attempt to use an RODC account that is not an RODC: %s\n",
176                         ldb_dn_get_linearized(rodc_msg->dn));
177                 TALLOC_FREE(frame);
178                 return WERR_DS_DRA_SECRETS_DENIED;
179         }
180
181         werr = samdb_result_sid_array_dn(sam_ctx, rodc_msg,
182                                          frame, "msDS-NeverRevealGroup",
183                                          &num_never_reveal_sids,
184                                          &never_reveal_sids);
185         if (!W_ERROR_IS_OK(werr)) {
186                 DBG_ERR("Failed to parse msDS-NeverRevealGroup on %s: %s\n",
187                         ldb_dn_get_linearized(rodc_msg->dn),
188                         win_errstr(werr));
189                 TALLOC_FREE(frame);
190                 return WERR_DS_DRA_SECRETS_DENIED;
191         }
192
193         werr = samdb_result_sid_array_dn(sam_ctx, rodc_msg,
194                                          frame, "msDS-RevealOnDemandGroup",
195                                          &num_reveal_sids,
196                                          &reveal_sids);
197         if (!W_ERROR_IS_OK(werr)) {
198                 DBG_ERR("Failed to parse msDS-RevealOnDemandGroup on %s: %s\n",
199                         ldb_dn_get_linearized(rodc_msg->dn),
200                         win_errstr(werr));
201                 TALLOC_FREE(frame);
202                 return WERR_DS_DRA_SECRETS_DENIED;
203         }
204
205         if (never_reveal_sids &&
206             sid_list_match(num_token_sids,
207                            token_sids,
208                            num_never_reveal_sids,
209                            never_reveal_sids)) {
210                 TALLOC_FREE(frame);
211                 return WERR_DS_DRA_SECRETS_DENIED;
212         }
213
214         if (reveal_sids &&
215             sid_list_match(num_token_sids,
216                            token_sids,
217                            num_reveal_sids,
218                            reveal_sids)) {
219                 TALLOC_FREE(frame);
220                 return WERR_OK;
221         }
222
223         TALLOC_FREE(frame);
224         return WERR_DS_DRA_SECRETS_DENIED;
225
226 }
227
228 /*
229  * This is a wrapper for the above that pulls in the tokenGroups
230  * rather than relying on the caller providing those
231  */
232 WERROR samdb_confirm_rodc_allowed_to_repl_to(struct ldb_context *sam_ctx,
233                                              struct ldb_message *rodc_msg,
234                                              struct ldb_message *obj_msg)
235 {
236         TALLOC_CTX *frame = talloc_stackframe();
237         WERROR werr;
238         uint32_t num_token_sids;
239         struct dom_sid *token_sids;
240         const struct dom_sid *object_sid = NULL;
241
242         object_sid = samdb_result_dom_sid(frame,
243                                           obj_msg,
244                                           "objectSid");
245         if (object_sid == NULL) {
246                 return WERR_DS_DRA_BAD_DN;
247         }
248         
249         /*
250          * The SID list needs to include itself as well as the tokenGroups.
251          *
252          * TODO determine if sIDHistory is required for this check
253          */
254         werr = samdb_result_sid_array_ndr(sam_ctx,
255                                           obj_msg,
256                                           frame, "tokenGroups",
257                                           &num_token_sids,
258                                           &token_sids,
259                                           object_sid, 1);
260         if (!W_ERROR_IS_OK(werr) || token_sids==NULL) {
261                 DBG_ERR("Failed to get tokenGroups on %s to confirm access via RODC %s: %s\n",
262                         ldb_dn_get_linearized(obj_msg->dn),
263                         ldb_dn_get_linearized(rodc_msg->dn),
264                         win_errstr(werr));
265                 return WERR_DS_DRA_SECRETS_DENIED;
266         }
267
268         werr = samdb_confirm_rodc_allowed_to_repl_to_sid_list(sam_ctx,
269                                                               rodc_msg,
270                                                               obj_msg,
271                                                               num_token_sids,
272                                                               token_sids);
273         TALLOC_FREE(frame);
274         return werr;
275 }