From 89e0220796bb09b23df46cf240a0de27bfc44bf1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Tue, 6 Nov 2012 14:57:56 +0100
Subject: [PATCH 4/6] sudo: allow fully qualified user name

Part of:
https://fedorahosted.org/sssd/ticket/1616
---
 src/responder/sudo/sudosrv_cmd.c     | 17 ++++++--
 src/responder/sudo/sudosrv_private.h |  1 +
 src/responder/sudo/sudosrv_query.c   | 81 +++++++++++++++++++++++++++++++++---
 src/sss_client/sss_cli.h             |  2 +-
 src/sss_client/sudo/sss_sudo.c       | 64 +++++++++++++++++-----------
 5 files changed, 130 insertions(+), 35 deletions(-)

diff --git a/src/responder/sudo/sudosrv_cmd.c b/src/responder/sudo/sudosrv_cmd.c
index 440c8c5da4a56105c1cb83fb6a7e8a79dd0c9289..8f6b432026d378a0ff98971d8afbd5a22443be83 100644
--- a/src/responder/sudo/sudosrv_cmd.c
+++ b/src/responder/sudo/sudosrv_cmd.c
@@ -164,6 +164,7 @@ static int sudosrv_cmd(enum sss_sudo_type type, struct cli_ctx *cli_ctx)
     struct sudo_cmd_ctx *cmd_ctx = NULL;
     uint8_t *query_body = NULL;
     size_t query_len = 0;
+    uint32_t protocol = cli_ctx->cli_protocol_version->version;
     errno_t ret;
 
     /* create cmd_ctx */
@@ -185,11 +186,18 @@ static int sudosrv_cmd(enum sss_sudo_type type, struct cli_ctx *cli_ctx)
     }
 
     /* if protocol is invalid return */
-    if (cli_ctx->cli_protocol_version->version != SSS_SUDO_PROTOCOL_VERSION) {
-        DEBUG(SSSDBG_FATAL_FAILURE, ("Invalid protocol! [%d]\n",
-              cli_ctx->cli_protocol_version->version));
+    switch (protocol) {
+    case 0:
+        DEBUG(SSSDBG_FATAL_FAILURE, ("Protocol [%d] is not secure. "
+              "SSSD does not allow to use this protocol.\n", protocol));
         ret = EFAULT;
         goto done;
+        break;
+    case 1:
+        DEBUG(SSSDBG_MINOR_FAILURE, ("Protocol [%d] does not support fully"
+              "qualified user names. Users from subdomains will not be"
+              "allowed to use sudo.\n", protocol));
+        break;
     }
 
     /* parse query */
@@ -201,7 +209,7 @@ static int sudosrv_cmd(enum sss_sudo_type type, struct cli_ctx *cli_ctx)
         goto done;
     }
 
