LCOV - code coverage report
Current view: top level - providers/ad - ad_access.c (source / functions) Hit Total Coverage
Test: .coverage.total Lines: 60 202 29.7 %
Date: 2015-10-19 Functions: 5 12 41.7 %

          Line data    Source code
       1             : /*
       2             :     SSSD
       3             : 
       4             :     Authors:
       5             :         Stephen Gallagher <sgallagh@redhat.com>
       6             : 
       7             :     Copyright (C) 2012 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 <security/pam_modules.h>
      24             : #include <syslog.h>
      25             : 
      26             : #include "src/util/util.h"
      27             : #include "src/providers/data_provider.h"
      28             : #include "src/providers/dp_backend.h"
      29             : #include "src/providers/ad/ad_access.h"
      30             : #include "providers/ad/ad_gpo.h"
      31             : #include "src/providers/ad/ad_common.h"
      32             : #include "src/providers/ldap/sdap_access.h"
      33             : 
      34             : /*
      35             :  * More advanced format can be used to restrict the filter to a specific
      36             :  * domain or a specific forest. This format is KEYWORD:NAME:FILTER
      37             :  *
      38             :  *  KEYWORD can be one of DOM or FOREST
      39             :  *      KEYWORD can be missing
      40             :  *  NAME is a label.
      41             :  *      - if KEYWORD equals DOM or missing completely, the filter is applied
      42             :  *        for users from domain named NAME only
      43             :  *      - if KEYWORD equals FOREST, the filter is applied on users from
      44             :  *        forest named NAME only
      45             :  *  examples of valid filters are:
      46             :  *      apply filter on domain called dom1 only:
      47             :  *          dom1:(memberOf=cn=admins,ou=groups,dc=dom1,dc=com)
      48             :  *      apply filter on domain called dom2 only:
      49             :  *          DOM:dom2:(memberOf=cn=admins,ou=groups,dc=dom2,dc=com)
      50             :  *      apply filter on forest called EXAMPLE.COM only:
      51             :  *          FOREST:EXAMPLE.COM:(memberOf=cn=admins,ou=groups,dc=example,dc=com)
      52             :  *
      53             :  * If any of the extended formats are used, the filter MUST be enclosed
      54             :  * already.
      55             :  */
      56             : 
      57             : /* From least specific */
      58             : #define AD_FILTER_GENERIC 0x01
      59             : #define AD_FILTER_FOREST  0x02
      60             : #define AD_FILTER_DOMAIN  0x04
      61             : 
      62             : #define KW_FOREST "FOREST"
      63             : #define KW_DOMAIN "DOM"
      64             : 
      65             : /* parse filter in the format domain_name:filter */
      66             : static errno_t
      67          16 : parse_sub_filter(TALLOC_CTX *mem_ctx, const char *full_filter,
      68             :                  char **filter, char **sub_name, int *flags,
      69             :                  const int flagconst)
      70             : {
      71             :     char *specdelim;
      72             : 
      73          16 :     specdelim = strchr(full_filter, ':');
      74          16 :     if (specdelim == NULL) return EINVAL;
      75             : 
      76             :     /* Make sure the filter is already enclosed in brackets */
      77          16 :     if (*(specdelim+1) != '(') return EINVAL;
      78             : 
      79          13 :     *sub_name = talloc_strndup(mem_ctx, full_filter, specdelim - full_filter);
      80          13 :     *filter = talloc_strdup(mem_ctx, specdelim+1);
      81          13 :     if (*sub_name == NULL || *filter == NULL) return ENOMEM;
      82             : 
      83          13 :     *flags = flagconst;
      84          13 :     return EOK;
      85             : }
      86             : 
      87             : static inline errno_t
      88          15 : parse_dom_filter(TALLOC_CTX *mem_ctx, const char *dom_filter,
      89             :                  char **filter, char **domname, int *flags)
      90             : {
      91          15 :     return parse_sub_filter(mem_ctx, dom_filter, filter, domname,
      92             :                             flags, AD_FILTER_DOMAIN);
      93             : }
      94             : 
      95             : static inline errno_t
      96           1 : parse_forest_filter(TALLOC_CTX *mem_ctx, const char *forest_filter,
      97             :                     char **filter, char **forest_name, int *flags)
      98             : {
      99           1 :     return parse_sub_filter(mem_ctx, forest_filter, filter, forest_name,
     100             :                             flags, AD_FILTER_FOREST);
     101             : }
     102             : 
     103             : 
     104             : static errno_t
     105          28 : parse_filter(TALLOC_CTX *mem_ctx, const char *full_filter,
     106             :              char **filter, char **spec, int *flags)
     107             : {
     108             :     char *kwdelim, *specdelim;
     109             : 
     110          28 :     if (filter == NULL || spec == NULL || flags == NULL) return EINVAL;
     111             : 
     112          28 :     kwdelim = strchr(full_filter, ':');
     113          28 :     if (kwdelim != NULL) {
     114          19 :         specdelim = strchr(kwdelim+1, ':');
     115             : 
     116          19 :         if (specdelim == NULL) {
     117             :             /* There is a single keyword. Treat it as a domain name */
     118          11 :             return parse_dom_filter(mem_ctx, full_filter, filter, spec, flags);
     119           8 :         } else if (strncmp(full_filter, "DOM", kwdelim-full_filter) == 0) {
     120             :             /* The format must be DOM:domain_name:filter */
     121           6 :             if (specdelim && specdelim-kwdelim <= 1) {
     122             :                 /* Check if there is some domain_name */
     123           2 :                 return EINVAL;
     124             :             }
     125             : 
     126           4 :             return parse_dom_filter(mem_ctx, kwdelim + 1, filter, spec, flags);
     127           2 :         } else if (strncmp(full_filter, "FOREST", kwdelim-full_filter) == 0) {
     128             :             /* The format must be FOREST:forest_name:filter */
     129           1 :             if (specdelim && specdelim-kwdelim <= 1) {
     130             :                 /* Check if there is some domain_name */
     131           0 :                 return EINVAL;
     132             :             }
     133             : 
     134           1 :             return parse_forest_filter(mem_ctx, kwdelim + 1,
     135             :                                        filter, spec, flags);
     136             :         }
     137             : 
     138             :         /* Malformed option */
     139           1 :         DEBUG(SSSDBG_CRIT_FAILURE,
     140             :               "Keyword in filter [%s] did not match expected format\n",
     141             :                full_filter);
     142           1 :         return EINVAL;
     143             :     }
     144             : 
     145             :     /* No keyword. Easy. */
     146           9 :     *filter = talloc_strdup(mem_ctx, full_filter);
     147           9 :     if (*filter == NULL) return ENOMEM;
     148             : 
     149           9 :     *spec = NULL;
     150           9 :     *flags = AD_FILTER_GENERIC;
     151           9 :     return EOK;
     152             : }
     153             : 
     154             : static errno_t
     155          11 : ad_parse_access_filter(TALLOC_CTX *mem_ctx,
     156             :                        struct sss_domain_info *dom,
     157             :                        const char *filter_list,
     158             :                        char **_filter)
     159             : {
     160             :     char **filters;
     161             :     int nfilters;
     162             :     errno_t ret;
     163             :     char *best_match;
     164             :     int best_flags;
     165             :     char *filter;
     166             :     char *spec;
     167             :     int flags;
     168             :     TALLOC_CTX *tmp_ctx;
     169          11 :     int i = 0;
     170             : 
     171          11 :     if (_filter == NULL) return EINVAL;
     172             : 
     173          11 :     tmp_ctx = talloc_new(mem_ctx);
     174          11 :     if (tmp_ctx == NULL) {
     175           0 :         ret = ENOMEM;
     176           0 :         goto done;
     177             :     }
     178             : 
     179          11 :     if (filter_list == NULL) {
     180           1 :         *_filter = NULL;
     181           1 :         ret = EOK;
     182           1 :         goto done;
     183             :     }
     184             : 
     185          10 :     ret = split_on_separator(tmp_ctx, filter_list, '?', true, true,
     186             :                              &filters, &nfilters);
     187          10 :     if (ret != EOK) {
     188           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     189             :               "Cannot parse the list of ad_access_filters\n");
     190           0 :         goto done;
     191             :     }
     192             : 
     193          10 :     best_match = NULL;
     194          10 :     best_flags = 0;
     195          27 :     for (i=0; i < nfilters; i++) {
     196          17 :         ret = parse_filter(tmp_ctx, filters[i], &filter, &spec, &flags);
     197          17 :         if (ret != EOK) {
     198             :             /* Skip the faulty filter. At worst, the user won't be
     199             :              * allowed access */
     200           0 :             DEBUG(SSSDBG_MINOR_FAILURE, "Access filter [%s] could not be "
     201             :                   "parsed, skipping\n", filters[i]);
     202           0 :             continue;
     203             :         }
     204             : 
     205          17 :         if (flags & AD_FILTER_DOMAIN && strcasecmp(spec, dom->name) != 0) {
     206             :             /* If the filter specifies a domain, it must match the
     207             :              * domain the user comes from
     208             :              */
     209           4 :             continue;
     210             :         }
     211             : 
     212          13 :         if (flags & AD_FILTER_FOREST && strcasecmp(spec, dom->forest) != 0) {
     213             :             /* If the filter specifies a forest, it must match the
     214             :              * forest the user comes from
     215             :              */
     216           0 :             continue;
     217             :         }
     218             : 
     219          13 :         if (flags > best_flags) {
     220          10 :             best_flags = flags;
     221          10 :             best_match = filter;
     222             :         }
     223             :     }
     224             : 
     225          10 :     ret = EOK;
     226             :     /* Make sure the result is enclosed in brackets */
     227          10 :     *_filter = sdap_get_access_filter(mem_ctx, best_match);
     228             : done:
     229          11 :     talloc_free(tmp_ctx);
     230          11 :     return ret;
     231             : }
     232             : 
     233             : struct ad_access_state {
     234             :     struct tevent_context *ev;
     235             :     struct ad_access_ctx *ctx;
     236             :     struct pam_data *pd;
     237             :     struct be_ctx *be_ctx;
     238             :     struct sss_domain_info *domain;
     239             : 
     240             :     char *filter;
     241             :     struct sdap_id_conn_ctx **clist;
     242             :     int cindex;
     243             : };
     244             : 
     245             : static errno_t
     246             : ad_sdap_access_step(struct tevent_req *req, struct sdap_id_conn_ctx *conn);
     247             : static void
     248             : ad_sdap_access_done(struct tevent_req *req);
     249             : 
     250             : static struct tevent_req *
     251           0 : ad_access_send(TALLOC_CTX *mem_ctx,
     252             :                struct tevent_context *ev,
     253             :                struct be_ctx *be_ctx,
     254             :                struct sss_domain_info *domain,
     255             :                struct ad_access_ctx *ctx,
     256             :                struct pam_data *pd)
     257             : {
     258             :     struct tevent_req *req;
     259             :     struct ad_access_state *state;
     260             :     errno_t ret;
     261             : 
     262           0 :     req = tevent_req_create(mem_ctx, &state, struct ad_access_state);
     263           0 :     if (req == NULL) {
     264           0 :         return NULL;
     265             :     }
     266             : 
     267           0 :     state->ev = ev;
     268           0 :     state->ctx = ctx;
     269           0 :     state->pd = pd;
     270           0 :     state->be_ctx = be_ctx;
     271           0 :     state->domain = domain;
     272             : 
     273           0 :     ret = ad_parse_access_filter(state, domain, ctx->sdap_access_ctx->filter,
     274           0 :                                  &state->filter);
     275           0 :     if (ret != EOK) {
     276           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Could not determine the best filter\n");
     277           0 :         ret = ERR_ACCESS_DENIED;
     278           0 :         goto done;
     279             :     }
     280             : 
     281           0 :     state->clist = ad_gc_conn_list(state, ctx->ad_id_ctx, domain);
     282           0 :     if (state->clist == NULL) {
     283           0 :         ret = ENOMEM;
     284           0 :         goto done;
     285             :     }
     286             : 
     287           0 :     ret = ad_sdap_access_step(req, state->clist[state->cindex]);
     288           0 :     if (ret != EOK) {
     289           0 :         goto done;
     290             :     }
     291             : 
     292           0 :     ret = EOK;
     293             : done:
     294           0 :     if (ret != EOK) {
     295           0 :         tevent_req_error(req, ret);
     296             : 
     297           0 :         tevent_req_post(req, ev);
     298             :     }
     299           0 :     return req;
     300             : }
     301             : 
     302             : static errno_t
     303           0 : ad_sdap_access_step(struct tevent_req *req, struct sdap_id_conn_ctx *conn)
     304             : {
     305             :     struct tevent_req *subreq;
     306             :     struct ad_access_state *state;
     307             :     struct sdap_access_ctx *req_ctx;
     308             : 
     309           0 :     state = tevent_req_data(req, struct ad_access_state);
     310             : 
     311           0 :     req_ctx = talloc(state, struct sdap_access_ctx);
     312           0 :     if (req_ctx == NULL) {
     313           0 :         return ENOMEM;
     314             :     }
     315           0 :     req_ctx->id_ctx = state->ctx->sdap_access_ctx->id_ctx;
     316           0 :     req_ctx->filter = state->filter;
     317           0 :     memcpy(&req_ctx->access_rule,
     318           0 :            state->ctx->sdap_access_ctx->access_rule,
     319             :            sizeof(int) * LDAP_ACCESS_LAST);
     320             : 
     321           0 :     subreq = sdap_access_send(state, state->ev, state->be_ctx,
     322             :                               state->domain, req_ctx,
     323             :                               conn, state->pd);
     324           0 :     if (req == NULL) {
     325           0 :         talloc_free(req_ctx);
     326           0 :         return ENOMEM;
     327             :     }
     328           0 :     tevent_req_set_callback(subreq, ad_sdap_access_done, req);
     329           0 :     return EOK;
     330             : }
     331             : 
     332             : static void
     333             : ad_gpo_access_done(struct tevent_req *subreq);
     334             : 
     335             : static void
     336           0 : ad_sdap_access_done(struct tevent_req *subreq)
     337             : {
     338             :     struct tevent_req *req;
     339             :     struct ad_access_state *state;
     340             :     errno_t ret;
     341             : 
     342           0 :     req = tevent_req_callback_data(subreq, struct tevent_req);
     343           0 :     state = tevent_req_data(req, struct ad_access_state);
     344             : 
     345           0 :     ret = sdap_access_recv(subreq);
     346           0 :     talloc_zfree(subreq);
     347             : 
     348           0 :     if (ret != EOK) {
     349           0 :         switch (ret) {
     350             :         case ERR_ACCOUNT_EXPIRED:
     351           0 :             tevent_req_error(req, ret);
     352           0 :             return;
     353             : 
     354             :         case ERR_ACCESS_DENIED:
     355             :             /* Retry on ACCESS_DENIED, too, to make sure that we don't
     356             :              * miss out any attributes not present in GC
     357             :              * FIXME - this is slow. We should retry only if GC failed
     358             :              * and LDAP succeeded after the first ACCESS_DENIED
     359             :              */
     360           0 :             break;
     361             : 
     362             :         default:
     363           0 :             break;
     364             :         }
     365             : 
     366             :         /* If possible, retry with LDAP */
     367           0 :         state->cindex++;
     368           0 :         if (state->clist[state->cindex] == NULL) {
     369           0 :             DEBUG(SSSDBG_OP_FAILURE,
     370             :                   "Error retrieving access check result: %s\n",
     371             :                   sss_strerror(ret));
     372           0 :             tevent_req_error(req, ret);
     373           0 :             return;
     374             :         }
     375             : 
     376           0 :         ret = ad_sdap_access_step(req, state->clist[state->cindex]);
     377           0 :         if (ret != EOK) {
     378           0 :             tevent_req_error(req, ret);
     379           0 :             return;
     380             :         }
     381             : 
     382             :         /* Another check in progress */
     383             : 
     384           0 :         return;
     385             :     }
     386             : 
     387           0 :     switch (state->ctx->gpo_access_control_mode) {
     388             :     case GPO_ACCESS_CONTROL_DISABLED:
     389             :         /* do not evaluate gpos; mark request done */
     390           0 :         tevent_req_done(req);
     391           0 :         return;
     392             :     case GPO_ACCESS_CONTROL_PERMISSIVE:
     393             :     case GPO_ACCESS_CONTROL_ENFORCING:
     394             :         /* continue on to evaluate gpos */
     395           0 :         break;
     396             :     default:
     397           0 :         tevent_req_error(req, EINVAL);
     398           0 :         return;
     399             :     }
     400             : 
     401           0 :     subreq = ad_gpo_access_send(state,
     402           0 :                                 state->be_ctx->ev,
     403             :                                 state->domain,
     404             :                                 state->ctx,
     405           0 :                                 state->pd->user,
     406           0 :                                 state->pd->service);
     407             : 
     408           0 :     if (!subreq) {
     409           0 :         tevent_req_error(req, ENOMEM);
     410           0 :         return;
     411             :     }
     412             : 
     413           0 :     tevent_req_set_callback(subreq, ad_gpo_access_done, req);
     414             : 
     415             : }
     416             : 
     417             : static void
     418           0 : ad_gpo_access_done(struct tevent_req *subreq)
     419             : {
     420             :     struct tevent_req *req;
     421             :     struct ad_access_state *state;
     422             :     errno_t ret;
     423             :     enum gpo_access_control_mode mode;
     424             : 
     425           0 :     req = tevent_req_callback_data(subreq, struct tevent_req);
     426           0 :     state = tevent_req_data(req, struct ad_access_state);
     427           0 :     mode = state->ctx->gpo_access_control_mode;
     428             : 
     429           0 :     ret = ad_gpo_access_recv(subreq);
     430           0 :     talloc_zfree(subreq);
     431             : 
     432           0 :     if (ret == EOK) {
     433           0 :         DEBUG(SSSDBG_TRACE_FUNC, "GPO-based access control successful.\n");
     434           0 :         tevent_req_done(req);
     435             :     } else {
     436           0 :         DEBUG(SSSDBG_OP_FAILURE, "GPO-based access control failed.\n");
     437           0 :         if (mode == GPO_ACCESS_CONTROL_ENFORCING) {
     438           0 :             tevent_req_error(req, ret);
     439             :         } else {
     440           0 :             DEBUG(SSSDBG_OP_FAILURE,
     441             :                   "Ignoring error: [%d](%s); GPO-based access control failed, "
     442             :                   "but GPO is not in enforcing mode.\n",
     443             :                   ret, sss_strerror(ret));
     444           0 :             sss_log_ext(SSS_LOG_WARNING, LOG_AUTHPRIV, "Warning: user would "
     445             :                   "have been denied GPO-based logon access if the "
     446             :                   "ad_gpo_access_control option were set to enforcing mode.");
     447           0 :             tevent_req_done(req);
     448             :         }
     449             :     }
     450           0 : }
     451             : 
     452             : static errno_t
     453           0 : ad_access_recv(struct tevent_req *req)
     454             : {
     455           0 :     TEVENT_REQ_RETURN_ON_ERROR(req);
     456             : 
     457           0 :     return EOK;
     458             : }
     459             : 
     460             : static void
     461             : ad_access_done(struct tevent_req *req);
     462             : 
     463             : void
     464           0 : ad_access_handler(struct be_req *breq)
     465             : {
     466             :     struct tevent_req *req;
     467           0 :     struct be_ctx *be_ctx = be_req_get_be_ctx(breq);
     468           0 :     struct ad_access_ctx *access_ctx =
     469           0 :             talloc_get_type(be_ctx->bet_info[BET_ACCESS].pvt_bet_data,
     470             :                             struct ad_access_ctx);
     471           0 :     struct pam_data *pd =
     472           0 :                     talloc_get_type(be_req_get_data(breq), struct pam_data);
     473             :     struct sss_domain_info *domain;
     474             : 
     475             :     /* Handle subdomains */
     476           0 :     if (strcasecmp(pd->domain, be_ctx->domain->name) != 0) {
     477           0 :         domain = find_domain_by_name(be_ctx->domain, pd->domain, true);
     478           0 :         if (domain == NULL) {
     479           0 :             DEBUG(SSSDBG_OP_FAILURE, "find_domain_by_name failed.\n");
     480           0 :             be_req_terminate(breq, DP_ERR_FATAL, PAM_SYSTEM_ERR, NULL);
     481           0 :             return;
     482             :         }
     483             :     } else {
     484           0 :         domain = be_ctx->domain;
     485             :     }
     486             : 
     487             :     /* Verify access control: locked accounts, ldap policies, GPOs, etc */
     488           0 :     req = ad_access_send(breq, be_ctx->ev, be_ctx, domain,
     489             :                          access_ctx, pd);
     490           0 :     if (!req) {
     491           0 :         be_req_terminate(breq, DP_ERR_FATAL, PAM_SYSTEM_ERR, NULL);
     492           0 :         return;
     493             :     }
     494           0 :     tevent_req_set_callback(req, ad_access_done, breq);
     495             : }
     496             : 
     497             : static void
     498           0 : ad_access_done(struct tevent_req *req)
     499             : {
     500             :     errno_t ret;
     501           0 :     struct be_req *breq =
     502           0 :             tevent_req_callback_data(req, struct be_req);
     503           0 :     struct pam_data *pd =
     504           0 :                     talloc_get_type(be_req_get_data(breq), struct pam_data);
     505             : 
     506           0 :     ret = ad_access_recv(req);
     507           0 :     talloc_zfree(req);
     508           0 :     switch (ret) {
     509             :     case EOK:
     510           0 :         pd->pam_status = PAM_SUCCESS;
     511           0 :         be_req_terminate(breq, DP_ERR_OK, PAM_SUCCESS, NULL);
     512           0 :         return;
     513             :     case ERR_ACCESS_DENIED:
     514             :         /* We got the proper denial */
     515           0 :         pd->pam_status = PAM_PERM_DENIED;
     516           0 :         be_req_terminate(breq, DP_ERR_OK, PAM_PERM_DENIED, NULL);
     517           0 :         return;
     518             :     case ERR_ACCOUNT_EXPIRED:
     519           0 :         pd->pam_status = PAM_ACCT_EXPIRED;
     520           0 :         be_req_terminate(breq, DP_ERR_OK, PAM_ACCT_EXPIRED, NULL);
     521           0 :         return;
     522             :     default:
     523             :         /* Something went wrong */
     524           0 :         pd->pam_status = PAM_SYSTEM_ERR;
     525           0 :         be_req_terminate(breq, DP_ERR_FATAL,
     526             :                          PAM_SYSTEM_ERR, sss_strerror(ret));
     527           0 :         return;
     528             :     }
     529             : }

Generated by: LCOV version 1.10