Imported Upstream version 4.6.2

This commit is contained in:
Mario Fetka
2021-07-25 07:32:41 +02:00
commit 8ff3be4216
1788 changed files with 1900965 additions and 0 deletions

108
daemons/ipa-kdb/Makefile.am Normal file
View File

@@ -0,0 +1,108 @@
AUTOMAKE_OPTIONS = 1.7 subdir-objects
NULL =
AM_CPPFLAGS = \
-I$(srcdir) \
-I$(top_srcdir)/util \
-DPREFIX=\""$(prefix)"\" \
-DBINDIR=\""$(bindir)"\" \
-DLIBDIR=\""$(libdir)"\" \
-DLIBEXECDIR=\""$(libexecdir)"\" \
-DDATADIR=\""$(datadir)"\" \
-DLDAPIDIR=\""$(localstatedir)/run"\" \
$(AM_CFLAGS) \
$(LDAP_CFLAGS) \
$(KRB5_CFLAGS) \
$(WARN_CFLAGS) \
$(NDRPAC_CFLAGS) \
$(NSS_CFLAGS) \
$(SSSCERTMAP_CFLAGS) \
$(NULL)
plugindir = $(libdir)/krb5/plugins/kdb
plugin_LTLIBRARIES = \
ipadb.la \
$(NULL)
ipadb_la_SOURCES = \
ipa_kdb.c \
ipa_kdb.h \
ipa_kdb_common.c \
ipa_kdb_mkey.c \
ipa_kdb_passwords.c \
ipa_kdb_principals.c \
ipa_kdb_pwdpolicy.c \
ipa_kdb_mspac.c \
ipa_kdb_mspac_private.h \
ipa_kdb_delegation.c \
ipa_kdb_audit_as.c \
$(NULL)
dist_noinst_DATA = ipa_kdb.exports
if BUILD_IPA_CERTAUTH_PLUGIN
ipadb_la_SOURCES += ipa_kdb_certauth.c
endif
ipadb_la_LDFLAGS = \
-avoid-version \
-module \
-Wl,--version-script,$(srcdir)/ipa_kdb.exports
ipadb_la_LIBADD = \
$(KRB5_LIBS) \
$(LDAP_LIBS) \
$(NDRPAC_LIBS) \
$(UNISTRING_LIBS) \
$(NSS_LIBS) \
$(SSSCERTMAP_LIBS) \
$(top_builddir)/util/libutil.la \
$(NULL)
if HAVE_CMOCKA
TESTS_ENVIRONMENT = . $(srcdir)/tests/test_setup.sh;
dist_ipa_kdb_tests_SOURCES = tests/test_setup.sh
TESTS = ipa_kdb_tests
check_PROGRAMS = ipa_kdb_tests
endif
ipa_kdb_tests_SOURCES = \
tests/ipa_kdb_tests.c \
ipa_kdb.c \
ipa_kdb_common.c \
ipa_kdb_mkey.c \
ipa_kdb_passwords.c \
ipa_kdb_principals.c \
ipa_kdb_pwdpolicy.c \
ipa_kdb_mspac.c \
ipa_kdb_delegation.c \
ipa_kdb_audit_as.c \
$(NULL)
if BUILD_IPA_CERTAUTH_PLUGIN
ipa_kdb_tests_SOURCES += ipa_kdb_certauth.c
endif
ipa_kdb_tests_CFLAGS = $(CMOCKA_CFLAGS)
ipa_kdb_tests_LDADD = \
$(CMOCKA_LIBS) \
$(KRB5_LIBS) \
$(LDAP_LIBS) \
$(NDRPAC_LIBS) \
$(UNISTRING_LIBS) \
$(NSS_LIBS) \
$(SSSCERTMAP_LIBS) \
$(top_builddir)/util/libutil.la \
-lkdb5 \
-lsss_idmap \
$(NULL)
clean-local:
rm -f tests/.dirstamp
EXTRA_DIST = \
README \
README.s4u2proxy.txt \
$(NULL)

1427
daemons/ipa-kdb/Makefile.in Normal file

File diff suppressed because it is too large Load Diff

1
daemons/ipa-kdb/README Normal file
View File

@@ -0,0 +1 @@
This is the ipa krb5kdc database backend.

View File

@@ -0,0 +1,136 @@
It is now possible to allow constrained delegation of credentials so
that a service can impersonate a user when communicating with another
service w/o requiring the user to actually forward their TGT.
This makes for a much better method of delegating credentials as it
prevents exposure of the short term secret of the user.
I added a relatively simple access control method that allow the KDC to
decide exactly which services are allowed to impersonate which users
against other services. A simple grouping mechanism is used so that in
large environments, clusters and otherwise classes of services can be
much more easily managed.
The grouping mechanism has been built so that lookup is highly optimized
and is basically reduced to a single search that uses the derefernce
control. Speed is very important in this case because KDC operations
time out very quickly and unless we add a caching layer in ipa-kdb we
must keep the number of searches down to avoid client timeouts.
The grouping mechanism is very simple a groupOfPrincipals object is
introduced, this Auxiliary class have a single optional attribute called
memberPrincipal which is a string containing a principal name.
A separate objectclass is also introduced called ipaKrb5DelegationACL,
it is a subclass of groupOfPrincipals and is a Structural class.
It has 2 additional optional attributes: ipaAllowedTarget and
ipaAllowToImpersonate. They are both DNs.
The memberPrincipal attribute in this class contains the list of
principals that are being considered proxies[1]. That is: the
principals of the services that want to impersonate client principals
against other services.
The ipaAllowToImpersonate must point to a groupOfPrincipal based
object that contains the list of client principals (normally these are
user principals) that can be impersonated by this service.
If the attribute is missing than the service is allowed to impersonate
*any* user.
The ipaAllowedTarget DN must point to a groupOfPrincipal based object
that contains the list of service principals that the proxy service is
allowed target when impersonating users. A target must be specified in
order to allow a service to access it impersonating another principal.
At the moment no wildcarding is implemented so services have to be
explicitly listed in their respective groups.
I have some idea of adding wildcard support at least for the
ipaAllowToImpersonate group in order to separate user principals by
REALM. So you can say all users of REALM1 can be impersonated by this
service but no users of REALM2.
It is unclear how this wildcarding may be implemented, but it must be
simple to avoid potentially very expensive computations every time a
ticket for the target services is requested.
I have briefly tested this patch by manually creating a few objects then
using the kvno command to test that I could get a ldap ticket just using
the HTTP credentials (in order to do this I had to allow also s4u2self
operations for the HTTP service, but this is *not* generally required
and it is *not* desired in the IPA framework implementation).
This patchset does not contain any CLI or UI nor installation changes to
create ipaKrb5DelegationACL obujects. It is indeed yet unclear where we
want to store them (suggestions are welcome) and how/when we may want to
expose this mechanism through UI/CLI for general usage.
The initial intended usage is to allow us to move away from using
forwarded TGTs in the IPA framework and instead use S4U2Proxy in order
to access the ldap service. In order to do this some changes will need
to be made in installation scripts and replica management scripts later.
How to test:
Create 2 objects like these:
dn: cn=ipa-http-delegation,...
objectClass: ipaKrb5DelegationACL
objectClass: groupOfPrincipals
cn: ipa-http-delegation
memberPrincipal: HTTP/ipaserver.example.com@EXAMPLE.COM
ipaAllowedTarget: cn=ipa-ldap-delegation-targets,...
dn: cn=ipa-ldap-delegation-targets,...
objectClass: groupOfPrincipals
cn: ipa-ldap-delegation-targets
memberPrincipal: ldap/ipaserver.example.com@EXAMPLE.COM
In order to test with kvno which pretend to do s4u2self too you will
need to allow the HTTP service to impersonate arbitrary users.
This is done with:
kdamin.local
modprinc +ok_to_auth_as_delegate HTTP/ipaserver.example.com
NOTE: Do not grant +ok_to_auth_as_delegate in production without
carefully considering the outcome. This flags grants a service the
ability to impersonate any user to itself, which, combined with the
permission to proxy, means it will be allowed to impersonate any user
to the target service w/o any explicit user permission/delegation.
This flag is *NOT* necessary to permit proxying, it is used in this
example only because the kvno utility is hardwired to test both s4u2self
and s4u2proxy at the same time and would fail to operate without it.
Then run kvno as follows:
# Init credntials as HTTP
kinit -kt /etc/httpd/conf/ipa.keytab HTTP/ipaserver.example.com
# Perform S4U2Self
kvno -U admin HTTP/ipaserver.example.com
# Perform S4U2Proxy
kvno -k /etc/httpd/conf/ipa.keytab -U admin -P HTTP/ipaserver.example.com
ldap/ipaserver.example.com
If this works it means you successfully impersonated the admin user with
the HTTP service against the ldap service.
Cleanup by removing the self-impersonation flag:
modprinc -ok_to_auth_as_delegate HTTP/ipaserver.example.com
Simo.
[1]
Note that here I use the term proxy in a different way than it is used in
the krb interfaces. It may seem a bit confusing but I think people will
understand it better this way.
In this document 'client' connects to 'proxy' which impersonates 'client'
against 'service'.
In the Code/API the 'client' connects to 'server' which impersonates
'client' against 'proxy'.

748
daemons/ipa-kdb/ipa_kdb.c Normal file
View File

