ad_access.c revision a8356a0c98ee44e7256bb1c7767159c70e1fc218
/*
SSSD
Authors:
Stephen Gallagher <sgallagh@redhat.com>
Copyright (C) 2012 Red Hat
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <security/pam_modules.h>
#include <syslog.h>
#include "src/providers/data_provider.h"
#include "src/providers/dp_backend.h"
#include "src/providers/ad/ad_access.h"
#include "src/providers/ad/ad_common.h"
#include "src/providers/ldap/sdap_access.h"
/*
* More advanced format can be used to restrict the filter to a specific
* domain or a specific forest. This format is KEYWORD:NAME:FILTER
*
* KEYWORD can be one of DOM or FOREST
* KEYWORD can be missing
* NAME is a label.
* - if KEYWORD equals DOM or missing completely, the filter is applied
* for users from domain named NAME only
* - if KEYWORD equals FOREST, the filter is applied on users from
* forest named NAME only
* examples of valid filters are:
* apply filter on domain called dom1 only:
* dom1:(memberOf=cn=admins,ou=groups,dc=dom1,dc=com)
* apply filter on domain called dom2 only:
* DOM:dom2:(memberOf=cn=admins,ou=groups,dc=dom2,dc=com)
* apply filter on forest called EXAMPLE.COM only:
* FOREST:EXAMPLE.COM:(memberOf=cn=admins,ou=groups,dc=example,dc=com)
*
* If any of the extended formats are used, the filter MUST be enclosed
* already.
*/
/* From least specific */
#define AD_FILTER_GENERIC 0x01
#define AD_FILTER_FOREST 0x02
#define AD_FILTER_DOMAIN 0x04
#define KW_FOREST "FOREST"
#define KW_DOMAIN "DOM"
/* parse filter in the format domain_name:filter */
static errno_t
const int flagconst)
{
char *specdelim;
/* Make sure the filter is already enclosed in brackets */
return EOK;
}
static inline errno_t
{
}
static inline errno_t
{
}
static errno_t
{
/* There is a single keyword. Treat it as a domain name */
/* The format must be DOM:domain_name:filter */
/* Check if there is some domain_name */
return EINVAL;
}
/* The format must be FOREST:forest_name:filter */
/* Check if there is some domain_name */
return EINVAL;
}
}
/* Malformed option */
"Keyword in filter [%s] did not match expected format\n",
return EINVAL;
}
/* No keyword. Easy. */
return EOK;
}
static errno_t
struct sss_domain_info *dom,
const char *filter_list,
char **_filter)
{
char **filters;
int nfilters;
char *best_match;
int best_flags;
char *filter;
char *spec;
int flags;
int i = 0;
goto done;
}
if (filter_list == NULL) {
goto done;
}
"Cannot parse the list of ad_access_filters\n");
goto done;
}
best_match = NULL;
best_flags = 0;
for (i=0; i < nfilters; i++) {
/* Skip the faulty filter. At worst, the user won't be
* allowed access */
"parsed, skipping\n", filters[i]);
continue;
}
/* If the filter specifies a domain, it must match the
* domain the user comes from
*/
continue;
}
/* If the filter specifies a forest, it must match the
* forest the user comes from
*/
continue;
}
if (flags > best_flags) {
best_flags = flags;
best_match = filter;
}
}
/* Make sure the result is enclosed in brackets */
done:
return ret;
}
struct ad_access_state {
struct tevent_context *ev;
struct ad_access_ctx *ctx;
struct sss_domain_info *domain;
char *filter;
struct sdap_id_conn_ctx **clist;
int cindex;
};
static errno_t
static void
static struct tevent_req *
struct tevent_context *ev,
struct sss_domain_info *domain,
struct ad_access_ctx *ctx,
{
struct tevent_req *req;
struct ad_access_state *state;
return NULL;
}
goto done;
}
goto done;
}
goto done;
}
done:
}
return req;
}
static errno_t
{
struct tevent_req *subreq;
struct ad_access_state *state;
struct sdap_access_ctx *req_ctx;
return ENOMEM;
}
sizeof(int) * LDAP_ACCESS_LAST);
return ENOMEM;
}
return EOK;
}
static void
static void
{
struct tevent_req *req;
struct ad_access_state *state;
switch (ret) {
case ERR_ACCOUNT_EXPIRED:
return;
case ERR_ACCESS_DENIED:
/* Retry on ACCESS_DENIED, too, to make sure that we don't
* miss out any attributes not present in GC
* FIXME - this is slow. We should retry only if GC failed
* and LDAP succeeded after the first ACCESS_DENIED
*/
break;
default:
break;
}
/* If possible, retry with LDAP */
"Error retrieving access check result: %s\n",
sss_strerror(ret));
return;
}
return;
}
/* Another check in progress */
return;
}
/* do not evaluate gpos; mark request done */
return;
/* continue on to evaluate gpos */
break;
default:
return;
}
if (!subreq) {
return;
}
}
static void
{
struct tevent_req *req;
struct ad_access_state *state;
enum gpo_access_control_mode mode;
} else {
if (mode == GPO_ACCESS_CONTROL_ENFORCING) {
} else {
"Ignoring error: [%d](%s); GPO-based access control failed, "
"but GPO is not in enforcing mode.\n",
"have been denied GPO-based logon access if the "
"ad_gpo_access_control option were set to enforcing mode.");
}
}
}
static errno_t
{
return EOK;
}
static void
void
{
struct tevent_req *req;
struct ad_access_ctx *access_ctx =
struct ad_access_ctx);
struct sss_domain_info *domain;
/* Handle subdomains */
return;
}
} else {
}
/* Verify access control: locked accounts, ldap policies, GPOs, etc */
access_ctx, pd);
if (!req) {
return;
}
}
static void
{
switch (ret) {
case EOK:
return;
case ERR_ACCESS_DENIED:
/* We got the proper denial */
return;
case ERR_ACCOUNT_EXPIRED:
return;
default:
/* Something went wrong */
return;
}
}