/* Authors: Sumit Bose Copyright (C) 2009 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 "config.h" #include "util/util.h" #include "util/sss_sockets.h" #include "util/sss_ldap.h" #include "providers/ldap/sdap.h" const char* sss_ldap_err2string(int err) { if (IS_SSSD_ERROR(err)) { return sss_strerror(err); } else { return ldap_err2string(err); } } int sss_ldap_get_diagnostic_msg(TALLOC_CTX *mem_ctx, LDAP *ld, char **_errmsg) { char *errmsg = NULL; int optret; optret = ldap_get_option(ld, SDAP_DIAGNOSTIC_MESSAGE, (void*)&errmsg); if (optret != LDAP_SUCCESS) { return EINVAL; } *_errmsg = talloc_strdup(mem_ctx, errmsg ? errmsg : "unknown error"); ldap_memfree(errmsg); if (*_errmsg == NULL) { return ENOMEM; } return EOK; } int sss_ldap_control_create(const char *oid, int iscritical, struct berval *value, int dupval, LDAPControl **ctrlp) { #ifdef HAVE_LDAP_CONTROL_CREATE return ldap_control_create(oid, iscritical, value, dupval, ctrlp); #else LDAPControl *lc = NULL; if (oid == NULL || ctrlp == NULL) { return LDAP_PARAM_ERROR; } lc = calloc(sizeof(LDAPControl), 1); if (lc == NULL) { return LDAP_NO_MEMORY; } lc->ldctl_oid = strdup(oid); if (lc->ldctl_oid == NULL) { free(lc); return LDAP_NO_MEMORY; } if (value != NULL && value->bv_val != NULL) { if (dupval == 0) { lc->ldctl_value = *value; } else { ber_dupbv(&lc->ldctl_value, value); if (lc->ldctl_value.bv_val == NULL) { free(lc->ldctl_oid); free(lc); return LDAP_NO_MEMORY; } } } lc->ldctl_iscritical = iscritical; *ctrlp = lc; return LDAP_SUCCESS; #endif } #ifdef HAVE_LDAP_INIT_FD #define LDAP_PROTO_TCP 1 /* ldap:// */ #define LDAP_PROTO_UDP 2 /* reserved */ #define LDAP_PROTO_IPC 3 /* ldapi:// */ #define LDAP_PROTO_EXT 4 /* user-defined socket/sockbuf */ extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url, LDAP **ld); static void sss_ldap_init_sys_connect_done(struct tevent_req *subreq); #endif struct sss_ldap_init_state { LDAP *ldap; int sd; const char *uri; }; static int sss_ldap_init_state_destructor(void *data) { struct sss_ldap_init_state *state = (struct sss_ldap_init_state *)data; if (state->ldap) { DEBUG(SSSDBG_TRACE_FUNC, "calling ldap_unbind_ext for ldap:[%p] sd:[%d]\n", state->ldap, state->sd); ldap_unbind_ext(state->ldap, NULL, NULL); } if (state->sd != -1) { DEBUG(SSSDBG_TRACE_FUNC, "closing socket [%d]\n", state->sd); close(state->sd); state->sd = -1; } return 0; } struct tevent_req *sss_ldap_init_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, const char *uri, struct sockaddr_storage *addr, int addr_len, int timeout) { int ret = EOK; struct tevent_req *req; struct sss_ldap_init_state *state; req = tevent_req_create(mem_ctx, &state, struct sss_ldap_init_state); if (req == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create failed.\n"); return NULL; } talloc_set_destructor((TALLOC_CTX *)state, sss_ldap_init_state_destructor); state->ldap = NULL; state->sd = -1; state->uri = uri; #ifdef HAVE_LDAP_INIT_FD struct tevent_req *subreq; subreq = sssd_async_socket_init_send(state, ev, addr, addr_len, timeout); if (subreq == NULL) { ret = ENOMEM; DEBUG(SSSDBG_CRIT_FAILURE, "sssd_async_socket_init_send failed.\n"); goto fail; } tevent_req_set_callback(subreq, sss_ldap_init_sys_connect_done, req); return req; fail: tevent_req_error(req, ret); #else DEBUG(SSSDBG_MINOR_FAILURE, "ldap_init_fd not available, " "will use ldap_initialize with uri [%s].\n", uri); ret = ldap_initialize(&state->ldap, uri); if (ret == LDAP_SUCCESS) { tevent_req_done(req); } else { DEBUG(SSSDBG_CRIT_FAILURE, "ldap_initialize failed [%s].\n", sss_ldap_err2string(ret)); if (ret == LDAP_SERVER_DOWN) { tevent_req_error(req, ETIMEDOUT); } else { tevent_req_error(req, EIO); } } #endif tevent_req_post(req, ev); return req; } #ifdef HAVE_LDAP_INIT_FD static void sss_ldap_init_sys_connect_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sss_ldap_init_state *state = tevent_req_data(req, struct sss_ldap_init_state); char *tlserr; int ret; int lret; int optret; ret = sssd_async_socket_init_recv(subreq, &state->sd); talloc_zfree(subreq); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "sssd_async_socket_init request failed: [%d]: %s.\n", ret, sss_strerror(ret)); goto fail; } /* Initialize LDAP handler */ lret = ldap_init_fd(state->sd, LDAP_PROTO_TCP, state->uri, &state->ldap); if (lret != LDAP_SUCCESS) { DEBUG(SSSDBG_CRIT_FAILURE, "ldap_init_fd failed: %s. [%d][%s]\n", sss_ldap_err2string(lret), state->sd, state->uri); ret = lret == LDAP_SERVER_DOWN ? ETIMEDOUT : EIO; goto fail; } if (ldap_is_ldaps_url(state->uri)) { lret = ldap_install_tls(state->ldap); if (lret != LDAP_SUCCESS) { if (lret == LDAP_LOCAL_ERROR) { DEBUG(SSSDBG_FUNC_DATA, "TLS/SSL already in place.\n"); } else { optret = sss_ldap_get_diagnostic_msg(state, state->ldap, &tlserr); if (optret == LDAP_SUCCESS) { DEBUG(SSSDBG_CRIT_FAILURE, "ldap_install_tls failed: [%s] [%s]\n", sss_ldap_err2string(lret), tlserr); sss_log(SSS_LOG_ERR, "Could not start TLS encryption. %s", tlserr); } else { DEBUG(SSSDBG_CRIT_FAILURE, "ldap_install_tls failed: [%s]\n", sss_ldap_err2string(lret)); sss_log(SSS_LOG_ERR, "Could not start TLS encryption. " "Check for certificate issues."); } ret = EIO; goto fail; } } } tevent_req_done(req); return; fail: tevent_req_error(req, ret); } #endif int sss_ldap_init_recv(struct tevent_req *req, LDAP **ldap, int *sd) { struct sss_ldap_init_state *state = tevent_req_data(req, struct sss_ldap_init_state); TEVENT_REQ_RETURN_ON_ERROR(req); /* Everything went well therefore we do not want to release resources */ talloc_set_destructor(state, NULL); *ldap = state->ldap; *sd = state->sd; return EOK; } /* * _filter will contain combined filters from all possible search bases * or NULL if it should be empty */ bool sss_ldap_dn_in_search_bases_len(TALLOC_CTX *mem_ctx, const char *dn, struct sdap_search_base **search_bases, char **_filter, int *_match_len) { struct sdap_search_base *base; int basedn_len, dn_len; int len_diff; int i, j; bool base_confirmed = false; bool comma_found = false; bool backslash_found = false; char *filter = NULL; bool ret = false; int match_len; if (dn == NULL) { DEBUG(SSSDBG_FUNC_DATA, "dn is NULL\n"); ret = false; goto done; } if (search_bases == NULL) { DEBUG(SSSDBG_FUNC_DATA, "search_bases is NULL\n"); ret = false; goto done; } dn_len = strlen(dn); for (i = 0; search_bases[i] != NULL; i++) { base = search_bases[i]; basedn_len = strlen(base->basedn); if (basedn_len > dn_len) { continue; } len_diff = dn_len - basedn_len; base_confirmed = (strncasecmp(&dn[len_diff], base->basedn, basedn_len) == 0); if (!base_confirmed) { continue; } match_len = basedn_len; switch (base->scope) { case LDAP_SCOPE_BASE: /* dn > base? */ if (len_diff != 0) { continue; } break; case LDAP_SCOPE_ONELEVEL: if (len_diff == 0) { /* Base object doesn't belong to scope=one * search */ continue; } comma_found = false; for (j = 0; j < len_diff - 1; j++) { /* ignore comma before base */ if (dn[j] == '\\') { backslash_found = true; } else if (dn[j] == ',' && !backslash_found) { comma_found = true; break; } else { backslash_found = false; } } /* it has at least one more level */ if (comma_found) { continue; } break; case LDAP_SCOPE_SUBTREE: /* dn length >= base dn length && base_confirmed == true */ break; default: DEBUG(SSSDBG_FUNC_DATA, "Unsupported scope: %d\n", base->scope); continue; } /* * If we get here, the dn is valid. * If no filter is set, than return true immediately. * Append filter otherwise. */ ret = true; if (_match_len) { *_match_len = match_len; } if (base->filter == NULL || _filter == NULL) { goto done; } else { filter = talloc_strdup_append(filter, base->filter); if (filter == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup_append() failed\n"); ret = false; goto done; } } } if (_filter != NULL) { if (filter != NULL) { *_filter = talloc_asprintf(mem_ctx, "(|%s)", filter); if (*_filter == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf_append() failed\n"); ret = false; goto done; } } else { *_filter = NULL; } } done: talloc_free(filter); return ret; } bool sss_ldap_dn_in_search_bases(TALLOC_CTX *mem_ctx, const char *dn, struct sdap_search_base **search_bases, char **_filter) { return sss_ldap_dn_in_search_bases_len(mem_ctx, dn, search_bases, _filter, NULL); } char *sss_ldap_encode_ndr_uint32(TALLOC_CTX *mem_ctx, uint32_t flags) { char hex[9]; /* 4 bytes in hex + terminating zero */ errno_t ret; ret = snprintf(hex, 9, "%08x", flags); if (ret != 8) { return NULL; } return talloc_asprintf(mem_ctx, "\\%c%c\\%c%c\\%c%c\\%c%c", hex[6], hex[7], hex[4], hex[5], hex[2], hex[3], hex[0], hex[1]); }