@@ -0,0 +1,748 @@
/*
* MIT Kerberos KDC database backend for FreeIPA
*
* Authors: Simo Sorce <ssorce@redhat.com>
*
* Copyright (C) 2011 Simo Sorce, Red Hat
* see file 'COPYING' for use and warranty information
*
* This program is free software you can redistribute it and/or modify
* 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 <talloc.h>
#include <sys/utsname.h>
#include "ipa_kdb.h"
#define IPADB_GLOBAL_CONFIG_CACHE_TIME 60
struct ipadb_context *ipadb_get_context(krb5_context kcontext)
{
void *db_ctx;
krb5_error_code kerr;
kerr = krb5_db_get_context(kcontext, &db_ctx);
if (kerr != 0) {
return NULL;
}
return (struct ipadb_context *)db_ctx;
}
static void ipadb_context_free(krb5_context kcontext,
struct ipadb_context **ctx)
{
struct ipadb_global_config *cfg;
size_t c;
if (*ctx != NULL) {
free((*ctx)->uri);
free((*ctx)->base);
free((*ctx)->realm_base);
free((*ctx)->accounts_base);
free((*ctx)->kdc_hostname);
/* ldap free lcontext */
if ((*ctx)->lcontext) {
ldap_unbind_ext_s((*ctx)->lcontext, NULL, NULL);
}
free((*ctx)->supp_encs);
free((*ctx)->def_encs);
ipadb_mspac_struct_free(&(*ctx)->mspac);
krb5_free_default_realm(kcontext, (*ctx)->realm);
cfg = &(*ctx)->config;
for (c = 0; cfg->authz_data && cfg->authz_data[c]; c++) {
free(cfg->authz_data[c]);
}
free(cfg->authz_data);
#ifdef HAVE_KRB5_CERTAUTH_PLUGIN
ipa_certauth_free_moddata(&((*ctx)->certauth_moddata));
#endif
free(*ctx);
*ctx = NULL;
}
}
#define LDAPI_URI_PREFIX "ldapi://"
#define LDAPI_PATH_PREFIX "%2fslapd-"
#define SOCKET_SUFFIX ".socket"
#define APPEND_PATH_PART(pos, part) \
do { \
int partlen = strlen(part); \
strncpy(pos, part, partlen + 1); \
p += partlen; \
} while (0)
static char *ipadb_realm_to_ldapi_uri(char *realm)
{
char *uri = NULL;
char *p;
const char *q;
int len;
/* uri length, assume worst case for LDAPIDIR */
len = strlen(LDAPI_URI_PREFIX) + strlen(LDAPIDIR) * 3
+ strlen(LDAPI_PATH_PREFIX) + strlen(realm)
+ strlen(SOCKET_SUFFIX) + 1;
/* worst case they are all '/' to escape */
uri = malloc(len);
if (!uri) {
return NULL;
}
p = uri;
APPEND_PATH_PART(p, LDAPI_URI_PREFIX);
/* copy path and escape '/' to '%2f' */
for (q = LDAPIDIR; *q; q++) {
if (*q == '/') {
strncpy(p, "%2f", 3);
p += 3;
} else {
*p = *q;
p++;
}
}
APPEND_PATH_PART(p, LDAPI_PATH_PREFIX);
/* copy realm and convert '.' to '-' */
for (q = realm; *q; q++) {
if (*q == '.') {
*p = '-';
} else {
*p = *q;
}
p++;
}
/* terminate string */
APPEND_PATH_PART(p, SOCKET_SUFFIX);
return uri;
}
/* in IPA the base is always derived from the realm name */
static char *ipadb_get_base_from_realm(krb5_context kcontext)
{
krb5_error_code kerr;
char *realm = NULL;
char *base = NULL;
char *tmp;
size_t bi, ri;
size_t len;
kerr = krb5_get_default_realm(kcontext, &realm);
if (kerr != 0) {
return NULL;
}
bi = 3;
len = strlen(realm) + 3 + 1;
base = malloc(len);
if (!base) {
goto done;
}
strcpy(base, "dc=");
/* convert EXAMPLE.COM in dc=example,dc=com */
for (ri = 0; realm[ri]; ri++) {
if (realm[ri] == '.') {
len += 4;
tmp = realloc(base, len);
if (!tmp) {
free(base);
base = NULL;
goto done;
}
base = tmp;
strcpy(&base[bi], ",dc=");
bi += 4;
} else {
base[bi] = tolower(realm[ri]);
bi++;
}
}
base[bi] = '\0';
done:
krb5_free_default_realm(kcontext, realm);
return base;
}
static const struct {
const char *name;
enum ipadb_user_auth flag;
} userauth_table[] = {
{ "disabled", IPADB_USER_AUTH_DISABLED },
{ "password", IPADB_USER_AUTH_PASSWORD },
{ "radius", IPADB_USER_AUTH_RADIUS },
{ "otp", IPADB_USER_AUTH_OTP },
{ }
};
void ipadb_parse_user_auth(LDAP *lcontext, LDAPMessage *le,
enum ipadb_user_auth *userauth)
{
struct berval **vals;
int i, j;
*userauth = IPADB_USER_AUTH_NONE;
vals = ldap_get_values_len(lcontext, le, IPA_USER_AUTH_TYPE);
if (!vals)
return;
for (i = 0; vals[i]; i++) {
for (j = 0; userauth_table[j].name; j++) {
if (strcasecmp(vals[i]->bv_val, userauth_table[j].name) == 0) {
*userauth |= userauth_table[j].flag;
break;
}
}
}
ldap_value_free_len(vals);
}
static int ipadb_load_global_config(struct ipadb_context *ipactx)
{
char *attrs[] = { "ipaConfigString", IPA_KRB_AUTHZ_DATA_ATTR,
IPA_USER_AUTH_TYPE, NULL };
struct berval **vals = NULL;
LDAPMessage *res = NULL;
LDAPMessage *first;
char *base = NULL;
int ret;
char **authz_data_list;
if (!ipactx || !ipactx->lcontext) {
return EINVAL;
}
ret = asprintf(&base, "cn=ipaConfig,cn=etc,%s", ipactx->base);
if (ret == -1) {
ret = ENOMEM;
goto done;
}
ret = ipadb_simple_search(ipactx, base, LDAP_SCOPE_BASE,
"(objectclass=*)", attrs, &res);
if (ret) {
goto done;
}
first = ldap_first_entry(ipactx->lcontext, res);
if (!first) {
/* no results, set nothing */
ret = 0;
goto done;
}
/* Check for permitted authentication types. */
ipadb_parse_user_auth(ipactx->lcontext, res, &ipactx->config.user_auth);
/* Load config strings. */
vals = ldap_get_values_len(ipactx->lcontext, first, "ipaConfigString");
if (vals) {
ipactx->config.disable_last_success = false;
ipactx->config.disable_lockout = false;
for (int i = 0; vals[i]; i++) {
if (strncasecmp("KDC:Disable Last Success",
vals[i]->bv_val, vals[i]->bv_len) == 0) {
ipactx->config.disable_last_success = true;
continue;
} else if (strncasecmp("KDC:Disable Lockout",
vals[i]->bv_val, vals[i]->bv_len) == 0) {
ipactx->config.disable_lockout = true;
continue;
} else if (strncasecmp("KDC:Disable Default Preauth for SPNs",
vals[i]->bv_val, vals[i]->bv_len) == 0) {
ipactx->config.disable_preauth_for_spns = true;
}
}
}
/* Load authz data. */
ret = ipadb_ldap_attr_to_strlist(ipactx->lcontext, first,
IPA_KRB_AUTHZ_DATA_ATTR, &authz_data_list);
if (ret == 0) {
if (ipactx->config.authz_data != NULL) {
for (int i = 0; ipactx->config.authz_data[i]; i++)
free(ipactx->config.authz_data[i]);
free(ipactx->config.authz_data);
}
ipactx->config.authz_data = authz_data_list;
} else if (ret != ENOENT)
goto done;
/* Success! */
ipactx->config.last_update = time(NULL);
ret = 0;
done:
ldap_value_free_len(vals);
ldap_msgfree(res);
free(base);
return ret;
}
const struct ipadb_global_config *
ipadb_get_global_config(struct ipadb_context *ipactx)
{
time_t now = 0;
int ret;
if (time(&now) != (time_t)-1 &&
now - ipactx->config.last_update > IPADB_GLOBAL_CONFIG_CACHE_TIME) {
if (!ipactx->lcontext) {
ret = ipadb_get_connection(ipactx);
if (ret != 0)
return NULL;
}
ret = ipadb_load_global_config(ipactx);
if (ret != 0)
return NULL;
}
return &ipactx->config;
}
int ipadb_get_enc_salt_types(struct ipadb_context *ipactx,
LDAPMessage *entry, char *attr,
krb5_key_salt_tuple **enc_salt_types,
int *n_enc_salt_types)
{
struct berval **vals = NULL;
char **cvals = NULL;
int c = 0;
int i;
int ret = 0;
krb5_key_salt_tuple *kst;
int n_kst;
vals = ldap_get_values_len(ipactx->lcontext, entry, attr);
if (!vals || !vals[0]) {
goto done;
}
for (c = 0; vals[c]; c++) /* count */ ;
cvals = calloc(c, sizeof(char *));
if (!cvals) {
ret = ENOMEM;
goto done;
}
for (i = 0; i < c; i++) {
cvals[i] = strndup(vals[i]->bv_val, vals[i]->bv_len);
if (!cvals[i]) {
ret = ENOMEM;
goto done;
}
}
ret = parse_bval_key_salt_tuples(ipactx->kcontext,
(const char * const *)cvals, c,
&kst, &n_kst);
if (ret) {
goto done;
}
if (*enc_salt_types) {
free(*enc_salt_types);
}
*enc_salt_types = kst;
*n_enc_salt_types = n_kst;
done:
ldap_value_free_len(vals);
for (i = 0; i < c && cvals[i]; i++) {
free(cvals[i]);
}
free(cvals);
return ret;
}
int ipadb_get_connection(struct ipadb_context *ipactx)
{
struct timeval tv = { 5, 0 };
LDAPMessage *res = NULL;
LDAPMessage *first;
int ret;
int v3;
if (!ipactx->uri) {
return EINVAL;
}
/* free existing conneciton if any */
if (ipactx->lcontext) {
ldap_unbind_ext_s(ipactx->lcontext, NULL, NULL);
ipactx->lcontext = NULL;
}
ret = ldap_initialize(&ipactx->lcontext, ipactx->uri);
if (ret != LDAP_SUCCESS) {
goto done;
}
/* make sure we talk LDAPv3 */
v3 = LDAP_VERSION3;
ret = ldap_set_option(ipactx->lcontext, LDAP_OPT_PROTOCOL_VERSION, &v3);
if (ret != LDAP_OPT_SUCCESS) {
goto done;
}
ret = ldap_set_option(ipactx->lcontext, LDAP_OPT_NETWORK_TIMEOUT, &tv);
if (ret != LDAP_OPT_SUCCESS) {
goto done;
}
ret = ldap_set_option(ipactx->lcontext, LDAP_OPT_TIMEOUT, &tv);
if (ret != LDAP_OPT_SUCCESS) {
goto done;
}
ret = ldap_sasl_bind_s(ipactx->lcontext,
NULL, "EXTERNAL",
NULL, NULL, NULL, NULL);
if (ret != LDAP_SUCCESS) {
goto done;
}
/* TODO: search rootdse */
ret = ipadb_simple_search(ipactx,
ipactx->realm_base, LDAP_SCOPE_BASE,
"(objectclass=*)", NULL, &res);
if (ret) {
goto done;
}
first = ldap_first_entry(ipactx->lcontext, res);
if (!first) {
goto done;
}
/* defaults first, this is used to tell what default enc:salts to use
* for kadmin password changes */
ret = ipadb_get_enc_salt_types(ipactx, first, "krbDefaultEncSaltTypes",
&ipactx->def_encs, &ipactx->n_def_encs);
if (ret) {
goto done;
}
/* supported enc salt types, use to tell kadmin what to accept
* but also to detect if kadmin is requesting the default set */
ret = ipadb_get_enc_salt_types(ipactx, first, "krbSupportedEncSaltTypes",
&ipactx->supp_encs, &ipactx->n_supp_encs);
if (ret) {
goto done;
}
/* get additional options */
ret = ipadb_load_global_config(ipactx);
if (ret) {
goto done;
}
/* get adtrust options using default refresh interval */
ret = ipadb_reinit_mspac(ipactx, false);
if (ret && ret != ENOENT) {
/* TODO: log that there is an issue with adtrust settings */
if (ipactx->lcontext == NULL) {
/* for some reason ldap connection was reset in ipadb_reinit_mspac
* and is no longer established => failure of ipadb_get_connection
*/
goto done;
}
}
ret = 0;
done:
ldap_msgfree(res);
if (ret) {
if (ipactx->lcontext) {
ldap_unbind_ext_s(ipactx->lcontext, NULL, NULL);
ipactx->lcontext = NULL;
}
if (ret == LDAP_SERVER_DOWN) {
return ETIMEDOUT;
}
return EIO;
}
return 0;
}
/* INTERFACE */
static krb5_error_code ipadb_init_library(void)
{
return 0;
}
static krb5_error_code ipadb_fini_library(void)
{
return 0;
}
static krb5_error_code ipadb_init_module(krb5_context kcontext,
char *conf_section,
char **db_args, int mode)
{
struct ipadb_context *ipactx;
krb5_error_code kerr;
int ret;
int i;
struct utsname uname_data;
/* make sure the context is freed to avoid leaking it */
ipactx = ipadb_get_context(kcontext);
ipadb_context_free(kcontext, &ipactx);
ipactx = calloc(1, sizeof(struct ipadb_context));
if (!ipactx) {
return ENOMEM;
}
/* only check for unsupported 'temporary' value for now */
for (i = 0; db_args != NULL && db_args[i] != NULL; i++) {
if (strncmp(db_args[i], IPA_SETUP, sizeof(IPA_SETUP)) == 0) {
ipactx->override_restrictions = true;
}
if (strncmp(db_args[i], "temporary", 9) == 0) {
krb5_set_error_message(kcontext, EINVAL,
"Plugin requires -update argument!");
ret = EINVAL;
goto fail;
}
}
ipactx->kcontext = kcontext;
kerr = krb5_get_default_realm(kcontext, &ipactx->realm);
if (kerr != 0) {
ret = EINVAL;
goto fail;
}
ipactx->uri = ipadb_realm_to_ldapi_uri(ipactx->realm);
if (!ipactx->uri) {
ret = ENOMEM;
goto fail;
}
ipactx->base = ipadb_get_base_from_realm(kcontext);
if (!ipactx->base) {
ret = ENOMEM;
goto fail;
}
ret = asprintf(&ipactx->realm_base, "cn=%s,cn=kerberos,%s",
ipactx->realm, ipactx->base);
if (ret == -1) {
ret = ENOMEM;
goto fail;
}
ret = asprintf(&ipactx->accounts_base, "cn=accounts,%s", ipactx->base);
if (ret == -1) {
ret = ENOMEM;
goto fail;
}
ret = uname(&uname_data);
if (ret) {
ret = EINVAL;
goto fail;
}
ipactx->kdc_hostname = strdup(uname_data.nodename);
if (!ipactx->kdc_hostname) {
ret = ENOMEM;
goto fail;
}
ret = ipadb_get_connection(ipactx);
if (ret != 0) {
/* not a fatal failure, as the LDAP server may be temporarily down */
/* TODO: spam syslog with this error */
}
kerr = krb5_db_set_context(kcontext, ipactx);
if (kerr != 0) {
ret = EACCES;
goto fail;
}
return 0;
fail:
ipadb_context_free(kcontext, &ipactx);
return ret;
}
static krb5_error_code ipadb_fini_module(krb5_context kcontext)
{
struct ipadb_context *ipactx;
ipactx = ipadb_get_context(kcontext);
ipadb_context_free(kcontext, &ipactx);
talloc_free(talloc_autofree_context());
return 0;
}
static krb5_error_code ipadb_create(krb5_context kcontext,
char *conf_section,
char **db_args)
{
return ipadb_init_module(kcontext, conf_section, db_args, 0);
}
static krb5_error_code ipadb_get_age(krb5_context kcontext,
char *db_name, time_t *age)
{
/* just return the current time for now,
* until we can use persistent searches and have
* a better estimate */
*age = time(NULL);
return 0;
}
static void *ipadb_alloc(krb5_context context, void *ptr, size_t size)
{
return realloc(ptr, size);
}
static void ipadb_free(krb5_context context, void *ptr)
{
free(ptr);
}
/* KDB Virtual Table */
/* We explicitly want to keep different ABI tables below separate. */
/* Do not merge them together. Older ABI does not need to be updated */
#if KRB5_KDB_DAL_MAJOR_VERSION == 5
kdb_vftabl kdb_function_table = {
.maj_ver = KRB5_KDB_DAL_MAJOR_VERSION,
.min_ver = 0,
.init_library = ipadb_init_library,
.fini_library = ipadb_fini_library,
.init_module = ipadb_init_module,
.fini_module = ipadb_fini_module,
.create = ipadb_create,
.get_age = ipadb_get_age,
.get_principal = ipadb_get_principal,
.free_principal = ipadb_free_principal,
.put_principal = ipadb_put_principal,
.delete_principal = ipadb_delete_principal,
.iterate = ipadb_iterate,
.create_policy = ipadb_create_pwd_policy,
.get_policy = ipadb_get_pwd_policy,
.put_policy = ipadb_put_pwd_policy,
.iter_policy = ipadb_iterate_pwd_policy,
.delete_policy = ipadb_delete_pwd_policy,
.free_policy = ipadb_free_pwd_policy,
.alloc = ipadb_alloc,
.free = ipadb_free,
.fetch_master_key = ipadb_fetch_master_key,
.store_master_key_list = ipadb_store_master_key_list,
.change_pwd = ipadb_change_pwd,
.sign_authdata = ipadb_sign_authdata,
.check_transited_realms = ipadb_check_transited_realms,
.check_policy_as = ipadb_check_policy_as,
.audit_as_req = ipadb_audit_as_req,
.check_allowed_to_delegate = ipadb_check_allowed_to_delegate
};
#endif
#if (KRB5_KDB_DAL_MAJOR_VERSION == 6) && !defined(HAVE_KDB_FREEPRINCIPAL_EDATA)
kdb_vftabl kdb_function_table = {
.maj_ver = KRB5_KDB_DAL_MAJOR_VERSION,
.min_ver = 0,
.init_library = ipadb_init_library,
.fini_library = ipadb_fini_library,
.init_module = ipadb_init_module,
.fini_module = ipadb_fini_module,
.create = ipadb_create,
.get_age = ipadb_get_age,
.get_principal = ipadb_get_principal,
.put_principal = ipadb_put_principal,
.delete_principal = ipadb_delete_principal,
.iterate = ipadb_iterate,
.create_policy = ipadb_create_pwd_policy,
.get_policy = ipadb_get_pwd_policy,
.put_policy = ipadb_put_pwd_policy,
.iter_policy = ipadb_iterate_pwd_policy,
.delete_policy = ipadb_delete_pwd_policy,
.fetch_master_key = ipadb_fetch_master_key,
.store_master_key_list = ipadb_store_master_key_list,
.change_pwd = ipadb_change_pwd,
.sign_authdata = ipadb_sign_authdata,
.check_transited_realms = ipadb_check_transited_realms,
.check_policy_as = ipadb_check_policy_as,
.audit_as_req = ipadb_audit_as_req,
.check_allowed_to_delegate = ipadb_check_allowed_to_delegate
};
#endif
#if (KRB5_KDB_DAL_MAJOR_VERSION == 6) && defined(HAVE_KDB_FREEPRINCIPAL_EDATA)
kdb_vftabl kdb_function_table = {
.maj_ver = KRB5_KDB_DAL_MAJOR_VERSION,
.min_ver = 1,
.init_library = ipadb_init_library,
.fini_library = ipadb_fini_library,
.init_module = ipadb_init_module,
.fini_module = ipadb_fini_module,
.create = ipadb_create,
.get_age = ipadb_get_age,
.get_principal = ipadb_get_principal,
.put_principal = ipadb_put_principal,
.delete_principal = ipadb_delete_principal,
.iterate = ipadb_iterate,
.create_policy = ipadb_create_pwd_policy,
.get_policy = ipadb_get_pwd_policy,
.put_policy = ipadb_put_pwd_policy,
.iter_policy = ipadb_iterate_pwd_policy,
.delete_policy = ipadb_delete_pwd_policy,
.fetch_master_key = ipadb_fetch_master_key,
.store_master_key_list = ipadb_store_master_key_list,
.change_pwd = ipadb_change_pwd,
.sign_authdata = ipadb_sign_authdata,
.check_transited_realms = ipadb_check_transited_realms,
.check_policy_as = ipadb_check_policy_as,
.audit_as_req = ipadb_audit_as_req,
.check_allowed_to_delegate = ipadb_check_allowed_to_delegate,
/* The order is important, DAL version 6.1 added
* the free_principal_e_data callback */
.free_principal_e_data = ipadb_free_principal_e_data,
};
#endif
#if (KRB5_KDB_DAL_MAJOR_VERSION != 5) && (KRB5_KDB_DAL_MAJOR_VERSION != 6)
#error unsupported DAL major version
#endif

View File

@@ -0,0 +1,11 @@
EXPORTED {
# public symbols
global:
kdb_function_table;
certauth_ipakdb_initvt;
# everything else is local
local:
*;
};

346
daemons/ipa-kdb/ipa_kdb.h Normal file
View File

