On Tue, Sep 03, 2013 at 10:07:13PM -0400, Simo Sorce wrote:
> After the recent patches to explicitly enable the KEYRING type in SSSD I
> realized that the code that manipulates ccaches had grown too much, and,
> most importantly, was doing unnecessary operations already performed in
> an abstract way by krb5 functions.
> 
> This patch set mostly addresses ticket #2061
> 
> The aims has been to remove as much as possible type-specific code,
> resorting to type specific behavior only as an explicit exception where
> necessary due to historical or other reasons.
> 
> The combined diff gives a nice total stat of:
>   815 insertions(+)
>   1529 deletions(-)
> 
> 
> The last patch is an attempt to address ticket #2071,
> 
> It was necessary to add it here otherwise sssd will fail to operate
> correctly with some templates (as noted in #2071).
> However I am not sure that's the way we want to resolve the problem.
> The patch aimed at maintaining as much as possible a reasonable
> behavior, although the intended behavior was not really written
> anywhere. Personally I would rather scrap the patch and instead provide
> a new one that would simply stop creating public directories at all, I
> do not think it is sssd's role to fix/create directories that should be
> set up by the admin appropriately ahead of time (either manually of via
> tmpfiles.d or whatever).
> 
> I will try to follow up with a proposed patch that 'simplifies' sssd
> behavior instead of fixing it for #2071
> 
> Simo.
> 
> -- 
> Simo Sorce * Red Hat, Inc * New York

I've reviewed the first 14 patches and my comments and the result of a
discussion on irc about how security can be improved when running as a
user are below.

I'll review the 15th patch together with the alternative one and send my
comments separately.

bye,
Sumit

> From e68d428e3d2b6de23d42aeaf174ab7f04fd593b7 Mon Sep 17 00:00:00 2001
> From: Simo Sorce <s...@redhat.com>
> Date: Wed, 28 Aug 2013 21:19:32 -0400
> Subject: [PATCH 01/15] krb5: Add calls to change and restore credentials
> 
> In some cases we want to temporarily assume user credentials but allow the
> process to regain back the original credentials (normally regaining uid 0).
> 
> Related:
> https://fedorahosted.org/sssd/ticket/2061
> ---
>  src/providers/krb5/krb5_become_user.c | 126 
> ++++++++++++++++++++++++++++++++++
>  src/providers/krb5/krb5_utils.h       |   6 ++
>  2 files changed, 132 insertions(+)
> 
> diff --git a/src/providers/krb5/krb5_become_user.c 
> b/src/providers/krb5/krb5_become_user.c
> index 
> 70bc5630ece21505b58bd1a8795d7ab4b7864460..e78ad04734d85175ede82c4635503f6bf6af1a2c
>  100644
> --- a/src/providers/krb5/krb5_become_user.c
> +++ b/src/providers/krb5/krb5_become_user.c
> @@ -70,3 +70,129 @@ errno_t become_user(uid_t uid, gid_t gid)
>      return EOK;
>  }
>  
> +struct sss_creds {
> +    uid_t uid;
> +    gid_t gid;
> +    int num_gids;
> +    gid_t gids[];
> +};
> +
> +errno_t restore_creds(struct sss_creds *saved_creds);
> +
> +/* This is a reversible version of become_user, and returns the saved
> + * credentials so that creds can be switched back calling restore_creds */
> +errno_t switch_creds(TALLOC_CTX *mem_ctx,
> +                     uid_t uid, gid_t gid,
> +                     int num_gids, gid_t *gids,
> +                     struct sss_creds **saved_creds)
> +{
> +    struct sss_creds *ssc = NULL;
> +    int size;
> +    int ret;
> +
> +    DEBUG(SSSDBG_FUNC_DATA, ("Switch user to [%d][%d].\n", uid, gid));
> +
> +    if (saved_creds) {
> +        /* save current user credentials */
> +        size = getgroups(0, NULL);
> +        if (size == -1) {
> +            ret = errno;
> +            DEBUG(SSSDBG_CRIT_FAILURE, ("Getgroups failed! (%d, %s)\n",
> +                                        ret, strerror(ret)));
> +            goto done;
> +        }
> +
> +        ssc = talloc_size(mem_ctx,
> +                          (sizeof(struct sss_creds) + size * sizeof(gid_t)));
> +        if (!ssc) {
> +            DEBUG(SSSDBG_CRIT_FAILURE, ("Allocation failed!\n"));
> +            ret = ENOMEM;
> +            goto done;
> +        }
> +        ssc->num_gids = size;
> +
> +        size = getgroups(ssc->num_gids, ssc->gids);
> +        if (size == -1) {
> +            ret = errno;
> +            DEBUG(SSSDBG_CRIT_FAILURE, ("Getgroups failed! (%d, %s)\n",
> +                                        ret, strerror(ret)));
> +            /* free ssc immediately otherwise the code will try to restore
> +             * wrong creds */
> +            free(ssc);

talloc_free or talloc_zfree

> +            ssc = NULL;
> +            goto done;
> +        }
> +
> +        /* we care only about effective ids */
> +        ssc->uid = geteuid();
> +        ssc->gid = getegid();
> +    }
> +
> +    /* if we are regaining root set euid first so that we have CAP_SETUID 
> back,
> +     * ane the other calls work too, otherwise call it last so that we can
> +     * change groups before we loose CAP_SETUID */
> +    if (uid == 0) {
> +        ret = setresuid(0, 0, 0);
> +        if (ret == -1) {
> +            ret = errno;
> +            DEBUG(SSSDBG_CRIT_FAILURE,
> +                  ("setresuid failed [%d][%s].\n", ret, strerror(ret)));
> +            goto done;
> +        }
> +    }
> +
> +    /* TODO: use prctl to get/set capabilities too ? */
> +
> +    /* try to setgroups first should always work if CAP_SETUID is set,
> +     * otherwise it will always fail, failure is not critical though as
> +     * generally we only really care about uid and at mot primary gid */
> +    ret = setgroups(num_gids, gids);
> +    if (ret == -1) {
> +        ret = errno;
> +        DEBUG(SSSDBG_TRACE_FUNC,
> +              ("setgroups failed [%d][%s].\n", ret, strerror(ret)));
> +    }
> +
> +    /* change gid now, (leaves saved gid to current, so we can restore) */
> +    ret = setresgid(gid, gid, -1);

only setting the egid should be sufficient and safer

> +    if (ret == -1) {
> +        ret = errno;
> +        DEBUG(SSSDBG_CRIT_FAILURE,
> +              ("setresgid failed [%d][%s].\n", ret, strerror(ret)));
> +        goto done;
> +    }
> +
> +    if (uid != 0) {
> +        /* change uid, (leaves saved uid to current, so we can restore) */
> +        ret = setresuid(uid, uid, -1);

only setting the euid should be sufficient and safer

> +        if (ret == -1) {
> +            ret = errno;
> +            DEBUG(SSSDBG_CRIT_FAILURE,
> +                  ("setresuid failed [%d][%s].\n", ret, strerror(ret)));
> +            goto done;
> +        }
> +    }
> +
> +    ret = 0;
> +
> +done:
> +    if (ret) {
> +        if (ssc) {
> +            /* attempt to restore creds first */
> +            restore_creds(ssc);
> +            talloc_free(ssc);
> +        }
> +    } else if (saved_creds) {
> +        *saved_creds = ssc;
> +    }
> +    return ret;
> +}
> +
> +errno_t restore_creds(struct sss_creds *saved_creds)
> +{
> +    return switch_creds(saved_creds,
> +                        saved_creds->uid,
> +                        saved_creds->gid,
> +                        saved_creds->num_gids,
> +                        saved_creds->gids, NULL);
> +}
> diff --git a/src/providers/krb5/krb5_utils.h b/src/providers/krb5/krb5_utils.h
> index 
> cdc9f23641905cff300077f708087e98ba683e0d..aac3ec72ec7e1664ae96cc4e53d368e22448f5f1
>  100644
> --- a/src/providers/krb5/krb5_utils.h
> +++ b/src/providers/krb5/krb5_utils.h
> @@ -80,6 +80,12 @@ char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct 
> krb5child_req *kr,
>                               bool case_sensitive, bool *private_path);
>  
>  errno_t become_user(uid_t uid, gid_t gid);
> +struct sss_creds;
> +errno_t switch_creds(TALLOC_CTX *mem_ctx,
> +                     uid_t uid, gid_t gid,
> +                     int num_gids, gid_t *gids,
> +                     struct sss_creds **saved_creds);
> +errno_t restore_creds(struct sss_creds *saved_creds);
>  
>  errno_t get_ccache_file_data(const char *ccache_file, const char 
> *client_name,
>                               struct tgt_times *tgtt);
> -- 
> 1.8.3.1
> 

