diff options
Diffstat (limited to 'server/providers/ldap/sdap.c')
-rw-r--r-- | server/providers/ldap/sdap.c | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/server/providers/ldap/sdap.c b/server/providers/ldap/sdap.c new file mode 100644 index 00000000..9abcf566 --- /dev/null +++ b/server/providers/ldap/sdap.c @@ -0,0 +1,367 @@ +/* + SSSD + + LDAP Helper routines + + Copyright (C) Simo Sorce <ssorce@redhat.com> + + 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/>. +*/ + +#define LDAP_DEPRECATED 1 +#include <ldap.h> +#include "util/util.h" +#include "confdb/confdb.h" +#include "providers/ldap/sdap.h" + +struct sdap_gen_opts default_basic_opts[] = { + { "ldapUri", "ldap://localhost", NULL }, + { "defaultBindDn", NULL, NULL }, + { "defaultAuthtokType", NULL, NULL }, + { "defaultAuthtok", NULL, NULL }, + { "network_timeout", "5", NULL }, + { "opt_timeout", "5", NULL }, + { "tls_reqcert", "always", NULL }, + { "userSearchBase", "dc=example,dc=com", NULL }, + { "userSearchScope", "sub", NULL }, + { "userSearchFilter", NULL, NULL }, + { "groupSearchBase", "dc=example,dc=com", NULL }, + { "groupSearchScope", "sub", NULL }, + { "groupSearchFilter", NULL, NULL }, + { "ldapSchema", "rfc2307", NULL }, + { "offline_timeout", "5", NULL } +}; + +struct sdap_id_map default_user_map[] = { + { "userObjectClass", "posixAccount", SYSDB_USER_CLASS, NULL }, + { "userName", "uid", SYSDB_NAME, NULL }, + { "userPwd", "userPassword", SYSDB_PWD, NULL }, + { "userUidNumber", "uidNumber", SYSDB_UIDNUM, NULL }, + { "userGidNumber", "gidNumber", SYSDB_GIDNUM, NULL }, + { "userGecos", "gecos", SYSDB_GECOS, NULL }, + { "userHomeDirectory", "homeDirectory", SYSDB_HOMEDIR, NULL }, + { "userShell", "loginShell", SYSDB_SHELL, NULL }, + { "userUUID", "nsUniqueId", SYSDB_UUID, NULL }, + { "userPrincipal", "krbPrincipalName", SYSDB_UPN, NULL }, + { "userFullname", "cn", SYSDB_FULLNAME, NULL }, + { "userMemberOf", "memberOf", SYSDB_MEMBEROF, NULL } +}; + +struct sdap_id_map default_group_map[] = { + { "groupObjectClass", "posixGroup", SYSDB_GROUP_CLASS, NULL }, + { "groupName", "cn", SYSDB_NAME, NULL }, + { "groupPwd", "userPassword", SYSDB_PWD, NULL }, + { "groupGidNumber", "gidNumber", SYSDB_GIDNUM, NULL }, + { "groupMember", "memberuid", SYSDB_LEGACY_MEMBER, NULL }, + { "groupUUID", "nsUniqueId", SYSDB_UUID, NULL } +}; + +/* =Retrieve-Options====================================================== */ + +int sdap_get_options(TALLOC_CTX *memctx, + struct confdb_ctx *cdb, + const char *conf_path, + struct sdap_options **_opts) +{ + struct sdap_options *opts; + int i, ret; + + opts = talloc(memctx, struct sdap_options); + if (!opts) return ENOMEM; + + opts->basic = talloc_array(opts, struct sdap_gen_opts, SDAP_OPTS_BASIC); + if (!opts) return ENOMEM; + + opts->user_map = talloc_array(opts, struct sdap_id_map, SDAP_OPTS_USER); + if (!opts) return ENOMEM; + + opts->group_map = talloc_array(opts, struct sdap_id_map, SDAP_OPTS_GROUP); + if (!opts) return ENOMEM; + + for (i = 0; i < SDAP_OPTS_BASIC; i++) { + + opts->basic[i].opt_name = default_basic_opts[i].opt_name; + opts->basic[i].def_value = default_basic_opts[i].def_value; + + ret = confdb_get_string(cdb, opts, conf_path, + opts->basic[i].opt_name, + opts->basic[i].def_value, + &opts->basic[i].value); + if (ret != EOK || + (opts->basic[i].def_value && !opts->basic[i].value)) { + DEBUG(0, ("Failed to retrieve a value (%s)\n", + opts->basic[i].opt_name)); + if (ret != EOK) ret = EINVAL; + goto done; + } + + DEBUG(5, ("Option %s has value %s\n", + opts->basic[i].opt_name, opts->basic[i].value)); + } + + for (i = 0; i < SDAP_OPTS_USER; i++) { + + opts->user_map[i].opt_name = default_user_map[i].opt_name; + opts->user_map[i].def_name = default_user_map[i].def_name; + opts->user_map[i].sys_name = default_user_map[i].sys_name; + + ret = confdb_get_string(cdb, opts, conf_path, + opts->user_map[i].opt_name, + opts->user_map[i].def_name, + &opts->user_map[i].name); + if (ret != EOK || + (opts->user_map[i].def_name && !opts->user_map[i].name)) { + DEBUG(0, ("Failed to retrieve a value (%s)\n", + opts->user_map[i].opt_name)); + if (ret != EOK) ret = EINVAL; + goto done; + } + + DEBUG(5, ("Option %s has value %s\n", + opts->user_map[i].opt_name, opts->user_map[i].name)); + } + + for (i = 0; i < SDAP_OPTS_GROUP; i++) { + + opts->group_map[i].opt_name = default_group_map[i].opt_name; + opts->group_map[i].def_name = default_group_map[i].def_name; + opts->group_map[i].sys_name = default_group_map[i].sys_name; + + ret = confdb_get_string(cdb, opts, conf_path, + opts->group_map[i].opt_name, + opts->group_map[i].def_name, + &opts->group_map[i].name); + if (ret != EOK || + (opts->group_map[i].def_name && !opts->group_map[i].name)) { + DEBUG(0, ("Failed to retrieve a value (%s)\n", + opts->group_map[i].opt_name)); + if (ret != EOK) ret = EINVAL; + goto done; + } + + DEBUG(5, ("Option %s has value %s\n", + opts->group_map[i].opt_name, opts->group_map[i].name)); + } + + /* re-read special options that are easier to be consumed after they are + * transformed */ + +/* TODO: better to have a blob object than a string here */ + ret = confdb_get_string(cdb, opts, conf_path, + "defaultAuthtok", NULL, + &opts->default_authtok); + if (ret != EOK) goto done; + if (opts->default_authtok) { + opts->default_authtok_size = strlen(opts->default_authtok); + } + + ret = confdb_get_int(cdb, opts, conf_path, + "network_timeout", 5, + &opts->network_timeout); + if (ret != EOK) goto done; + + ret = confdb_get_int(cdb, opts, conf_path, + "opt_timeout", 5, + &opts->opt_timeout); + if (ret != EOK) goto done; + + ret = confdb_get_int(cdb, opts, conf_path, + "offline_timeout", 60, + &opts->offline_timeout); + if (ret != EOK) goto done; + + ret = EOK; + *_opts = opts; + +done: + if (ret != EOK) talloc_zfree(opts); + return ret; +} + +/* =Parse-msg============================================================= */ + +static int sdap_parse_entry(TALLOC_CTX *memctx, + struct sdap_handle *sh, struct sdap_msg *sm, + struct sdap_id_map *map, int attrs_num, + struct sysdb_attrs **_attrs, char **_dn) +{ + struct sysdb_attrs *attrs; + BerElement *ber = NULL; + char **vals; + char *str; + int lerrno; + int a, i, ret; + + lerrno = 0; + ldap_set_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno); + + attrs = sysdb_new_attrs(memctx); + if (!attrs) return ENOMEM; + + str = ldap_get_dn(sh->ldap, sm->msg); + if (!str) { + ldap_get_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno); + DEBUG(1, ("ldap_get_dn failed: %d(%s)\n", + lerrno, ldap_err2string(lerrno))); + ret = EIO; + goto fail; + } + + ret = sysdb_attrs_add_string(attrs, SYSDB_ORIG_DN, str); + if (ret) goto fail; + if (_dn) { + *_dn = talloc_strdup(memctx, str); + if (!*_dn) { + ret = ENOMEM; + ldap_memfree(str); + goto fail; + } + } + ldap_memfree(str); + + vals = ldap_get_values(sh->ldap, sm->msg, "objectClass"); + if (!vals) { + DEBUG(1, ("Unknown entry type, no objectClasses found!\n")); + ret = EINVAL; + goto fail; + } + + for (i = 0; vals[i]; i++) { + /* the objectclass is always the first name in the map */ + if (strcasecmp(vals[i], map[0].name) == 0) { + /* ok it's a user */ + break; + } + } + if (!vals[i]) { + DEBUG(1, ("Not a user entry, objectClass not matching: %s\n", + map[0].name)); + ldap_value_free(vals); + ret = EINVAL; + goto fail; + } + ldap_value_free(vals); + + str = ldap_first_attribute(sh->ldap, sm->msg, &ber); + if (!str) { + ldap_get_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno); + DEBUG(1, ("Entry has no attributes [%d(%s)]!?\n", + lerrno, ldap_err2string(lerrno))); + ret = EINVAL; + goto fail; + } + while (str) { + for (a = 1; a < attrs_num; a++) { + /* check if it is an attr we are interested in */ + if (strcasecmp(str, map[a].name) == 0) break; + } + if (a < attrs_num) { + /* interesting attr */ + + vals = ldap_get_values(sh->ldap, sm->msg, str); + if (!vals) { + ldap_get_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno); + DEBUG(1, ("LDAP Library error: %d(%s)", + lerrno, ldap_err2string(lerrno))); + ret = EIO; + goto fail; + } + if (!vals[0]) { + DEBUG(1, ("Missing value after ldap_get_values() ??\n")); + ret = EINVAL; + goto fail; + } + for (i = 0; vals[i]; i++) { + ret = sysdb_attrs_add_string(attrs, map[a].sys_name, vals[i]); + if (ret) goto fail; + } + ldap_value_free(vals); + } + + ldap_memfree(str); + str = ldap_next_attribute(sh->ldap, sm->msg, ber); + } + ber_free(ber, 0); + + ldap_get_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno); + if (lerrno) { + DEBUG(1, ("LDAP Library error: %d(%s)", + lerrno, ldap_err2string(lerrno))); + ret = EIO; + goto fail; + } + + *_attrs = attrs; + return EOK; + +fail: + if (ber) ber_free(ber, 0); + talloc_free(attrs); + return ret; +} + +/* This function converts an ldap message into a sysdb_attrs structure. + * It converts only known user attributes, the rest are ignored. + * If the entry is not that of an user an error is returned. + * The original DN is stored as an attribute named originalDN */ + +int sdap_parse_user(TALLOC_CTX *memctx, struct sdap_options *opts, + struct sdap_handle *sh, struct sdap_msg *sm, + struct sysdb_attrs **_attrs, char **_dn) +{ + + return sdap_parse_entry(memctx, sh, sm, opts->user_map, + SDAP_OPTS_USER, _attrs, _dn); +} + +/* This function converts an ldap message into a sysdb_attrs structure. + * It converts only known group attributes, the rest are ignored. + * If the entry is not that of an user an error is returned. + * The original DN is stored as an attribute named originalDN */ + +int sdap_parse_group(TALLOC_CTX *memctx, struct sdap_options *opts, + struct sdap_handle *sh, struct sdap_msg *sm, + struct sysdb_attrs **_attrs, char **_dn) +{ + + return sdap_parse_entry(memctx, sh, sm, opts->group_map, + SDAP_OPTS_GROUP, _attrs, _dn); +} + +/* =Get-DN-from-message=================================================== */ + +int sdap_get_msg_dn(TALLOC_CTX *memctx, struct sdap_handle *sh, + struct sdap_msg *sm, char **_dn) +{ + char *str; + int lerrno; + + lerrno = 0; + ldap_set_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno); + + str = ldap_get_dn(sh->ldap, sm->msg); + if (!str) { + ldap_get_option(sh->ldap, LDAP_OPT_RESULT_CODE, &lerrno); + DEBUG(1, ("ldap_get_dn failed: %d(%s)\n", + lerrno, ldap_err2string(lerrno))); + return EIO; + } + + *_dn = talloc_strdup(memctx, str); + ldap_memfree(str); + if (!*_dn) return ENOMEM; + + return EOK; +} + |