@@ -0,0 +1,346 @@
/*
* MIT Kerberos KDC database backend for FreeIPA
*
* Authors: Simo Sorce <ssorce@redhat.com>
*
* Copyright (C) 2011 Simo Sorce, Red Hat
* see file 'COPYING' for use and warranty information
*
* This program is free software you can redistribute it and/or modify
* 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/>.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#endif
/* although we have nothing to do with SECURID yet, there are a
* couple of TL_DATA Ids that need it to be available.
* We need them to be avilable even if SECURID is not used for
* filtering purposes */
#define SECURID 1
#include "config.h"
#include <errno.h>
#include <kdb.h>
#include <ldap.h>
#include <time.h>
#include <stdio.h>
#include <stdbool.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <endian.h>
#include <unistd.h>
#ifdef HAVE_KRB5_CERTAUTH_PLUGIN
#include <krb5/certauth_plugin.h>
#endif
#include "ipa_krb5.h"
#include "ipa_pwd.h"
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
#endif
/* easier to copy the defines here than to mess with kadm5/admin.h
* for now */
#define KMASK_PRINCIPAL 0x000001
#define KMASK_PRINC_EXPIRE_TIME 0x000002
#define KMASK_PW_EXPIRATION 0x000004
#define KMASK_LAST_PWD_CHANGE 0x000008
#define KMASK_ATTRIBUTES 0x000010
#define KMASK_MAX_LIFE 0x000020
#define KMASK_MOD_TIME 0x000040
#define KMASK_MOD_NAME 0x000080
#define KMASK_KVNO 0x000100
#define KMASK_MKVNO 0x000200
#define KMASK_AUX_ATTRIBUTES 0x000400
#define KMASK_POLICY 0x000800
#define KMASK_POLICY_CLR 0x001000
/* version 2 masks */
#define KMASK_MAX_RLIFE 0x002000
#define KMASK_LAST_SUCCESS 0x004000
#define KMASK_LAST_FAILED 0x008000
#define KMASK_FAIL_AUTH_COUNT 0x010000
#define KMASK_KEY_DATA 0x020000
#define KMASK_TL_DATA 0x040000
#define KMASK_LOAD 0x200000
#define IPA_SETUP "ipa-setup-override-restrictions"
#define IPA_KRB_AUTHZ_DATA_ATTR "ipaKrbAuthzData"
#define IPA_USER_AUTH_TYPE "ipaUserAuthType"
struct ipadb_mspac;
enum ipadb_user_auth {
IPADB_USER_AUTH_NONE = 0,
IPADB_USER_AUTH_DISABLED = 1 << 0,
IPADB_USER_AUTH_PASSWORD = 1 << 1,
IPADB_USER_AUTH_RADIUS = 1 << 2,
IPADB_USER_AUTH_OTP = 1 << 3,
};
struct ipadb_global_config {
time_t last_update;
bool disable_last_success;
bool disable_lockout;
char **authz_data;
enum ipadb_user_auth user_auth;
bool disable_preauth_for_spns;
};
struct ipadb_context {
char *uri;
char *base;
char *realm;
char *realm_base;
char *accounts_base;
char *kdc_hostname;
LDAP *lcontext;
krb5_context kcontext;
bool override_restrictions;
krb5_key_salt_tuple *supp_encs;
int n_supp_encs;
krb5_key_salt_tuple *def_encs;
int n_def_encs;
struct ipadb_mspac *mspac;
#ifdef HAVE_KRB5_CERTAUTH_PLUGIN
krb5_certauth_moddata certauth_moddata;
#endif
/* Don't access this directly, use ipadb_get_global_config(). */
struct ipadb_global_config config;
};
#define IPA_E_DATA_MAGIC 0x0eda7a
struct ipadb_e_data {
int magic;
bool ipa_user;
char *entry_dn;
char *passwd;
time_t last_pwd_change;
char *pw_policy_dn;
char **pw_history;
struct ipapwd_policy *pol;
time_t last_admin_unlock;
char **authz_data;
bool has_tktpolaux;
};
struct ipadb_context *ipadb_get_context(krb5_context kcontext);
int ipadb_get_connection(struct ipadb_context *ipactx);
/* COMMON LDAP FUNCTIONS */
char *ipadb_filter_escape(const char *input, bool star);
krb5_error_code ipadb_simple_search(struct ipadb_context *ipactx,
char *basedn, int scope,
char *filter, char **attrs,
LDAPMessage **res);
krb5_error_code ipadb_simple_delete(struct ipadb_context *ipactx, char *dn);
krb5_error_code ipadb_simple_add(struct ipadb_context *ipactx,
char *dn, LDAPMod **mods);
krb5_error_code ipadb_simple_modify(struct ipadb_context *ipactx,
char *dn, LDAPMod **mods);
krb5_error_code ipadb_simple_delete_val(struct ipadb_context *ipactx,
char *dn, char *attr, char *value);
krb5_error_code ipadb_deref_search(struct ipadb_context *ipactx,
char *base_dn, int scope,
char *filter,
char **entry_attrs,
char **deref_attr_names,
char **deref_attrs,
LDAPMessage **res);
int ipadb_ldap_attr_to_int(LDAP *lcontext, LDAPMessage *le,
char *attrname, int *result);
int ipadb_ldap_attr_to_uint32(LDAP *lcontext, LDAPMessage *le,
char *attrname, uint32_t *result);
int ipadb_ldap_attr_to_str(LDAP *lcontext, LDAPMessage *le,
char *attrname, char **result);
int ipadb_ldap_attr_to_strlist(LDAP *lcontext, LDAPMessage *le,
char *attrname, char ***result);
int ipadb_ldap_attr_to_bool(LDAP *lcontext, LDAPMessage *le,
char *attrname, bool *result);
int ipadb_ldap_attr_to_time_t(LDAP *lcontext, LDAPMessage *le,
char *attrname, time_t *result);
int ipadb_ldap_attr_to_krb5_timestamp(LDAP *lcontext, LDAPMessage *le,
char *attrname, krb5_timestamp *result);
int ipadb_ldap_attr_has_value(LDAP *lcontext, LDAPMessage *le,
char *attrname, const char *value);
int ipadb_ldap_deref_results(LDAP *lcontext, LDAPMessage *le,
LDAPDerefRes **results);
struct ipadb_multires;
krb5_error_code ipadb_multires_init(LDAP *lcontext, struct ipadb_multires **r);
void ipadb_multires_free(struct ipadb_multires *r);
LDAPMessage *ipadb_multires_next_entry(struct ipadb_multires *r);
krb5_error_code ipadb_multibase_search(struct ipadb_context *ipactx,
char **basedns, int scope,
char *filter, char **attrs,
struct ipadb_multires **res,
bool any);
/* PRINCIPALS FUNCTIONS */
krb5_error_code ipadb_get_principal(krb5_context kcontext,
krb5_const_principal search_for,
unsigned int flags,
krb5_db_entry **entry);
void ipadb_free_principal(krb5_context kcontext, krb5_db_entry *entry);
/* Helper function for DAL API 6.1 or later */
void ipadb_free_principal_e_data(krb5_context kcontext, krb5_octet *e_data);
krb5_error_code ipadb_put_principal(krb5_context kcontext,
krb5_db_entry *entry,
char **db_args);
krb5_error_code ipadb_delete_principal(krb5_context kcontext,
krb5_const_principal search_for);
krb5_error_code
ipadb_fetch_principals_with_extra_filter(struct ipadb_context *ipactx,
unsigned int flags,
const char *principal,
const char *filter,
LDAPMessage **result);
krb5_error_code ipadb_find_principal(krb5_context kcontext,
unsigned int flags,
LDAPMessage *res,
char **principal,
LDAPMessage **entry);
#if KRB5_KDB_API_VERSION < 8
krb5_error_code ipadb_iterate(krb5_context kcontext,
char *match_entry,
int (*func)(krb5_pointer, krb5_db_entry *),
krb5_pointer func_arg);
#else
krb5_error_code ipadb_iterate(krb5_context kcontext,
char *match_entry,
int (*func)(krb5_pointer, krb5_db_entry *),
krb5_pointer func_arg, krb5_flags iterflags);
#endif
/* POLICY FUNCTIONS */
krb5_error_code ipadb_get_ipapwd_policy(struct ipadb_context *ipactx,
char *pw_policy_dn,
struct ipapwd_policy **pol);
krb5_error_code ipadb_create_pwd_policy(krb5_context kcontext,
osa_policy_ent_t policy);
krb5_error_code ipadb_get_pwd_policy(krb5_context kcontext, char *name,
osa_policy_ent_t *policy);
krb5_error_code ipadb_put_pwd_policy(krb5_context kcontext,
osa_policy_ent_t policy);
krb5_error_code ipadb_iterate_pwd_policy(krb5_context kcontext,
char *match_entry,
osa_adb_iter_policy_func func,
void *data);
krb5_error_code ipadb_delete_pwd_policy(krb5_context kcontext,
char *policy);
void ipadb_free_pwd_policy(krb5_context kcontext, osa_policy_ent_t val);
krb5_error_code ipadb_check_policy_as(krb5_context kcontext,
krb5_kdc_req *request,
krb5_db_entry *client,
krb5_db_entry *server,
krb5_timestamp kdc_time,
const char **status,
krb5_pa_data ***e_data);
/* MASTER KEY FUNCTIONS */
krb5_error_code ipadb_fetch_master_key(krb5_context kcontext,
krb5_principal mname,
krb5_keyblock *key,
krb5_kvno *kvno,
char *db_args);
krb5_error_code ipadb_store_master_key_list(krb5_context kcontext,
char *db_arg,
krb5_principal mname,
krb5_keylist_node *keylist,
char *master_pwd);
krb5_error_code ipadb_create_master_key(krb5_context kcontext);
/* PASSWORD FUNCTIONS */
krb5_error_code ipadb_change_pwd(krb5_context context,
krb5_keyblock *master_key,
krb5_key_salt_tuple *ks_tuple,
int ks_tuple_count, char *passwd,
int new_kvno, krb5_boolean keepold,
krb5_db_entry *db_entry);
krb5_error_code ipadb_get_pwd_expiration(krb5_context context,
krb5_db_entry *entry,
struct ipadb_e_data *ied,
time_t *expire_time);
/* MS-PAC FUNCTIONS */
krb5_error_code ipadb_sign_authdata(krb5_context context,
unsigned int flags,
krb5_const_principal client_princ,
krb5_db_entry *client,
krb5_db_entry *server,
krb5_db_entry *krbtgt,
krb5_keyblock *client_key,
krb5_keyblock *server_key,
krb5_keyblock *krbtgt_key,
krb5_keyblock *session_key,
krb5_timestamp authtime,
krb5_authdata **tgt_auth_data,
krb5_authdata ***signed_auth_data);
krb5_error_code ipadb_reinit_mspac(struct ipadb_context *ipactx, bool force_reinit);
void ipadb_mspac_struct_free(struct ipadb_mspac **mspac);
krb5_error_code ipadb_check_transited_realms(krb5_context kcontext,
const krb5_data *tr_contents,
const krb5_data *client_realm,
const krb5_data *server_realm);
/* Checks whether a principal's realm is one of trusted domains' realm or NetBIOS name
* and returns the realm of the matched trusted domain in 'trusted_domain'
* Returns 0 in case of success and KRB5_KDB_NOENTRY otherwise
* If DAL driver is not initialized, returns KRB5_KDB_DBNOTINITED */
krb5_error_code ipadb_is_princ_from_trusted_realm(krb5_context kcontext,
const char *test_realm, size_t size,
char **trusted_realm);
/* DELEGATION CHECKS */
krb5_error_code ipadb_check_allowed_to_delegate(krb5_context kcontext,
krb5_const_principal client,
const krb5_db_entry *server,
krb5_const_principal proxy);
/* AS AUDIT */
void ipadb_audit_as_req(krb5_context kcontext,
krb5_kdc_req *request,
krb5_db_entry *client,
krb5_db_entry *server,
krb5_timestamp authtime,
krb5_error_code error_code);
/* AUTH METHODS */
void ipadb_parse_user_auth(LDAP *lcontext, LDAPMessage *le,
enum ipadb_user_auth *user_auth);
const struct ipadb_global_config *
ipadb_get_global_config(struct ipadb_context *ipactx);
int ipadb_get_enc_salt_types(struct ipadb_context *ipactx, LDAPMessage *entry,
char *attr, krb5_key_salt_tuple **enc_salt_types,
int *n_enc_salt_types);
#ifdef HAVE_KRB5_CERTAUTH_PLUGIN
/* CERTAUTH PLUGIN */
void ipa_certauth_free_moddata(krb5_certauth_moddata *moddata);
#endif

View File

@@ -0,0 +1,138 @@
/*
* MIT Kerberos KDC database backend for FreeIPA
*
* Authors: Simo Sorce <ssorce@redhat.com>
*
* Copyright (C) 2012 Simo Sorce, Red Hat
* see file 'COPYING' for use and warranty information
*
* This program is free software you can redistribute it and/or modify
* 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 <syslog.h>
#include "ipa_kdb.h"
#include "ipa_pwd.h"
void ipadb_audit_as_req(krb5_context kcontext,
krb5_kdc_req *request,
krb5_db_entry *client,
krb5_db_entry *server,
krb5_timestamp authtime,
krb5_error_code error_code)
{
const struct ipadb_global_config *gcfg;
struct ipadb_context *ipactx;
struct ipadb_e_data *ied;
krb5_error_code kerr;
if (!client) {
return;
}
if (error_code != 0 &&
error_code != KRB5KDC_ERR_PREAUTH_FAILED &&
error_code != KRB5KRB_AP_ERR_BAD_INTEGRITY) {
return;
}
ipactx = ipadb_get_context(kcontext);
if (!ipactx) {
return;
}
ied = (struct ipadb_e_data *)client->e_data;
if (!ied) {
return;
}
if (!ied->pol) {
kerr = ipadb_get_ipapwd_policy(ipactx, ied->pw_policy_dn, &ied->pol);
if (kerr != 0) {
return;
}
}
client->mask = 0;
gcfg = ipadb_get_global_config(ipactx);
if (gcfg == NULL)
return;
switch (error_code) {
case 0:
/* Check if preauth flag is specified (default), otherwise we have
* no data to know if auth was successful or not */
if (client->attributes & KRB5_KDB_REQUIRES_PRE_AUTH) {
if (client->fail_auth_count != 0) {
client->fail_auth_count = 0;
client->mask |= KMASK_FAIL_AUTH_COUNT;
}
if (gcfg->disable_last_success) {
break;
}
client->last_success = authtime;
client->mask |= KMASK_LAST_SUCCESS;
}
break;
case KRB5KDC_ERR_PREAUTH_FAILED:
case KRB5KRB_AP_ERR_BAD_INTEGRITY:
if (gcfg->disable_lockout) {
break;
}
if (client->last_failed <= ied->last_admin_unlock) {
/* Reset fail_auth_count, and admin unlocked the account */
client->fail_auth_count = 0;
client->mask |= KMASK_FAIL_AUTH_COUNT;
}
if (ied->pol->lockout_duration != 0 &&
ied->pol->failcnt_interval != 0 &&
client->last_failed + ied->pol->failcnt_interval < authtime) {
/* Reset fail_auth_count, the interval's expired already */
client->fail_auth_count = 0;
client->mask |= KMASK_FAIL_AUTH_COUNT;
}
if (client->last_failed + ied->pol->lockout_duration > authtime &&
(client->fail_auth_count >= ied->pol->max_fail &&
ied->pol->max_fail != 0)) {
/* client already locked, nothing more to do */
break;
}
if (ied->pol->max_fail == 0 ||
client->fail_auth_count < ied->pol->max_fail) {
/* let's increase the fail counter */
client->fail_auth_count++;
client->mask |= KMASK_FAIL_AUTH_COUNT;
}
client->last_failed = authtime;
client->mask |= KMASK_LAST_FAILED;
break;
default:
krb5_klog_syslog(LOG_ERR,
"File '%s' line %d: Got an unexpected value of "
"error_code: %d\n", __FILE__, __LINE__, error_code);
return;
}
if (client->mask) {
kerr = ipadb_put_principal(kcontext, client, NULL);
if (kerr != 0) {
return;
}
}
client->mask = 0;
}

View File

