Imported Upstream version 4.6.2
This commit is contained in:
108
daemons/ipa-kdb/Makefile.am
Normal file
108
daemons/ipa-kdb/Makefile.am
Normal 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
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
1
daemons/ipa-kdb/README
Normal file
@@ -0,0 +1 @@
|
||||
This is the ipa krb5kdc database backend.
|
||||
136
daemons/ipa-kdb/README.s4u2proxy.txt
Normal file
136
daemons/ipa-kdb/README.s4u2proxy.txt
Normal 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
748
daemons/ipa-kdb/ipa_kdb.c
Normal 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
|
||||
|
||||
11
daemons/ipa-kdb/ipa_kdb.exports
Normal file
11
daemons/ipa-kdb/ipa_kdb.exports
Normal 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
346
daemons/ipa-kdb/ipa_kdb.h
Normal 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
|
||||
138
daemons/ipa-kdb/ipa_kdb_audit_as.c
Normal file
138
daemons/ipa-kdb/ipa_kdb_audit_as.c
Normal 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;
|
||||
}
|
||||
439
daemons/ipa-kdb/ipa_kdb_certauth.c
Normal file
439
daemons/ipa-kdb/ipa_kdb_certauth.c
Normal 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;
|
||||
}
|
||||
715
daemons/ipa-kdb/ipa_kdb_common.c
Normal file
715
daemons/ipa-kdb/ipa_kdb_common.c
Normal 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);
|
||||
}
|
||||
|
||||
214
daemons/ipa-kdb/ipa_kdb_delegation.c
Normal file
214
daemons/ipa-kdb/ipa_kdb_delegation.c
Normal 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;
|
||||
}
|
||||
225
daemons/ipa-kdb/ipa_kdb_mkey.c
Normal file
225
daemons/ipa-kdb/ipa_kdb_mkey.c
Normal 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;
|
||||
}
|
||||
2912
daemons/ipa-kdb/ipa_kdb_mspac.c
Normal file
2912
daemons/ipa-kdb/ipa_kdb_mspac.c
Normal file
File diff suppressed because it is too large
Load Diff
54
daemons/ipa-kdb/ipa_kdb_mspac_private.h
Normal file
54
daemons/ipa-kdb/ipa_kdb_mspac_private.h
Normal 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);
|
||||
279
daemons/ipa-kdb/ipa_kdb_passwords.c
Normal file
279
daemons/ipa-kdb/ipa_kdb_passwords.c
Normal 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;
|
||||
}
|
||||
2610
daemons/ipa-kdb/ipa_kdb_principals.c
Normal file
2610
daemons/ipa-kdb/ipa_kdb_principals.c
Normal file
File diff suppressed because it is too large
Load Diff
344
daemons/ipa-kdb/ipa_kdb_pwdpolicy.c
Normal file
344
daemons/ipa-kdb/ipa_kdb_pwdpolicy.c
Normal 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;
|
||||
}
|
||||
494
daemons/ipa-kdb/tests/ipa_kdb_tests.c
Normal file
494
daemons/ipa-kdb/tests/ipa_kdb_tests.c
Normal 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);
|
||||
}
|
||||
2
daemons/ipa-kdb/tests/test_setup.sh
Executable file
2
daemons/ipa-kdb/tests/test_setup.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
KRB5_CONFIG=/dev/null
|
||||
export KRB5_CONFIG
|
||||
Reference in New Issue
Block a user