a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher Stephen Gallagher <sgallagh@redhat.com>
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher Copyright (C) 2012 Red Hat
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher This program is free software; you can redistribute it and/or modify
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher it under the terms of the GNU General Public License as published by
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher the Free Software Foundation; either version 3 of the License, or
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher (at your option) any later version.
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher This program is distributed in the hope that it will be useful,
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher but WITHOUT ANY WARRANTY; without even the implied warranty of
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher GNU General Public License for more details.
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher You should have received a copy of the GNU General Public License
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher along with this program. If not, see <http://www.gnu.org/licenses/>.
a4cce2c98eedecb5d3b47da62104634cae268434Stephen Gallagher#include "src/providers/ldap/sdap_access.h"
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek * More advanced format can be used to restrict the filter to a specific
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek * domain or a specific forest. This format is KEYWORD:NAME:FILTER
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek * KEYWORD can be one of DOM or FOREST
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek * KEYWORD can be missing
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek * NAME is a label.
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek * - if KEYWORD equals DOM or missing completely, the filter is applied
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek * for users from domain named NAME only
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek * - if KEYWORD equals FOREST, the filter is applied on users from
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek * forest named NAME only
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek * examples of valid filters are:
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek * apply filter on domain called dom1 only:
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek * dom1:(memberOf=cn=admins,ou=groups,dc=dom1,dc=com)
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek * apply filter on domain called dom2 only:
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek * DOM:dom2:(memberOf=cn=admins,ou=groups,dc=dom2,dc=com)
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek * apply filter on forest called EXAMPLE.COM only:
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek * FOREST:EXAMPLE.COM:(memberOf=cn=admins,ou=groups,dc=example,dc=com)
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek * If any of the extended formats are used, the filter MUST be enclosed
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek/* From least specific */
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek/* parse filter in the format domain_name:filter */
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozekparse_sub_filter(TALLOC_CTX *mem_ctx, const char *full_filter,
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek /* Make sure the filter is already enclosed in brackets */
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek *sub_name = talloc_strndup(mem_ctx, full_filter, specdelim - full_filter);
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek if (*sub_name == NULL || *filter == NULL) return ENOMEM;
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozekparse_dom_filter(TALLOC_CTX *mem_ctx, const char *dom_filter,
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek return parse_sub_filter(mem_ctx, dom_filter, filter, domname,
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozekparse_forest_filter(TALLOC_CTX *mem_ctx, const char *forest_filter,
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek return parse_sub_filter(mem_ctx, forest_filter, filter, forest_name,
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozekparse_filter(TALLOC_CTX *mem_ctx, const char *full_filter,
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek if (filter == NULL || spec == NULL || flags == NULL) return EINVAL;
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek /* There is a single keyword. Treat it as a domain name */
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek return parse_dom_filter(mem_ctx, full_filter, filter, spec, flags);
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek } else if (strncmp(full_filter, "DOM", kwdelim-full_filter) == 0) {
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek /* The format must be DOM:domain_name:filter */
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek /* Check if there is some domain_name */
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek return parse_dom_filter(mem_ctx, kwdelim + 1, filter, spec, flags);
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek } else if (strncmp(full_filter, "FOREST", kwdelim-full_filter) == 0) {
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek /* The format must be FOREST:forest_name:filter */
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek /* Check if there is some domain_name */
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek return parse_forest_filter(mem_ctx, kwdelim + 1,
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek /* Malformed option */
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov "Keyword in filter [%s] did not match expected format\n",
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek /* No keyword. Easy. */
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek ret = split_on_separator(tmp_ctx, filter_list, '?', true, true,
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov "Cannot parse the list of ad_access_filters\n");
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley for (i=0; i < nfilters; i++) {
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek ret = parse_filter(tmp_ctx, filters[i], &filter, &spec, &flags);
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek /* Skip the faulty filter. At worst, the user won't be
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek * allowed access */
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov DEBUG(SSSDBG_MINOR_FAILURE, "Access filter [%s] could not be "
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek if (flags & AD_FILTER_DOMAIN && strcasecmp(spec, dom->name) != 0) {
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek /* If the filter specifies a domain, it must match the
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek * domain the user comes from
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek if (flags & AD_FILTER_FOREST && strcasecmp(spec, dom->forest) != 0) {
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek /* If the filter specifies a forest, it must match the
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek * forest the user comes from
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek /* Make sure the result is enclosed in brackets */
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek *_filter = sdap_get_access_filter(mem_ctx, best_match);
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elleyad_sdap_access_step(struct tevent_req *req, struct sdap_id_conn_ctx *conn);
67b1fc914190e12ab014c0616b7f0a642fbe6356Jakub Hrozekstatic struct tevent_req *
67b1fc914190e12ab014c0616b7f0a642fbe6356Jakub Hrozek req = tevent_req_create(mem_ctx, &state, struct ad_access_state);
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek ret = ad_parse_access_filter(state, domain, ctx->sdap_access_ctx->filter,
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov DEBUG(SSSDBG_CRIT_FAILURE, "Could not determine the best filter\n");
72ae534f5aef6d2e5d3f2f51299aede5abf9687eJakub Hrozek state->clist = ad_gc_conn_list(state, ctx->ad_id_ctx, domain);
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley ret = ad_sdap_access_step(req, state->clist[state->cindex]);
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elleyad_sdap_access_step(struct tevent_req *req, struct sdap_id_conn_ctx *conn)
67b1fc914190e12ab014c0616b7f0a642fbe6356Jakub Hrozek state = tevent_req_data(req, struct ad_access_state);
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek req_ctx = talloc(state, struct sdap_access_ctx);
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek req_ctx->id_ctx = state->ctx->sdap_access_ctx->id_ctx;
1ce58f139699dd26b8888f4131c996263b6a80a5Jakub Hrozek sizeof(int) * LDAP_ACCESS_LAST);
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley subreq = sdap_access_send(state, state->ev, state->be_ctx,
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley tevent_req_set_callback(subreq, ad_sdap_access_done, req);
67b1fc914190e12ab014c0616b7f0a642fbe6356Jakub Hrozek req = tevent_req_callback_data(subreq, struct tevent_req);
67b1fc914190e12ab014c0616b7f0a642fbe6356Jakub Hrozek state = tevent_req_data(req, struct ad_access_state);
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley /* Retry on ACCESS_DENIED, too, to make sure that we don't
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley * miss out any attributes not present in GC
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley * FIXME - this is slow. We should retry only if GC failed
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley * and LDAP succeeded after the first ACCESS_DENIED
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley /* If possible, retry with LDAP */
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley "Error retrieving access check result: %s\n",
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley ret = ad_sdap_access_step(req, state->clist[state->cindex]);
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley /* Another check in progress */
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley /* do not evaluate gpos; mark request done */
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley /* continue on to evaluate gpos */
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley tevent_req_set_callback(subreq, ad_gpo_access_done, req);
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley req = tevent_req_callback_data(subreq, struct tevent_req);
f929e9e5a6daa71a22176b08eb7983fb4b708180Lukas Slebodnik state = tevent_req_data(req, struct ad_access_state);
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley DEBUG(SSSDBG_TRACE_FUNC, "GPO-based access control successful.\n");
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley DEBUG(SSSDBG_OP_FAILURE, "GPO-based access control failed.\n");
f929e9e5a6daa71a22176b08eb7983fb4b708180Lukas Slebodnik "Ignoring error: [%d](%s); GPO-based access control failed, "
f929e9e5a6daa71a22176b08eb7983fb4b708180Lukas Slebodnik "but GPO is not in enforcing mode.\n",
f929e9e5a6daa71a22176b08eb7983fb4b708180Lukas Slebodnik sss_log_ext(SSS_LOG_WARNING, LOG_AUTHPRIV, "Warning: user would "
f929e9e5a6daa71a22176b08eb7983fb4b708180Lukas Slebodnik "have been denied GPO-based logon access if the "
f929e9e5a6daa71a22176b08eb7983fb4b708180Lukas Slebodnik "ad_gpo_access_control option were set to enforcing mode.");
dea636af4d1902a081ee891f1b19ee2f8729d759Pavel Březinastatic void ad_pam_access_handler_done(struct tevent_req *subreq);
dea636af4d1902a081ee891f1b19ee2f8729d759Pavel Březinaad_pam_access_handler_send(TALLOC_CTX *mem_ctx,
dea636af4d1902a081ee891f1b19ee2f8729d759Pavel Březina DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
dea636af4d1902a081ee891f1b19ee2f8729d759Pavel Březina subreq = ad_access_send(state, params->ev, params->be_ctx,
dea636af4d1902a081ee891f1b19ee2f8729d759Pavel Březina tevent_req_set_callback(subreq, ad_pam_access_handler_done, req);
dea636af4d1902a081ee891f1b19ee2f8729d759Pavel Březina /* TODO For backward compatibility we always return EOK to DP now. */
dea636af4d1902a081ee891f1b19ee2f8729d759Pavel Březinastatic void ad_pam_access_handler_done(struct tevent_req *subreq)
dea636af4d1902a081ee891f1b19ee2f8729d759Pavel Březina req = tevent_req_callback_data(subreq, struct tevent_req);
dea636af4d1902a081ee891f1b19ee2f8729d759Pavel Březina state = tevent_req_data(req, struct ad_pam_access_handler_state);
dea636af4d1902a081ee891f1b19ee2f8729d759Pavel Březina /* TODO For backward compatibility we always return EOK to DP now. */
dea636af4d1902a081ee891f1b19ee2f8729d759Pavel Březinaad_pam_access_handler_recv(TALLOC_CTX *mem_ctx,
dea636af4d1902a081ee891f1b19ee2f8729d759Pavel Březina struct ad_pam_access_handler_state *state = NULL;