@@ -0,0 +1,439 @@
/** BEGIN COPYRIGHT BLOCK
* This program is free software; you can redistribute it and/or modify
* 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/>.
*
* Additional permission under GPLv3 section 7:
*
* In the following paragraph, "GPL" means the GNU General Public
* License, version 3 or any later version, and "Non-GPL Code" means
* code that is governed neither by the GPL nor a license
* compatible with the GPL.
*
* You may link the code of this Program with Non-GPL Code and convey
* linked combinations including the two, provided that such Non-GPL
* Code only links to the code of this Program through those well
* defined interfaces identified in the file named EXCEPTION found in
* the source code files (the "Approved Interfaces"). The files of
* Non-GPL Code may instantiate templates or use macros or inline
* functions from the Approved Interfaces without causing the resulting
* work to be covered by the GPL. Only the copyright holders of this
* Program may make changes or additions to the list of Approved
* Interfaces.
*
* Authors:
* Sumit Bose <sbose@redhat.com>
*
* Copyright (C) 2017 Red Hat, Inc.
* All rights reserved.
* END COPYRIGHT BLOCK **/
#include <errno.h>
//#include <krb5/certauth_plugin.h>
#include <syslog.h>
#include <sss_certmap.h>
#include "util/ipa_krb5.h"
#include "ipa_kdb.h"
#define IPA_OC_CERTMAP_RULE "ipaCertMapRule"
#define IPA_CERTMAP_MAPRULE "ipaCertMapMapRule"
#define IPA_CERTMAP_MATCHRULE "ipaCertMapMatchRule"
#define IPA_CERTMAP_PRIORITY "ipaCertMapPriority"
#define IPA_ENABLED_FLAG "ipaEnabledFlag"
#define IPA_TRUE_VALUE "TRUE"
#define IPA_ASSOCIATED_DOMAIN "associatedDomain"
#define OBJECTCLASS "objectClass"
#define CERTMAP_FILTER "(&("OBJECTCLASS"="IPA_OC_CERTMAP_RULE")" \
"("IPA_ENABLED_FLAG"="IPA_TRUE_VALUE"))"
#define DEFAULT_CERTMAP_LIFETIME 300
#ifndef discard_const
#define discard_const(ptr) ((void *)((uintptr_t)(ptr)))
#endif
struct krb5_certauth_moddata_st {
char *local_domain;
struct sss_certmap_ctx *sss_certmap_ctx;
struct ipadb_context *ipactx;
time_t valid_until;
};
void ipa_certmap_debug(void *private,
const char *file, long line,
const char *function,
const char *format, ...)
{
va_list ap;
char str[255] = { 0 };
va_start(ap, format);
vsnprintf(str, sizeof(str)-1, format, ap);
va_end(ap);
krb5_klog_syslog(LOG_INFO, str);
}
void ipa_certauth_free_moddata(krb5_certauth_moddata *moddata)
{
if (moddata == NULL || *moddata == NULL) {
return;
}
free((*moddata)->local_domain);
(*moddata)->local_domain = NULL;
sss_certmap_free_ctx((*moddata)->sss_certmap_ctx);
(*moddata)->sss_certmap_ctx = NULL;
free(*moddata);
return;
}
static krb5_error_code ipa_get_init_data(krb5_context kcontext,
krb5_certauth_moddata moddata_out)
{
int ret;
struct sss_certmap_ctx *ctx = NULL;
struct ipadb_context *ipactx;
krb5_error_code kerr;
char *basedn = NULL;
LDAPMessage *result = NULL;
LDAPMessage *le;
LDAP *lc;
size_t c;
uint32_t prio;
char *map_rule = NULL;
char *match_rule = NULL;
char **domains = NULL;
const char *certmap_attrs[] = { OBJECTCLASS,
IPA_CERTMAP_PRIORITY,
IPA_CERTMAP_MATCHRULE,
IPA_CERTMAP_MAPRULE,
IPA_ASSOCIATED_DOMAIN,
IPA_ENABLED_FLAG,
NULL};
krb5_klog_syslog(LOG_INFO, "Initializing IPA certauth plugin.");
ipactx = ipadb_get_context(kcontext);
if (ipactx == NULL) {
return KRB5_KDB_DBNOTINITED;
}
if (ipactx->certauth_moddata == NULL) {
ipactx->certauth_moddata = moddata_out;
if (ipactx->realm != NULL) {
ipactx->certauth_moddata->local_domain = strdup(ipactx->realm);
if (ipactx->certauth_moddata->local_domain == NULL) {
free(ipactx->certauth_moddata);
ipactx->certauth_moddata = NULL;
ret = ENOMEM;
goto done;
}
}
ipactx->certauth_moddata->ipactx = ipactx;
}
ret = asprintf(&basedn, "cn=certmap,%s", ipactx->base);
if (ret == -1) {
return ENOMEM;
}
kerr = ipadb_simple_search(ipactx,basedn, LDAP_SCOPE_SUBTREE,
CERTMAP_FILTER, discard_const(certmap_attrs),
&result);
if (kerr != 0 && kerr != KRB5_KDB_NOENTRY) {
goto done;
}
ret = sss_certmap_init(NULL, ipa_certmap_debug, NULL, &ctx);
if (ret != 0) {
return ret;
}
if (kerr == KRB5_KDB_NOENTRY) {
ret = sss_certmap_add_rule(ctx, SSS_CERTMAP_MIN_PRIO,
NULL, NULL, NULL);
if (ret != 0) {
goto done;
}
} else {
lc = ipactx->lcontext;
for (le = ldap_first_entry(lc, result); le;
le = ldap_next_entry(lc, le)) {
prio = SSS_CERTMAP_MIN_PRIO;
ret = ipadb_ldap_attr_to_uint32(lc, le, IPA_CERTMAP_PRIORITY,
&prio);
if (ret != 0 && ret != ENOENT) {
goto done;
}
free(map_rule);
map_rule = NULL;
ret = ipadb_ldap_attr_to_str(lc, le, IPA_CERTMAP_MAPRULE,
&map_rule);
if (ret != 0 && ret != ENOENT) {
goto done;
}
free(match_rule);
match_rule = NULL;
ret = ipadb_ldap_attr_to_str(lc, le, IPA_CERTMAP_MATCHRULE,
&match_rule);
if (ret != 0 && ret != ENOENT) {
goto done;
}
if (domains != NULL) {
for (c = 0; domains[c] != NULL; c++) {
free(domains[c]);
}
free(domains);
domains = NULL;
}
ret = ipadb_ldap_attr_to_strlist(lc, le, IPA_ASSOCIATED_DOMAIN,
&domains);
if (ret != 0 && ret != ENOENT) {
goto done;
}
ret = sss_certmap_add_rule(ctx, prio, match_rule, map_rule,
(const char **) domains);
if (ret != 0) {
goto done;
}
}
}
sss_certmap_free_ctx(ipactx->certauth_moddata->sss_certmap_ctx);
ipactx->certauth_moddata->sss_certmap_ctx = ctx;
ipactx->certauth_moddata->valid_until = time(NULL)
+ DEFAULT_CERTMAP_LIFETIME;
krb5_klog_syslog(LOG_DEBUG,
"Successfully updates certificate mapping rules.");
ret = 0;
done:
ldap_msgfree(result);
free(basedn);
free(map_rule);
free(match_rule);
if (domains != NULL) {
for (c = 0; domains[c] != NULL; c++) {
free(domains[c]);
}
free(domains);
domains = NULL;
}
if (ret != 0) {
sss_certmap_free_ctx(ctx);
}
return ret;
}
static krb5_error_code ipa_certauth_authorize(krb5_context context,
krb5_certauth_moddata moddata,
const uint8_t *cert,
size_t cert_len,
krb5_const_principal princ,
const void *opts,
const krb5_db_entry *db_entry,
char ***authinds_out)
{
char *cert_filter = NULL;
char **domains = NULL;
int ret;
size_t c;
char *principal = NULL;
char **auth_inds = NULL;
LDAPMessage *res = NULL;
krb5_error_code kerr;
LDAPMessage *lentry;
if (moddata == NULL) {
return KRB5_PLUGIN_NO_HANDLE;
}
if (moddata->sss_certmap_ctx == NULL || time(NULL) > moddata->valid_until) {
kerr = ipa_get_init_data(context, moddata);
if (kerr != 0) {
krb5_klog_syslog(LOG_ERR, "Failed to init certmapping data");
return KRB5_PLUGIN_NO_HANDLE;
}
}
ret = krb5_unparse_name(context, db_entry->princ, &principal);
if (ret != 0) {
ret = KRB5KDC_ERR_CERTIFICATE_MISMATCH;
goto done;
}
krb5_klog_syslog(LOG_INFO, "Doing certauth authorize for [%s]", principal);
ret = sss_certmap_get_search_filter(moddata->sss_certmap_ctx,
cert, cert_len,
&cert_filter, &domains);
if (ret != 0) {
if (ret == ENOENT) {
ret = KRB5KDC_ERR_CERTIFICATE_MISMATCH;
}
goto done;
}
krb5_klog_syslog(LOG_INFO, "Got cert filter [%s]", cert_filter);
/* If there are no domains assigned the rule will apply to the local
* domain only. */
if (domains != NULL) {
if (moddata->local_domain == NULL) {
/* We don't know our own domain name, in general this should not
* happen. But to be fault tolerant we allow matching rule which
* do not have a domain assigned. */
ret = KRB5KDC_ERR_CERTIFICATE_MISMATCH;
goto done;
}
for (c = 0; domains[c] != NULL; c++) {
if (strcasecmp(domains[c], moddata->local_domain) == 0) {
break;
}
}
/* Our domain was not in the list */
if (domains[c] == NULL) {
ret = KRB5KDC_ERR_CERTIFICATE_MISMATCH;
goto done;
}
}
kerr = ipadb_fetch_principals_with_extra_filter(moddata->ipactx,
KRB5_KDB_FLAG_ALIAS_OK,
principal,
cert_filter,
&res);
if (kerr != 0) {
krb5_klog_syslog(LOG_ERR, "Search failed [%d]", kerr);
ret = KRB5KDC_ERR_CERTIFICATE_MISMATCH;
goto done;
}
kerr = ipadb_find_principal(context, KRB5_KDB_FLAG_ALIAS_OK, res,
&principal, &lentry);
if (kerr == KRB5_KDB_NOENTRY) {
krb5_klog_syslog(LOG_INFO, "No matching entry found");
ret = KRB5KDC_ERR_CERTIFICATE_MISMATCH;
goto done;
} else if (kerr != 0) {
krb5_klog_syslog(LOG_ERR, "ipadb_find_principal failed [%d]", kerr);
ret = KRB5KDC_ERR_CERTIFICATE_MISMATCH;
goto done;
}
/* Associate authentication indicator "pkinit" with the successful match.
* SSSD interface doesn't give us a clue which rule did match
* so there is nothing more to add here. */
auth_inds = calloc(2, sizeof(char *));
if (auth_inds != NULL) {
ret = asprintf(&auth_inds[0], "pkinit");
if (ret != -1) {
auth_inds[1] = NULL;
*authinds_out = auth_inds;
} else {
free(auth_inds);
}
}
/* TODO: add more tests ? */
ret = 0;
done:
sss_certmap_free_filter_and_domains(cert_filter, domains);
krb5_free_unparsed_name(context, principal);
ldap_msgfree(res);
return ret;
}
static krb5_error_code ipa_certauth_init(krb5_context kcontext,
krb5_certauth_moddata *moddata_out)
{
struct krb5_certauth_moddata_st *certauth_moddata;
certauth_moddata = calloc(1, sizeof(struct krb5_certauth_moddata_st));
if (certauth_moddata == NULL) {
return ENOMEM;
}
*moddata_out = certauth_moddata;
return 0;
}
static void ipa_certauth_fini(krb5_context context,
krb5_certauth_moddata moddata_out)
{
krb5_klog_syslog(LOG_INFO, "IPA certauth plugin un-loaded.");
return;
}
static void ipa_certauth_free_indicator(krb5_context context,
krb5_certauth_moddata moddata,
char **authinds)
{
size_t i = 0;
if ((authinds == NULL) || (moddata == NULL)) {
return;
}
for(i=0; authinds[i]; i++) {
free(authinds[i]);
authinds[i] = NULL;
}
free(authinds);
}
krb5_error_code certauth_ipakdb_initvt(krb5_context context,
int maj_ver, int min_ver,
krb5_plugin_vtable vtable)
{
krb5_certauth_vtable vt;
if (maj_ver != 1) {
return KRB5_PLUGIN_VER_NOTSUPP;
}
vt = (krb5_certauth_vtable) vtable;
vt->name = "ipakdb";
vt->authorize = ipa_certauth_authorize;
vt->init = ipa_certauth_init;
vt->fini = ipa_certauth_fini;
vt->free_ind = ipa_certauth_free_indicator;
return 0;
}

View File

