summaryrefslogtreecommitdiffstats
path: root/src/providers/ldap/ldap_child.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 05:31:45 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 05:31:45 +0000
commit74aa0bc6779af38018a03fd2cf4419fe85917904 (patch)
tree9cb0681aac9a94a49c153d5823e7a55d1513d91f /src/providers/ldap/ldap_child.c
parentInitial commit. (diff)
downloadsssd-74aa0bc6779af38018a03fd2cf4419fe85917904.tar.xz
sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.zip
Adding upstream version 2.9.4.upstream/2.9.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/providers/ldap/ldap_child.c')
-rw-r--r--src/providers/ldap/ldap_child.c788
1 files changed, 788 insertions, 0 deletions
diff --git a/src/providers/ldap/ldap_child.c b/src/providers/ldap/ldap_child.c
new file mode 100644
index 0000000..6c167d2
--- /dev/null
+++ b/src/providers/ldap/ldap_child.c
@@ -0,0 +1,788 @@
+/*
+ SSSD
+
+ LDAP Backend Module -- prime ccache with TGT in a child process
+
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <popt.h>
+#include <sys/prctl.h>
+
+#include "util/util.h"
+#include "util/sss_krb5.h"
+#include "util/child_common.h"
+#include "providers/backend.h"
+#include "providers/krb5/krb5_common.h"
+
+char *global_ccname_file_dummy = NULL;
+
+static void sig_term_handler(int sig)
+{
+ if (global_ccname_file_dummy != NULL) {
+ /* Cast to void to avoid a complaint by Coverity */
+ (void) unlink(global_ccname_file_dummy);
+ }
+
+ _exit(CHILD_TIMEOUT_EXIT_CODE);
+}
+
+static krb5_context krb5_error_ctx;
+#define LDAP_CHILD_DEBUG(level, error) KRB5_DEBUG(level, krb5_error_ctx, error)
+
+struct input_buffer {
+ const char *realm_str;
+ const char *princ_str;
+ char *keytab_name;
+ krb5_deltat lifetime;
+ krb5_context context;
+ uid_t uid;
+ gid_t gid;
+};
+
+static errno_t unpack_buffer(uint8_t *buf, size_t size,
+ struct input_buffer *ibuf)
+{
+ size_t p = 0;
+ uint32_t len;
+
+ DEBUG(SSSDBG_TRACE_LIBS, "total buffer size: %zu\n", size);
+
+ /* realm_str size and length */
+ SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
+
+ DEBUG(SSSDBG_TRACE_LIBS, "realm_str size: %d\n", len);
+ if (len) {
+ if (len > size - p) return EINVAL;
+ ibuf->realm_str = talloc_strndup(ibuf, (char *)(buf + p), len);
+ DEBUG(SSSDBG_TRACE_LIBS, "got realm_str: %s\n", ibuf->realm_str);
+ if (ibuf->realm_str == NULL) return ENOMEM;
+ p += len;
+ }
+
+ /* princ_str size and length */
+ SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
+
+ DEBUG(SSSDBG_TRACE_LIBS, "princ_str size: %d\n", len);
+ if (len) {
+ if (len > size - p) return EINVAL;
+ ibuf->princ_str = talloc_strndup(ibuf, (char *)(buf + p), len);
+ DEBUG(SSSDBG_TRACE_LIBS, "got princ_str: %s\n", ibuf->princ_str);
+ if (ibuf->princ_str == NULL) return ENOMEM;
+ p += len;
+ }
+
+ /* keytab_name size and length */
+ SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
+
+ DEBUG(SSSDBG_TRACE_LIBS, "keytab_name size: %d\n", len);
+ if (len) {
+ if (len > size - p) return EINVAL;
+ ibuf->keytab_name = talloc_strndup(ibuf, (char *)(buf + p), len);
+ DEBUG(SSSDBG_TRACE_LIBS, "got keytab_name: %s\n", ibuf->keytab_name);
+ if (ibuf->keytab_name == NULL) return ENOMEM;
+ p += len;
+ }
+
+ /* ticket lifetime */
+ SAFEALIGN_COPY_UINT32_CHECK(&ibuf->lifetime, buf + p, size, &p);
+ DEBUG(SSSDBG_TRACE_LIBS, "lifetime: %u\n", ibuf->lifetime);
+
+ /* UID and GID to run as */
+ SAFEALIGN_COPY_UINT32_CHECK(&ibuf->uid, buf + p, size, &p);
+ SAFEALIGN_COPY_UINT32_CHECK(&ibuf->gid, buf + p, size, &p);
+ DEBUG(SSSDBG_FUNC_DATA,
+ "Will run as [%"SPRIuid"][%"SPRIgid"].\n", ibuf->uid, ibuf->gid);
+
+ return EOK;
+}
+
+static int pack_buffer(struct response *r, int result, krb5_error_code krberr,
+ const char *msg, time_t expire_time)
+{
+ int len;
+ size_t p = 0;
+
+ len = strlen(msg);
+ r->size = 2 * sizeof(uint32_t) + sizeof(krb5_error_code) +
+ len + sizeof(time_t);
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "response size: %zu\n",r->size);
+
+ r->buf = talloc_array(r, uint8_t, r->size);
+ if(!r->buf) {
+ return ENOMEM;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "result [%d] krberr [%d] msgsize [%d] msg [%s]\n",
+ result, krberr, len, msg);
+
+ /* result */
+ SAFEALIGN_SET_UINT32(&r->buf[p], result, &p);
+
+ /* krb5 error code */
+ safealign_memcpy(&r->buf[p], &krberr, sizeof(krberr), &p);
+
+ /* message size */
+ SAFEALIGN_SET_UINT32(&r->buf[p], len, &p);
+
+ /* message itself */
+ safealign_memcpy(&r->buf[p], msg, len, &p);
+
+ /* ticket expiration time */
+ safealign_memcpy(&r->buf[p], &expire_time, sizeof(expire_time), &p);
+
+ return EOK;
+}
+
+static errno_t
+set_child_debugging(krb5_context ctx)
+{
+ krb5_error_code kerr;
+
+ /* Set the global error context */
+ krb5_error_ctx = ctx;
+
+ if (debug_level & SSSDBG_TRACE_ALL) {
+ kerr = sss_child_set_krb5_tracing(ctx);
+ if (kerr) {
+ LDAP_CHILD_DEBUG(SSSDBG_MINOR_FAILURE, kerr);
+ return EIO;
+ }
+ }
+
+ return EOK;
+}
+
+static int lc_verify_keytab_ex(const char *principal,
+ const char *keytab_name,
+ krb5_context context,
+ krb5_keytab keytab)
+{
+ bool found;
+ char *kt_principal;
+ krb5_error_code krberr;
+ krb5_kt_cursor cursor;
+ krb5_keytab_entry entry;
+
+ krberr = krb5_kt_start_seq_get(context, keytab, &cursor);
+ if (krberr) {
+ const char *__err_msg = sss_krb5_get_error_message(context, krberr);
+
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Cannot read keytab [%s]: [%d][%s].\n",
+ sss_printable_keytab_name(context, keytab_name),
+ krberr, __err_msg);
+
+ sss_log(SSS_LOG_ERR, "Error reading keytab file [%s]: [%d][%s]. "
+ "Unable to create GSSAPI-encrypted LDAP "
+ "connection.",
+ sss_printable_keytab_name(context, keytab_name),
+ krberr, __err_msg);
+
+ sss_krb5_free_error_message(context, __err_msg);
+ return EIO;
+ }
+
+ found = false;
+ while ((krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0) {
+ krberr = krb5_unparse_name(context, entry.principal, &kt_principal);
+ if (krberr) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Could not parse keytab entry\n");
+ sss_log(SSS_LOG_ERR, "Could not parse keytab entry\n");
+ krb5_kt_end_seq_get(context, keytab, &cursor);
+ return EIO;
+ }
+
+ if (strcmp(principal, kt_principal) == 0) {
+ found = true;
+ }
+ free(kt_principal);
+ krberr = sss_krb5_free_keytab_entry_contents(context, &entry);
+ if (krberr) {
+ /* This should never happen. The API docs for this function
+ * specify only success for this function
+ */
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not free keytab entry contents\n");
+ /* This is non-fatal, so we'll continue here */
+ }
+
+ if (found) {
+ break;
+ }
+ }
+
+ krberr = krb5_kt_end_seq_get(context, keytab, &cursor);
+ if (krberr) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Could not close keytab.\n");
+ sss_log(SSS_LOG_ERR, "Could not close keytab file [%s].",
+ sss_printable_keytab_name(context, keytab_name));
+ return EIO;
+ }
+
+ if (!found) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Principal [%s] not found in keytab [%s]\n",
+ principal,
+ sss_printable_keytab_name(context, keytab_name));
+ sss_log(SSS_LOG_ERR, "Error processing keytab file [%s]: "
+ "Principal [%s] was not found. "
+ "Unable to create GSSAPI-encrypted LDAP connection.",
+ sss_printable_keytab_name(context, keytab_name),
+ principal);
+
+ return EFAULT;
+ }
+
+ return EOK;
+}
+
+static krb5_error_code ldap_child_get_tgt_sync(TALLOC_CTX *memctx,
+ krb5_context context,
+ const char *realm_str,
+ const char *princ_str,
+ const char *keytab_name,
+ const krb5_deltat lifetime,
+ const char **ccname_out,
+ time_t *expire_time_out,
+ char **_krb5_msg)
+{
+ char *ccname;
+ char *ccname_dummy;
+ char *realm_name = NULL;
+ char *full_princ = NULL;
+ char *default_realm = NULL;
+ char *tmp_str = NULL;
+ krb5_keytab keytab = NULL;
+ krb5_ccache ccache = NULL;
+ krb5_principal kprinc;
+ krb5_creds my_creds;
+ krb5_get_init_creds_opt *options = NULL;
+ krb5_error_code krberr;
+ krb5_timestamp kdc_time_offset;
+ int canonicalize = 0;
+ int kdc_time_offset_usec;
+ int ret;
+ errno_t error_code;
+ TALLOC_CTX *tmp_ctx;
+ char *ccname_file_dummy = NULL;
+ char *ccname_file;
+
+ *_krb5_msg = NULL;
+
+ tmp_ctx = talloc_new(memctx);
+ if (tmp_ctx == NULL) {
+ krberr = KRB5KRB_ERR_GENERIC;
+ *_krb5_msg = talloc_strdup(memctx, strerror(ENOMEM));
+ goto done;
+ }
+
+ error_code = set_child_debugging(context);
+ if (error_code != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot set krb5_child debugging\n");
+ }
+
+ if (!realm_str) {
+ krberr = krb5_get_default_realm(context, &default_realm);
+ if (krberr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "krb5_get_default_realm() failed: %d\n", krberr);
+ goto done;
+ }
+
+ realm_name = talloc_strdup(tmp_ctx, default_realm);
+ krb5_free_default_realm(context, default_realm);
+ if (!realm_name) {
+ krberr = KRB5KRB_ERR_GENERIC;
+ *_krb5_msg = talloc_strdup(memctx, strerror(ENOMEM));
+ goto done;
+ }
+ } else {
+ realm_name = talloc_strdup(tmp_ctx, realm_str);
+ if (!realm_name) {
+ krberr = KRB5KRB_ERR_GENERIC;
+ *_krb5_msg = talloc_strdup(memctx, strerror(ENOMEM));
+ goto done;
+ }
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "got realm_name: [%s]\n", realm_name);
+
+ if (princ_str) {
+ if (!strchr(princ_str, '@')) {
+ full_princ = talloc_asprintf(tmp_ctx, "%s@%s",
+ princ_str, realm_name);
+ } else {
+ full_princ = talloc_strdup(tmp_ctx, princ_str);
+ }
+ } else {
+ char hostname[HOST_NAME_MAX + 1];
+
+ ret = gethostname(hostname, sizeof(hostname));
+ if (ret == -1) {
+ krberr = KRB5KRB_ERR_GENERIC;
+ *_krb5_msg = talloc_asprintf(memctx, "hostname() failed: [%d][%s]",
+ errno, strerror(errno));
+ goto done;
+ }
+ hostname[HOST_NAME_MAX] = '\0';
+
+ DEBUG(SSSDBG_TRACE_LIBS, "got hostname: [%s]\n", hostname);
+
+ ret = select_principal_from_keytab(tmp_ctx, hostname, realm_name,
+ keytab_name, &full_princ, NULL, NULL);
+ if (ret) {
+ krberr = KRB5_KT_IOERR;
+ *_krb5_msg = talloc_strdup(memctx,
+ "select_principal_from_keytab() failed");
+ goto done;
+ }
+ }
+ if (!full_princ) {
+ krberr = KRB5KRB_ERR_GENERIC;
+ *_krb5_msg = talloc_strdup(memctx, strerror(ENOMEM));
+ goto done;
+ }
+ DEBUG(SSSDBG_CONF_SETTINGS, "Principal name is: [%s]\n", full_princ);
+
+ if (keytab_name) {
+ krberr = krb5_kt_resolve(context, keytab_name, &keytab);
+ } else {
+ krberr = krb5_kt_default(context, &keytab);
+ }
+ DEBUG(SSSDBG_CONF_SETTINGS, "Using keytab [%s]\n",
+ sss_printable_keytab_name(context, keytab_name));
+ if (krberr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to read keytab file: %d\n", krberr);
+ goto done;
+ }
+
+ /* Verify the keytab */
+ ret = lc_verify_keytab_ex(full_princ, keytab_name, context, keytab);
+ if (ret) {
+ krberr = KRB5_KT_IOERR;
+ *_krb5_msg = talloc_strdup(memctx, "Unable to verify principal is present in the keytab");
+ goto done;
+ }
+
+ memset(&my_creds, 0, sizeof(my_creds));
+
+ krberr = krb5_get_init_creds_opt_alloc(context, &options);
+ if (krberr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "krb5_get_init_creds_opt_alloc failed.\n");
+ goto done;
+ }
+
+ krb5_get_init_creds_opt_set_address_list(options, NULL);
+ krb5_get_init_creds_opt_set_forwardable(options, 0);
+ krb5_get_init_creds_opt_set_proxiable(options, 0);
+ krb5_get_init_creds_opt_set_tkt_life(options, lifetime);
+ krberr = krb5_get_init_creds_opt_set_pa(context, options,
+ "X509_user_identity", "");
+ if (krberr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "krb5_get_init_creds_opt_set_pa failed [%d], ignored.\n",
+ krberr);
+ }
+
+
+ tmp_str = getenv("KRB5_CANONICALIZE");
+ if (tmp_str != NULL && strcasecmp(tmp_str, "true") == 0) {
+ DEBUG(SSSDBG_CONF_SETTINGS, "Will canonicalize principals\n");
+ canonicalize = 1;
+ }
+ sss_krb5_get_init_creds_opt_set_canonicalize(options, canonicalize);
+
+ ccname_file = talloc_asprintf(tmp_ctx, "%s/ccache_%s",
+ DB_PATH, realm_name);
+ if (ccname_file == NULL) {
+ krberr = KRB5KRB_ERR_GENERIC;
+ *_krb5_msg = talloc_strdup(memctx, strerror(ENOMEM));
+ goto done;
+ }
+
+ ccname_file_dummy = talloc_asprintf(tmp_ctx, "%s/ccache_%s_XXXXXX",
+ DB_PATH, realm_name);
+ if (ccname_file_dummy == NULL) {
+ krberr = KRB5KRB_ERR_GENERIC;
+ *_krb5_msg = talloc_strdup(memctx, strerror(ENOMEM));
+ goto done;
+ }
+ global_ccname_file_dummy = ccname_file_dummy;
+
+ ret = sss_unique_filename(tmp_ctx, ccname_file_dummy);
+ if (ret != EOK) {
+ krberr = KRB5KRB_ERR_GENERIC;
+ *_krb5_msg = talloc_asprintf(memctx,
+ "sss_unique_filename() failed: [%d][%s]",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ krberr = krb5_parse_name(context, full_princ, &kprinc);
+ if (krberr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "krb5_parse_name() failed: %d\n", krberr);
+ goto done;
+ }
+ krberr = krb5_get_init_creds_keytab(context, &my_creds, kprinc,
+ keytab, 0, NULL, options);
+ krb5_free_principal(context, kprinc);
+ if (krberr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "krb5_get_init_creds_keytab() failed: %d\n", krberr);
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL, "credentials initialized\n");
+ krb5_kt_close(context, keytab);
+ keytab = NULL;
+
+ ccname_dummy = talloc_asprintf(tmp_ctx, "FILE:%s", ccname_file_dummy);
+ ccname = talloc_asprintf(tmp_ctx, "FILE:%s", ccname_file);
+ if (ccname_dummy == NULL || ccname == NULL) {
+ krberr = KRB5KRB_ERR_GENERIC;
+ *_krb5_msg = talloc_strdup(memctx, strerror(ENOMEM));
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL, "keytab ccname: [%s]\n", ccname_dummy);
+
+ krberr = krb5_cc_resolve(context, ccname_dummy, &ccache);
+ if (krberr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "krb5_cc_resolve() failed: %d\n", krberr);
+ goto done;
+ }
+
+ /* Use updated principal if changed due to canonicalization. */
+ krberr = krb5_cc_initialize(context, ccache, my_creds.client);
+ if (krberr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "krb5_cc_initialize() failed: %d\n", krberr);
+ goto done;
+ }
+
+ krberr = krb5_cc_store_cred(context, ccache, &my_creds);
+ if (krberr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "krb5_cc_store_cred() failed: %d\n", krberr);
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL, "credentials stored\n");
+
+#ifdef HAVE_KRB5_GET_TIME_OFFSETS
+ krberr = krb5_get_time_offsets(context, &kdc_time_offset,
+ &kdc_time_offset_usec);
+ if (krberr != 0) {
+ const char *__err_msg = sss_krb5_get_error_message(context, krberr);
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to get KDC time offset: %s\n",
+ __err_msg);
+ sss_krb5_free_error_message(context, __err_msg);
+ kdc_time_offset = 0;
+ } else {
+ if (kdc_time_offset_usec > 0) {
+ kdc_time_offset++;
+ }
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Got KDC time offset\n");
+#else
+ /* If we don't have this function, just assume no offset */
+ kdc_time_offset = 0;
+#endif
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Renaming [%s] to [%s]\n", ccname_file_dummy, ccname_file);
+ ret = rename(ccname_file_dummy, ccname_file);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "rename failed [%d][%s].\n", ret, strerror(ret));
+ krberr = KRB5KRB_ERR_GENERIC;
+ *_krb5_msg = talloc_asprintf(memctx,
+ "rename() failed: [%d][%s]",
+ ret, strerror(ret));
+
+ goto done;
+ }
+ global_ccname_file_dummy = NULL;
+
+ krberr = 0;
+ *ccname_out = talloc_steal(memctx, ccname);
+ *expire_time_out = my_creds.times.endtime - kdc_time_offset;
+
+done:
+ krb5_get_init_creds_opt_free(context, options);
+ if (krberr != 0) {
+ if (*_krb5_msg == NULL) {
+ /* no custom error message provided hence get one from libkrb5 */
+ const char *__krberr_msg = sss_krb5_get_error_message(context, krberr);
+ *_krb5_msg = talloc_strdup(memctx, __krberr_msg);
+ sss_krb5_free_error_message(context, __krberr_msg);
+ }
+
+ sss_log(SSS_LOG_ERR,
+ "Failed to initialize credentials using keytab [%s]: %s. "
+ "Unable to create GSSAPI-encrypted LDAP connection.",
+ sss_printable_keytab_name(context, keytab_name), *_krb5_msg);
+
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Failed to initialize credentials using keytab [%s]: %s. "
+ "Unable to create GSSAPI-encrypted LDAP connection.\n",
+ sss_printable_keytab_name(context, keytab_name), *_krb5_msg);
+ }
+ if (keytab) krb5_kt_close(context, keytab);
+ if (context) krb5_free_context(context);
+ talloc_free(tmp_ctx);
+ return krberr;
+}
+
+static int prepare_response(TALLOC_CTX *mem_ctx,
+ const char *ccname,
+ time_t expire_time,
+ krb5_error_code kerr,
+ char *krb5_msg,
+ struct response **rsp)
+{
+ int ret;
+ struct response *r = NULL;
+
+ r = talloc_zero(mem_ctx, struct response);
+ if (!r) return ENOMEM;
+
+ r->buf = NULL;
+ r->size = 0;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Building response for result [%d]\n", kerr);
+
+ if (kerr == 0) {
+ ret = pack_buffer(r, EOK, kerr, ccname, expire_time);
+ } else {
+ if (krb5_msg == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Empty krb5 error message for non-zero kerr: %"PRIi32"\n",
+ kerr);
+ return ENOMEM;
+ }
+ ret = pack_buffer(r, EFAULT, kerr, krb5_msg, 0);
+ }
+
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "pack_buffer failed\n");
+ return ret;
+ }
+
+ *rsp = r;
+ return EOK;
+}
+
+static krb5_error_code privileged_krb5_setup(struct input_buffer *ibuf)
+{
+ krb5_error_code kerr;
+ char *keytab_name;
+
+ kerr = sss_krb5_init_context(&ibuf->context);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to init kerberos context\n");
+ return kerr;
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Kerberos context initialized\n");
+
+ kerr = copy_keytab_into_memory(ibuf, ibuf->context, ibuf->keytab_name,
+ &keytab_name, NULL);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "copy_keytab_into_memory failed.\n");
+ return kerr;
+ }
+ talloc_free(ibuf->keytab_name);
+ ibuf->keytab_name = keytab_name;
+
+ return 0;
+}
+
+int main(int argc, const char *argv[])
+{
+ int ret;
+ int kerr;
+ int opt;
+ int dumpable = 1;
+ int debug_fd = -1;
+ const char *opt_logger = NULL;
+ poptContext pc;
+ TALLOC_CTX *main_ctx = NULL;
+ uint8_t *buf = NULL;
+ ssize_t len = 0;
+ const char *ccname = NULL;
+ char *krb5_msg = NULL;
+ time_t expire_time = 0;
+ struct input_buffer *ibuf = NULL;
+ struct response *resp = NULL;
+ ssize_t written;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ SSSD_DEBUG_OPTS
+ {"dumpable", 0, POPT_ARG_INT, &dumpable, 0,
+ _("Allow core dumps"), NULL },
+ {"debug-fd", 0, POPT_ARG_INT, &debug_fd, 0,
+ _("An open file descriptor for the debug logs"), NULL},
+ SSSD_LOGGER_OPTS
+ POPT_TABLEEND
+ };
+
+ /* Set debug level to invalid value so we can decide if -d 0 was used. */
+ debug_level = SSSDBG_INVALID;
+
+ pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch(opt) {
+ default:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ _exit(-1);
+ }
+ }
+
+ poptFreeContext(pc);
+
+ prctl(PR_SET_DUMPABLE, (dumpable == 0) ? 0 : 1);
+
+ debug_prg_name = talloc_asprintf(NULL, "ldap_child[%d]", getpid());
+ if (!debug_prg_name) {
+ debug_prg_name = "ldap_child";
+ ERROR("talloc_asprintf failed.\n");
+ goto fail;
+ }
+
+ if (debug_fd != -1) {
+ opt_logger = sss_logger_str[FILES_LOGGER];
+ ret = set_debug_file_from_fd(debug_fd);
+ if (ret != EOK) {
+ opt_logger = sss_logger_str[STDERR_LOGGER];
+ ERROR("set_debug_file_from_fd failed.\n");
+ }
+ }
+
+ DEBUG_INIT(debug_level, opt_logger);
+
+ BlockSignals(false, SIGTERM);
+ CatchSignal(SIGTERM, sig_term_handler);
+
+ DEBUG(SSSDBG_TRACE_FUNC, "ldap_child started.\n");
+
+ main_ctx = talloc_new(NULL);
+ if (main_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed.\n");
+ talloc_free(discard_const(debug_prg_name));
+ goto fail;
+ }
+ talloc_steal(main_ctx, debug_prg_name);
+
+ buf = talloc_size(main_ctx, sizeof(uint8_t)*IN_BUF_SIZE);
+ if (buf == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_size failed.\n");
+ goto fail;
+ }
+
+ ibuf = talloc_zero(main_ctx, struct input_buffer);
+ if (ibuf == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_size failed.\n");
+ goto fail;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "context initialized\n");
+
+ errno = 0;
+ len = sss_atomic_read_s(STDIN_FILENO, buf, IN_BUF_SIZE);
+ if (len == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "read failed [%d][%s].\n", ret, strerror(ret));
+ goto fail;
+ }
+
+ close(STDIN_FILENO);
+
+ ret = unpack_buffer(buf, len, ibuf);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "unpack_buffer failed.[%d][%s].\n", ret, strerror(ret));
+ goto fail;
+ }
+
+ kerr = privileged_krb5_setup(ibuf);
+ if (kerr != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Privileged Krb5 setup failed.\n");
+ goto fail;
+ }
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Kerberos context initialized\n");
+
+ kerr = become_user(ibuf->uid, ibuf->gid);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "become_user failed.\n");
+ goto fail;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "Running as [%"SPRIuid"][%"SPRIgid"].\n", geteuid(), getegid());
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, "getting TGT sync\n");
+ kerr = ldap_child_get_tgt_sync(main_ctx, ibuf->context,
+ ibuf->realm_str, ibuf->princ_str,
+ ibuf->keytab_name, ibuf->lifetime,
+ &ccname, &expire_time, &krb5_msg);
+ if (kerr != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "ldap_child_get_tgt_sync failed.\n");
+ /* Do not return, must report failure */
+ }
+
+ ret = prepare_response(main_ctx, ccname, expire_time, kerr, krb5_msg,
+ &resp);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "prepare_response failed. [%d][%s].\n",
+ ret, strerror(ret));
+ goto fail;
+ }
+
+ errno = 0;
+ written = sss_atomic_write_s(STDOUT_FILENO, resp->buf, resp->size);
+ if (written == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "write failed [%d][%s].\n", ret,
+ strerror(ret));
+ goto fail;
+ }
+
+ if (written != resp->size) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Expected to write %zu bytes, wrote %zu\n",
+ resp->size, written);
+ goto fail;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "ldap_child completed successfully\n");
+ close(STDOUT_FILENO);
+ talloc_free(main_ctx);
+ _exit(0);
+
+fail:
+ DEBUG(SSSDBG_CRIT_FAILURE, "ldap_child failed!\n");
+ close(STDOUT_FILENO);
+ talloc_free(main_ctx);
+ _exit(-1);
+}