From 1c48b5a62f73234ed26bb20f0ab345ab61cda0ab Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Thu, 18 Feb 2010 07:49:04 -0500 Subject: Rename server/ directory to src/ Also update BUILD.txt --- src/providers/ldap/ldap_child.c | 429 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 429 insertions(+) create mode 100644 src/providers/ldap/ldap_child.c (limited to 'src/providers/ldap/ldap_child.c') diff --git a/src/providers/ldap/ldap_child.c b/src/providers/ldap/ldap_child.c new file mode 100644 index 000000000..0d34be2ca --- /dev/null +++ b/src/providers/ldap/ldap_child.c @@ -0,0 +1,429 @@ +/* + SSSD + + LDAP Backend Module -- prime ccache with TGT in a child process + + Authors: + Jakub Hrozek + + 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 +#include +#include +#include + +#include + +#include "util/util.h" +#include "util/sss_krb5.h" +#include "providers/child_common.h" +#include "providers/dp_backend.h" + +static krb5_context krb5_error_ctx; + +struct input_buffer { + const char *realm_str; + const char *princ_str; + const char *keytab_name; +}; + +static errno_t unpack_buffer(uint8_t *buf, size_t size, + struct input_buffer *ibuf) +{ + size_t p = 0; + uint32_t len; + + DEBUG(7, ("total buffer size: %d\n", size)); + + /* realm_str size and length */ + COPY_UINT32_CHECK(&len, buf + p, p, size); + + DEBUG(7, ("realm_str size: %d\n", len)); + if (len) { + if ((p + len ) > size) return EINVAL; + ibuf->realm_str = talloc_strndup(ibuf, (char *)(buf + p), len); + DEBUG(7, ("got realm_str: %s\n", ibuf->realm_str)); + if (ibuf->realm_str == NULL) return ENOMEM; + p += len; + } + + /* princ_str size and length */ + COPY_UINT32_CHECK(&len, buf + p, p, size); + + DEBUG(7, ("princ_str size: %d\n", len)); + if (len) { + if ((p + len ) > size) return EINVAL; + ibuf->princ_str = talloc_strndup(ibuf, (char *)(buf + p), len); + DEBUG(7, ("got princ_str: %s\n", ibuf->princ_str)); + if (ibuf->princ_str == NULL) return ENOMEM; + p += len; + } + + /* keytab_name size and length */ + COPY_UINT32_CHECK(&len, buf + p, p, size); + + DEBUG(7, ("keytab_name size: %d\n", len)); + if (len) { + if ((p + len ) > size) return EINVAL; + ibuf->keytab_name = talloc_strndup(ibuf, (char *)(buf + p), len); + DEBUG(7, ("got keytab_name: %s\n", ibuf->keytab_name)); + if (ibuf->keytab_name == NULL) return ENOMEM; + p += len; + } + + return EOK; +} + +static int pack_buffer(struct response *r, int result, const char *msg) +{ + int len; + int p = 0; + + len = strlen(msg); + r->size = 2 * sizeof(uint32_t) + len; + + /* result */ + COPY_UINT32_VALUE(&r->buf[p], result, p); + + /* message size */ + COPY_UINT32_VALUE(&r->buf[p], len, p); + + /* message itself */ + COPY_MEM(&r->buf[p], msg, p, len); + + return EOK; +} + +static int ldap_child_get_tgt_sync(TALLOC_CTX *memctx, + const char *realm_str, + const char *princ_str, + const char *keytab_name, + const char **ccname_out) +{ + char *ccname; + char *realm_name = NULL; + char *full_princ = NULL; + krb5_context context = NULL; + krb5_keytab keytab = NULL; + krb5_ccache ccache = NULL; + krb5_principal kprinc; + krb5_creds my_creds; + krb5_get_init_creds_opt options; + krb5_error_code krberr; + int ret; + + krberr = krb5_init_context(&context); + if (krberr) { + DEBUG(2, ("Failed to init kerberos context\n")); + return EFAULT; + } + + if (!realm_str) { + krberr = krb5_get_default_realm(context, &realm_name); + if (krberr) { + DEBUG(2, ("Failed to get default realm name: %s\n", + sss_krb5_get_error_message(context, krberr))); + ret = EFAULT; + goto done; + } + } else { + realm_name = talloc_strdup(memctx, realm_str); + if (!realm_name) { + ret = ENOMEM; + goto done; + } + } + + if (princ_str) { + if (!strchr(princ_str, '@')) { + full_princ = talloc_asprintf(memctx, "%s@%s", + princ_str, realm_name); + } else { + full_princ = talloc_strdup(memctx, princ_str); + } + } else { + char hostname[512]; + + ret = gethostname(hostname, 511); + if (ret == -1) { + ret = errno; + goto done; + } + hostname[511] = '\0'; + + full_princ = talloc_asprintf(memctx, "host/%s@%s", + hostname, realm_name); + } + if (!full_princ) { + ret = ENOMEM; + goto done; + } + DEBUG(4, ("Principal name is: [%s]\n", full_princ)); + + krberr = krb5_parse_name(context, full_princ, &kprinc); + if (krberr) { + DEBUG(2, ("Unable to build principal: %s\n", + sss_krb5_get_error_message(context, krberr))); + ret = EFAULT; + goto done; + } + + if (keytab_name) { + krberr = krb5_kt_resolve(context, keytab_name, &keytab); + } else { + krberr = krb5_kt_default(context, &keytab); + } + if (krberr) { + DEBUG(2, ("Failed to read keytab file: %s\n", + sss_krb5_get_error_message(context, krberr))); + ret = EFAULT; + goto done; + } + + ccname = talloc_asprintf(memctx, "FILE:%s/ccache_%s", DB_PATH, realm_name); + if (!ccname) { + ret = ENOMEM; + goto done; + } + + krberr = krb5_cc_resolve(context, ccname, &ccache); + if (krberr) { + DEBUG(2, ("Failed to set cache name: %s\n", + sss_krb5_get_error_message(context, krberr))); + ret = EFAULT; + goto done; + } + + memset(&my_creds, 0, sizeof(my_creds)); + memset(&options, 0, sizeof(options)); + + 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); + /* set a very short lifetime, we don't keep the ticket around */ + krb5_get_init_creds_opt_set_tkt_life(&options, 300); + + krberr = krb5_get_init_creds_keytab(context, &my_creds, kprinc, + keytab, 0, NULL, &options); + + if (krberr) { + DEBUG(2, ("Failed to init credentials: %s\n", + sss_krb5_get_error_message(context, krberr))); + ret = EFAULT; + goto done; + } + + krberr = krb5_cc_initialize(context, ccache, kprinc); + if (krberr) { + DEBUG(2, ("Failed to init ccache: %s\n", + sss_krb5_get_error_message(context, krberr))); + ret = EFAULT; + goto done; + } + + krberr = krb5_cc_store_cred(context, ccache, &my_creds); + if (krberr) { + DEBUG(2, ("Failed to store creds: %s\n", + sss_krb5_get_error_message(context, krberr))); + ret = EFAULT; + goto done; + } + + ret = EOK; + *ccname_out = ccname; + +done: + if (keytab) krb5_kt_close(context, keytab); + if (context) krb5_free_context(context); + return ret; +} + +static int prepare_response(TALLOC_CTX *mem_ctx, + const char *ccname, + krb5_error_code kerr, + struct response **rsp) +{ + int ret; + struct response *r = NULL; + const char *krb5_msg = NULL; + + r = talloc_zero(mem_ctx, struct response); + if (!r) return ENOMEM; + + r->buf = talloc_size(mem_ctx, MAX_CHILD_MSG_SIZE); + if (r->buf == NULL) { + DEBUG(1, ("talloc_size failed.\n")); + return ENOMEM; + } + r->max_size = MAX_CHILD_MSG_SIZE; + r->size = 0; + + if (kerr == 0) { + ret = pack_buffer(r, EOK, ccname); + } else { + krb5_msg = sss_krb5_get_error_message(krb5_error_ctx, kerr); + if (krb5_msg == NULL) { + DEBUG(1, ("sss_krb5_get_error_message failed.\n")); + return ENOMEM; + } + + ret = pack_buffer(r, EFAULT, krb5_msg); + sss_krb5_free_error_message(krb5_error_ctx, krb5_msg); + } + + if (ret != EOK) { + DEBUG(1, ("pack_buffer failed\n")); + return ret; + } + + *rsp = r; + return EOK; +} + +int main(int argc, const char *argv[]) +{ + int ret; + int kerr; + int opt; + int debug_fd = -1; + poptContext pc; + TALLOC_CTX *main_ctx; + uint8_t *buf = NULL; + ssize_t len = 0; + const char *ccname = NULL; + struct input_buffer *ibuf = NULL; + struct response *resp = NULL; + size_t written; + + struct poptOption long_options[] = { + POPT_AUTOHELP + {"debug-level", 'd', POPT_ARG_INT, &debug_level, 0, + _("Debug level"), NULL}, + {"debug-timestamps", 0, POPT_ARG_INT, &debug_timestamps, 0, + _("Add debug timestamps"), NULL}, + {"debug-fd", 0, POPT_ARG_INT, &debug_fd, 0, + _("An open file descriptor for the debug logs"), NULL}, + POPT_TABLEEND + }; + + 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); + + DEBUG(7, ("ldap_child started.\n")); + + main_ctx = talloc_new(NULL); + if (main_ctx == NULL) { + DEBUG(1, ("talloc_new failed.\n")); + _exit(-1); + } + + debug_prg_name = talloc_asprintf(main_ctx, "[sssd[ldap_child[%d]]]", getpid()); + + if (debug_fd != -1) { + ret = set_debug_file_from_fd(debug_fd); + if (ret != EOK) { + DEBUG(1, ("set_debug_file_from_fd failed.\n")); + } + } + + buf = talloc_size(main_ctx, sizeof(uint8_t)*IN_BUF_SIZE); + if (buf == NULL) { + DEBUG(1, ("talloc_size failed.\n")); + goto fail; + } + + ibuf = talloc_zero(main_ctx, struct input_buffer); + if (ibuf == NULL) { + DEBUG(1, ("talloc_size failed.\n")); + goto fail; + } + + while ((ret = read(STDIN_FILENO, buf + len, IN_BUF_SIZE - len)) != 0) { + if (ret == -1) { + if (errno == EINTR || errno == EAGAIN) { + continue; + } + DEBUG(1, ("read failed [%d][%s].\n", errno, strerror(errno))); + goto fail; + } else if (ret > 0) { + len += ret; + if (len > IN_BUF_SIZE) { + DEBUG(1, ("read too much, this should never happen.\n")); + goto fail; + } + continue; + } else { + DEBUG(1, ("unexpected return code of read [%d].\n", ret)); + goto fail; + } + } + close(STDIN_FILENO); + + ret = unpack_buffer(buf, len, ibuf); + if (ret != EOK) { + DEBUG(1, ("unpack_buffer failed.[%d][%s].\n", ret, strerror(ret))); + goto fail; + } + + kerr = ldap_child_get_tgt_sync(main_ctx, + ibuf->realm_str, ibuf->princ_str, + ibuf->keytab_name, &ccname); + if (kerr != EOK) { + DEBUG(1, ("ldap_child_get_tgt_sync failed.\n")); + /* Do not return, must report failure */ + } + + ret = prepare_response(main_ctx, ccname, kerr, &resp); + if (ret != EOK) { + DEBUG(1, ("prepare_response failed. [%d][%s].\n", ret, strerror(ret))); + return ENOMEM; + } + + written = 0; + while (written < resp->size) { + ret = write(STDOUT_FILENO, resp->buf + written, resp->size - written); + if (ret == -1) { + if (errno == EAGAIN || errno == EINTR) { + continue; + } + ret = errno; + DEBUG(1, ("write failed [%d][%s].\n", ret, strerror(ret))); + return ret; + } + written += ret; + } + + close(STDOUT_FILENO); + talloc_free(main_ctx); + _exit(0); + +fail: + close(STDOUT_FILENO); + talloc_free(main_ctx); + _exit(-1); +} -- cgit