From 4183a02c43eee1f74397e0f8eeedc7f66d2db46b Mon Sep 17 00:00:00 2001
From: Simo Sorce <simo@redhat.com>
Date: Thu, 17 Mar 2016 17:18:02 -0400
Subject: [PATCH 14/15] Secrets: Add local container entries support

The protocol requires a user to creat a container efore trying to store
an entry in it. Do the same in the local provider so that no surprises
arise when admins route request to a remote storage server.

Signed-off-by: Simo Sorce <simo@redhat.com>
---
 src/responder/secrets/local.c | 111 +++++++++++++++++++++++++++++++++++++++---
 1 file changed, 103 insertions(+), 8 deletions(-)

diff --git a/src/responder/secrets/local.c b/src/responder/secrets/local.c
index 314dff8f18cac536333a5abc517f8a7e6b1416e9..f2158446e1bdf37622b6dd7bdd1a85ace0097fab 100644
--- a/src/responder/secrets/local.c
+++ b/src/responder/secrets/local.c
@@ -21,6 +21,7 @@
 
 #include "responder/secrets/secsrv_private.h"
 #include "util/crypto/sss_crypto.h"
+#include <time.h>
 #include <ldb.h>
 
 #define MKEY_SIZE (256 / 8)
@@ -166,6 +167,8 @@ char *local_dn_to_path(TALLOC_CTX *mem_ctx,
     return path;
 }
 
+#define LOCAL_SIMPLE_FILTER "(type=simple)"
+
 int local_db_get_simple(TALLOC_CTX *mem_ctx,
                         struct local_context *lctx,
                         const char *req_path,
@@ -173,7 +176,6 @@ int local_db_get_simple(TALLOC_CTX *mem_ctx,
 {
     TALLOC_CTX *tmp_ctx;
     static const char *attrs[] = { "secret", "enctype", NULL };
-    const char *filter = "(type=simple)";
     struct ldb_result *res;
     struct ldb_dn *dn;
     const char *attr_secret;
@@ -187,7 +189,7 @@ int local_db_get_simple(TALLOC_CTX *mem_ctx,
     if (ret != EOK) goto done;
 
     ret = ldb_search(lctx->ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
-                     attrs, filter);
+                     attrs, LOCAL_SIMPLE_FILTER);
     if (ret != EOK) {
         ret = ENOENT;
         goto done;
@@ -233,7 +235,6 @@ int local_db_list_keys(TALLOC_CTX *mem_ctx,
 {
     TALLOC_CTX *tmp_ctx;
     static const char *attrs[] = { "secret", NULL };
-    const char *filter = "(type=simple)";
     struct ldb_result *res;
     struct ldb_dn *dn;
     char **keys;
@@ -246,7 +247,7 @@ int local_db_list_keys(TALLOC_CTX *mem_ctx,
     if (ret != EOK) goto done;
 
     ret = ldb_search(lctx->ldb, tmp_ctx, &res, dn, LDB_SCOPE_SUBTREE,
-                     attrs, filter);
+                     attrs, LOCAL_SIMPLE_FILTER);
     if (ret != EOK) {
         ret = ENOENT;
         goto done;
@@ -280,6 +281,40 @@ done:
     return ret;
 }
 
+int local_db_check_containers(TALLOC_CTX *mem_ctx,
+                              struct local_context *lctx,
+                              struct ldb_dn *leaf_dn)
+{
+    static const char *attrs[] = { NULL};
+    struct ldb_result *res = NULL;
+    struct ldb_dn *dn;
+    int num;
+    int ret;
+
+    dn = ldb_dn_copy(mem_ctx, leaf_dn);
+    if (!dn) return ENOMEM;
+
+    /* 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, mem_ctx, &res, dn, LDB_SCOPE_BASE,
+                         attrs, LOCAL_SIMPLE_FILTER);
+        if (ret != LDB_SUCCESS) return ENOENT;
+        if (res->count != 1) return ENOENT;
+        talloc_free(res);
+    }
+
+    return EOK;
+}
+
 int local_db_put_simple(TALLOC_CTX *mem_ctx,
                         struct local_context *lctx,
                         const char *req_path,
@@ -296,14 +331,16 @@ int local_db_put_simple(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
-    /* FIXME: verify containers (except for user's namespace) exists */
+    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 = local_db_dn(msg, lctx->ldb, req_path, &msg->dn);
-    if (ret != EOK) goto done;
-
     ret = ldb_msg_add_string(msg, "type", "simple");
     if (ret != EOK) goto done;
 
@@ -313,6 +350,9 @@ int local_db_put_simple(TALLOC_CTX *mem_ctx,
     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;
@@ -346,6 +386,46 @@ done:
     return ret;
 }
 
+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 = 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;
+}
+
 int local_secrets_map_path(TALLOC_CTX *mem_ctx,
                            struct sec_req_ctx *secreq,
                            char **local_db_path)
@@ -411,6 +491,7 @@ struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx,
     char *secret;
     char **keys;
     int nkeys;
+    int plen;
     int ret;
 
     req = tevent_req_create(mem_ctx, &state, struct local_secret_state);
@@ -490,6 +571,20 @@ struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx,
         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;
-- 
2.5.5

