From d694dc007a8520ecf0f6d22a1a8f1795047f9b54 Mon Sep 17 00:00:00 2001
From: Simo Sorce <simo@redhat.com>
Date: Thu, 17 Mar 2016 15:48:01 -0400
Subject: [PATCH 13/15] Local secrets provider Content-Type handling

Properly handle and enforce ithe presence of the content-type header
in the local and proxy providers to conform to the Custoida protocol.
Avoids different behavior between the local provider and a remote server
that may cause developers to have an application working against the
local storage and then fail when the administrator configures a remote
storage.

Signed-off-by: Simo Sorce <simo@redhat.com>
---
 src/responder/secrets/local.c          | 46 ++++++++++++++++++++++++++++------
 src/responder/secrets/providers.c      | 27 ++++++++++++++++++++
 src/responder/secrets/secsrv_private.h |  3 +++
 3 files changed, 68 insertions(+), 8 deletions(-)

diff --git a/src/responder/secrets/local.c b/src/responder/secrets/local.c
index f95817d6805529e5fdeec30971183cf609ad872e..314dff8f18cac536333a5abc517f8a7e6b1416e9 100644
--- a/src/responder/secrets/local.c
+++ b/src/responder/secrets/local.c
@@ -405,6 +405,8 @@ struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx,
     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;
@@ -423,6 +425,19 @@ struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx,
         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;
 
@@ -434,21 +449,36 @@ struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx,
 
             ret = sec_array_to_json(state, keys, nkeys, &body.data);
             if (ret) goto done;
-        } else {
-            ret = local_db_get_simple(state, lctx, req_path, &secret);
-            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;
 
-        body.length = strlen(body.data);
         break;
 
     case HTTP_PUT:
-        /*FIXME: check fot content-type */
-
-        ret = sec_json_to_simple_secret(state, secreq->body.data, &secret);
+        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);
@@ -467,7 +497,7 @@ struct tevent_req *local_secret_req(TALLOC_CTX *mem_ctx,
 
     if (body.data) {
         ret = sec_http_reply_with_body(secreq, &secreq->reply, STATUS_200,
-                                       "application/json", &body);
+                                       content_type, &body);
     } else {
         ret = sec_http_status_reply(secreq, &secreq->reply, STATUS_200);
     }
diff --git a/src/responder/secrets/providers.c b/src/responder/secrets/providers.c
index e83aedc9c544119334678b3bd404fa6e810b2eeb..e6b26cf73e026191148d70d428538fe7c2ca11b7 100644
--- a/src/responder/secrets/providers.c
+++ b/src/responder/secrets/providers.c
@@ -236,6 +236,8 @@ int sec_http_reply_with_headers(TALLOC_CTX *mem_ctx, struct sec_data *reply,
                                 struct sec_data *body)
 {
     const char *reason_phrase = reason ? reason : "";
+    bool add_content_length = true;
+    bool has_content_type = false;
     int ret;
 
     /* Status-Line */
@@ -245,11 +247,24 @@ int sec_http_reply_with_headers(TALLOC_CTX *mem_ctx, struct sec_data *reply,
 
     /* Headers */
     for (int i = 0; i < num_headers; i++) {
+        if (strcasecmp(headers[i].name, "Content-Length") == 0) {
+            add_content_length = false;
+        } else if (strcasecmp(headers[i].name, "Content-Type") == 0) {
+            has_content_type = true;
+        }
         ret = sec_http_append_header(mem_ctx, &reply->data,
                                      headers[i].name, headers[i].value);
         if (ret) return ret;
     }
 
+    if (!has_content_type) return EINVAL;
+
+    if (add_content_length) {
+        reply->data = talloc_asprintf_append_buffer(reply->data,
+                            "Content-Length: %u\r\n", (unsigned)body->length);
+        if (!reply->data) return ENOMEM;
+    }
+
     /* CRLF separator before body */
     reply->data = talloc_strdup_append_buffer(reply->data, "\r\n");
 
@@ -455,3 +470,15 @@ int sec_add_provider(struct sec_ctx *sctx, struct provider_handle *handle)
 
     return EOK;
 }
+
+bool sec_req_has_header(struct sec_req_ctx *req,
+                        const char *name, const char *value)
+{
+    for (int i = 0; i < req->num_headers; i++) {
+        if (strcasecmp(name, req->headers[i].name) == 0) {
+            if (value == NULL) return true;
+            return (strcasecmp(value, req->headers[i].value) == 0);
+        }
+    }
+    return false;
+}
diff --git a/src/responder/secrets/secsrv_private.h b/src/responder/secrets/secsrv_private.h
index ce8cf821620ff25039d38e00eb91a6e4d24dd093..c3b663996e138bbb14f1ab74db75398ffd0bd5c7 100644
--- a/src/responder/secrets/secsrv_private.h
+++ b/src/responder/secrets/secsrv_private.h
@@ -131,6 +131,9 @@ int sec_array_to_json(TALLOC_CTX *mem_ctx,
                       char **array, int count,
                       char **output);
 
+bool sec_req_has_header(struct sec_req_ctx *req,
+                        const char *name, const char *value);
+
 /* secsrv_cmd.c */
 #define SEC_REQUEST_MAX_SIZE 65536
 #define SEC_PACKET_MAX_RECV_SIZE 8192
-- 
2.5.5