@@ -0,0 +1,715 @@
/*
* MIT Kerberos KDC database backend for FreeIPA
*
* Authors: Simo Sorce <ssorce@redhat.com>
*
* Copyright (C) 2011 Simo Sorce, Red Hat
* see file 'COPYING' for use and warranty information
*
* This program is free software you can redistribute it and/or modify
* 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 "ipa_kdb.h"
#include <unicase.h>
static struct timeval std_timeout = {300, 0};
char *ipadb_filter_escape(const char *input, bool star)
{
char *output;
size_t i = 0;
size_t j = 0;
if (!input) {
return NULL;
}
/* Assume the worst-case. */
output = malloc(strlen(input) * 3 + 1);
if (!output) {
return NULL;
}
while (input[i]) {
switch(input[i]) {
case '*':
if (star) {
output[j++] = '\\';
output[j++] = '2';
output[j++] = 'a';
} else {
output[j++] = '*';
}
break;
case '(':
output[j++] = '\\';
output[j++] = '2';
output[j++] = '8';
break;
case ')':
output[j++] = '\\';
output[j++] = '2';
output[j++] = '9';
break;
case '\\':
output[j++] = '\\';
output[j++] = '5';
output[j++] = 'c';
break;
default:
output[j++] = input[i];
}
i++;
}
output[j] = '\0';
return output;
}
static krb5_error_code ipadb_simple_ldap_to_kerr(int ldap_error)
{
switch (ldap_error) {
case LDAP_SUCCESS:
return 0;
case LDAP_NO_SUCH_OBJECT:
case LDAP_NO_SUCH_ATTRIBUTE:
return KRB5_KDB_NOENTRY;
case LDAP_ALIAS_PROBLEM:
case LDAP_INVALID_DN_SYNTAX:
case LDAP_ALIAS_DEREF_PROBLEM:
case LDAP_UNDEFINED_TYPE:
case LDAP_INAPPROPRIATE_MATCHING:
case LDAP_INVALID_SYNTAX:
case LDAP_NAMING_VIOLATION:
case LDAP_OBJECT_CLASS_VIOLATION:
case LDAP_NO_OBJECT_CLASS_MODS:
return KRB5_KDB_INTERNAL_ERROR;
case LDAP_ALREADY_EXISTS:
case LDAP_NOT_ALLOWED_ON_NONLEAF:
case LDAP_NOT_ALLOWED_ON_RDN:
case LDAP_TIMELIMIT_EXCEEDED:
case LDAP_SIZELIMIT_EXCEEDED:
case LDAP_ADMINLIMIT_EXCEEDED:
case LDAP_STRONG_AUTH_REQUIRED:
case LDAP_CONFIDENTIALITY_REQUIRED:
case LDAP_INAPPROPRIATE_AUTH:
case LDAP_INVALID_CREDENTIALS:
case LDAP_INSUFFICIENT_ACCESS:
case LDAP_BUSY:
case LDAP_UNAVAILABLE:
case LDAP_UNWILLING_TO_PERFORM:
case LDAP_CONSTRAINT_VIOLATION:
case LDAP_TYPE_OR_VALUE_EXISTS:
return KRB5_KDB_CONSTRAINT_VIOLATION;
}
return KRB5_KDB_SERVER_INTERNAL_ERR;
}
static bool ipadb_need_retry(struct ipadb_context *ipactx, int error)
{
switch(error) {
/* connection errors */
case LDAP_SERVER_DOWN:
case LDAP_LOCAL_ERROR:
case LDAP_ENCODING_ERROR:
case LDAP_DECODING_ERROR:
case LDAP_TIMEOUT:
case LDAP_USER_CANCELLED:
case LDAP_PARAM_ERROR:
case LDAP_NO_MEMORY:
case LDAP_CONNECT_ERROR:
case LDAP_NOT_SUPPORTED:
case LDAP_CLIENT_LOOP:
case LDAP_X_CONNECTING:
/* server returned errors */
case LDAP_PROTOCOL_ERROR:
case LDAP_BUSY:
case LDAP_UNAVAILABLE:
case LDAP_UNWILLING_TO_PERFORM:
case LDAP_LOOP_DETECT:
/* prob connection error, try to reconnect */
error = ipadb_get_connection(ipactx);
if (error == 0) {
return true;
}
/* fall through */
default:
break;
}
return false;
}
static int ipadb_check_connection(struct ipadb_context *ipactx)
{
if (ipactx->lcontext == NULL) {
return ipadb_get_connection(ipactx);
}
return 0;
}
krb5_error_code ipadb_simple_search(struct ipadb_context *ipactx,
char *basedn, int scope,
char *filter, char **attrs,
LDAPMessage **res)
{
int ret;
ret = ipadb_check_connection(ipactx);
if (ret != 0)
return ipadb_simple_ldap_to_kerr(ret);
ret = ldap_search_ext_s(ipactx->lcontext, basedn, scope,
filter, attrs, 0, NULL, NULL,
&std_timeout, LDAP_NO_LIMIT,
res);
/* first test if we need to retry to connect */
if (ret != 0 &&
ipadb_need_retry(ipactx, ret)) {
ldap_msgfree(*res);
ret = ldap_search_ext_s(ipactx->lcontext, basedn, scope,
filter, attrs, 0, NULL, NULL,
&std_timeout, LDAP_NO_LIMIT,
res);
}
return ipadb_simple_ldap_to_kerr(ret);
}
krb5_error_code ipadb_simple_delete(struct ipadb_context *ipactx, char *dn)
{
int ret;
ret = ipadb_check_connection(ipactx);
if (ret != 0)
return ipadb_simple_ldap_to_kerr(ret);
ret = ldap_delete_ext_s(ipactx->lcontext, dn, NULL, NULL);
/* first test if we need to retry to connect */
if (ret != 0 &&
ipadb_need_retry(ipactx, ret)) {
ret = ldap_delete_ext_s(ipactx->lcontext, dn, NULL, NULL);
}
return ipadb_simple_ldap_to_kerr(ret);
}
krb5_error_code ipadb_simple_add(struct ipadb_context *ipactx,
char *dn, LDAPMod **mods)
{
int ret;
ret = ipadb_check_connection(ipactx);
if (ret != 0)
return ipadb_simple_ldap_to_kerr(ret);
ret = ldap_add_ext_s(ipactx->lcontext, dn, mods, NULL, NULL);
/* first test if we need to retry to connect */
if (ret != 0 &&
ipadb_need_retry(ipactx, ret)) {
ret = ldap_add_ext_s(ipactx->lcontext, dn, mods, NULL, NULL);
}
return ipadb_simple_ldap_to_kerr(ret);
}
krb5_error_code ipadb_simple_modify(struct ipadb_context *ipactx,
char *dn, LDAPMod **mods)
{
int ret;
ret = ipadb_check_connection(ipactx);
if (ret != 0)
return ipadb_simple_ldap_to_kerr(ret);
ret = ldap_modify_ext_s(ipactx->lcontext, dn, mods, NULL, NULL);
/* first test if we need to retry to connect */
if (ret != 0 &&
ipadb_need_retry(ipactx, ret)) {
ret = ldap_modify_ext_s(ipactx->lcontext, dn, mods, NULL, NULL);
}
return ipadb_simple_ldap_to_kerr(ret);
}
krb5_error_code ipadb_simple_delete_val(struct ipadb_context *ipactx,
char *dn, char *attr, char *value)
{
krb5_error_code kerr;
LDAPMod *mods[2];
mods[0] = calloc(1, sizeof(LDAPMod));
if (!mods[0]) {
return ENOMEM;
}
mods[1] = NULL;
mods[0]->mod_op = LDAP_MOD_DELETE;
mods[0]->mod_type = strdup(attr);
if (!mods[0]->mod_type) {
kerr = ENOMEM;
goto done;
}
mods[0]->mod_values = calloc(2, sizeof(char *));
if (!mods[0]->mod_values) {
kerr = ENOMEM;
goto done;
}
mods[0]->mod_values[0] = strdup(value);
if (!mods[0]->mod_values[0]) {
kerr = ENOMEM;
goto done;
}
kerr = ipadb_simple_modify(ipactx, dn, mods);
done:
ldap_mods_free(mods, 0);
return kerr;
}
krb5_error_code ipadb_deref_search(struct ipadb_context *ipactx,
char *base_dn, int scope,
char *filter,
char **entry_attrs,
char **deref_attr_names,
char **deref_attrs,
LDAPMessage **res)
{
struct berval derefval = { 0, NULL };
LDAPControl *ctrl[2] = { NULL, NULL };
LDAPDerefSpec *ds;
krb5_error_code kerr;
int times;
int ret;
int c, i;
bool retry;
for (c = 0; deref_attr_names[c]; c++) {
/* count */ ;
}
ds = calloc(c+1, sizeof(LDAPDerefSpec));
if (!ds) {
return ENOMEM;
}
for (i = 0; deref_attr_names[i]; i++) {
ds[i].derefAttr = deref_attr_names[i];
ds[i].attributes = deref_attrs;
}
ds[c].derefAttr = NULL;
ret = ldap_create_deref_control_value(ipactx->lcontext, ds, &derefval);
if (ret != LDAP_SUCCESS) {
kerr = ENOMEM;
goto done;
}
ret = ldap_control_create(LDAP_CONTROL_X_DEREF,
1, &derefval, 1, &ctrl[0]);
if (ret != LDAP_SUCCESS) {
kerr = ENOMEM;
goto done;
}
/* retry once if connection errors (tot. max. 2 tries) */
times = 2;
ret = LDAP_SUCCESS;
retry = true;
while (retry) {
times--;
ret = ipadb_check_connection(ipactx);
if (ret != 0)
break;
ret = ldap_search_ext_s(ipactx->lcontext, base_dn,
scope, filter,
entry_attrs, 0,
ctrl, NULL,
&std_timeout, LDAP_NO_LIMIT,
res);
retry = ipadb_need_retry(ipactx, ret) && times > 0;
if (retry) {
/* Free result before next try */
ldap_msgfree(*res);
}
}
kerr = ipadb_simple_ldap_to_kerr(ret);
done:
ldap_control_free(ctrl[0]);
ldap_memfree(derefval.bv_val);
free(ds);
return kerr;
}
/* result extraction */
int ipadb_ldap_attr_to_int(LDAP *lcontext, LDAPMessage *le,
char *attrname, int *result)
{
struct berval **vals;
int ret = ENOENT;
vals = ldap_get_values_len(lcontext, le, attrname);
if (vals) {
*result = atoi(vals[0]->bv_val);
ret = 0;
ldap_value_free_len(vals);
}
return ret;
}
int ipadb_ldap_attr_to_uint32(LDAP *lcontext, LDAPMessage *le,
char *attrname, uint32_t *result)
{
struct berval **vals;
long r;
int ret = ENOENT;
vals = ldap_get_values_len(lcontext, le, attrname);
if (vals) {
r = atol(vals[0]->bv_val);
if (r < 0 || r > (uint32_t)-1) {
ret = EINVAL;
} else {
*result = r;
ret = 0;
}
ldap_value_free_len(vals);
}
return ret;
}
int ipadb_ldap_attr_to_str(LDAP *lcontext, LDAPMessage *le,
char *attrname, char **result)
{
struct berval **vals;
int ret = ENOENT;
vals = ldap_get_values_len(lcontext, le, attrname);
if (vals) {
*result = strndup(vals[0]->bv_val, vals[0]->bv_len);
if (!*result) {
ret = ENOMEM;
} else {
ret = 0;
}
ldap_value_free_len(vals);
}
return ret;
}
int ipadb_ldap_attr_to_strlist(LDAP *lcontext, LDAPMessage *le,
char *attrname, char ***result)
{
struct berval **vals = NULL;
char **strlist = NULL;
int ret;
int i;
vals = ldap_get_values_len(lcontext, le, attrname);
if (!vals) {
return ENOENT;
}
for (i = 0; vals[i]; i++) /* count */ ;
strlist = calloc(i + 1, sizeof(char *));
if (!strlist) {
ret = ENOMEM;
goto fail;
}
for (i = 0; vals[i]; i++) {
strlist[i] = strndup(vals[i]->bv_val, vals[i]->bv_len);
if (!strlist[i]) {
ret = ENOMEM;
goto fail;
}
}
ldap_value_free_len(vals);
*result = strlist;
return 0;
fail:
ldap_value_free_len(vals);
for (i = 0; strlist && strlist[i]; i++) {
free(strlist[i]);
}
free(strlist);
return ret;
}
int ipadb_ldap_attr_to_bool(LDAP *lcontext, LDAPMessage *le,
char *attrname, bool *result)
{
struct berval **vals;
int ret = ENOENT;
vals = ldap_get_values_len(lcontext, le, attrname);
if (vals) {
if (strcasecmp("TRUE", vals[0]->bv_val) == 0) {
*result = true;
ret = 0;
} else if (strcasecmp("FALSE", vals[0]->bv_val) == 0) {
*result = false;
ret = 0;
} else {
ret = EINVAL;
}
ldap_value_free_len(vals);
}
return ret;
}
int ipadb_ldap_attr_to_time_t(LDAP *lcontext, LDAPMessage *le,
char *attrname, time_t *result)
{
struct berval **vals;
char *p;
struct tm stm = { 0 };
int ret = ENOENT;
vals = ldap_get_values_len(lcontext, le, attrname);
if (vals) {
p = strptime(vals[0]->bv_val, "%Y%m%d%H%M%SZ", &stm);
if (p && *p == '\0') {
*result = timegm(&stm);
ret = 0;
} else {
ret = EINVAL;
}
ldap_value_free_len(vals);
}
return ret;
}
int ipadb_ldap_attr_to_krb5_timestamp(LDAP *lcontext, LDAPMessage *le,
char *attrname, krb5_timestamp *result)
{
time_t res_time;
long long res_long;
int ret = ipadb_ldap_attr_to_time_t(lcontext, le,
attrname, &res_time);
if (ret) return ret;
/* this will cast correctly maintaing sign to a 64bit variable */
res_long = res_time;
/* For dates beyond IPAPWD_END_OF_TIME, rest_time might oveflow
* on 32-bit platforms. This does not apply for 64-bit platforms.
* However, since krb5 uses 32-bit time representation, we need
* to limit the result.*/
if (res_long < 0 || res_long > IPAPWD_END_OF_TIME) {
*result = IPAPWD_END_OF_TIME; // 1 Jan 2038, 00:00 GMT
} else {
*result = (krb5_timestamp)res_long;
}
return 0;
}
int ipadb_ldap_attr_has_value(LDAP *lcontext, LDAPMessage *le,
char *attrname, const char *value)
{
struct berval **vals;
int ret = ENOENT;
int i, result;
vals = ldap_get_values_len(lcontext, le, attrname);
if (vals) {
for (i = 0; vals[i]; i++) {
if (ulc_casecmp(vals[i]->bv_val, vals[i]->bv_len,
value, strlen(value),
NULL, NULL, &result) != 0) {
ret = errno;
break;
}
if (result == 0) {
ret = 0;
break;
}
}
ldap_value_free_len(vals);
}
return ret;
}
int ipadb_ldap_deref_results(LDAP *lcontext, LDAPMessage *le,
LDAPDerefRes **results)
{
LDAPControl **ctrls = NULL;
LDAPControl *derefctrl = NULL;
int ret;
ret = ldap_get_entry_controls(lcontext, le, &ctrls);
if (ret != LDAP_SUCCESS) {
return EINVAL;
}
if (!ctrls) {
return ENOENT;
}
derefctrl = ldap_control_find(LDAP_CONTROL_X_DEREF, ctrls, NULL);
if (!derefctrl) {
ret = ENOENT;
goto done;
}
ret = ldap_parse_derefresponse_control(lcontext, derefctrl, results);
if (ret) {
ret = EINVAL;
goto done;
}
ret = 0;
done:
ldap_controls_free(ctrls);
return ret;
}
struct ipadb_multires {
LDAP *lcontext;
LDAPMessage **res;
LDAPMessage *next;
ssize_t cursor;
ssize_t count;
};
krb5_error_code ipadb_multires_init(LDAP *lcontext, struct ipadb_multires **r)
{
*r = malloc(sizeof(struct ipadb_multires));
if (!*r) return ENOMEM;
(*r)->lcontext = lcontext;
(*r)->res = NULL;
(*r)->next = NULL;
(*r)->cursor = -1;
(*r)->count = 0;
return 0;
}
void ipadb_multires_free(struct ipadb_multires *r)
{
for (int i = 0; i < r->count; i++) {
ldap_msgfree(r->res[i]);
}
free(r);
}
LDAPMessage *ipadb_multires_next_entry(struct ipadb_multires *r)
{
if (r->count == 0) return NULL;
if (r->next) {
r->next = ldap_next_entry(r->lcontext, r->next);
}
if (r->next == NULL) {
if (r->cursor >= r->count - 1) {
return NULL;
}
r->cursor++;
r->next = ldap_first_entry(r->lcontext, r->res[r->cursor]);
}
return r->next;
}
krb5_error_code ipadb_multibase_search(struct ipadb_context *ipactx,
char **basedns, int scope,
char *filter, char **attrs,
struct ipadb_multires **res,
bool any)
{
int ret;
ret = ipadb_multires_init(ipactx->lcontext, res);
if (ret != 0) return ret;
ret = ipadb_check_connection(ipactx);
if (ret != 0)
return ipadb_simple_ldap_to_kerr(ret);
for (int b = 0; basedns[b]; b++) {
LDAPMessage *r;
ret = ldap_search_ext_s(ipactx->lcontext, basedns[b], scope,
filter, attrs, 0, NULL, NULL,
&std_timeout, LDAP_NO_LIMIT, &r);
/* first test if we need to retry to connect */
if (ret != 0 &&
ipadb_need_retry(ipactx, ret)) {
ldap_msgfree(r);
ret = ldap_search_ext_s(ipactx->lcontext, basedns[b], scope,
filter, attrs, 0, NULL, NULL,
&std_timeout, LDAP_NO_LIMIT, &r);
}
if (ret != 0) break;
if (ldap_count_entries(ipactx->lcontext, r) > 0) {
void *tmp = realloc((*res)->res, (((*res)->count + 1) *
sizeof(LDAPMessage *)));
if (tmp == NULL) {
ret = ENOMEM;
break;
}
(*res)->res = tmp;
(*res)->res[(*res)->count] = r;
(*res)->count++;
if (any) break;
}
}
if (ret != 0) {
ipadb_multires_free(*res);
*res = NULL;
}
return ipadb_simple_ldap_to_kerr(ret);
}

View File

