From 46c803a08d691c067d985791f2c98467219a8adc Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Fri, 10 Jun 2011 14:35:46 -0400 Subject: ipa-kdb: add common utility ldap wrapper functions --- daemons/ipa-kdb/Makefile.am | 1 + daemons/ipa-kdb/ipa_kdb.h | 31 +++ daemons/ipa-kdb/ipa_kdb_common.c | 432 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 464 insertions(+) create mode 100644 daemons/ipa-kdb/ipa_kdb_common.c (limited to 'daemons/ipa-kdb') diff --git a/daemons/ipa-kdb/Makefile.am b/daemons/ipa-kdb/Makefile.am index 309618a29..0d35ea973 100644 --- a/daemons/ipa-kdb/Makefile.am +++ b/daemons/ipa-kdb/Makefile.am @@ -23,6 +23,7 @@ plugin_LTLIBRARIES = \ ipadb_la_SOURCES = \ ipa_kdb.c \ + ipa_kdb_common.c \ $(NULL) ipadb_la_LDFLAGS = \ diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h index 008779f7c..760508d6d 100644 --- a/daemons/ipa-kdb/ipa_kdb.h +++ b/daemons/ipa-kdb/ipa_kdb.h @@ -42,3 +42,34 @@ struct ipadb_context { 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); + +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_has_value(LDAP *lcontext, LDAPMessage *le, + char *attrname, char *value); + diff --git a/daemons/ipa-kdb/ipa_kdb_common.c b/daemons/ipa-kdb/ipa_kdb_common.c new file mode 100644 index 000000000..21b7a4df4 --- /dev/null +++ b/daemons/ipa-kdb/ipa_kdb_common.c @@ -0,0 +1,432 @@ +/* + * MIT Kerberos KDC database backend for FreeIPA + * + * Authors: Simo Sorce + * + * 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 . + */ + +#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 (strcmp("TRUE", vals[0]->bv_val) == 0) { + *result = true; + ret = 0; + } else if (strcmp("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; +} -- cgit