Line data Source code
1 : /*
2 : SSSD
3 :
4 : Authors:
5 : Stephen Gallagher <sgallagh@redhat.com>
6 :
7 : Copyright (C) 2011 Red Hat
8 :
9 : This program is free software; you can redistribute it and/or modify
10 : it under the terms of the GNU General Public License as published by
11 : the Free Software Foundation; either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : This program is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU General Public License for more details.
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program. If not, see <http://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "util/util.h"
24 : #include "db/sysdb.h"
25 : #include "providers/ipa/ipa_hbac_private.h"
26 : #include "providers/ldap/sdap_async.h"
27 :
28 : /*
29 : * Functions to convert sysdb_attrs to the hbac_rule format
30 : */
31 0 : static errno_t hbac_host_attrs_to_rule(TALLOC_CTX *mem_ctx,
32 : struct sss_domain_info *domain,
33 : const char *rule_name,
34 : struct sysdb_attrs *rule_attrs,
35 : const char *category_attr,
36 : const char *member_attr,
37 : size_t *host_count,
38 : struct hbac_rule_element **hosts)
39 : {
40 : errno_t ret;
41 : TALLOC_CTX *tmp_ctx;
42 : struct hbac_rule_element *new_hosts;
43 0 : const char *attrs[] = { SYSDB_FQDN, SYSDB_NAME, NULL };
44 : struct ldb_message_element *el;
45 0 : size_t num_hosts = 0;
46 0 : size_t num_hostgroups = 0;
47 : size_t i;
48 : char *member_dn;
49 : char *filter;
50 : size_t count;
51 : struct ldb_message **msgs;
52 : const char *name;
53 :
54 0 : tmp_ctx = talloc_new(mem_ctx);
55 0 : if (tmp_ctx == NULL) return ENOMEM;
56 :
57 0 : new_hosts = talloc_zero(tmp_ctx, struct hbac_rule_element);
58 0 : if (new_hosts == NULL) {
59 0 : ret = ENOMEM;
60 0 : goto done;
61 : }
62 :
63 : /* First check for host category */
64 0 : ret = hbac_get_category(rule_attrs, category_attr, &new_hosts->category);
65 0 : if (ret != EOK) {
66 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not identify host categories\n");
67 0 : goto done;
68 : }
69 0 : if (new_hosts->category & HBAC_CATEGORY_ALL) {
70 : /* Short-cut to the exit */
71 0 : ret = EOK;
72 0 : goto done;
73 : }
74 :
75 : /* Get the list of DNs from the member_attr */
76 0 : ret = sysdb_attrs_get_el(rule_attrs, member_attr, &el);
77 0 : if (ret != EOK && ret != ENOENT) {
78 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_attrs_get_el failed.\n");
79 0 : goto done;
80 : }
81 0 : if (ret == ENOENT || el->num_values == 0) {
82 0 : el->num_values = 0;
83 0 : DEBUG(SSSDBG_CONF_SETTINGS,
84 : "No host specified, rule will never apply.\n");
85 : }
86 :
87 : /* Assume maximum size; We'll trim it later */
88 0 : new_hosts->names = talloc_array(new_hosts,
89 : const char *,
90 : el->num_values +1);
91 0 : if (new_hosts->names == NULL) {
92 0 : ret = ENOMEM;
93 0 : goto done;
94 : }
95 :
96 0 : new_hosts->groups = talloc_array(new_hosts,
97 : const char *,
98 : el->num_values + 1);
99 0 : if (new_hosts->groups == NULL) {
100 0 : ret = ENOMEM;
101 0 : goto done;
102 : }
103 :
104 0 : for (i = 0; i < el->num_values; i++) {
105 0 : ret = sss_filter_sanitize(tmp_ctx,
106 0 : (const char *)el->values[i].data,
107 : &member_dn);
108 0 : if (ret != EOK) goto done;
109 :
110 0 : filter = talloc_asprintf(member_dn, "(%s=%s)",
111 : SYSDB_ORIG_DN, member_dn);
112 0 : if (filter == NULL) {
113 0 : ret = ENOMEM;
114 0 : goto done;
115 : }
116 :
117 : /* First check if this is a specific host */
118 0 : ret = sysdb_search_custom(tmp_ctx, domain, filter,
119 : HBAC_HOSTS_SUBDIR, attrs,
120 : &count, &msgs);
121 0 : if (ret != EOK && ret != ENOENT) goto done;
122 0 : if (ret == EOK && count == 0) {
123 0 : ret = ENOENT;
124 : }
125 :
126 0 : if (ret == EOK) {
127 0 : if (count > 1) {
128 0 : DEBUG(SSSDBG_CRIT_FAILURE,
129 : "Original DN matched multiple hosts. Skipping \n");
130 0 : talloc_zfree(member_dn);
131 0 : continue;
132 : }
133 :
134 : /* Original DN matched a single host. Get the hostname */
135 0 : name = ldb_msg_find_attr_as_string(msgs[0],
136 : SYSDB_FQDN,
137 : NULL);
138 0 : if (name == NULL) {
139 0 : DEBUG(SSSDBG_CRIT_FAILURE, "FQDN is missing!\n");
140 0 : ret = EFAULT;
141 0 : goto done;
142 : }
143 :
144 0 : new_hosts->names[num_hosts] = talloc_strdup(new_hosts->names,
145 : name);
146 0 : if (new_hosts->names[num_hosts] == NULL) {
147 0 : ret = ENOMEM;
148 0 : goto done;
149 : }
150 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Added host [%s] to rule [%s]\n",
151 : name, rule_name);
152 0 : num_hosts++;
153 : } else { /* ret == ENOENT */
154 : /* Check if this is a hostgroup */
155 0 : ret = sysdb_search_custom(tmp_ctx, domain, filter,
156 : HBAC_HOSTGROUPS_SUBDIR, attrs,
157 : &count, &msgs);
158 0 : if (ret != EOK && ret != ENOENT) goto done;
159 0 : if (ret == EOK && count == 0) {
160 0 : ret = ENOENT;
161 : }
162 :
163 0 : if (ret == EOK) {
164 0 : if (count > 1) {
165 0 : DEBUG(SSSDBG_CRIT_FAILURE,
166 : "Original DN matched multiple hostgroups. "
167 : "Skipping\n");
168 0 : talloc_zfree(member_dn);
169 0 : continue;
170 : }
171 :
172 : /* Original DN matched a single group. Get the groupname */
173 0 : name = ldb_msg_find_attr_as_string(msgs[0], SYSDB_NAME, NULL);
174 0 : if (name == NULL) {
175 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Hostgroup name is missing!\n");
176 0 : ret = EFAULT;
177 0 : goto done;
178 : }
179 :
180 0 : new_hosts->groups[num_hostgroups] =
181 0 : talloc_strdup(new_hosts->groups, name);
182 0 : if (new_hosts->groups[num_hostgroups] == NULL) {
183 0 : ret = ENOMEM;
184 0 : goto done;
185 : }
186 :
187 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
188 : "Added hostgroup [%s] to rule [%s]\n",
189 : name, rule_name);
190 0 : num_hostgroups++;
191 : } else { /* ret == ENOENT */
192 : /* Neither a host nor a hostgroup? Skip it */
193 0 : DEBUG(SSSDBG_TRACE_LIBS,
194 : "[%s] does not map to either a host or hostgroup. "
195 : "Skipping\n", member_dn);
196 : }
197 : }
198 0 : talloc_zfree(member_dn);
199 : }
200 0 : new_hosts->names[num_hosts] = NULL;
201 0 : new_hosts->groups[num_hostgroups] = NULL;
202 :
203 : /* Shrink the arrays down to their real sizes */
204 0 : new_hosts->names = talloc_realloc(new_hosts, new_hosts->names,
205 : const char *, num_hosts + 1);
206 0 : if (new_hosts->names == NULL) {
207 0 : ret = ENOMEM;
208 0 : goto done;
209 : }
210 :
211 0 : new_hosts->groups = talloc_realloc(new_hosts, new_hosts->groups,
212 : const char *, num_hostgroups + 1);
213 0 : if (new_hosts->groups == NULL) {
214 0 : ret = ENOMEM;
215 0 : goto done;
216 : }
217 :
218 0 : ret = EOK;
219 :
220 : done:
221 0 : if (ret == EOK) {
222 0 : *hosts = talloc_steal(mem_ctx, new_hosts);
223 0 : if (host_count) *host_count = num_hosts;
224 : }
225 0 : talloc_free(tmp_ctx);
226 0 : return ret;
227 : }
228 :
229 : errno_t
230 0 : hbac_thost_attrs_to_rule(TALLOC_CTX *mem_ctx,
231 : struct sss_domain_info *domain,
232 : const char *rule_name,
233 : struct sysdb_attrs *rule_attrs,
234 : struct hbac_rule_element **thosts)
235 : {
236 0 : DEBUG(SSSDBG_TRACE_LIBS,
237 : "Processing target hosts for rule [%s]\n", rule_name);
238 :
239 0 : return hbac_host_attrs_to_rule(mem_ctx, domain,
240 : rule_name, rule_attrs,
241 : IPA_HOST_CATEGORY, IPA_MEMBER_HOST,
242 : NULL, thosts);
243 : }
244 :
245 : errno_t
246 0 : hbac_shost_attrs_to_rule(TALLOC_CTX *mem_ctx,
247 : struct sss_domain_info *domain,
248 : const char *rule_name,
249 : struct sysdb_attrs *rule_attrs,
250 : bool support_srchost,
251 : struct hbac_rule_element **source_hosts)
252 : {
253 : errno_t ret;
254 : size_t host_count;
255 : TALLOC_CTX *tmp_ctx;
256 : size_t idx;
257 : struct ldb_message_element *el;
258 : struct hbac_rule_element *shosts;
259 :
260 0 : tmp_ctx = talloc_new(mem_ctx);
261 0 : if (tmp_ctx == NULL) return ENOMEM;
262 :
263 0 : DEBUG(SSSDBG_TRACE_FUNC, "Processing source hosts for rule [%s]\n", rule_name);
264 :
265 0 : if (!support_srchost) {
266 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Source hosts disabled, setting ALL\n");
267 0 : shosts = talloc_zero(tmp_ctx, struct hbac_rule_element);
268 0 : if (shosts == NULL) {
269 0 : ret = ENOMEM;
270 0 : goto done;
271 : }
272 :
273 0 : shosts->category = HBAC_CATEGORY_ALL;
274 0 : ret = EOK;
275 0 : goto done;
276 : } else {
277 0 : DEBUG(SSSDBG_MINOR_FAILURE, "WARNING: Using deprecated option "
278 : "ipa_hbac_support_srchost.\n");
279 0 : sss_log(SSS_LOG_NOTICE, "WARNING: Using deprecated option "
280 : "ipa_hbac_support_srchost.\n");
281 : }
282 :
283 0 : ret = hbac_host_attrs_to_rule(tmp_ctx, domain,
284 : rule_name, rule_attrs,
285 : IPA_SOURCE_HOST_CATEGORY, IPA_SOURCE_HOST,
286 : &host_count, &shosts);
287 0 : if (ret != EOK) {
288 0 : goto done;
289 : }
290 :
291 0 : if (shosts->category & HBAC_CATEGORY_ALL) {
292 : /* All hosts (including external) are
293 : * allowed.
294 : */
295 0 : goto done;
296 : }
297 :
298 : /* Include external (non-IPA-managed) source hosts */
299 0 : ret = sysdb_attrs_get_el(rule_attrs, IPA_EXTERNAL_HOST, &el);
300 0 : if (ret != EOK && ret != ENOENT) goto done;
301 0 : if (ret == EOK && el->num_values == 0) ret = ENOENT;
302 :
303 0 : if (ret != ENOENT) {
304 0 : shosts->names = talloc_realloc(shosts, shosts->names, const char *,
305 : host_count + el->num_values + 1);
306 0 : if (shosts->names == NULL) {
307 0 : ret = ENOMEM;
308 0 : goto done;
309 : }
310 :
311 0 : for (idx = host_count; idx < host_count + el->num_values; idx++) {
312 0 : shosts->names[idx] =
313 0 : talloc_strdup(shosts->names,
314 0 : (const char *)el->values[idx - host_count].data);
315 0 : if (shosts->names[idx] == NULL) {
316 0 : ret = ENOMEM;
317 0 : goto done;
318 : }
319 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
320 : "Added external source host [%s] to rule [%s]\n",
321 : shosts->names[idx], rule_name);
322 : }
323 0 : shosts->names[idx] = NULL;
324 : }
325 :
326 0 : ret = EOK;
327 :
328 : done:
329 0 : if (ret == EOK) {
330 0 : *source_hosts = talloc_steal(mem_ctx, shosts);
331 : }
332 0 : talloc_free(tmp_ctx);
333 0 : return ret;
334 : }
335 :
336 : errno_t
337 0 : get_ipa_hostgroupname(TALLOC_CTX *mem_ctx,
338 : struct sysdb_ctx *sysdb,
339 : const char *host_dn,
340 : char **hostgroupname)
341 : {
342 : errno_t ret;
343 : struct ldb_dn *dn;
344 : const char *rdn_name;
345 : const char *hostgroup_comp_name;
346 : const char *account_comp_name;
347 : const struct ldb_val *rdn_val;
348 : const struct ldb_val *hostgroup_comp_val;
349 : const struct ldb_val *account_comp_val;
350 :
351 : /* This is an IPA-specific hack. It may not
352 : * work for non-IPA servers and will need to
353 : * be changed if SSSD ever supports HBAC on
354 : * a non-IPA server.
355 : */
356 0 : *hostgroupname = NULL;
357 :
358 0 : dn = ldb_dn_new(mem_ctx, sysdb_ctx_get_ldb(sysdb), host_dn);
359 0 : if (dn == NULL) {
360 0 : ret = ENOMEM;
361 0 : goto done;
362 : }
363 :
364 0 : if (!ldb_dn_validate(dn)) {
365 0 : ret = ERR_MALFORMED_ENTRY;
366 0 : goto done;
367 : }
368 :
369 0 : if (ldb_dn_get_comp_num(dn) < 4) {
370 : /* RDN, hostgroups, accounts, and at least one DC= */
371 : /* If it's fewer, it's not a group DN */
372 0 : ret = ERR_UNEXPECTED_ENTRY_TYPE;
373 0 : goto done;
374 : }
375 :
376 : /* If the RDN name is 'cn' */
377 0 : rdn_name = ldb_dn_get_rdn_name(dn);
378 0 : if (rdn_name == NULL) {
379 : /* Shouldn't happen if ldb_dn_validate()
380 : * passed, but we'll be careful.
381 : */
382 0 : ret = ERR_MALFORMED_ENTRY;
383 0 : goto done;
384 : }
385 :
386 0 : if (strcasecmp("cn", rdn_name) != 0) {
387 : /* RDN has the wrong attribute name.
388 : * It's not a host.
389 : */
390 0 : ret = ERR_UNEXPECTED_ENTRY_TYPE;
391 0 : goto done;
392 : }
393 :
394 : /* and the second component is "cn=hostgroups" */
395 0 : hostgroup_comp_name = ldb_dn_get_component_name(dn, 1);
396 0 : if (strcasecmp("cn", hostgroup_comp_name) != 0) {
397 : /* The second component name is not "cn" */
398 0 : ret = ERR_UNEXPECTED_ENTRY_TYPE;
399 0 : goto done;
400 : }
401 :
402 0 : hostgroup_comp_val = ldb_dn_get_component_val(dn, 1);
403 0 : if (strncasecmp("hostgroups",
404 0 : (const char *) hostgroup_comp_val->data,
405 : hostgroup_comp_val->length) != 0) {
406 : /* The second component value is not "hostgroups" */
407 0 : ret = ERR_UNEXPECTED_ENTRY_TYPE;
408 0 : goto done;
409 : }
410 :
411 : /* and the third component is "accounts" */
412 0 : account_comp_name = ldb_dn_get_component_name(dn, 2);
413 0 : if (strcasecmp("cn", account_comp_name) != 0) {
414 : /* The third component name is not "cn" */
415 0 : ret = ERR_UNEXPECTED_ENTRY_TYPE;
416 0 : goto done;
417 : }
418 :
419 0 : account_comp_val = ldb_dn_get_component_val(dn, 2);
420 0 : if (strncasecmp("accounts",
421 0 : (const char *) account_comp_val->data,
422 : account_comp_val->length) != 0) {
423 : /* The third component value is not "accounts" */
424 0 : ret = ERR_UNEXPECTED_ENTRY_TYPE;
425 0 : goto done;
426 : }
427 :
428 : /* Then the value of the RDN is the group name */
429 0 : rdn_val = ldb_dn_get_rdn_val(dn);
430 0 : *hostgroupname = talloc_strndup(mem_ctx,
431 0 : (const char *)rdn_val->data,
432 : rdn_val->length);
433 0 : if (*hostgroupname == NULL) {
434 0 : ret = ENOMEM;
435 0 : goto done;
436 : }
437 :
438 0 : ret = EOK;
439 :
440 : done:
441 0 : talloc_free(dn);
442 0 : return ret;
443 : }
|