@@ -0,0 +1,214 @@
/*
* MIT Kerberos KDC database backend for FreeIPA
*
* Authors: Simo Sorce <ssorce@redhat.com>
*
* Copyright (C) 2011 Simo Sorce, Red Hat
* see file 'COPYING' for use and warranty information
*
* This program is free software you can redistribute it and/or modify
* 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 "ipa_kdb.h"
static char *acl_attrs[] = {
"objectClass",
"memberPrincipal",
NULL
};
static char *search_attrs[] = {
"ipaAllowToImpersonate",
"ipaAllowedTarget",
NULL
};
static krb5_error_code ipadb_get_delegation_acl(krb5_context kcontext,
char *srv_principal,
LDAPMessage **results)
{
struct ipadb_context *ipactx;
krb5_error_code kerr;
char *filter = NULL;
int ret;
ipactx = ipadb_get_context(kcontext);
if (!ipactx) {
return KRB5_KDB_DBNOTINITED;
}
ret = asprintf(&filter,
"(&(objectclass=ipaKrb5DelegationACL)"
"(memberPrincipal=%s))", srv_principal);
if (ret == -1) {
kerr = ENOMEM;
goto done;
}
/* == Search ACL info == */
kerr = ipadb_deref_search(ipactx, ipactx->base,
LDAP_SCOPE_SUBTREE, filter, acl_attrs,
search_attrs, acl_attrs, results);
done:
free(filter);
return kerr;
}
static bool ipadb_match_member(char *princ, LDAPDerefRes *dres)
{
LDAPDerefVal *dval;
int i;
for (dval = dres->attrVals; dval; dval = dval->next) {
if (strcasecmp(dval->type, "memberPrincipal") != 0) {
continue;
}
for (i = 0; dval->vals[i].bv_val != NULL; i++) {
/* FIXME: use utf8 aware comparison ? */
/* FIXME: support wildcards ? */
if (strncasecmp(princ, dval->vals[i].bv_val,
dval->vals[i].bv_len) == 0) {
return true;
}
}
}
return false;
}
static krb5_error_code ipadb_match_acl(krb5_context kcontext,
LDAPMessage *results,
krb5_const_principal client,
krb5_const_principal target)
{
struct ipadb_context *ipactx;
krb5_error_code kerr;
LDAPMessage *lentry;
LDAPDerefRes *deref_results;
LDAPDerefRes *dres;
char *client_princ = NULL;
char *target_princ = NULL;
bool client_missing;
bool client_found;
bool target_found;
int ret;
ipactx = ipadb_get_context(kcontext);
if (!ipactx) {
return KRB5_KDB_DBNOTINITED;
}
kerr = krb5_unparse_name(kcontext, client, &client_princ);
if (kerr != 0) {
goto done;
}
kerr = krb5_unparse_name(kcontext, target, &target_princ);
if (kerr != 0) {
goto done;
}
lentry = ldap_first_entry(ipactx->lcontext, results);
if (!lentry) {
kerr = ENOENT;
goto done;
}
/* the default is that we fail */
kerr = ENOENT;
while (lentry) {
/* both client and target must be found in the same ACI */
client_missing = true;
client_found = false;
target_found = false;
ret = ipadb_ldap_deref_results(ipactx->lcontext, lentry,
&deref_results);
switch (ret) {
case 0:
for (dres = deref_results; dres; dres = dres->next) {
if (client_found == false &&
strcasecmp(dres->derefAttr, "ipaAllowToImpersonate") == 0) {
/* NOTE: client_missing is used to signal that the
* attribute was completely missing. This signals that
* ANY client is allowed to be impersonated.
* This logic is valid only for clients, not for targets */
client_missing = false;
client_found = ipadb_match_member(client_princ, dres);
}
if (target_found == false &&
strcasecmp(dres->derefAttr, "ipaAllowedTarget") == 0) {
target_found = ipadb_match_member(target_princ, dres);
}
}
ldap_derefresponse_free(deref_results);
break;
case ENOENT:
break;
default:
kerr = ret;
goto done;
}
if ((client_found == true || client_missing == true) &&
target_found == true) {
kerr = 0;
goto done;
}
lentry = ldap_next_entry(ipactx->lcontext, lentry);
}
done:
krb5_free_unparsed_name(kcontext, client_princ);
krb5_free_unparsed_name(kcontext, target_princ);
return kerr;
}
/* Ok terminology is confusing here so read carefully:
* here 'proxy' is the service for which 'server' wants a ticket on behalf of
* 'client' */
krb5_error_code ipadb_check_allowed_to_delegate(krb5_context kcontext,
krb5_const_principal client,
const krb5_db_entry *server,
krb5_const_principal proxy)
{
krb5_error_code kerr;
char *srv_principal = NULL;
LDAPMessage *res = NULL;
kerr = krb5_unparse_name(kcontext, server->princ, &srv_principal);
if (kerr) {
goto done;
}
kerr = ipadb_get_delegation_acl(kcontext, srv_principal, &res);
if (kerr) {
goto done;
}
kerr = ipadb_match_acl(kcontext, res, client, proxy);
if (kerr) {
goto done;
}
done:
krb5_free_unparsed_name(kcontext, srv_principal);
ldap_msgfree(res);
return kerr;
}

View File

@@ -0,0 +1,225 @@
/*
* MIT Kerberos KDC database backend for FreeIPA
*
* Authors: Simo Sorce <ssorce@redhat.com>
*
* Copyright (C) 2011 Simo Sorce, Red Hat
* see file 'COPYING' for use and warranty information
*
* This program is free software you can redistribute it and/or modify
* 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 "ipa_kdb.h"
static char *krbmkey_attrs[] = {
"krbMKey",
NULL
};
krb5_error_code ipadb_fetch_master_key(krb5_context kcontext,
krb5_principal mname,
krb5_keyblock *key,
krb5_kvno *kvno,
char *db_args)
{
struct ipadb_context *ipactx;
LDAPMessage *res = NULL;
LDAPMessage *first;
struct berval **vals = NULL;
BerElement *be = NULL;
krb5_error_code kerr;
krb5_keyblock k;
int mkvno;
int ret;
int i;
ipactx = ipadb_get_context(kcontext);
if (!ipactx) {
return KRB5_KDB_DBNOTINITED;
}
if (!ipactx->lcontext) {
ret = ipadb_get_connection(ipactx);
if (ret != 0) {
kerr = KRB5_KDB_SERVER_INTERNAL_ERR;
goto done;
}
}
be = ber_alloc_t(LBER_USE_DER);
if (!be) {
kerr = ENOMEM;
goto done;
}
kerr = ipadb_simple_search(ipactx, ipactx->realm_base, LDAP_SCOPE_BASE,
"(krbMKey=*)", krbmkey_attrs, &res);
if (kerr) {
goto done;
}
first = ldap_first_entry(ipactx->lcontext, res);
if (!first) {
kerr = KRB5_KDB_NOENTRY;
goto done;
}
mkvno = 0;
k.contents = NULL;
vals = ldap_get_values_len(ipactx->lcontext, first, "krbmkey");
for (i = 0; vals[i]; i++) {
struct berval *mkey;
ber_tag_t tag;
ber_int_t tvno;
ber_int_t ttype;
ber_init2(be, vals[i], LBER_USE_DER);
tag = ber_scanf(be, "{i{iO}}", &tvno, &ttype, &mkey);
if (tag == LBER_ERROR) {
kerr = KRB5_KDB_SERVER_INTERNAL_ERR;
goto done;
}
if (tvno > mkvno) {
mkvno = tvno;
k.enctype = ttype;
k.length = mkey->bv_len;
if (k.contents) {
free(k.contents);
}
k.contents = malloc(k.length);
if (!k.contents) {
kerr = ENOMEM;
goto done;
}
memcpy(k.contents, mkey->bv_val, k.length);
}
ber_bvfree(mkey);
}
if (mkvno == 0) {
kerr = KRB5_KDB_NOENTRY;
goto done;
}
*kvno = mkvno;
key->magic = KV5M_KEYBLOCK;
key->enctype = k.enctype;
key->length = k.length;
key->contents = k.contents;
kerr = 0;
done:
if (be) {
ber_free(be, 0);
}
ldap_value_free_len(vals);
ldap_msgfree(res);
return kerr;
}
krb5_error_code ipadb_store_master_key_list(krb5_context kcontext,
char *db_arg,
krb5_principal mname,
krb5_keylist_node *keylist,
char *master_pwd)
{
struct ipadb_context *ipactx;
BerElement *be = NULL;
krb5_keyblock k = { 0, 0, 0, NULL };
struct berval mkey;
ber_int_t tvno;
ber_int_t ttype;
LDAPMod **mods = NULL;
krb5_error_code kerr;
int ret;
ipactx = ipadb_get_context(kcontext);
if (!ipactx) {
return KRB5_KDB_DBNOTINITED;
}
/* we support storing only one key for now */
if (!keylist || keylist->next) {
return EINVAL;
}
if (!ipactx->lcontext) {
ret = ipadb_get_connection(ipactx);
if (ret != 0) {
kerr = KRB5_KDB_SERVER_INTERNAL_ERR;
goto done;
}
}
be = ber_alloc_t(LBER_USE_DER);
if (!be) {
kerr = ENOMEM;
goto done;
}
tvno = keylist->kvno;
ttype = keylist->keyblock.enctype;
mkey.bv_len = keylist->keyblock.length;
mkey.bv_val = (void *)keylist->keyblock.contents;
ret = ber_printf(be, "{i{iO}}", tvno, ttype, &mkey);
if (ret == -1) {
kerr = KRB5_KDB_INTERNAL_ERROR;
goto done;
}
mods = calloc(2, sizeof(LDAPMod *));
if (!mods) {
kerr = ENOMEM;
goto done;
}
mods[0] = calloc(1, sizeof(LDAPMod));
if (!mods[0]) {
kerr = ENOMEM;
goto done;
}
mods[0]->mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES;
mods[0]->mod_type = strdup("krbMKey");
if (!mods[0]->mod_type) {
kerr = ENOMEM;
goto done;
}
mods[0]->mod_bvalues = calloc(2, sizeof(struct berval *));
if (!mods[0]->mod_bvalues) {
kerr = ENOMEM;
goto done;
}
ret = ber_flatten(be, &mods[0]->mod_bvalues[0]);
if (ret == -1) {
kerr = KRB5_KDB_INTERNAL_ERROR;
goto done;
}
kerr = ipadb_simple_modify(ipactx, ipactx->realm_base, mods);
kerr = 0;
done:
if (be) {
ber_free(be, 1);
}
krb5_free_keyblock_contents(kcontext, &k);
ldap_mods_free(mods, 1);
return kerr;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,54 @@
/*
* MIT Kerberos KDC database backend for FreeIPA
* This head file contains private declarations for ipa_kdb_mspac.c and should
* be used only there or in unit-test.
*
* Authors: Sumit Bose <sbose@redhat.com>
*
* see file 'COPYING' for use and warranty information
*
* This program is free software you can redistribute it and/or modify
* 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/>.
*/
#pragma once
struct ipadb_mspac {
char *flat_domain_name;
char *flat_server_name;
struct dom_sid domsid;
char *fallback_group;
uint32_t fallback_rid;
int num_trusts;
struct ipadb_adtrusts *trusts;
time_t last_update;
};
struct ipadb_adtrusts {
char *domain_name;
char *flat_name;
char *domain_sid;
struct dom_sid domsid;
struct dom_sid *sid_blacklist_incoming;
int len_sid_blacklist_incoming;
struct dom_sid *sid_blacklist_outgoing;
int len_sid_blacklist_outgoing;
struct ipadb_adtrusts *parent;
char *parent_name;
char **upn_suffixes;
};
int string_to_sid(const char *str, struct dom_sid *sid);
char *dom_sid_string(TALLOC_CTX *memctx, const struct dom_sid *dom_sid);

View File