-    req = sudosrv_parse_query_send(cmd_ctx, cli_ctx->rctx,
+    req = sudosrv_parse_query_send(cmd_ctx, protocol, cli_ctx->rctx,
                                    query_body, query_len);
     if (req == NULL) {
         ret = ENOMEM;
@@ -278,6 +286,7 @@ static int sudosrv_cmd_get_defaults(struct cli_ctx *cli_ctx)
 struct cli_protocol_version *register_cli_protocol_version(void)
 {
     static struct cli_protocol_version sudo_cli_protocol_version[] = {
+        {2, "2012-11-06", "support fully qualified username"},
 		{1, "2012-05-14", "require uid and domain"},
         {0, NULL, NULL}
     };
diff --git a/src/responder/sudo/sudosrv_private.h b/src/responder/sudo/sudosrv_private.h
index b805940759f409ebd532707faf93d09dc3e51e04..d031ca0fdc8c90d8b2f34436b75edbac0e9f2746 100644
--- a/src/responder/sudo/sudosrv_private.h
+++ b/src/responder/sudo/sudosrv_private.h
@@ -91,6 +91,7 @@ errno_t sudosrv_get_sudorules(struct sudo_dom_ctx *dctx);
 errno_t sudosrv_get_rules(struct sudo_cmd_ctx *cmd_ctx);
 
 struct tevent_req *sudosrv_parse_query_send(TALLOC_CTX *mem_ctx,
+                                            uint32_t protocol,
                                             struct resp_ctx *rctx,
                                             uint8_t *query_body,
                                             size_t query_len);
diff --git a/src/responder/sudo/sudosrv_query.c b/src/responder/sudo/sudosrv_query.c
index d76ecbb9deb7a59adaa319fc7ef4ddb5e3071f4c..d076c88601e12f7c10d6ad57c01d7a78ed1ddb09 100644
--- a/src/responder/sudo/sudosrv_query.c
+++ b/src/responder/sudo/sudosrv_query.c
@@ -255,11 +255,13 @@ struct sudosrv_parse_query_state {
     struct resp_ctx *rctx;
     uid_t uid;
     char *rawname;
+    char *query_domainname;
 };
 
 static void sudosrv_parse_query_done(struct tevent_req *subreq);
 
 struct tevent_req *sudosrv_parse_query_send(TALLOC_CTX *mem_ctx,
+                                            uint32_t protocol,
                                             struct resp_ctx *rctx,
                                             uint8_t *query_body,
                                             size_t query_len)
@@ -271,6 +273,8 @@ struct tevent_req *sudosrv_parse_query_send(TALLOC_CTX *mem_ctx,
     size_t rawname_len = 0;
     char *rawname = NULL;
     char *domainname = NULL;
+    size_t query_domainname_len = 0;
+    char *query_domainname = NULL;
     errno_t ret;
 
     /* create request */
@@ -283,19 +287,21 @@ struct tevent_req *sudosrv_parse_query_send(TALLOC_CTX *mem_ctx,
 
     state->rctx = rctx;
 
-    /* uid */
-
-    if (query_len < sizeof(uid_t)) {
+    if (query_len < sizeof(uid_t) + 2*sizeof(size_t)) {
         DEBUG(SSSDBG_CRIT_FAILURE, ("Query is too small\n"));
         ret = EINVAL;
         goto done;
     }
+
+    /* uid */
+
     safealign_memcpy(&state->uid, query_body, sizeof(uid_t), &offset);
 
-    /* username[@domain] */
+    /* username[@domain] (*/
 
+    safealign_memcpy(&rawname_len, query_body + offset, sizeof(size_t),
+                     &offset);
     rawname = (char*)(query_body + offset);
-    rawname_len = query_len - offset; /* strlen + zero */
 
     if (rawname[rawname_len - 1] != '\0') {
         DEBUG(SSSDBG_CRIT_FAILURE, ("Username is not zero terminated\n"));
@@ -315,7 +321,56 @@ struct tevent_req *sudosrv_parse_query_send(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
-    /* parse username */
+    state->rawname = talloc_strndup(state, rawname, rawname_len);
+    if (state->rawname == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    offset += rawname_len;
+
+    /* domainname */
+
+    if (protocol > 1) {
+        safealign_memcpy(&query_domainname_len, query_body + offset,
+                         sizeof(size_t), &offset);
+    }
+
+    if (query_domainname_len > 0) {
+        query_domainname = (char*)(query_body + offset);
+
+        if (query_domainname[query_domainname_len - 1] != '\0') {
+            DEBUG(SSSDBG_CRIT_FAILURE, ("Username is not zero terminated\n"));
+            ret = EINVAL;
+            goto done;
+        }
+
+        if (query_domainname_len < 2) { /* at least one character and zero */
+            DEBUG(SSSDBG_CRIT_FAILURE, ("Query does not contain username\n"));
+            ret = EINVAL;
+            goto done;
+        }
+
+        if (!sss_utf8_check((uint8_t*)query_domainname,
+                            query_domainname_len - 1)) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  ("Supplied data is not valid UTF-8 string\n"));
+            ret = EINVAL;
+            goto done;
+        }
+
+        state->query_domainname = talloc_strndup(state, query_domainname,
+                                                 query_domainname_len);
+        if (state->query_domainname == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+
+        offset += query_domainname_len;
+    }
+
+    /* Try to parse username as fully qualified name. We will store the
+     * username later in _recv. */
 
     state->rawname = rawname;
     ret = sss_parse_name_for_domains(state, rctx->domains,
@@ -409,6 +464,20 @@ errno_t sudosrv_parse_query_recv(TALLOC_CTX *mem_ctx,
         return EINVAL;
     }
 
+    if (state->query_domainname != NULL) {
+        if (domainname == NULL) {
+            /* no domain was specified,
+             * we will use domain from previous request */
+            domainname = state->query_domainname;
+        } else {
+            if (strcmp(domainname, state->query_domainname) != 0) {
+                DEBUG(SSSDBG_OP_FAILURE, ("Domain of previous request doesn't "
+                      "match! [%s] [%s]\n", domain, state->query_domainname));
+                return EFAULT;
+            }
+        }
+    }
+
     if (domainname != NULL) {
         /* mem_ctx because it duplicates only subdomains not domains
          * so I cannot easily steal it */
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
index 372bcee596a72e85d02516acdb10b775c4ddc2df..a21a10ed3841c4367a2cf1029ec97b4b48a6c602 100644
--- a/src/sss_client/sss_cli.h
+++ b/src/sss_client/sss_cli.h
@@ -44,7 +44,7 @@ typedef int errno_t;
 
 #define SSS_NSS_PROTOCOL_VERSION 1
 #define SSS_PAM_PROTOCOL_VERSION 3
-#define SSS_SUDO_PROTOCOL_VERSION 1
+#define SSS_SUDO_PROTOCOL_VERSION 2
 #define SSS_AUTOFS_PROTOCOL_VERSION 1
 #define SSS_SSH_PROTOCOL_VERSION 0
 #define SSS_PAC_PROTOCOL_VERSION 1
diff --git a/src/sss_client/sudo/sss_sudo.c b/src/sss_client/sudo/sss_sudo.c
index e2bb3e00d123bfbfecef91ce2d01edaf7b7d1a76..daece9551f50b324ea59dec81e1d2c425540ca27 100644
--- a/src/sss_client/sudo/sss_sudo.c
+++ b/src/sss_client/sudo/sss_sudo.c
@@ -32,6 +32,7 @@
 
 int sss_sudo_create_query(uid_t uid,
                           const char *username,
+                          const char *domainname,
                           uint8_t **_query,
                           size_t *_query_len);
 
@@ -44,6 +45,7 @@ static void sss_sudo_free_attrs(unsigned int num_attrs,
 static int sss_sudo_send_recv_generic(enum sss_cli_command command,
                                       uid_t uid,
                                       const char *username,
+                                      const char *domainname,
                                       uint32_t *_error,
                                       char **_domainname,
                                       struct sss_sudo_result **_result)
@@ -58,7 +60,8 @@ static int sss_sudo_send_recv_generic(enum sss_cli_command command,
 
     /* create query */
 
-    ret = sss_sudo_create_query(uid, username, &query_buf, &query_len);
+    ret = sss_sudo_create_query(uid, username, domainname,
+                                &query_buf, &query_len);
     if (ret != EOK) {
         goto done;
     }
@@ -93,30 +96,14 @@ int sss_sudo_send_recv(uid_t uid,
                        uint32_t *_error,
                        struct sss_sudo_result **_result)
 {
-    char *fullname = NULL;
     int ret;
 
     if (username == NULL || strlen(username) == 0) {
         return EINVAL;
     }
 
-    if (domainname != NULL) {
-        ret = asprintf(&fullname, "%s@%s", username, domainname);
-        if (ret == -1) {
-            return ENOMEM;
-        }
-    } else {
-        fullname = strdup(username);
-        if (fullname == NULL) {
-            return ENOMEM;
-        }
-    }
-
-    /* send query and receive response */
-
-    ret = sss_sudo_send_recv_generic(SSS_SUDO_GET_SUDORULES, uid, fullname,
-                                     _error, NULL, _result);
-    free(fullname);
+    ret = sss_sudo_send_recv_generic(SSS_SUDO_GET_SUDORULES, uid, username,
+                                     domainname, _error, NULL, _result);
     return ret;
 }
 
@@ -131,24 +118,53 @@ int sss_sudo_send_recv_defaults(uid_t uid,
     }
 
     return sss_sudo_send_recv_generic(SSS_SUDO_GET_DEFAULTS, uid, username,
-                                      _error, _domainname, _result);
+                                      NULL, _error, _domainname, _result);
 }
 
-int sss_sudo_create_query(uid_t uid, const char *username,
-                          uint8_t **_query, size_t *_query_len)
+/**
+ * Query format:
+ * uid|username_length|username|domainname_length|domainname
+ * (uid_t)(size_t)(char[])(size_t)(char[])
+ */
+int sss_sudo_create_query(uid_t uid,
+                          const char *username,
+                          const char *domainname,
+                          uint8_t **_query,
+                          size_t *_query_len)
 {
     uint8_t *data = NULL;
-    size_t username_len = strlen(username) * sizeof(char) + 1;
-    size_t data_len = sizeof(uid_t) + username_len;
+    size_t username_len = 0;
+    size_t domainname_len = 0;
+    size_t data_len = 0;
     size_t offset = 0;
 
+    if (username == NULL) {
+        return EINVAL;
+    }
+
+    username_len = strlen(username) + 1;
+    if (domainname != NULL) {
+        domainname_len = strlen(domainname) + 1;
+    }
+
+    data_len = sizeof(uid_t) + 2*sizeof(size_t);
+    data_len += username_len + domainname_len;
+
     data = (uint8_t*)malloc(data_len * sizeof(uint8_t));
     if (data == NULL) {
         return ENOMEM;
     }
 
     SAFEALIGN_SET_VALUE(data, uid, uid_t, &offset);
+
+    SAFEALIGN_SET_VALUE(data + offset, username_len, size_t, &offset);
     memcpy(data + offset, username, username_len);
+    offset += username_len;
+
+    SAFEALIGN_SET_VALUE(data + offset, domainname_len, size_t, &offset);
+    if (domainname != NULL) {
+        memcpy(data + offset, domainname, domainname_len);
+    }
 
     *_query = data;
     *_query_len = data_len;
-- 
1.7.11.7

