local.c revision efc65e78fa4e01e6cecc8690a9899af61213be62
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen/*
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen SSSD
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen Secrets Responder
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen Copyright (C) Simo Sorce <ssorce@redhat.com> 2016
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen This program is free software; you can redistribute it and/or modify
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen it under the terms of the GNU General Public License as published by
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen the Free Software Foundation; either version 3 of the License, or
82eadbc4311faf7719d5db33fddaa06cb3a7010bTimo Sirainen (at your option) any later version.
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen This program is distributed in the hope that it will be useful,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen but WITHOUT ANY WARRANTY; without even the implied warranty of
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen GNU General Public License for more details.
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen You should have received a copy of the GNU General Public License
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen along with this program. If not, see <http://www.gnu.org/licenses/>.
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen*/
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen#include "responder/secrets/secsrv_private.h"
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen#include "util/crypto/sss_crypto.h"
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen#include <time.h>
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen#include <ldb.h>
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen#define MKEY_SIZE (256 / 8)
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainenstruct local_context {
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen struct ldb_context *ldb;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen struct sec_data master_key;
48afa4224df2a6bcfe75fec11a59c224426dcdc1Teemu Huovila int containers_nest_level;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen};
48afa4224df2a6bcfe75fec11a59c224426dcdc1Teemu Huovila
48afa4224df2a6bcfe75fec11a59c224426dcdc1Teemu Huovilastatic int local_decrypt(struct local_context *lctx, TALLOC_CTX *mem_ctx,
48afa4224df2a6bcfe75fec11a59c224426dcdc1Teemu Huovila const char *secret, const char *enctype,
48afa4224df2a6bcfe75fec11a59c224426dcdc1Teemu Huovila char **plain_secret)
48afa4224df2a6bcfe75fec11a59c224426dcdc1Teemu Huovila{
48afa4224df2a6bcfe75fec11a59c224426dcdc1Teemu Huovila char *output;
48afa4224df2a6bcfe75fec11a59c224426dcdc1Teemu Huovila
48afa4224df2a6bcfe75fec11a59c224426dcdc1Teemu Huovila if (enctype && strcmp(enctype, "masterkey") == 0) {
48afa4224df2a6bcfe75fec11a59c224426dcdc1Teemu Huovila struct sec_data _secret;
48afa4224df2a6bcfe75fec11a59c224426dcdc1Teemu Huovila size_t outlen;
48afa4224df2a6bcfe75fec11a59c224426dcdc1Teemu Huovila int ret;
48afa4224df2a6bcfe75fec11a59c224426dcdc1Teemu Huovila
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen _secret.data = (char *)sss_base64_decode(mem_ctx, secret,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen &_secret.length);
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (!_secret.data) return EINVAL;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen ret = sss_decrypt(mem_ctx, AES256CBC_HMAC_SHA256,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen (uint8_t *)lctx->master_key.data,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen lctx->master_key.length,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen (uint8_t *)_secret.data, _secret.length,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen (uint8_t **)&output, &outlen);
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (ret) return ret;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (((strnlen(output, outlen) + 1) != outlen) ||
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen output[outlen - 1] != '\0') {
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen return EIO;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen }
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen } else {
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen output = talloc_strdup(mem_ctx, secret);
a9b3887f4d9ed75a76fed964c1930432bf84f4f5Timo Sirainen if (!output) return ENOMEM;
a9b3887f4d9ed75a76fed964c1930432bf84f4f5Timo Sirainen }
a9b3887f4d9ed75a76fed964c1930432bf84f4f5Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen *plain_secret = output;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen return EOK;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen}
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
a9b3887f4d9ed75a76fed964c1930432bf84f4f5Timo Sirainenstatic int local_encrypt(struct local_context *lctx, TALLOC_CTX *mem_ctx,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen const char *secret, const char *enctype,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen char **ciphertext)
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen{
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen struct sec_data _secret;
a9b3887f4d9ed75a76fed964c1930432bf84f4f5Timo Sirainen char *output;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen int ret;
a9b3887f4d9ed75a76fed964c1930432bf84f4f5Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (!enctype || strcmp(enctype, "masterkey") != 0) return EINVAL;
a9b3887f4d9ed75a76fed964c1930432bf84f4f5Timo Sirainen
a9b3887f4d9ed75a76fed964c1930432bf84f4f5Timo Sirainen ret = sss_encrypt(mem_ctx, AES256CBC_HMAC_SHA256,
a9b3887f4d9ed75a76fed964c1930432bf84f4f5Timo Sirainen (uint8_t *)lctx->master_key.data,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen lctx->master_key.length,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen (const uint8_t *)secret, strlen(secret) + 1,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen (uint8_t **)&_secret.data, &_secret.length);
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (ret) return ret;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen output = sss_base64_encode(mem_ctx,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen (uint8_t *)_secret.data, _secret.length);
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (!output) return ENOMEM;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen *ciphertext = output;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen return EOK;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen}
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainenstatic int local_db_dn(TALLOC_CTX *mem_ctx,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen struct ldb_context *ldb,
a9b3887f4d9ed75a76fed964c1930432bf84f4f5Timo Sirainen const char *req_path,
a9b3887f4d9ed75a76fed964c1930432bf84f4f5Timo Sirainen struct ldb_dn **req_dn)
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen{
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen struct ldb_dn *dn;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen const char *s, *e;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen int ret;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen dn = ldb_dn_new(mem_ctx, ldb, "cn=secrets");
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (!dn) {
82eadbc4311faf7719d5db33fddaa06cb3a7010bTimo Sirainen ret = ENOMEM;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen goto done;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen }
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen s = req_path;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen while (s && *s) {
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen e = strchr(s, '/');
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (e) {
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (e == s) {
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen s++;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen continue;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen }
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (!ldb_dn_add_child_fmt(dn, "cn=%.*s", (int)(e - s), s)) {
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen ret = ENOMEM;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen goto done;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen }
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen s = e + 1;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen } else {
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (!ldb_dn_add_child_fmt(dn, "cn=%s", s)) {
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen ret = ENOMEM;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen goto done;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen }
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen s = NULL;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen }
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen }
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen *req_dn = dn;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen ret = EOK;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainendone:
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen return ret;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen}
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainenstatic char *local_dn_to_path(TALLOC_CTX *mem_ctx,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen struct ldb_dn *basedn,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen struct ldb_dn *dn)
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen{
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen int basecomps;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen int dncomps;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen char *path = NULL;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen basecomps = ldb_dn_get_comp_num(basedn);
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen dncomps = ldb_dn_get_comp_num(dn);
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen for (int i = dncomps - basecomps; i > 0; i--) {
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen const struct ldb_val *val;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen val = ldb_dn_get_component_val(dn, i - 1);
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (!val) return NULL;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (path) {
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen path = talloc_strdup_append_buffer(path, "/");
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (!path) return NULL;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen path = talloc_strndup_append_buffer(path, (char *)val->data,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen val->length);
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen } else {
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen path = talloc_strndup(mem_ctx, (char *)val->data, val->length);
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen }
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (!path) return NULL;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen }
82eadbc4311faf7719d5db33fddaa06cb3a7010bTimo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen return path;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen}
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen#define LOCAL_SIMPLE_FILTER "(type=simple)"
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen#define LOCAL_CONTAINER_FILTER "(type=container)"
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainenstatic int local_db_get_simple(TALLOC_CTX *mem_ctx,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen struct local_context *lctx,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen const char *req_path,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen char **secret)
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen{
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen TALLOC_CTX *tmp_ctx;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen static const char *attrs[] = { "secret", "enctype", NULL };
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen struct ldb_result *res;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen struct ldb_dn *dn;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen const char *attr_secret;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen const char *attr_enctype;
82eadbc4311faf7719d5db33fddaa06cb3a7010bTimo Sirainen int ret;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen tmp_ctx = talloc_new(mem_ctx);
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (!tmp_ctx) return ENOMEM;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen ret = local_db_dn(tmp_ctx, lctx->ldb, req_path, &dn);
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (ret != EOK) goto done;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen ret = ldb_search(lctx->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen attrs, "%s", LOCAL_SIMPLE_FILTER);
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (ret != EOK) {
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen ret = ENOENT;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen goto done;
a6f1ded5a5df8ba467c4026e9cd9c15e6880997bTimo Sirainen }
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
a6f1ded5a5df8ba467c4026e9cd9c15e6880997bTimo Sirainen switch (res->count) {
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen case 0:
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen ret = ENOENT;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen goto done;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen case 1:
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen break;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen default:
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen ret = E2BIG;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen goto done;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen }
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen attr_secret = ldb_msg_find_attr_as_string(res->msgs[0], "secret", NULL);
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (!attr_secret) {
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen ret = ENOENT;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen goto done;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen }
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen attr_enctype = ldb_msg_find_attr_as_string(res->msgs[0], "enctype", NULL);
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (attr_enctype) {
82eadbc4311faf7719d5db33fddaa06cb3a7010bTimo Sirainen ret = local_decrypt(lctx, mem_ctx, attr_secret, attr_enctype, secret);
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (ret) goto done;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen } else {
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen *secret = talloc_strdup(mem_ctx, attr_secret);
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen }
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen ret = EOK;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainendone:
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen talloc_free(tmp_ctx);
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen return ret;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen}
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainenstatic int local_db_list_keys(TALLOC_CTX *mem_ctx,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen struct local_context *lctx,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen const char *req_path,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen char ***_keys,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen int *num_keys)
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen{
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen TALLOC_CTX *tmp_ctx;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen static const char *attrs[] = { "secret", NULL };
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen struct ldb_result *res;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen struct ldb_dn *dn;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen char **keys;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen int ret;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen tmp_ctx = talloc_new(mem_ctx);
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (!tmp_ctx) return ENOMEM;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen ret = local_db_dn(tmp_ctx, lctx->ldb, req_path, &dn);
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (ret != EOK) goto done;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen ret = ldb_search(lctx->ldb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen attrs, "%s", LOCAL_SIMPLE_FILTER);
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (ret != EOK) {
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen ret = ENOENT;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen goto done;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen }
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (res->count == 0) {
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen ret = ENOENT;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen goto done;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen }
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen keys = talloc_array(mem_ctx, char *, res->count);
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (!keys) {
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen ret = ENOMEM;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen goto done;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen }
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen for (unsigned i = 0; i < res->count; i++) {
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen keys[i] = local_dn_to_path(keys, dn, res->msgs[i]->dn);
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen if (!keys[i]) {
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen ret = ENOMEM;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen goto done;
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen }
c865b0e9c65fd77f7b2ab6f8616d3def5501ecb3Timo Sirainen }
*_keys = keys;
*num_keys = res->count;
ret = EOK;
done:
talloc_free(tmp_ctx);
return ret;
}
static int local_db_check_containers(TALLOC_CTX *mem_ctx,
struct local_context *lctx,
struct ldb_dn *leaf_dn)
{
TALLOC_CTX *tmp_ctx;
static const char *attrs[] = { NULL};
struct ldb_result *res = NULL;
struct ldb_dn *dn;
int num;
int ret;
tmp_ctx = talloc_new(mem_ctx);
if (!tmp_ctx) return ENOMEM;
dn = ldb_dn_copy(tmp_ctx, leaf_dn);
if (!dn) {
ret = ENOMEM;
goto done;
}
/* We need to exclude the leaf as that will be the new child entry,
* We also do not care for the synthetic containers that constitute the
* base path (cn=<uidnumber>,cn=users,cn=secrets), so in total we remove
* 4 components */
num = ldb_dn_get_comp_num(dn) - 4;
for (int i = 0; i < num; i++) {
/* remove the child first (we do not want to check the leaf) */
if (!ldb_dn_remove_child_components(dn, 1)) return EFAULT;
/* and check the parent container exists */
ret = ldb_search(lctx->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
attrs, LOCAL_CONTAINER_FILTER);
if (ret != LDB_SUCCESS) {
ret = ENOENT;
goto done;
}
if (res->count != 1) {
ret = ENOENT;
goto done;
}
}
ret = EOK;
done:
talloc_free(tmp_ctx);
return ret;
}
static int local_db_check_containers_nest_level(struct local_context *lctx,
struct ldb_dn *leaf_dn)
{
int nest_level;
/* We need do not care for the synthetic containers that constitute the
* base path (cn=<uidnumber>,cn=user,cn=secrets). */
nest_level = ldb_dn_get_comp_num(leaf_dn) - 3;
if (nest_level > lctx->containers_nest_level) {
DEBUG(SSSDBG_OP_FAILURE,
"Cannot create a nested container of depth %d as the maximum"
"allowed number of nested containers is %d.\n",
nest_level, lctx->containers_nest_level);
return ERR_SEC_INVALID_CONTAINERS_NEST_LEVEL;
}
return EOK;
}
static int local_db_put_simple(TALLOC_CTX *mem_ctx,
struct local_context *lctx,
const char *req_path,
const char *secret)
{
struct ldb_message *msg;
const char *enctype = "masterkey";
char *enc_secret;
int ret;
msg = ldb_msg_new(mem_ctx);
if (!msg) {
ret = ENOMEM;
goto done;
}
ret = local_db_dn(msg, lctx->ldb, req_path, &msg->dn);
if (ret != EOK) goto done;
/* make sure containers exist */
ret = local_db_check_containers(msg, lctx, msg->dn);
if (ret != EOK) goto done;
ret = local_encrypt(lctx, msg, secret, enctype, &enc_secret);
if (ret != EOK) goto done;
ret = ldb_msg_add_string(msg, "type", "simple");
if (ret != EOK) goto done;
ret = ldb_msg_add_string(msg, "enctype", enctype);
if (ret != EOK) goto done;
ret = ldb_msg_add_string(msg, "secret", enc_secret);
if (ret != EOK) goto done;
ret = ldb_msg_add_fmt(msg, "creationTime", "%lu", time(NULL));
if (ret != EOK) goto done;
ret = ldb_add(lctx->ldb, msg);
if (ret != EOK) {
if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) ret = EEXIST;
else ret = EIO;
goto done;
}
ret = EOK;
done:
talloc_free(msg);
return ret;
}
static int local_db_delete(TALLOC_CTX *mem_ctx,
struct local_context *lctx,
const char *req_path)
{
TALLOC_CTX *tmp_ctx;
struct ldb_dn *dn;
static const char *attrs[] = { NULL };
struct ldb_result *res;
int ret;
tmp_ctx = talloc_new(mem_ctx);
if (!tmp_ctx) return ENOMEM;
ret = local_db_dn(mem_ctx, lctx->ldb, req_path, &dn);
if (ret != EOK) goto done;
ret = ldb_search(lctx->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
attrs, LOCAL_CONTAINER_FILTER);
if (ret != EOK) goto done;
if (res->count == 1) {
ret = ldb_search(lctx->ldb, tmp_ctx, &res, dn, LDB_SCOPE_ONELEVEL,
attrs, NULL);
if (ret != EOK) goto done;
if (res->count > 0) {
ret = EEXIST;
DEBUG(SSSDBG_OP_FAILURE,
"Failed to remove '%s': Container is not empty\n",
ldb_dn_get_linearized(dn));
goto done;
}
}
ret = ldb_delete(lctx->ldb, dn);
ret = sysdb_error_to_errno(ret);
done:
talloc_free(tmp_ctx);
return ret;
}
static int local_db_create(TALLOC_CTX *mem_ctx,
struct local_context *lctx,
const char *req_path)
{
struct ldb_message *msg;
int ret;
msg = ldb_msg_new(mem_ctx);
if (!msg) {
ret = ENOMEM;
goto done;
}
ret = local_db_dn(msg, lctx->ldb, req_path, &msg->dn);
if (ret != EOK) goto done;
/* make sure containers exist */
ret = local_db_check_containers(msg, lctx, msg->dn);
if (ret != EOK) goto done;
ret = local_db_check_containers_nest_level(lctx, msg->dn);
if (ret != EOK) goto done;
ret = ldb_msg_add_string(msg, "type", "container");
if (ret != EOK) goto done;
ret = ldb_msg_add_fmt(msg, "creationTime", "%lu", time(NULL));
if (ret != EOK) goto done;
ret = ldb_add(lctx->ldb, msg);
if (ret != EOK) {
if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) ret = EEXIST;
else ret = EIO;
goto done;
}
ret = EOK;
done:
talloc_free(msg);
return ret;
}
static int local_secrets_map_path(TALLOC_CTX *mem_ctx,
struct sec_req_ctx *secreq,
char **local_db_path)
{
int ret;
/* be strict for now */
if (secreq->parsed_url.fragment != NULL) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Unrecognized URI fragments: [%s]\n",
secreq->parsed_url.fragment);
return EINVAL;
}
if (secreq->parsed_url.userinfo != NULL) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Unrecognized URI userinfo: [%s]\n",
secreq->parsed_url.userinfo);
return EINVAL;
}
/* only type simple for now */
if (secreq->parsed_url.query != NULL) {
ret = strcmp(secreq->parsed_url.query, "type=simple");
if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Invalid URI query: [%s]\n",
secreq->parsed_url.query);
return EINVAL;
}
}
/* drop SEC_BASEPATH prefix */
*local_db_path =
talloc_strdup(mem_ctx, &secreq->mapped_path[sizeof(SEC_BASEPATH) - 1]);
if (!*local_db_path) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Failed to map request to local db path\n");
return ENOMEM;
}
return EOK;
}
struct local_secret_state {
struct tevent_context *ev;
struct sec_req_ctx *secreq;
};
static struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
void *provider_ctx,
struct sec_req_ctx *secreq)
{
struct tevent_req *req;
struct local_secret_state *state;
struct local_context *lctx;
struct sec_data body = { 0 };
const char *content_type;
bool body_is_json;
char *req_path;
char *secret;
char **keys;
int nkeys;
int plen;
int ret;
req = tevent_req_create(mem_ctx, &state, struct local_secret_state);
if (!req) return NULL;
state->ev = ev;
state->secreq = secreq;
lctx = talloc_get_type(provider_ctx, struct local_context);
if (!lctx) {
ret = EIO;
goto done;
}
if (sec_req_has_header(secreq, "Content-Type",
"application/json")) {
body_is_json = true;
content_type = "application/json";
} else if (sec_req_has_header(secreq, "Content-Type",
"application/octet-stream")) {
body_is_json = false;
content_type = "application/octet-stream";
} else {
ret = EINVAL;
goto done;
}
ret = local_secrets_map_path(state, secreq, &req_path);
if (ret) goto done;
switch (secreq->method) {
case HTTP_GET:
if (req_path[strlen(req_path) - 1] == '/') {
ret = local_db_list_keys(state, lctx, req_path, &keys, &nkeys);
if (ret) goto done;
ret = sec_array_to_json(state, keys, nkeys, &body.data);
if (ret) goto done;
body.length = strlen(body.data);
break;
}
ret = local_db_get_simple(state, lctx, req_path, &secret);
if (ret) goto done;
if (body_is_json) {
ret = sec_simple_secret_to_json(state, secret, &body.data);
if (ret) goto done;
body.length = strlen(body.data);
} else {
body.data = (void *)sss_base64_decode(state, secret, &body.length);
ret = body.data ? EOK : ENOMEM;
}
if (ret) goto done;
break;
case HTTP_PUT:
if (body_is_json) {
ret = sec_json_to_simple_secret(state, secreq->body.data,
&secret);
} else {
secret = sss_base64_encode(state, (uint8_t *)secreq->body.data,
secreq->body.length);
ret = secret ? EOK : ENOMEM;
}
if (ret) goto done;
ret = local_db_put_simple(state, lctx, req_path, secret);
if (ret) goto done;
break;
case HTTP_DELETE:
ret = local_db_delete(state, lctx, req_path);
if (ret) goto done;
break;
case HTTP_POST:
plen = strlen(req_path);
if (req_path[plen - 1] != '/') {
ret = EINVAL;
goto done;
}
req_path[plen - 1] = '\0';
ret = local_db_create(state, lctx, req_path);
if (ret) goto done;
break;
default:
ret = EINVAL;
goto done;
}
if (body.data) {
ret = sec_http_reply_with_body(secreq, &secreq->reply, STATUS_200,
content_type, &body);
} else {
ret = sec_http_status_reply(secreq, &secreq->reply, STATUS_200);
}
done:
if (ret != EOK) {
tevent_req_error(req, ret);
} else {
/* shortcircuit the request here as all called functions are
* synchronous and final and no further subrequests are made */
tevent_req_done(req);
}
return tevent_req_post(req, state->ev);
}
static int generate_master_key(const char *filename, size_t size)
{
uint8_t buf[size];
ssize_t rsize;
int ret;
int fd;
ret = generate_csprng_buffer(buf, size);
if (ret) return ret;
fd = open(filename, O_CREAT|O_EXCL|O_WRONLY, 0600);
if (fd == -1) return errno;
rsize = sss_atomic_write_s(fd, buf, size);
close(fd);
if (rsize != size) {
ret = unlink(filename);
/* non-fatal failure */
if (ret != EOK) {
ret = errno;
DEBUG(SSSDBG_MINOR_FAILURE,
"Failed to remove file: %s - %d [%s]!\n",
filename, ret, sss_strerror(ret));
}
return EFAULT;
}
return EOK;
}
int local_secrets_provider_handle(struct sec_ctx *sctx,
struct provider_handle **out_handle)
{
const char *mkey = SECRETS_DB_PATH"/.secrets.mkey";
const char *dbpath = SECRETS_DB_PATH"/secrets.ldb";
struct provider_handle *handle;
struct local_context *lctx;
ssize_t size;
int mfd;
int ret;
handle = talloc_zero(sctx, struct provider_handle);
if (!handle) return ENOMEM;
handle->name = "LOCAL";
handle->fn = local_secret_req;
lctx = talloc_zero(handle, struct local_context);
if (!lctx) return ENOMEM;
lctx->ldb = ldb_init(lctx, NULL);
if (!lctx->ldb) return ENOMEM;
ret = ldb_connect(lctx->ldb, dbpath, 0, NULL);
if (ret != LDB_SUCCESS) {
talloc_free(lctx->ldb);
return EIO;
}
lctx->containers_nest_level = sctx->containers_nest_level;
lctx->master_key.data = talloc_size(lctx, MKEY_SIZE);
if (!lctx->master_key.data) return ENOMEM;
lctx->master_key.length = MKEY_SIZE;
ret = check_and_open_readonly(mkey, &mfd, 0, 0,
S_IFREG|S_IRUSR|S_IWUSR, 0);
if (ret == ENOENT) {
ret = generate_master_key(mkey, MKEY_SIZE);
if (ret) return EFAULT;
ret = check_and_open_readonly(mkey, &mfd, 0, 0,
S_IFREG|S_IRUSR|S_IWUSR, 0);
}
if (ret) return EFAULT;
size = sss_atomic_read_s(mfd, lctx->master_key.data,
lctx->master_key.length);
close(mfd);
if (size < 0 || size != lctx->master_key.length) return EIO;
handle->context = lctx;
*out_handle = handle;
return EOK;
}