freeipa/daemons/ipa-kdb/ipa_kdb_common.c
Simo Sorce d8de2d1b7b ipa-kdb: Be flexible
Although the proper values for booleans from LDAP should be only uppercase,
389ds does allow wrong cased values without complaining. And we still have some
places where the wrong case is used.
Avoid getting frustrating errors when reading these values out.
2011-08-26 08:24:50 -04:00

433 lines
11 KiB
C

/*
* 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 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;
/* 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;
}
krb5_error_code ipadb_simple_search(struct ipadb_context *ipactx,
char *basedn, int scope,
char *filter, char **attrs,
LDAPMessage **res)
{
int 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)) {
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 = 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 = 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 = 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;
}
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_has_value(LDAP *lcontext, LDAPMessage *le,
char *attrname, char *value)
{
struct berval **vals;
int ret = ENOENT;
int i;
vals = ldap_get_values_len(lcontext, le, attrname);
if (vals) {
for (i = 0; vals[i]; i++) {
if (strcasecmp(vals[i]->bv_val, value) == 0) {
ret = 0;
break;
}
}
ldap_value_free_len(vals);
}
return ret;
}