/* SSSD LDAP Provider Common Functions Authors: Simo Sorce Copyright (C) 2008-2010 Red Hat 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 "providers/ldap/ldap_common.h" #include "providers/fail_over.h" #include "providers/ldap/sdap_async_private.h" #include "providers/krb5/krb5_common.h" #include "db/sysdb_sudo.h" #include "db/sysdb_services.h" #include "db/sysdb_autofs.h" #include "util/sss_krb5.h" #include "util/crypto/sss_crypto.h" #include "providers/ldap/sdap_idmap.h" /* a fd the child process would log into */ int ldap_child_debug_fd = -1; void sdap_handler_done(struct be_req *req, int dp_err, int error, const char *errstr) { return be_req_terminate(req, dp_err, error, errstr); } void sdap_mark_offline(struct sdap_id_ctx *ctx) { be_mark_offline(ctx->be); } int ldap_id_setup_tasks(struct sdap_id_ctx *ctx) { return sdap_id_setup_tasks(ctx->be, ctx, ctx->opts->sdom, ldap_enumeration_send, ldap_enumeration_recv, ctx); } int sdap_id_setup_tasks(struct be_ctx *be_ctx, struct sdap_id_ctx *ctx, struct sdap_domain *sdom, be_ptask_send_t send_fn, be_ptask_recv_t recv_fn, void *pvt) { int ret; /* set up enumeration task */ if (sdom->dom->enumerate) { DEBUG(SSSDBG_TRACE_FUNC, "Setting up enumeration for %s\n", sdom->dom->name); ret = ldap_setup_enumeration(be_ctx, ctx->opts, sdom, send_fn, recv_fn, pvt); } else { /* the enumeration task, runs the cleanup process by itself, * but if enumeration is not running we need to schedule it */ DEBUG(SSSDBG_TRACE_FUNC, "Setting up cleanup task for %s\n", sdom->dom->name); ret = ldap_setup_cleanup(ctx, sdom); } return ret; } static void sdap_uri_callback(void *private_data, struct fo_server *server) { TALLOC_CTX *tmp_ctx = NULL; struct sdap_service *service; struct resolv_hostent *srvaddr; struct sockaddr_storage *sockaddr; const char *tmp; const char *srv_name; char *new_uri; tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed\n"); return; } service = talloc_get_type(private_data, struct sdap_service); if (!service) { talloc_free(tmp_ctx); return; } tmp = (const char *)fo_get_server_user_data(server); srvaddr = fo_get_server_hostent(server); if (!srvaddr) { DEBUG(SSSDBG_CRIT_FAILURE, "FATAL: No hostent available for server (%s)\n", fo_get_server_str_name(server)); talloc_free(tmp_ctx); return; } sockaddr = resolv_get_sockaddr_address(tmp_ctx, srvaddr, fo_get_server_port(server)); if (sockaddr == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "resolv_get_sockaddr_address failed.\n"); talloc_free(tmp_ctx); return; } if (fo_is_srv_lookup(server)) { if (!tmp) { DEBUG(SSSDBG_CRIT_FAILURE, "Unknown service, using ldap\n"); tmp = SSS_LDAP_SRV_NAME; } srv_name = fo_get_server_name(server); if (srv_name == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Could not get server host name\n"); talloc_free(tmp_ctx); return; } new_uri = talloc_asprintf(service, "%s://%s:%d", tmp, srv_name, fo_get_server_port(server)); } else { new_uri = talloc_strdup(service, tmp); } if (!new_uri) { DEBUG(SSSDBG_OP_FAILURE, "Failed to copy URI ...\n"); talloc_free(tmp_ctx); return; } DEBUG(SSSDBG_TRACE_FUNC, "Constructed uri '%s'\n", new_uri); /* free old one and replace with new one */ talloc_zfree(service->uri); service->uri = new_uri; talloc_zfree(service->sockaddr); service->sockaddr = talloc_steal(service, sockaddr); talloc_free(tmp_ctx); } static void sdap_finalize(struct tevent_context *ev, struct tevent_signal *se, int signum, int count, void *siginfo, void *private_data) { char *realm = (char *) private_data; int ret; ret = remove_krb5_info_files(se, realm); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "remove_krb5_info_files failed.\n"); } orderly_shutdown(0); } errno_t sdap_install_sigterm_handler(TALLOC_CTX *mem_ctx, struct tevent_context *ev, const char *realm) { char *sig_realm; struct tevent_signal *sige; BlockSignals(false, SIGTERM); sig_realm = talloc_strdup(mem_ctx, realm); if (sig_realm == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed!\n"); return ENOMEM; } sige = tevent_add_signal(ev, mem_ctx, SIGTERM, SA_SIGINFO, sdap_finalize, sig_realm); if (sige == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_signal failed.\n"); talloc_free(sig_realm); return ENOMEM; } talloc_steal(sige, sig_realm); return EOK; } void sdap_remove_kdcinfo_files_callback(void *pvt) { int ret; TALLOC_CTX *tmp_ctx = NULL; struct remove_info_files_ctx *ctx = talloc_get_type(pvt, struct remove_info_files_ctx); ret = be_fo_run_callbacks_at_next_request(ctx->be_ctx, ctx->kdc_service_name); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "be_fo_run_callbacks_at_next_request failed, " "krb5 info files will not be removed, because " "it is unclear if they will be recreated properly.\n"); return; } tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed, cannot remove krb5 info files.\n"); return; } ret = remove_krb5_info_files(tmp_ctx, ctx->realm); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "remove_krb5_info_files failed.\n"); } talloc_zfree(tmp_ctx); } errno_t sdap_install_offline_callback(TALLOC_CTX *mem_ctx, struct be_ctx *be_ctx, const char *realm, const char *service_name) { int ret; struct remove_info_files_ctx *ctx; ctx = talloc_zero(mem_ctx, struct remove_info_files_ctx); if (ctx == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zfree failed.\n"); return ENOMEM; } ctx->be_ctx = be_ctx; ctx->realm = talloc_strdup(ctx, realm); ctx->kdc_service_name = talloc_strdup(ctx, service_name); if (ctx->realm == NULL || ctx->kdc_service_name == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed!\n"); ret = ENOMEM; goto done; } ret = be_add_offline_cb(ctx, be_ctx, sdap_remove_kdcinfo_files_callback, ctx, NULL); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "be_add_offline_cb failed.\n"); goto done; } ret = EOK; done: if (ret != EOK) { talloc_zfree(ctx); } return ret; } errno_t sdap_set_sasl_options(struct sdap_options *id_opts, char *default_primary, char *default_realm, const char *keytab_path) { errno_t ret; TALLOC_CTX *tmp_ctx; char *sasl_primary; char *desired_primary; char *primary_realm; char *sasl_realm; char *desired_realm; bool primary_requested = true; bool realm_requested = true; tmp_ctx = talloc_new(NULL); if (!tmp_ctx) return ENOMEM; /* Configuration of SASL auth ID and realm */ desired_primary = dp_opt_get_string(id_opts->basic, SDAP_SASL_AUTHID); if (!desired_primary) { primary_requested = false; desired_primary = default_primary; } if ((primary_realm = strchr(desired_primary, '@'))) { *primary_realm = '\0'; desired_realm = primary_realm+1; DEBUG(SSSDBG_TRACE_INTERNAL, "authid contains realm [%s]\n", desired_realm); } else { desired_realm = dp_opt_get_string(id_opts->basic, SDAP_SASL_REALM); if (!desired_realm) { realm_requested = false; desired_realm = default_realm; } } DEBUG(SSSDBG_CONF_SETTINGS, "Will look for %s@%s in %s\n", desired_primary, desired_realm, keytab_path ? keytab_path : "default keytab"); ret = select_principal_from_keytab(tmp_ctx, desired_primary, desired_realm, keytab_path, NULL, &sasl_primary, &sasl_realm); if (ret != EOK) { goto done; } if (primary_requested && strcmp(desired_primary, sasl_primary) != 0) { DEBUG(SSSDBG_MINOR_FAILURE, "Configured SASL auth ID not found in keytab. " "Requested %s, found %s\n", desired_primary, sasl_primary); } if (realm_requested && strcmp(desired_realm, sasl_realm) != 0) { DEBUG(SSSDBG_MINOR_FAILURE, "Configured SASL realm not found in keytab. " "Requested %s, found %s\n", desired_realm, sasl_realm); } ret = dp_opt_set_string(id_opts->basic, SDAP_SASL_AUTHID, sasl_primary); if (ret != EOK) { goto done; } DEBUG(SSSDBG_CONF_SETTINGS, "Option %s set to %s\n", id_opts->basic[SDAP_SASL_AUTHID].opt_name, dp_opt_get_string(id_opts->basic, SDAP_SASL_AUTHID)); ret = dp_opt_set_string(id_opts->basic, SDAP_SASL_REALM, sasl_realm); if (ret != EOK) { goto done; } DEBUG(SSSDBG_CONF_SETTINGS, "Option %s set to %s\n", id_opts->basic[SDAP_SASL_REALM].opt_name, dp_opt_get_string(id_opts->basic, SDAP_SASL_REALM)); ret = EOK; done: talloc_free(tmp_ctx); return ret; } static const char * sdap_gssapi_get_default_realm(TALLOC_CTX *mem_ctx) { char *krb5_realm = NULL; const char *realm = NULL; krb5_error_code krberr; krb5_context context = NULL; krberr = krb5_init_context(&context); if (krberr) { DEBUG(SSSDBG_OP_FAILURE, "Failed to init kerberos context\n"); goto done; } krberr = krb5_get_default_realm(context, &krb5_realm); if (krberr) { DEBUG(SSSDBG_OP_FAILURE, "Failed to get default realm name: %s\n", sss_krb5_get_error_message(context, krberr)); goto done; } realm = talloc_strdup(mem_ctx, krb5_realm); krb5_free_default_realm(context, krb5_realm); if (!realm) { DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory\n"); goto done; } DEBUG(SSSDBG_TRACE_LIBS, "Will use default realm %s\n", realm); done: if (context) krb5_free_context(context); return realm; } int sdap_gssapi_init(TALLOC_CTX *mem_ctx, struct dp_option *opts, struct be_ctx *bectx, struct sdap_service *sdap_service, struct krb5_service **krb5_service) { int ret; const char *krb5_servers; const char *krb5_backup_servers; const char *krb5_realm; const char *krb5_opt_realm; struct krb5_service *service = NULL; TALLOC_CTX *tmp_ctx; tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) return ENOMEM; krb5_servers = dp_opt_get_string(opts, SDAP_KRB5_KDC); krb5_backup_servers = dp_opt_get_string(opts, SDAP_KRB5_BACKUP_KDC); krb5_opt_realm = dp_opt_get_string(opts, SDAP_KRB5_REALM); if (krb5_opt_realm == NULL) { DEBUG(SSSDBG_OP_FAILURE, "Missing krb5_realm option, will use libkrb default\n"); krb5_realm = sdap_gssapi_get_default_realm(tmp_ctx); if (krb5_realm == NULL) { DEBUG(SSSDBG_FATAL_FAILURE, "Cannot determine the Kerberos realm, aborting\n"); ret = EIO; goto done; } } else { krb5_realm = talloc_strdup(tmp_ctx, krb5_opt_realm); if (krb5_realm == NULL) { ret = ENOMEM; goto done; } } ret = krb5_service_init(mem_ctx, bectx, SSS_KRB5KDC_FO_SRV, krb5_servers, krb5_backup_servers, krb5_realm, dp_opt_get_bool(opts, SDAP_KRB5_USE_KDCINFO), &service); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Failed to init KRB5 failover service!\n"); goto done; } ret = sdap_install_sigterm_handler(mem_ctx, bectx->ev, krb5_realm); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Failed to install sigterm handler\n"); goto done; } ret = sdap_install_offline_callback(mem_ctx, bectx, krb5_realm, SSS_KRB5KDC_FO_SRV); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Failed to install sigterm handler\n"); goto done; } sdap_service->kinit_service_name = talloc_strdup(sdap_service, service->name); if (sdap_service->kinit_service_name == NULL) { ret = ENOMEM; goto done; } ret = EOK; *krb5_service = service; done: talloc_free(tmp_ctx); if (ret != EOK) talloc_free(service); return ret; } static errno_t _sdap_urls_init(struct be_ctx *ctx, struct sdap_service *service, const char *service_name, const char *dns_service_name, const char *urls, bool primary) { TALLOC_CTX *tmp_ctx; char *srv_user_data; char **list = NULL; LDAPURLDesc *lud; errno_t ret = 0; int i; tmp_ctx = talloc_new(NULL); if (!tmp_ctx) { return ENOMEM; } /* split server parm into a list */ ret = split_on_separator(tmp_ctx, urls, ',', true, true, &list, NULL); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Failed to parse server list!\n"); goto done; } /* now for each URI add a new server to the failover service */ for (i = 0; list[i]; i++) { if (be_fo_is_srv_identifier(list[i])) { if (!primary) { DEBUG(SSSDBG_MINOR_FAILURE, "Failed to add server [%s] to failover service: " "SRV resolution only allowed for primary servers!\n", list[i]); continue; } if (!dns_service_name) { DEBUG(SSSDBG_FATAL_FAILURE, "Missing DNS service name for service [%s].\n", service_name); ret = EINVAL; goto done; } srv_user_data = talloc_strdup(service, dns_service_name); if (!srv_user_data) { ret = ENOMEM; goto done; } ret = be_fo_add_srv_server(ctx, service_name, dns_service_name, NULL, BE_FO_PROTO_TCP, false, srv_user_data); if (ret) { DEBUG(SSSDBG_FATAL_FAILURE, "Failed to add server\n"); goto done; } DEBUG(SSSDBG_TRACE_FUNC, "Added service lookup\n"); continue; } ret = ldap_url_parse(list[i], &lud); if (ret != LDAP_SUCCESS) { DEBUG(SSSDBG_FATAL_FAILURE, "Failed to parse ldap URI (%s)!\n", list[i]); ret = EINVAL; goto done; } if (lud->lud_host == NULL) { DEBUG(SSSDBG_OP_FAILURE, "The LDAP URI (%s) did not contain a host name\n", list[i]); ldap_free_urldesc(lud); continue; } DEBUG(SSSDBG_TRACE_FUNC, "Added URI %s\n", list[i]); talloc_steal(service, list[i]); /* It could be ipv6 address in square brackets. Remove * the brackets if needed. */ ret = remove_ipv6_brackets(lud->lud_host); if (ret != EOK) { goto done; } ret = be_fo_add_server(ctx, service->name, lud->lud_host, lud->lud_port, list[i], primary); ldap_free_urldesc(lud); if (ret) { goto done; } } done: talloc_free(tmp_ctx); return ret; } static inline errno_t sdap_primary_urls_init(struct be_ctx *ctx, struct sdap_service *service, const char *service_name, const char *dns_service_name, const char *urls) { return _sdap_urls_init(ctx, service, service_name, dns_service_name, urls, true); } static inline errno_t sdap_backup_urls_init(struct be_ctx *ctx, struct sdap_service *service, const char *service_name, const char *dns_service_name, const char *urls) { return _sdap_urls_init(ctx, service, service_name, dns_service_name, urls, false); } static int ldap_user_data_cmp(void *ud1, void *ud2) { return strcasecmp((char*) ud1, (char*) ud2); } int sdap_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, const char *service_name, const char *dns_service_name, const char *urls, const char *backup_urls, struct sdap_service **_service) { TALLOC_CTX *tmp_ctx; struct sdap_service *service; int ret; tmp_ctx = talloc_new(NULL); if (!tmp_ctx) { return ENOMEM; } service = talloc_zero(tmp_ctx, struct sdap_service); if (!service) { ret = ENOMEM; goto done; } ret = be_fo_add_service(ctx, service_name, ldap_user_data_cmp); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create failover service!\n"); goto done; } service->name = talloc_strdup(service, service_name); if (!service->name) { ret = ENOMEM; goto done; } if (!urls) { DEBUG(SSSDBG_CONF_SETTINGS, "No primary servers defined, using service discovery\n"); urls = BE_SRV_IDENTIFIER; } ret = sdap_primary_urls_init(ctx, service, service_name, dns_service_name, urls); if (ret != EOK) { goto done; } if (backup_urls) { ret = sdap_backup_urls_init(ctx, service, service_name, dns_service_name, backup_urls); if (ret != EOK) { goto done; } } ret = be_fo_service_add_callback(memctx, ctx, service->name, sdap_uri_callback, service); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Failed to add failover callback!\n"); goto done; } ret = EOK; done: if (ret == EOK) { *_service = talloc_steal(memctx, service); } talloc_zfree(tmp_ctx); return ret; } errno_t string_to_shadowpw_days(const char *s, long *d) { long l; char *endptr; if (s == NULL || *s == '\0') { *d = -1; return EOK; } errno = 0; l = strtol(s, &endptr, 10); if (errno != 0) { DEBUG(SSSDBG_CRIT_FAILURE, "strtol failed [%d][%s].\n", errno, strerror(errno)); return errno; } if (*endptr != '\0') { DEBUG(SSSDBG_CRIT_FAILURE, "Input string [%s] is invalid.\n", s); return EINVAL; } if (l < -1) { DEBUG(SSSDBG_CRIT_FAILURE, "Input string contains not allowed negative value [%ld].\n", l); return EINVAL; } *d = l; return EOK; } errno_t get_sysdb_attr_name(TALLOC_CTX *mem_ctx, struct sdap_attr_map *map, size_t map_size, const char *ldap_name, char **sysdb_name) { size_t i; for (i = 0; i < map_size; i++) { /* Skip map entries with no name (may depend on * schema selected) */ if (!map[i].name) continue; /* Check if it is a mapped attribute */ if(strcasecmp(ldap_name, map[i].name) == 0) break; } if (i < map_size) { /* We found a mapped name, return that */ *sysdb_name = talloc_strdup(mem_ctx, map[i].sys_name); } else { /* Not mapped, use the same name */ *sysdb_name = talloc_strdup(mem_ctx, ldap_name); } if (!*sysdb_name) { return ENOMEM; } return EOK; } errno_t list_missing_attrs(TALLOC_CTX *mem_ctx, struct sdap_attr_map *map, size_t map_size, struct sysdb_attrs *recvd_attrs, char ***missing_attrs) { errno_t ret; size_t attr_count = 0; size_t i, j, k; char **missing = NULL; const char **expected_attrs; char *sysdb_name; TALLOC_CTX *tmp_ctx; if (!recvd_attrs || !missing_attrs) { return EINVAL; } tmp_ctx = talloc_new(NULL); if (!tmp_ctx) { return ENOMEM; } ret = build_attrs_from_map(tmp_ctx, map, map_size, NULL, &expected_attrs, &attr_count); if (ret != EOK) { goto done; } /* Allocate the maximum possible values for missing_attrs, to * be on the safe side */ missing = talloc_array(tmp_ctx, char *, attr_count); if (!missing) { ret = ENOMEM; goto done; } k = 0; /* Check for each expected attribute */ for (i = 0; i < attr_count; i++) { ret = get_sysdb_attr_name(tmp_ctx, map, map_size, expected_attrs[i], &sysdb_name); if (ret != EOK) { goto done; } /* objectClass is a special-case and we need to * check for it explicitly. */ if (strcasecmp(sysdb_name, "objectClass") == 0) { talloc_free(sysdb_name); continue; } /* GECOS is another special case. Its value can come * either from the 'gecos' attribute or the 'cn' * attribute. It's best if we just never remove it. */ if (strcasecmp(sysdb_name, SYSDB_GECOS) == 0) { talloc_free(sysdb_name); continue; } for (j = 0; j < recvd_attrs->num; j++) { /* Check whether this expected attribute appeared in the * received attributes and had a non-zero number of * values. */ if ((strcasecmp(recvd_attrs->a[j].name, sysdb_name) == 0) && (recvd_attrs->a[j].num_values > 0)) { break; } } if (j < recvd_attrs->num) { /* Attribute was found, therefore not missing */ talloc_free(sysdb_name); } else { /* Attribute could not be found. Add to the missing list */ missing[k] = talloc_steal(missing, sysdb_name); k++; } } if (k == 0) { *missing_attrs = NULL; } else { /* Terminate the list */ missing[k] = NULL; *missing_attrs = talloc_steal(mem_ctx, missing); } ret = EOK; done: talloc_free(tmp_ctx); return ret; } bool sdap_is_secure_uri(const char *uri) { /* LDAPS URI's are secure channels */ if (strncasecmp(uri, LDAP_SSL_URI, strlen(LDAP_SSL_URI)) == 0) { return true; } return false; } char *sdap_get_access_filter(TALLOC_CTX *mem_ctx, const char *base_filter) { char *filter = NULL; if (base_filter == NULL) return NULL; if (base_filter[0] == '(') { /* This filter is wrapped in parentheses. * Pass it as-is to the openldap libraries. */ filter = talloc_strdup(mem_ctx, base_filter); } else { filter = talloc_asprintf(mem_ctx, "(%s)", base_filter); } return filter; } errno_t sdap_attrs_get_sid_str(TALLOC_CTX *mem_ctx, struct sdap_idmap_ctx *idmap_ctx, struct sysdb_attrs *sysdb_attrs, const char *sid_attr, char **_sid_str) { errno_t ret; enum idmap_error_code err; struct ldb_message_element *el; char *sid_str; ret = sysdb_attrs_get_el(sysdb_attrs, sid_attr, &el); if (ret != EOK || el->num_values != 1) { DEBUG(SSSDBG_TRACE_LIBS, "No [%s] attribute. [%d][%s]\n", sid_attr, el->num_values, strerror(ret)); return ENOENT; } if (el->values[0].length > 2 && el->values[0].data[0] == 'S' && el->values[0].data[1] == '-') { sid_str = talloc_strndup(mem_ctx, (char *) el->values[0].data, el->values[0].length); if (sid_str == NULL) { DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n"); return ENOMEM; } } else { err = sss_idmap_bin_sid_to_sid(idmap_ctx->map, el->values[0].data, el->values[0].length, &sid_str); if (err != IDMAP_SUCCESS) { DEBUG(SSSDBG_MINOR_FAILURE, "Could not convert SID: [%s]\n", idmap_error_string(err)); return EIO; } } *_sid_str = talloc_steal(mem_ctx, sid_str); return EOK; } struct sdap_id_conn_ctx * sdap_id_ctx_conn_add(struct sdap_id_ctx *id_ctx, struct sdap_service *sdap_service) { struct sdap_id_conn_ctx *conn; errno_t ret; conn = talloc_zero(id_ctx, struct sdap_id_conn_ctx); if (conn == NULL) { return NULL; } conn->service = talloc_steal(conn, sdap_service); conn->id_ctx = id_ctx; /* Create a connection cache */ ret = sdap_id_conn_cache_create(conn, id_ctx, conn, &conn->conn_cache); if (ret != EOK) { talloc_free(conn); return NULL; } DLIST_ADD_END(id_ctx->conn, conn, struct sdap_id_conn_ctx *); return conn; } struct sdap_id_ctx * sdap_id_ctx_new(TALLOC_CTX *mem_ctx, struct be_ctx *bectx, struct sdap_service *sdap_service) { struct sdap_id_ctx *sdap_ctx; sdap_ctx = talloc_zero(mem_ctx, struct sdap_id_ctx); if (sdap_ctx == NULL) { return NULL; } sdap_ctx->be = bectx; /* There should be at least one connection context */ sdap_ctx->conn = sdap_id_ctx_conn_add(sdap_ctx, sdap_service); if (sdap_ctx->conn == NULL) { talloc_free(sdap_ctx); return NULL; } return sdap_ctx; }