From 95e2439dd21882d4a30777a746f6941d10f28c73 Mon Sep 17 00:00:00 2001
From: Jan Zeleny <jzeleny@redhat.com>
Date: Mon, 23 Apr 2012 14:08:00 -0400
Subject: [PATCH 06/10] Ghost members - sysdb upgrade routine

It is remotely possible to have sysdb in an inconsistent state that
might need upgrade. Consider scenario when user asks for group
information. Some fake users are added as a part of this operation.
Before users can be fully resolved and stored properly, SSSD is shut
down and upgrade is performed.

In this case we need to go over all fake user records (uidNumber=0) and
replace each of them with ghost record in all group objects that are stated in
its memberof attribute.
---
 src/db/sysdb.c         |    7 +++
 src/db/sysdb_private.h |    4 +-
 src/db/sysdb_upgrade.c |  103 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 113 insertions(+), 1 deletions(-)

diff --git a/src/db/sysdb.c b/src/db/sysdb.c
index de23607a51a0731c1864cba05417b5d52c7ceae7..d4ea695236184088345587d12250baf2b0dda3a0 100644
--- a/src/db/sysdb.c
+++ b/src/db/sysdb.c
@@ -1061,6 +1061,13 @@ int sysdb_domain_init_internal(TALLOC_CTX *mem_ctx,
                 }
             }
 
+            if (strcmp(version, SYSDB_VERSION_0_10) == 0) {
+                ret = sysdb_upgrade_10(sysdb, &version);
+                if (ret != EOK) {
+                    goto done;
+                }
+            }
+
             /* The version should now match SYSDB_VERSION.
              * If not, it means we didn't match any of the
              * known older versions. The DB might be
diff --git a/src/db/sysdb_private.h b/src/db/sysdb_private.h
index a162cbba82bba8fef1e57ab2774d156307ead2cd..0fb7316b48b5c53be0198f719673080275c34b81 100644
--- a/src/db/sysdb_private.h
+++ b/src/db/sysdb_private.h
@@ -23,6 +23,7 @@
 #ifndef __INT_SYS_DB_H__
 #define __INT_SYS_DB_H__
 
+#define SYSDB_VERSION_0_11 "0.11"
 #define SYSDB_VERSION_0_10 "0.10"
 #define SYSDB_VERSION_0_9 "0.9"
 #define SYSDB_VERSION_0_8 "0.8"
@@ -34,7 +35,7 @@
 #define SYSDB_VERSION_0_2 "0.2"
 #define SYSDB_VERSION_0_1 "0.1"
 
-#define SYSDB_VERSION SYSDB_VERSION_0_10
+#define SYSDB_VERSION SYSDB_VERSION_0_11
 
 #define SYSDB_BASE_LDIF \
      "dn: @ATTRIBUTES\n" \
@@ -104,6 +105,7 @@ int sysdb_upgrade_06(struct sysdb_ctx *sysdb, const char **ver);
 int sysdb_upgrade_07(struct sysdb_ctx *sysdb, const char **ver);
 int sysdb_upgrade_08(struct sysdb_ctx *sysdb, const char **ver);
 int sysdb_upgrade_09(struct sysdb_ctx *sysdb, const char **ver);
+int sysdb_upgrade_10(struct sysdb_ctx *sysdb, const char **ver);
 
 int add_string(struct ldb_message *msg, int flags,
                const char *attr, const char *value);
diff --git a/src/db/sysdb_upgrade.c b/src/db/sysdb_upgrade.c
index 434cc832a66ade786eb1b2f6524d4a1604da11e1..ec7cdca309b7b117a87ef353852432fa78be518c 100644
--- a/src/db/sysdb_upgrade.c
+++ b/src/db/sysdb_upgrade.c
@@ -1160,3 +1160,106 @@ done:
     talloc_free(tmp_ctx);
     return ret;
 }
+
+int sysdb_upgrade_10(struct sysdb_ctx *sysdb, const char **ver)
+{
+
+    TALLOC_CTX *tmp_ctx;
+    int ret;
+    struct ldb_result *res;
+    struct ldb_message *msg;
+    struct ldb_message *user;
+    struct ldb_message_element *memberof;
+    struct ldb_message_element *name;
+    struct ldb_dn *basedn;
+    const char *filter = "(&(objectClass=user)(uidNumber=0))";
+    const char *attrs[] = { "name", "memberof", NULL };
+    int i, j;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    basedn = ldb_dn_new(tmp_ctx, sysdb->ldb, SYSDB_USERS_CONTAINER);
+    if (basedn == NULL) {
+        ret = EIO;
+        goto done;
+    }
+
+    ret = ldb_search(sysdb->ldb, tmp_ctx, &res, basedn, LDB_SCOPE_SUBTREE,
+                     attrs, "%s", filter);
+    if (ret != LDB_SUCCESS) {
+        ret = EIO;
+        goto done;
+    }
+
+    DEBUG(SSSDBG_CRIT_FAILURE, ("UPGRADING DB TO VERSION %s\n", SYSDB_VERSION_0_11));
+
+    ret = ldb_transaction_start(sysdb->ldb);
+    if (ret != LDB_SUCCESS) {
+        ret = EIO;
+        goto done;
+    }
+
+    for (i = 0; i < res->count; i++) {
+        user = res->msgs[0];
+        memberof = ldb_msg_find_element(user, "memberof");
+        name = ldb_msg_find_element(user, "name");
+        for (j = 0; j < memberof->num_values; j++) {
+            msg = ldb_msg_new(tmp_ctx);
+            if (msg == NULL) {
+                ret = ENOMEM;
+                goto done;
+            }
+
+            msg->dn = ldb_dn_from_ldb_val(tmp_ctx, sysdb->ldb, &memberof->values[j]);
+            if (msg->dn == NULL) {
+                ret = ENOMEM;
+                goto done;
+            }
+
+            ret = ldb_msg_add_empty(msg, "ghost", LDB_FLAG_MOD_ADD, NULL);
+            if (ret != LDB_SUCCESS) {
+                ret = ENOMEM;
+                goto done;
+            }
+            ret = ldb_msg_add_string(msg, "ghost", (char *)name->values[0].data);
+            if (ret != LDB_SUCCESS) {
+                ret = ENOMEM;
+                goto done;
+            }
+
+            ret = ldb_msg_add_empty(msg, "member", LDB_FLAG_MOD_DELETE, NULL);
+            if (ret != LDB_SUCCESS) {
+                ret = ENOMEM;
+                goto done;
+            }
+            ret = ldb_msg_add_string(msg, "member", ldb_dn_get_linearized(user->dn));
+            if (ret != LDB_SUCCESS) {
+                ret = ENOMEM;
+                goto done;
+            }
+
+            ret = ldb_modify(sysdb->ldb, msg);
+            talloc_free(msg);
+            if (ret != LDB_SUCCESS) {
+                ret = sysdb_error_to_errno(ret);
+                goto done;
+            }
+        }
+
+        ret = ldb_delete(sysdb->ldb, user->dn);
+        if (ret != LDB_SUCCESS) {
+            ret = sysdb_error_to_errno(ret);
+            goto done;
+        }
+    }
+
+    ret = EOK;
+
+done:
+    ret = finish_upgrade(ret, sysdb->ldb, SYSDB_VERSION_0_11, ver);
+    talloc_free(tmp_ctx);
+    return ret;
+}
-- 
1.7.6.5