@@ -0,0 +1,279 @@
/*
* MIT Kerberos KDC database backend for FreeIPA
*
* Authors: Simo Sorce <ssorce@redhat.com>
*
* Copyright (C) 2011 Simo Sorce, Red Hat
* see file 'COPYING' for use and warranty information
*
* This program is free software you can redistribute it and/or modify
* 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 "ipa_kdb.h"
#include "ipa_pwd.h"
#include <kadm5/kadm_err.h>
static krb5_error_code ipapwd_error_to_kerr(krb5_context context,
enum ipapwd_error err)
{
krb5_error_code kerr;
switch(err) {
case IPAPWD_POLICY_OK:
kerr = 0;
break;
case IPAPWD_POLICY_ACCOUNT_EXPIRED:
kerr = KADM5_BAD_PRINCIPAL;
krb5_set_error_message(context, kerr, "Account expired");
break;
case IPAPWD_POLICY_PWD_TOO_YOUNG:
kerr = KADM5_PASS_TOOSOON;
krb5_set_error_message(context, kerr, "Too soon to change password");
break;
case IPAPWD_POLICY_PWD_TOO_SHORT:
kerr = KADM5_PASS_Q_TOOSHORT;
krb5_set_error_message(context, kerr, "Password is too short");
break;
case IPAPWD_POLICY_PWD_IN_HISTORY:
kerr = KADM5_PASS_REUSE;
krb5_set_error_message(context, kerr, "Password reuse not permitted");
break;
case IPAPWD_POLICY_PWD_COMPLEXITY:
kerr = KADM5_PASS_Q_CLASS;
krb5_set_error_message(context, kerr, "Password is too simple");
break;
default:
kerr = KADM5_PASS_Q_GENERIC;
break;
}
return kerr;
}
static krb5_error_code ipadb_check_pw_policy(krb5_context context,
char *passwd,
krb5_db_entry *db_entry)
{
krb5_error_code kerr;
struct ipadb_e_data *ied;
struct ipadb_context *ipactx;
int ret;
ipactx = ipadb_get_context(context);
if (!ipactx) {
return KRB5_KDB_DBNOTINITED;
}
ied = (struct ipadb_e_data *)db_entry->e_data;
if (ied->magic != IPA_E_DATA_MAGIC) {
return EINVAL;
}
ied->passwd = strdup(passwd);
if (!ied->passwd) {
return ENOMEM;
}
kerr = ipadb_get_ipapwd_policy(ipactx, ied->pw_policy_dn, &ied->pol);
if (kerr != 0) {
return kerr;
}
ret = ipapwd_check_policy(ied->pol, passwd, time(NULL),
db_entry->expiration,
db_entry->pw_expiration,
ied->last_pwd_change,
ied->pw_history);
return ipapwd_error_to_kerr(context, ret);
}
krb5_error_code ipadb_change_pwd(krb5_context context,
krb5_keyblock *master_key,
krb5_key_salt_tuple *ks_tuple,
int ks_tuple_count, char *passwd,
int new_kvno, krb5_boolean keepold,
krb5_db_entry *db_entry)
{
krb5_error_code kerr;
krb5_data pwd;
struct ipadb_context *ipactx;
struct ipadb_e_data *ied;
krb5_key_salt_tuple *fks = NULL;
int n_fks;
krb5_key_data *keys = NULL;
int n_keys;
krb5_key_data *tdata;
int t_keys;
int old_kvno;
int ret;
int i;
ipactx = ipadb_get_context(context);
if (!ipactx) {
return KRB5_KDB_DBNOTINITED;
}
if (!db_entry->e_data) {
if (!ipactx->override_restrictions) {
return EINVAL;
} else {
/* kadmin is creating a new principal */
ied = calloc(1, sizeof(struct ipadb_e_data));
if (!ied) {
return ENOMEM;
}
ied->magic = IPA_E_DATA_MAGIC;
/* set the default policy on new entries */
ret = asprintf(&ied->pw_policy_dn,
"cn=global_policy,%s", ipactx->realm_base);
if (ret == -1) {
free(ied);
return ENOMEM;
}
db_entry->e_data = (krb5_octet *)ied;
}
}
/* check pwd policy before doing any other work */
kerr = ipadb_check_pw_policy(context, passwd, db_entry);
if (kerr) {
return kerr;
}
old_kvno = krb5_db_get_key_data_kvno(context, db_entry->n_key_data,
db_entry->key_data);
if (old_kvno >= new_kvno) {
new_kvno = old_kvno + 1;
}
pwd.data = passwd;
pwd.length = strlen(passwd);
/* detect if kadmin is just passing along the default set */
if (ks_tuple_count == ipactx->n_supp_encs) {
for (i = 0; i < ks_tuple_count; i++) {
if (ks_tuple[i].ks_enctype != ipactx->supp_encs[i].ks_enctype)
break;
if (ks_tuple[i].ks_salttype != ipactx->supp_encs[i].ks_salttype)
break;
}
if (i == ks_tuple_count) {
/* we got passed the default supported enctypes, replace with
* the actual default enctypes to use */
ks_tuple = ipactx->def_encs;
ks_tuple_count = ipactx->n_def_encs;
}
}
/* We further filter supported enctypes to restrict to the list
* we have in ldap */
kerr = filter_key_salt_tuples(context, ks_tuple, ks_tuple_count,
ipactx->supp_encs, ipactx->n_supp_encs,
&fks, &n_fks);
if (kerr) {
return kerr;
}
kerr = ipa_krb5_generate_key_data(context, db_entry->princ,
pwd, new_kvno, master_key,
n_fks, fks, &n_keys, &keys);
free(fks);
if (kerr) {
return kerr;
}
if (keepold) {
/* need to add the new keys to the old list */
t_keys = db_entry->n_key_data;
tdata = realloc(db_entry->key_data,
sizeof(krb5_key_data) * (t_keys + n_keys));
if (!tdata) {
ipa_krb5_free_key_data(keys, n_keys);
return ENOMEM;
}
db_entry->key_data = tdata;
db_entry->n_key_data = t_keys + n_keys;
for (i = 0; i < n_keys; i++) {
db_entry->key_data[t_keys + i] = keys[i];
}
free(keys);
} else {
ipa_krb5_free_key_data(db_entry->key_data, db_entry->n_key_data);
db_entry->key_data = keys;
db_entry->n_key_data = n_keys;
}
return 0;
}
/*
* Check who actually changed the password, if it is not 'self' then
* we need to expire it if it is a user principal.
*/
krb5_error_code ipadb_get_pwd_expiration(krb5_context context,
krb5_db_entry *entry,
struct ipadb_e_data *ied,
time_t *expire_time)
{
krb5_error_code kerr;
krb5_timestamp mod_time = 0;
krb5_principal mod_princ = NULL;
krb5_boolean truexp = true;
if (ied->ipa_user) {
kerr = krb5_dbe_lookup_mod_princ_data(context, entry,
&mod_time, &mod_princ);
if (kerr) {
goto done;
}
/* If the mod principal is kadmind then we have to assume an actual
* password change for now. Apparently kadmind does not properly pass
* the actual user principal down when said user is performing a
* password change */
if (mod_princ->length == 1 &&
strcmp(mod_princ->data[0].data, "kadmind") != 0) {
truexp = krb5_principal_compare(context, mod_princ, entry->princ);
}
}
if (truexp) {
if (ied->pol) {
if (ied->pol->max_pwd_life) {
*expire_time = mod_time + ied->pol->max_pwd_life;
} else {
*expire_time = 0;
}
} else {
*expire_time = mod_time + IPAPWD_DEFAULT_PWDLIFE;
}
} else {
/* not 'self', so reset */
*expire_time = mod_time;
}
/* in the case of integer owerflow, set expiration to IPAPWD_END_OF_TIME */
if ((*expire_time) < 0 || (*expire_time) > IPAPWD_END_OF_TIME) {
*expire_time = IPAPWD_END_OF_TIME; // 1 Jan 2038, 00:00 GMT
}
kerr = 0;
done:
krb5_free_principal(context, mod_princ);
return kerr;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,344 @@
/*
* MIT Kerberos KDC database backend for FreeIPA
*
* Authors: Simo Sorce <ssorce@redhat.com>
*
* Copyright (C) 2011 Simo Sorce, Red Hat
* see file 'COPYING' for use and warranty information
*
* This program is free software you can redistribute it and/or modify
* 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 "ipa_kdb.h"
#include "ipa_pwd.h"
#define POLICY_SEARCH_FILTER "(&(objectClass=krbPwdPolicy)(cn=%s))"
char *std_pwdpolicy_attrs[] = {
"krbmaxpwdlife",
"krbminpwdlife",
"krbpwdmindiffchars",
"krbpwdminlength",
"krbpwdhistorylength",
"krbpwdmaxfailure",
"krbpwdfailurecountinterval",
"krbpwdlockoutduration",
NULL
};
krb5_error_code ipadb_get_ipapwd_policy(struct ipadb_context *ipactx,
char *pw_policy_dn,
struct ipapwd_policy **_pol)
{
struct ipapwd_policy *pol;
krb5_error_code kerr;
LDAPMessage *res = NULL;
LDAPMessage *lentry;
uint32_t result;
int ret;
pol = calloc(1, sizeof(struct ipapwd_policy));
if (!pol) {
return ENOMEM;
}
pol->max_pwd_life = IPAPWD_DEFAULT_PWDLIFE;
pol->min_pwd_length = IPAPWD_DEFAULT_MINLEN;
kerr = ipadb_simple_search(ipactx, pw_policy_dn, LDAP_SCOPE_BASE,
"(objectClass=*)", std_pwdpolicy_attrs, &res);
if (kerr) {
goto done;
}
lentry = ldap_first_entry(ipactx->lcontext, res);
if (!lentry) {
kerr = KRB5_KDB_INTERNAL_ERROR;
goto done;
}
ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
"krbMinPwdLife", &result);
if (ret == 0) {
pol->min_pwd_life = result;
}
ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
"krbMaxPwdLife", &result);
if (ret == 0) {
pol->max_pwd_life = result;
}
ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
"krbPwdMinLength", &result);
if (ret == 0) {
pol->min_pwd_length = result;
}
ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
"krbPwdHistoryLength", &result);
if (ret == 0) {
pol->history_length = result;
}
ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
"krbPwdMinDiffChars", &result);
if (ret == 0) {
pol->min_complexity = result;
}
ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
"krbPwdMaxFailure", &result);
if (ret == 0) {
pol->max_fail = result;
}
ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
"krbPwdFailureCountInterval", &result);
if (ret == 0) {
pol->failcnt_interval = result;
}
ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
"krbPwdLockoutDuration", &result);
if (ret == 0) {
pol->lockout_duration = result;
}
*_pol = pol;
done:
ldap_msgfree(res);
if (kerr) {
free(pol);
}
return kerr;
}
krb5_error_code ipadb_create_pwd_policy(krb5_context kcontext,
osa_policy_ent_t policy)
{
return KRB5_PLUGIN_OP_NOTSUPP;
}
krb5_error_code ipadb_get_pwd_policy(krb5_context kcontext, char *name,
osa_policy_ent_t *policy)
{
struct ipadb_context *ipactx;
char *bases[3] = { NULL };
char *esc_name = NULL;
char *src_filter = NULL;
krb5_error_code kerr;
struct ipadb_multires *res;
LDAPMessage *lentry;
osa_policy_ent_t pentry = NULL;
uint32_t result;
int ret;
ipactx = ipadb_get_context(kcontext);
if (!ipactx) {
return KRB5_KDB_DBNOTINITED;
}
bases[0] = ipactx->realm_base;
bases[1] = ipactx->accounts_base;
esc_name = ipadb_filter_escape(name, true);
if (!esc_name) {
return ENOMEM;
}
ret = asprintf(&src_filter, POLICY_SEARCH_FILTER, esc_name);
if (ret == -1) {
kerr = KRB5_KDB_INTERNAL_ERROR;
goto done;
}
kerr = ipadb_multibase_search(ipactx, bases, LDAP_SCOPE_SUBTREE,
src_filter, std_pwdpolicy_attrs, &res,
true);
if (kerr) {
goto done;
}
lentry = ipadb_multires_next_entry(res);
if (!lentry) {
kerr = KRB5_KDB_INTERNAL_ERROR;
goto done;
}
pentry = calloc(1, sizeof(osa_policy_ent_rec));
if (!pentry) {
kerr = ENOMEM;
goto done;
}
pentry->version = 1;
pentry->name = strdup(name);
if (!pentry->name) {
kerr = ENOMEM;
goto done;
}
/* FIXME: what to do with missing attributes ? */
ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
"krbMinPwdLife", &result);
if (ret == 0) {
pentry->pw_min_life = result;
}
ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
"krbMaxPwdLife", &result);
if (ret == 0) {
pentry->pw_max_life = result;
}
ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
"krbPwdMinLength", &result);
if (ret == 0) {
pentry->pw_min_length = result;
}
ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
"krbPwdMinDiffChars", &result);
if (ret == 0) {
pentry->pw_min_classes = result;
}
ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
"krbPwdHistoryLength", &result);
if (ret == 0) {
pentry->pw_history_num = result;
}
ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
"krbPwdMaxFailure", &result);
if (ret == 0) {
pentry->pw_max_fail = result;
}
ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
"krbPwdFailureCountInterval", &result);
if (ret == 0) {
pentry->pw_failcnt_interval = result;
}
ret = ipadb_ldap_attr_to_uint32(ipactx->lcontext, lentry,
"krbPwdLockoutDuration", &result);
if (ret == 0) {
pentry->pw_lockout_duration = result;
}
ret = ipa_kstuples_to_string(ipactx->supp_encs, ipactx->n_supp_encs,
&pentry->allowed_keysalts);
if (ret != 0) {
kerr = KRB5_KDB_INTERNAL_ERROR;
goto done;
}
*policy = pentry;
done:
if (kerr) {
free(pentry);
}
free(esc_name);
free(src_filter);
ipadb_multires_free(res);
return kerr;
}
krb5_error_code ipadb_put_pwd_policy(krb5_context kcontext,
osa_policy_ent_t policy)
{
return KRB5_PLUGIN_OP_NOTSUPP;
}
krb5_error_code ipadb_iterate_pwd_policy(krb5_context kcontext,
char *match_entry,
osa_adb_iter_policy_func func,
void *data)
{
return KRB5_PLUGIN_OP_NOTSUPP;
}
krb5_error_code ipadb_delete_pwd_policy(krb5_context kcontext,
char *policy)
{
return KRB5_PLUGIN_OP_NOTSUPP;
}
void ipadb_free_pwd_policy(krb5_context kcontext, osa_policy_ent_t val)
{
if (val) {
free(val->name);
free(val->allowed_keysalts);
free(val);
}
}
krb5_error_code ipadb_check_policy_as(krb5_context kcontext,
krb5_kdc_req *request,
krb5_db_entry *client,
krb5_db_entry *server,
krb5_timestamp kdc_time,
const char **status,
krb5_pa_data ***e_data)
{
struct ipadb_context *ipactx;
struct ipadb_e_data *ied;
krb5_error_code kerr;
if (!client) {
return ENOENT;
}
ipactx = ipadb_get_context(kcontext);
if (!ipactx) {
return EINVAL;
}
ied = (struct ipadb_e_data *)client->e_data;
if (!ied) {
return EINVAL;
}
if (!ied->pol) {
kerr = ipadb_get_ipapwd_policy(ipactx, ied->pw_policy_dn, &ied->pol);
if (kerr != 0) {
return kerr;
}
}
if (client->last_failed <= ied->last_admin_unlock) {
/* admin unlocked the account */
return 0;
}
if (ied->pol->max_fail == 0 ||
client->fail_auth_count < ied->pol->max_fail) {
/* still within allowed failures range */
return 0;
}
if (ied->pol->lockout_duration == 0 ||
client->last_failed + ied->pol->lockout_duration > kdc_time) {
/* ok client permanently locked, or within lockout period */
*status = "LOCKED_OUT";
return KRB5KDC_ERR_CLIENT_REVOKED;
}
return 0;
}

View File