....

> From 69347a7f7281b1cdbe3906bcc86a8f9c515808e3 Mon Sep 17 00:00:00 2001
> From: Simo Sorce <s...@redhat.com>
> Date: Mon, 2 Sep 2013 23:52:46 -0400
> Subject: [PATCH 13/15] krb5_child: Simplify ccache creation
> 
> The containing ccache directory is precreated by the parent code,
> so there is no special need to do so here for any type.
> Also the special handling for the FILE ccache temporary file is not really
> useful, because libkrb5 internally unlinks and then recreate the file, so
> mkstemp cannot really prevent subtle races, it can only make sure the file is
> unique at creation time.
> 
> Resolves:
> https://fedorahosted.org/sssd/ticket/2061
> ---

....

> -static krb5_error_code create_ccache_file(krb5_context ctx,
> -                                          krb5_principal princ,
> -                                          char *ccname, krb5_creds *creds)
> +static errno_t handle_randomized(TALLOC_CTX *mem_ctx, char *in)

mem_ctx is not used in handle_randomized()

>  {
> -    krb5_error_code kerr;
> -    krb5_ccache tmp_cc = NULL;
> -    char *cc_file_name;
> -    int fd = -1;
>      size_t ccname_len;
> -    char *dummy;
> -    char *tmp_ccname;
> -    TALLOC_CTX *tmp_ctx = NULL;
> -    mode_t old_umask;
> +    char *ccname = NULL;
> +    int ret;
> +    int fd;
>  
> -    DEBUG(SSSDBG_FUNC_DATA, ("Creating ccache at [%s]\n", ccname));
> -
> -    if (strncmp(ccname, "FILE:", 5) == 0) {
> -        cc_file_name = ccname + 5;
> +    /* We only treat the FILE type case in a special way due to the history
> +     * of storing FILE type ccache in /tmp and associated security issues */
> +    if (in[0] == '/') {
> +        ccname = in;
> +    } else if (strncmp(in, "FILE:", 5) == 0) {
> +        ccname = in + 5;
>      } else {
> -        cc_file_name = ccname;
> -    }
> -
> -    if (cc_file_name[0] != '/') {
> -        DEBUG(1, ("Ccache filename is not an absolute path.\n"));
> -        return EINVAL;
> -    }
> -
> -    tmp_ctx = talloc_new(tmp_ctx);
> -    if (tmp_ctx == NULL) {
> -        DEBUG(1, ("talloc_new failed.\n"));
> -        return ENOMEM;
> -    }
> -
> -    dummy = strrchr(cc_file_name, '/');
> -    tmp_ccname = talloc_strndup(tmp_ctx, cc_file_name,
> -                                (size_t) (dummy-cc_file_name));
> -    if (tmp_ccname == NULL) {
> -        DEBUG(1, ("talloc_strdup failed.\n"));
> -        kerr = ENOMEM;
> -        goto done;
> -    }
> -    tmp_ccname = talloc_asprintf_append(tmp_ccname, "/.krb5cc_dummy_XXXXXX");
> -    if (tmp_ccname == NULL) {
> -        kerr = ENOMEM;
> -        goto done;
> -    }
> -
> -    old_umask = umask(077);
> -    fd = mkstemp(tmp_ccname);
> -    umask(old_umask);
> -    if (fd == -1) {
> -        kerr = errno;
> -        DEBUG(SSSDBG_CRIT_FAILURE,
> -              ("mkstemp failed [%d][%s].\n", kerr, strerror(kerr)));
> -        goto done;
> -    }
> -
> -    kerr = krb5_cc_resolve(ctx, tmp_ccname, &tmp_cc);
> -    if (kerr != 0) {
> -        KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
> -        goto done;
> -    }
> -
> -    kerr = store_creds_in_ccache(ctx, princ, tmp_cc, creds);
> -    if (fd != -1) {
> -        close(fd);
> -        fd = -1;
> -    }
> -    if (kerr != 0) {
> -        KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
> -        goto done;
> -    }
> -
> -
> -    ccname_len = strlen(cc_file_name);
> -    if (ccname_len >= 6 && strcmp(cc_file_name + (ccname_len - 6), "XXXXXX") 
> == 0) {
> -        fd = mkstemp(cc_file_name);
> +        return EOK;
> +    }
> +
> +    ccname_len = strlen(ccname);
> +    if (ccname_len >= 6 && strcmp(ccname + (ccname_len - 6), "XXXXXX") == 0) 
> {
> +        /* NOTE: this call is only used to create a unique name, as later
> +         * krb5_cc_initialize() will unlink and recreate the file.
> +         * This is ok because this part of the code is called with
> +         * privileges already dropped when handling user ccache, or the 
> ccache
> +         * is stored in a private directory. So we do not have huge issues if
> +         * something races, we mostly care only about not accidentally use
> +         * an existing name and thus failing in the process of saving the
> +         * cache. Malicious races can only be avoided by libkrb5 itself. */
> +        fd = mkstemp(ccname);
>          if (fd == -1) {
> -            kerr = errno;
> -            DEBUG(SSSDBG_CRIT_FAILURE,
> -                  ("mkstemp failed [%d][%s].\n", kerr, strerror(kerr)));
> -            goto done;
> -        }
> -    }
> -
> -    kerr = rename(tmp_ccname, cc_file_name);
> -    if (kerr == -1) {
> -        kerr = errno;
> -        DEBUG(1, ("rename failed [%d][%s].\n", kerr, strerror(kerr)));
> -    }
> -
> -    DEBUG(SSSDBG_TRACE_LIBS, ("Created ccache file: [%s]\n", ccname));
> -
> -done:
> -    if (kerr != 0 && tmp_cc != NULL) {
> -        krb5_cc_destroy(ctx, tmp_cc);
> -    }
> -
> -    if (fd != -1) {
> -        close(fd);
> -    }
> -
> -    talloc_free(tmp_ctx);
> -    return kerr;
> -}
> -
_______________________________________________
sssd-devel mailing list
sssd-devel@lists.fedorahosted.org
https://lists.fedorahosted.org/mailman/listinfo/sssd-devel

Reply via email to