@@ -0,0 +1,494 @@
/*
Authors:
Sumit Bose <sbose@redhat.com>
Copyright (C) 2015 Red Hat
ipa-kdb tests
This program is free software; you can redistribute it and/or modify
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 <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <setjmp.h>
#include <cmocka.h>
#include <talloc.h>
#include "gen_ndr/ndr_krb5pac.h"
#include "gen_ndr/netlogon.h"
#include "ipa_kdb.h"
#include "ipa_kdb_mspac_private.h"
#define NFS_PRINC_STRING "nfs/fully.qualified.host.name@REALM.NAME"
#define NON_NFS_PRINC_STRING "abcdef/fully.qualified.host.name@REALM.NAME"
int krb5_klog_syslog(int l, const char *format, ...)
{
va_list ap;
char *s = NULL;
int ret;
va_start(ap, format);
ret = vasprintf(&s, format, ap);
va_end(ap);
if (ret < 0) {
/* ENOMEM */
return -1;
}
fprintf(stderr, "%s\n", s);
free(s);
return 0;
}
struct test_ctx {
krb5_context krb5_ctx;
};
#define DOMAIN_NAME "my.domain"
#define REALM "MY.DOMAIN"
#define REALM_LEN (sizeof(REALM) - 1)
#define FLAT_NAME "MYDOM"
#define DOM_SID "S-1-5-21-1-2-3"
#define DOM_SID_TRUST "S-1-5-21-4-5-6"
#define BLACKLIST_SID "S-1-5-1"
static int setup(void **state)
{
int ret;
krb5_context krb5_ctx;
krb5_error_code kerr;
struct ipadb_context *ipa_ctx;
struct test_ctx *test_ctx;
kerr = krb5_init_context(&krb5_ctx);
assert_int_equal(kerr, 0);
kerr = krb5_set_default_realm(krb5_ctx, "EXAMPLE.COM");
assert_int_equal(kerr, 0);
kerr = krb5_db_setup_lib_handle(krb5_ctx);
assert_int_equal(kerr, 0);
ipa_ctx = calloc(1, sizeof(struct ipadb_context));
assert_non_null(ipa_ctx);
ipa_ctx->mspac = calloc(1, sizeof(struct ipadb_mspac));
assert_non_null(ipa_ctx->mspac);
/* make sure data is not read from LDAP */
ipa_ctx->mspac->last_update = time(NULL) - 1;
ret = string_to_sid(DOM_SID, &ipa_ctx->mspac->domsid);
assert_int_equal(ret, 0);
ipa_ctx->mspac->num_trusts = 1;
ipa_ctx->mspac->trusts = calloc(1, sizeof(struct ipadb_adtrusts));
assert_non_null(ipa_ctx->mspac->trusts);
ipa_ctx->mspac->trusts[0].domain_name = strdup(DOMAIN_NAME);
assert_non_null(ipa_ctx->mspac->trusts[0].domain_name);
ipa_ctx->mspac->trusts[0].flat_name = strdup(FLAT_NAME);
assert_non_null(ipa_ctx->mspac->trusts[0].flat_name);
ipa_ctx->mspac->trusts[0].domain_sid = strdup(DOM_SID_TRUST);
assert_non_null(ipa_ctx->mspac->trusts[0].domain_sid);
ret = string_to_sid(DOM_SID_TRUST, &ipa_ctx->mspac->trusts[0].domsid);
assert_int_equal(ret, 0);
ipa_ctx->mspac->trusts[0].len_sid_blacklist_incoming = 1;
ipa_ctx->mspac->trusts[0].sid_blacklist_incoming = calloc(
ipa_ctx->mspac->trusts[0].len_sid_blacklist_incoming,
sizeof(struct dom_sid));
assert_non_null(ipa_ctx->mspac->trusts[0].sid_blacklist_incoming);
ret = string_to_sid(BLACKLIST_SID,
&ipa_ctx->mspac->trusts[0].sid_blacklist_incoming[0]);
assert_int_equal(ret, 0);
ipa_ctx->kcontext = krb5_ctx;
kerr = krb5_db_set_context(krb5_ctx, ipa_ctx);
assert_int_equal(kerr, 0);
test_ctx = talloc(NULL, struct test_ctx);
assert_non_null(test_ctx);
test_ctx->krb5_ctx = krb5_ctx;
*state = test_ctx;
return 0;
}
static int teardown(void **state)
{
struct test_ctx *test_ctx;
struct ipadb_context *ipa_ctx;
test_ctx = (struct test_ctx *) *state;
ipa_ctx = ipadb_get_context(test_ctx->krb5_ctx);
assert_non_null(ipa_ctx);
ipadb_mspac_struct_free(&ipa_ctx->mspac);
krb5_db_fini(test_ctx->krb5_ctx);
krb5_free_context(test_ctx->krb5_ctx);
talloc_free(test_ctx);
return 0;
}
extern krb5_error_code filter_logon_info(krb5_context context,
TALLOC_CTX *memctx,
krb5_data realm,
struct PAC_LOGON_INFO_CTR *info);
void test_filter_logon_info(void **state)
{
krb5_error_code kerr;
krb5_data realm = {KV5M_DATA, REALM_LEN, REALM};
struct test_ctx *test_ctx;
struct PAC_LOGON_INFO_CTR *info;
int ret;
struct dom_sid dom_sid;
size_t c;
size_t d;
test_ctx = (struct test_ctx *) *state;
info = talloc_zero(test_ctx, struct PAC_LOGON_INFO_CTR);
assert_non_null(info);
info->info = talloc_zero(info, struct PAC_LOGON_INFO);
assert_non_null(info->info);
/* wrong flat name */
info->info->info3.base.logon_domain.string = talloc_strdup(info->info,
"WRONG");
assert_non_null(info->info->info3.base.logon_domain.string);
kerr = filter_logon_info(test_ctx->krb5_ctx, test_ctx, realm, info);
assert_int_equal(kerr, EINVAL);
info->info->info3.base.logon_domain.string = talloc_strdup(info->info,
FLAT_NAME);
assert_non_null(info->info->info3.base.logon_domain.string);
/* missing domain SID */
kerr = filter_logon_info(test_ctx->krb5_ctx, test_ctx, realm, info);
assert_int_equal(kerr, EINVAL);
/* wrong domain SID */
ret = string_to_sid("S-1-5-21-1-1-1", &dom_sid);
assert_int_equal(ret, 0);
info->info->info3.base.domain_sid = &dom_sid;
kerr = filter_logon_info(test_ctx->krb5_ctx, test_ctx, realm, info);
assert_int_equal(kerr, EINVAL);
/* matching domain SID */
ret = string_to_sid(DOM_SID_TRUST, &dom_sid);
assert_int_equal(ret, 0);
info->info->info3.base.domain_sid = &dom_sid;
kerr = filter_logon_info(test_ctx->krb5_ctx, test_ctx, realm, info);
assert_int_equal(kerr, 0);
/* empty SIDs */
info->info->info3.sidcount = 3;
info->info->info3.sids = talloc_zero_array(info->info,
struct netr_SidAttr,
info->info->info3.sidcount);
assert_non_null(info->info->info3.sids);
for(c = 0; c < info->info->info3.sidcount; c++) {
info->info->info3.sids[c].sid = talloc_zero(info->info->info3.sids,
struct dom_sid2);
assert_non_null(info->info->info3.sids[c].sid);
}
kerr = filter_logon_info(test_ctx->krb5_ctx, NULL, realm, info);
assert_int_equal(kerr, 0);
assert_int_equal(info->info->info3.sidcount, 3);
struct test_data {
size_t sidcount;
const char *sids[3];
size_t exp_sidcount;
const char *exp_sids[3];
} test_data[] = {
/* only allowed SIDs */
{3, {DOM_SID_TRUST"-1000", DOM_SID_TRUST"-1001", DOM_SID_TRUST"-1002"},
3, {DOM_SID_TRUST"-1000", DOM_SID_TRUST"-1001", DOM_SID_TRUST"-1002"}},
/* last SID filtered */
{3, {DOM_SID_TRUST"-1000", DOM_SID_TRUST"-1001", BLACKLIST_SID"-1002"},
2, {DOM_SID_TRUST"-1000", DOM_SID_TRUST"-1001"}},
/* center SID filtered */
{3, {DOM_SID_TRUST"-1000", BLACKLIST_SID"-1001", DOM_SID_TRUST"-1002"},
2, {DOM_SID_TRUST"-1000", DOM_SID_TRUST"-1002"}},
/* first SID filtered */
{3, {BLACKLIST_SID"-1000", DOM_SID_TRUST"-1001", DOM_SID_TRUST"-1002"},
2, {DOM_SID_TRUST"-1001", DOM_SID_TRUST"-1002"}},
/* first and last SID filtered */
{3, {BLACKLIST_SID"-1000", DOM_SID_TRUST"-1001", BLACKLIST_SID"-1002"},
1, {DOM_SID_TRUST"-1001"}},
/* two SIDs in a rwo filtered */
{3, {BLACKLIST_SID"-1000", BLACKLIST_SID"-1001", DOM_SID_TRUST"-1002"},
1, {DOM_SID_TRUST"-1002"}},
/* all SIDs filtered*/
{3, {BLACKLIST_SID"-1000", BLACKLIST_SID"-1001", BLACKLIST_SID"-1002"},
0, {}},
{0, {}, 0 , {}}
};
for (c = 0; test_data[c].sidcount != 0; c++) {
talloc_free(info->info->info3.sids);
info->info->info3.sidcount = test_data[c].sidcount;
info->info->info3.sids = talloc_zero_array(info->info,
struct netr_SidAttr,
info->info->info3.sidcount);
assert_non_null(info->info->info3.sids);
for(d = 0; d < info->info->info3.sidcount; d++) {
info->info->info3.sids[d].sid = talloc_zero(info->info->info3.sids,
struct dom_sid2);
assert_non_null(info->info->info3.sids[d].sid);
}
for (d = 0; d < info->info->info3.sidcount; d++) {
ret = string_to_sid(test_data[c].sids[d],
info->info->info3.sids[d].sid);
assert_int_equal(ret, 0);
}
kerr = filter_logon_info(test_ctx->krb5_ctx, NULL, realm, info);
assert_int_equal(kerr, 0);
assert_int_equal(info->info->info3.sidcount, test_data[c].exp_sidcount);
if (test_data[c].exp_sidcount == 0) {
assert_null(info->info->info3.sids);
} else {
for (d = 0; d < test_data[c].exp_sidcount; d++) {
assert_string_equal(test_data[c].exp_sids[d],
dom_sid_string(info->info->info3.sids,
info->info->info3.sids[d].sid));
}
}
}
talloc_free(info);
}
extern void get_authz_data_types(krb5_context context, krb5_db_entry *entry,
bool *with_pac, bool *with_pad);
void test_get_authz_data_types(void **state)
{
bool with_pac;
bool with_pad;
krb5_db_entry *entry;
struct ipadb_e_data *ied;
size_t c;
char *ad_none_only[] = {"NONE", NULL};
char *ad_pad_only[] = {"PAD", NULL};
char *ad_pac_only[] = {"MS-PAC", NULL};
char *ad_illegal_only[] = {"abc", NULL};
char *ad_pac_and_pad[] = {"MS-PAC", "PAD", NULL};
char *ad_pac_and_none[] = {"MS-PAC", "NONE", NULL};
char *ad_none_and_pad[] = {"NONE", "PAD", NULL};
char *ad_global_pac_nfs_none[] = {"MS-PAC", "nfs:NONE", NULL};
char *ad_global_pac_nfs_pad[] = {"MS-PAC", "nfs:PAD", NULL};
krb5_error_code kerr;
struct ipadb_context *ipa_ctx;
krb5_principal nfs_princ;
krb5_principal non_nfs_princ;
struct test_ctx *test_ctx;
test_ctx = (struct test_ctx *) *state;
ipa_ctx = ipadb_get_context(test_ctx->krb5_ctx);
assert_non_null(ipa_ctx);
get_authz_data_types(NULL, NULL, NULL, NULL);
with_pad = true;
get_authz_data_types(NULL, NULL, NULL, &with_pad);
assert_false(with_pad);
with_pac = true;
get_authz_data_types(NULL, NULL, &with_pac, NULL);
assert_false(with_pad);
with_pad = true;
with_pac = true;
get_authz_data_types(NULL, NULL, &with_pac, &with_pad);
assert_false(with_pac);
assert_false(with_pad);
entry = calloc(1, sizeof(krb5_db_entry));
assert_non_null(entry);
ied = calloc(1, sizeof(struct ipadb_e_data));
assert_non_null(ied);
entry->e_data = (void *) ied;
kerr = krb5_parse_name(test_ctx->krb5_ctx, NFS_PRINC_STRING, &nfs_princ);
assert_int_equal(kerr, 0);
kerr = krb5_parse_name(test_ctx->krb5_ctx, NON_NFS_PRINC_STRING,
&non_nfs_princ);
assert_int_equal(kerr, 0);
struct test_set {
char **authz_data;
char **global_authz_data;
krb5_principal princ;
bool exp_with_pac;
bool exp_with_pad;
const char *err_msg;
} test_set[] = {
{ad_none_only, NULL, NULL, false, false, "with only NONE in entry"},
{ad_pac_only, NULL, NULL, true, false, "with only MS-PAC in entry"},
{ad_pad_only, NULL, NULL, false, true, "with only PAD in entry"},
{ad_illegal_only, NULL, NULL, false, false, "with only an invalid value in entry"},
{ad_pac_and_pad, NULL, NULL, true, true, "with MS-PAC and PAD in entry"},
{ad_pac_and_none, NULL, NULL, false, false, "with MS-PAC and NONE in entry"},
{ad_none_and_pad, NULL, NULL, false, false, "with NONE and PAD in entry"},
{NULL, ad_none_only, NULL, false, false, "with only NONE in global config"},
{NULL, ad_pac_only, NULL, true, false, "with only MS-PAC in global config"},
{NULL, ad_pad_only, NULL, false, true, "with only PAD in global config"},
{NULL, ad_illegal_only, NULL, false, false, "with only an invalid value in global config"},
{NULL, ad_pac_and_pad, NULL, true, true, "with MS-PAC and PAD in global config"},
{NULL, ad_pac_and_none, NULL, false, false, "with MS-PAC and NONE in global config"},
{NULL, ad_none_and_pad, NULL, false, false, "with NONE and PAD in global entry"},
{NULL, ad_global_pac_nfs_none, NULL, true, false, "with NULL principal and PAC and nfs:NONE in global entry"},
{NULL, ad_global_pac_nfs_none, nfs_princ, false, false, "with nfs principal and PAC and nfs:NONE in global entry"},
{NULL, ad_global_pac_nfs_none, non_nfs_princ, true, false, "with non-nfs principal and PAC and nfs:NONE in global entry"},
{NULL, ad_global_pac_nfs_pad, NULL, true, false, "with NULL principal and PAC and nfs:PAD in global entry"},
{NULL, ad_global_pac_nfs_pad, nfs_princ, false, true, "with nfs principal and PAC and nfs:PAD in global entry"},
{NULL, ad_global_pac_nfs_pad, non_nfs_princ, true, false, "with non-nfs principal and PAC and nfs:PAD in global entry"},
{ad_none_only, ad_pac_only, NULL, false, false, "with NONE overriding PAC in global entry"},
{ad_pad_only, ad_pac_only, NULL, false, true, "with PAC overriding PAC in global entry"},
{ad_illegal_only, ad_pac_only, NULL, false, false, "with invalid value overriding PAC in global entry"},
{ad_pac_and_pad, ad_pac_only, NULL, true, true, "with PAC and PAD overriding PAC in global entry"},
{ad_none_and_pad, ad_pac_only, NULL, false, false, "with NONE and PAD overriding PAC in global entry"},
{NULL, NULL, NULL, false, false, NULL}
};
for (c = 0; test_set[c].authz_data != NULL ||
test_set[c].global_authz_data != NULL; c++) {
ied->authz_data = test_set[c].authz_data;
ipa_ctx->config.authz_data = test_set[c].global_authz_data;
/* Set last_update to avoid LDAP lookups during tests */
ipa_ctx->config.last_update = time(NULL);
entry->princ = test_set[c].princ;
get_authz_data_types(test_ctx->krb5_ctx, entry, &with_pac, &with_pad);
assert_true(with_pad == test_set[c].exp_with_pad);
assert_true(with_pac == test_set[c].exp_with_pac);
/* test if global default are returned if there is no server entry */
if (test_set[c].authz_data == NULL && test_set[c].princ == NULL) {
get_authz_data_types(test_ctx->krb5_ctx, NULL, &with_pac,
&with_pad);
assert_true(with_pad == test_set[c].exp_with_pad);
assert_true(with_pac == test_set[c].exp_with_pac);
}
}
free(ied);
free(entry);
krb5_free_principal(test_ctx->krb5_ctx, nfs_princ);
krb5_free_principal(test_ctx->krb5_ctx, non_nfs_princ);
}
void test_string_to_sid(void **state)
{
int ret;
struct dom_sid sid;
struct dom_sid exp_sid = {1, 5, {0, 0, 0, 0, 0, 5},
{21, 2127521184, 1604012920, 1887927527, 72713,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
ret = string_to_sid(NULL, &sid);
assert_int_equal(ret, EINVAL);
ret = string_to_sid("abc", &sid);
assert_int_equal(ret, EINVAL);
ret = string_to_sid("S-", &sid);
assert_int_equal(ret, EINVAL);
ret = string_to_sid("S-ABC", &sid);
assert_int_equal(ret, EINVAL);
ret = string_to_sid("S-123", &sid);
assert_int_equal(ret, EINVAL);
ret = string_to_sid("S-1-123-1-2-3-4-5-6-7-8-9-0-1-2-3-4-5-6", &sid);
assert_int_equal(ret, EINVAL);
ret = string_to_sid("S-1-5-21-2127521184-1604012920-1887927527-72713",
&sid);
assert_int_equal(ret, 0);
assert_memory_equal(&exp_sid, &sid, sizeof(struct dom_sid));
}
void test_dom_sid_string(void **state)
{
struct test_ctx *test_ctx;
char *str_sid;
struct dom_sid test_sid = {1, 5, {0, 0, 0, 0, 0, 5},
{21, 2127521184, 1604012920, 1887927527, 72713,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
test_ctx = (struct test_ctx *) *state;
str_sid = dom_sid_string(test_ctx, NULL);
assert_null(str_sid);
str_sid = dom_sid_string(test_ctx, &test_sid);
assert_non_null(str_sid);
assert_string_equal(str_sid,
"S-1-5-21-2127521184-1604012920-1887927527-72713");
test_sid.num_auths = -3;
str_sid = dom_sid_string(test_ctx, &test_sid);
test_sid.num_auths = 16;
str_sid = dom_sid_string(test_ctx, &test_sid);
}
int main(int argc, const char *argv[])
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(test_get_authz_data_types,
setup, teardown),
cmocka_unit_test_setup_teardown(test_filter_logon_info,
setup, teardown),
cmocka_unit_test(test_string_to_sid),
cmocka_unit_test_setup_teardown(test_dom_sid_string,
setup, teardown),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}

View File

@@ -0,0 +1,2 @@
KRB5_CONFIG=/dev/null
export KRB5_CONFIG