sysdb_upgrade.c revision e61b0e41cb44004d2b260ad9d05802995f7bcb2e
/*
SSSD
Authors:
Simo Sorce <ssorce@redhat.com>
Stephen Gallagher <sgallagh@redhat.com>
Copyright (C) 2008-2011 Simo Sorce <ssorce@redhat.com>
Copyright (C) 2008-2011 Stephen Gallagher
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "util/util.h"
#include "db/sysdb_private.h"
#include "db/sysdb_autofs.h"
struct upgrade_ctx {
struct ldb_context *ldb;
const char *new_version;
};
static errno_t commence_upgrade(TALLOC_CTX *mem_ctx, struct ldb_context *ldb,
const char *new_ver, struct upgrade_ctx **_ctx)
{
struct upgrade_ctx *ctx;
int ret;
DEBUG(SSSDBG_CRIT_FAILURE, "UPGRADING DB TO VERSION %s\n", new_ver);
ctx = talloc(mem_ctx, struct upgrade_ctx);
if (!ctx) {
return ENOMEM;
}
ctx->ldb = ldb;
ctx->new_version = new_ver;
ret = ldb_transaction_start(ldb);
if (ret != LDB_SUCCESS) {
ret = EIO;
goto done;
}
ret = EOK;
done:
if (ret != EOK) {
talloc_free(ctx);
} else {
*_ctx = ctx;
}
return ret;
}
static errno_t update_version(struct upgrade_ctx *ctx)
{
struct ldb_message *msg = NULL;
errno_t ret;
msg = ldb_msg_new(ctx);
if (!msg) {
ret = ENOMEM;
goto done;
}
msg->dn = ldb_dn_new(msg, ctx->ldb, SYSDB_BASE);
if (!msg->dn) {
ret = ENOMEM;
goto done;
}
ret = ldb_msg_add_empty(msg, "version", LDB_FLAG_MOD_REPLACE, NULL);
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_msg_add_string(msg, "version", ctx->new_version);
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_modify(ctx->ldb, msg);
if (ret != LDB_SUCCESS) {
ret = sysdb_error_to_errno(ret);
goto done;
}
ret = EOK;
done:
talloc_free(msg);
return ret;
}
static int finish_upgrade(int ret, struct upgrade_ctx **ctx, const char **ver)
{
int lret;
if (ret == EOK) {
lret = ldb_transaction_commit((*ctx)->ldb);
ret = sysdb_error_to_errno(lret);
if (ret == EOK) {
*ver = (*ctx)->new_version;
}
}
if (ret != EOK) {
lret = ldb_transaction_cancel((*ctx)->ldb);
if (lret != LDB_SUCCESS) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Could not cancel transaction! [%s]\n",
ldb_strerror(lret));
/* Do not overwrite ret here, we want to return
* the original failure, not the failure of the
* transaction cancellation.
*/
}
}
talloc_zfree(*ctx);
return ret;
}
/* serach all groups that have a memberUid attribute.
* change it into a member attribute for a user of same domain.
* remove the memberUid attribute
* add the new member attribute
* finally stop indexing memberUid
* upgrade version to 0.2
*/
int sysdb_upgrade_01(struct ldb_context *ldb, const char **ver)
{
struct ldb_message_element *el;
struct ldb_result *res;
struct ldb_dn *basedn;
struct ldb_dn *mem_dn;
struct ldb_message *msg;
const struct ldb_val *val;
const char *filter = "(&(memberUid=*)(objectclass=group))";
const char *attrs[] = { "memberUid", NULL };
const char *mdn;
char *domain;
int ret, i, j;
TALLOC_CTX *tmp_ctx;
struct upgrade_ctx *ctx;
tmp_ctx = talloc_new(NULL);
if (!tmp_ctx) {
return ENOMEM;
}
ret = commence_upgrade(tmp_ctx, ldb, SYSDB_VERSION_0_2, &ctx);
if (ret) {
talloc_free(tmp_ctx);
return ret;
}
basedn = ldb_dn_new(tmp_ctx, ldb, SYSDB_BASE);
if (!basedn) {
ret = EIO;
goto done;
}
ret = ldb_search(ldb, tmp_ctx, &res,
basedn, LDB_SCOPE_SUBTREE,
attrs, "%s", filter);
if (ret != LDB_SUCCESS) {
ret = EIO;
goto done;
}
for (i = 0; i < res->count; i++) {
el = ldb_msg_find_element(res->msgs[i], "memberUid");
if (!el) {
DEBUG(SSSDBG_CRIT_FAILURE,
"memberUid is missing from message [%s], skipping\n",
ldb_dn_get_linearized(res->msgs[i]->dn));
continue;
}
/* create modification message */
msg = ldb_msg_new(tmp_ctx);
if (!msg) {
ret = ENOMEM;
goto done;
}
msg->dn = res->msgs[i]->dn;
ret = ldb_msg_add_empty(msg, "memberUid", LDB_FLAG_MOD_DELETE, NULL);
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_msg_add_empty(msg, SYSDB_MEMBER, LDB_FLAG_MOD_ADD, NULL);
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
/* get domain name component value */
val = ldb_dn_get_component_val(res->msgs[i]->dn, 2);
domain = talloc_strndup(tmp_ctx, (const char *)val->data, val->length);
if (!domain) {
ret = ENOMEM;
goto done;
}
for (j = 0; j < el->num_values; j++) {
mem_dn = ldb_dn_new_fmt(tmp_ctx, ldb, SYSDB_TMPL_USER,
(const char *)el->values[j].data, domain);
if (!mem_dn) {
ret = ENOMEM;
goto done;
}
mdn = talloc_strdup(msg, ldb_dn_get_linearized(mem_dn));
if (!mdn) {
ret = ENOMEM;
goto done;
}
ret = ldb_msg_add_string(msg, SYSDB_MEMBER, mdn);
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
talloc_zfree(mem_dn);
}
/* ok now we are ready to modify the entry */
ret = ldb_modify(ldb, msg);
if (ret != LDB_SUCCESS) {
ret = sysdb_error_to_errno(ret);
goto done;
}
talloc_zfree(msg);
}
/* conversion done, update version number */
ret = update_version(ctx);
done:
ret = finish_upgrade(ret, &ctx, ver);
talloc_free(tmp_ctx);
return ret;
}
int sysdb_check_upgrade_02(struct sss_domain_info *domains,
const char *db_path)
{
TALLOC_CTX *tmp_ctx = NULL;
struct ldb_context *ldb;
char *ldb_file;
struct sysdb_ctx *sysdb;
struct sss_domain_info *dom;
struct ldb_message_element *el;
struct ldb_message *msg;
struct ldb_result *res;
struct ldb_dn *verdn;
const char *version = NULL;
bool do_02_upgrade = false;
bool ctx_trans = false;
int ret;
tmp_ctx = talloc_new(NULL);
if (!tmp_ctx) {
return ENOMEM;
}
ret = sysdb_get_db_file(tmp_ctx,
"local", "UPGRADE",
db_path, &ldb_file);
if (ret != EOK) {
goto exit;
}
ret = sysdb_ldb_connect(tmp_ctx, ldb_file, &ldb);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_ldb_connect failed.\n");
return ret;
}
verdn = ldb_dn_new(tmp_ctx, ldb, SYSDB_BASE);
if (!verdn) {
ret = EIO;
goto exit;
}
ret = ldb_search(ldb, tmp_ctx, &res,
verdn, LDB_SCOPE_BASE,
NULL, NULL);
if (ret != LDB_SUCCESS) {
ret = EIO;
goto exit;
}
if (res->count > 1) {
ret = EIO;
goto exit;
}
if (res->count == 1) {
el = ldb_msg_find_element(res->msgs[0], "version");
if (el) {
if (el->num_values != 1) {
ret = EINVAL;
goto exit;
}
version = talloc_strndup(tmp_ctx,
(char *)(el->values[0].data),
el->values[0].length);
if (!version) {
ret = ENOMEM;
goto exit;
}
if (strcmp(version, SYSDB_VERSION) == 0) {
/* all fine, return */
ret = EOK;
goto exit;
}
DEBUG(SSSDBG_CONF_SETTINGS,
"Upgrading DB from version: %s\n", version);
if (strcmp(version, SYSDB_VERSION_0_1) == 0) {
/* convert database */
ret = sysdb_upgrade_01(ldb, &version);
if (ret != EOK) goto exit;
}
if (strcmp(version, SYSDB_VERSION_0_2) == 0) {
/* need to convert database to split files */
do_02_upgrade = true;
}
}
}
if (!do_02_upgrade) {
/* not a v2 upgrade, return and let the normal code take over any
* further upgrade */
ret = EOK;
goto exit;
}
/* == V2->V3 UPGRADE == */
DEBUG(SSSDBG_FATAL_FAILURE,
"UPGRADING DB TO VERSION %s\n", SYSDB_VERSION_0_3);
/* ldb uses posix locks,
* posix is stupid and kills all locks when you close *any* file
* descriptor associated to the same file.
* Therefore we must close and reopen the ldb file here */
/* == Backup and reopen ldb == */
/* close */
talloc_zfree(ldb);
/* backup*/
ret = backup_file(ldb_file, SSSDBG_FATAL_FAILURE);
if (ret != EOK) {
goto exit;
}
/* reopen */
ret = sysdb_ldb_connect(tmp_ctx, ldb_file, &ldb);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_ldb_connect failed.\n");
return ret;
}
/* open a transaction */
ret = ldb_transaction_start(ldb);
if (ret != LDB_SUCCESS) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Failed to start ldb transaction! (%d)\n", ret);
ret = EIO;
goto exit;
}
/* == Upgrade contents == */
for (dom = domains; dom; dom = dom->next) {
struct ldb_dn *domain_dn;
struct ldb_dn *users_dn;
struct ldb_dn *groups_dn;
int i;
/* skip local */
if (strcasecmp(dom->provider, "local") == 0) {
continue;
}
/* create new dom db */
ret = sysdb_domain_init_internal(tmp_ctx, dom,
db_path, false, &sysdb);
if (ret != EOK) {
goto done;
}
ret = ldb_transaction_start(sysdb->ldb);
if (ret != LDB_SUCCESS) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Failed to start ldb transaction! (%d)\n", ret);
ret = EIO;
goto done;
}
ctx_trans = true;
/* search all entries for this domain in local,
* copy them all in the new database,
* then remove them from local */
domain_dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb,
SYSDB_DOM_BASE, dom->name);
if (!domain_dn) {
ret = ENOMEM;
goto done;
}
ret = ldb_search(ldb, tmp_ctx, &res,
domain_dn, LDB_SCOPE_SUBTREE,
NULL, NULL);
if (ret != LDB_SUCCESS) {
ret = EIO;
goto done;
}
users_dn = sysdb_user_base_dn(tmp_ctx, dom);
if (!users_dn) {
ret = ENOMEM;
goto done;
}
groups_dn = sysdb_group_base_dn(tmp_ctx, dom);
if (!groups_dn) {
ret = ENOMEM;
goto done;
}
for (i = 0; i < res->count; i++) {
struct ldb_dn *orig_dn;
msg = res->msgs[i];
/* skip pre-created congtainers */
if ((ldb_dn_compare(msg->dn, domain_dn) == 0) ||
(ldb_dn_compare(msg->dn, users_dn) == 0) ||
(ldb_dn_compare(msg->dn, groups_dn) == 0)) {
continue;
}
/* regenerate the DN against the new ldb as it may have different
* casefolding rules (example: name changing from case insensitive
* to case sensitive) */
orig_dn = msg->dn;
msg->dn = ldb_dn_new(msg, sysdb->ldb,
ldb_dn_get_linearized(orig_dn));
if (!msg->dn) {
ret = ENOMEM;
goto done;
}
ret = ldb_add(sysdb->ldb, msg);
if (ret != LDB_SUCCESS) {
DEBUG(SSSDBG_FATAL_FAILURE, "WARNING: Could not add entry %s,"
" to new ldb file! (%d [%s])\n",
ldb_dn_get_linearized(msg->dn),
ret, ldb_errstring(sysdb->ldb));
}
ret = ldb_delete(ldb, orig_dn);
if (ret != LDB_SUCCESS) {
DEBUG(SSSDBG_FATAL_FAILURE,
"WARNING: Could not remove entry %s,"
" from old ldb file! (%d [%s])\n",
ldb_dn_get_linearized(orig_dn),
ret, ldb_errstring(ldb));
}
}
/* now remove the basic containers from local */
/* these were optional so debug at level 9 in case
* of failure just for tracing */
ret = ldb_delete(ldb, groups_dn);
if (ret != LDB_SUCCESS) {
DEBUG(SSSDBG_TRACE_ALL, "WARNING: Could not remove entry %s,"
" from old ldb file! (%d [%s])\n",
ldb_dn_get_linearized(groups_dn),
ret, ldb_errstring(ldb));
}
ret = ldb_delete(ldb, users_dn);
if (ret != LDB_SUCCESS) {
DEBUG(SSSDBG_TRACE_ALL, "WARNING: Could not remove entry %s,"
" from old ldb file! (%d [%s])\n",
ldb_dn_get_linearized(users_dn),
ret, ldb_errstring(ldb));
}
ret = ldb_delete(ldb, domain_dn);
if (ret != LDB_SUCCESS) {
DEBUG(SSSDBG_TRACE_ALL, "WARNING: Could not remove entry %s,"
" from old ldb file! (%d [%s])\n",
ldb_dn_get_linearized(domain_dn),
ret, ldb_errstring(ldb));
}
ret = ldb_transaction_commit(sysdb->ldb);
if (ret != LDB_SUCCESS) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Failed to commit ldb transaction! (%d)\n", ret);
ret = EIO;
goto done;
}
ctx_trans = false;
talloc_zfree(domain_dn);
talloc_zfree(groups_dn);
talloc_zfree(users_dn);
talloc_zfree(res);
}
/* conversion done, upgrade version number */
msg = ldb_msg_new(tmp_ctx);
if (!msg) {
ret = ENOMEM;
goto done;
}
msg->dn = ldb_dn_new(tmp_ctx, ldb, SYSDB_BASE);
if (!msg->dn) {
ret = ENOMEM;
goto done;
}
ret = ldb_msg_add_empty(msg, "version", LDB_FLAG_MOD_REPLACE, NULL);
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_msg_add_string(msg, "version", SYSDB_VERSION_0_3);
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_modify(ldb, msg);
if (ret != LDB_SUCCESS) {
ret = sysdb_error_to_errno(ret);
goto done;
}
ret = ldb_transaction_commit(ldb);
if (ret != LDB_SUCCESS) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Failed to commit ldb transaction! (%d)\n", ret);
ret = EIO;
goto exit;
}
ret = EOK;
done:
if (ret != EOK) {
if (ctx_trans) {
ret = ldb_transaction_cancel(sysdb->ldb);
if (ret != LDB_SUCCESS) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Failed to cancel ldb transaction! (%d)\n", ret);
}
}
ret = ldb_transaction_cancel(ldb);
if (ret != LDB_SUCCESS) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Failed to cancel ldb transaction! (%d)\n", ret);
}
}
exit:
talloc_free(tmp_ctx);
return ret;
}
int sysdb_upgrade_03(struct sysdb_ctx *sysdb, const char **ver)
{
TALLOC_CTX *tmp_ctx;
int ret;
struct ldb_message *msg;
struct upgrade_ctx *ctx;
tmp_ctx = talloc_new(NULL);
if (!tmp_ctx) {
return ENOMEM;
}
ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_4, &ctx);
if (ret) {
return ret;
}
/* Make this database case-sensitive */
msg = ldb_msg_new(tmp_ctx);
if (!msg) {
ret = ENOMEM;
goto done;
}
msg->dn = ldb_dn_new(tmp_ctx, sysdb->ldb, "@ATTRIBUTES");
if (!msg->dn) {
ret = ENOMEM;
goto done;
}
ret = ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_DELETE, NULL);
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_modify(sysdb->ldb, msg);
if (ret != LDB_SUCCESS) {
ret = sysdb_error_to_errno(ret);
goto done;
}
/* conversion done, update version number */
ret = update_version(ctx);
done:
ret = finish_upgrade(ret, &ctx, ver);
talloc_free(tmp_ctx);
return ret;
}
int sysdb_upgrade_04(struct sysdb_ctx *sysdb, const char **ver)
{
TALLOC_CTX *tmp_ctx;
int ret;
struct ldb_message *msg;
struct upgrade_ctx *ctx;
tmp_ctx = talloc_new(NULL);
if (!tmp_ctx) {
return ENOMEM;
}
ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_5, &ctx);
if (ret) {
return ret;
}
/* Add new index */
msg = ldb_msg_new(tmp_ctx);
if (!msg) {
ret = ENOMEM;
goto done;
}
msg->dn = ldb_dn_new(tmp_ctx, sysdb->ldb, "@INDEXLIST");
if (!msg->dn) {
ret = ENOMEM;
goto done;
}
ret = ldb_msg_add_empty(msg, "@IDXATTR", LDB_FLAG_MOD_ADD, NULL);
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_msg_add_string(msg, "@IDXATTR", "originalDN");
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_modify(sysdb->ldb, msg);
if (ret != LDB_SUCCESS) {
ret = sysdb_error_to_errno(ret);
goto done;
}
/* Rebuild memberuid and memberoif attributes */
msg = ldb_msg_new(tmp_ctx);
if (!msg) {
ret = ENOMEM;
goto done;
}
msg->dn = ldb_dn_new(tmp_ctx, sysdb->ldb, "@MEMBEROF-REBUILD");
if (!msg->dn) {
ret = ENOMEM;
goto done;
}
ret = ldb_add(sysdb->ldb, msg);
if (ret != LDB_SUCCESS) {
ret = sysdb_error_to_errno(ret);
goto done;
}
/* conversion done, update version number */
ret = update_version(ctx);
done:
ret = finish_upgrade(ret, &ctx, ver);
talloc_free(tmp_ctx);
return ret;
}
int sysdb_upgrade_05(struct sysdb_ctx *sysdb, const char **ver)
{
TALLOC_CTX *tmp_ctx;
int ret;
struct ldb_message *msg;
struct upgrade_ctx *ctx;
tmp_ctx = talloc_new(NULL);
if (!tmp_ctx) {
return ENOMEM;
}
ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_6, &ctx);
if (ret) {
return ret;
}
/* Add new indexes */
msg = ldb_msg_new(tmp_ctx);
if (!msg) {
ret = ENOMEM;
goto done;
}
msg->dn = ldb_dn_new(tmp_ctx, sysdb->ldb, "@INDEXLIST");
if (!msg->dn) {
ret = ENOMEM;
goto done;
}
/* Add Index for dataExpireTimestamp */
ret = ldb_msg_add_empty(msg, "@IDXATTR", LDB_FLAG_MOD_ADD, NULL);
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_msg_add_string(msg, "@IDXATTR", "dataExpireTimestamp");
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
/* Add index to speed up ONELEVEL searches */
ret = ldb_msg_add_empty(msg, "@IDXONE", LDB_FLAG_MOD_ADD, NULL);
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_msg_add_string(msg, "@IDXONE", "1");
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_modify(sysdb->ldb, msg);
if (ret != LDB_SUCCESS) {
ret = sysdb_error_to_errno(ret);
goto done;
}
/* conversion done, update version number */
ret = update_version(ctx);
done:
ret = finish_upgrade(ret, &ctx, ver);
talloc_free(tmp_ctx);
return ret;
}
int sysdb_upgrade_06(struct sysdb_ctx *sysdb, const char **ver)
{
TALLOC_CTX *tmp_ctx;
int ret;
struct ldb_message *msg;
struct upgrade_ctx *ctx;
tmp_ctx = talloc_new(NULL);
if (!tmp_ctx) {
return ENOMEM;
}
ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_7, &ctx);
if (ret) {
return ret;
}
/* Add new indexes */
msg = ldb_msg_new(tmp_ctx);
if (!msg) {
ret = ENOMEM;
goto done;
}
msg->dn = ldb_dn_new(tmp_ctx, sysdb->ldb, "@ATTRIBUTES");
if (!msg->dn) {
ret = ENOMEM;
goto done;
}
/* Case insensitive search for originalDN */
ret = ldb_msg_add_empty(msg, SYSDB_ORIG_DN, LDB_FLAG_MOD_ADD, NULL);
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_msg_add_string(msg, SYSDB_ORIG_DN, "CASE_INSENSITIVE");
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_modify(sysdb->ldb, msg);
if (ret != LDB_SUCCESS) {
ret = sysdb_error_to_errno(ret);
goto done;
}
/* conversion done, update version number */
ret = update_version(ctx);
done:
ret = finish_upgrade(ret, &ctx, ver);
talloc_free(tmp_ctx);
return ret;
}
int sysdb_upgrade_07(struct sysdb_ctx *sysdb, const char **ver)
{
TALLOC_CTX *tmp_ctx;
int ret;
struct ldb_message *msg;
struct upgrade_ctx *ctx;
tmp_ctx = talloc_new(NULL);
if (!tmp_ctx) {
return ENOMEM;
}
ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_8, &ctx);
if (ret) {
return ret;
}
/* Add new indexes */
msg = ldb_msg_new(tmp_ctx);
if (!msg) {
ret = ENOMEM;
goto done;
}
msg->dn = ldb_dn_new(tmp_ctx, sysdb->ldb, "@INDEXLIST");
if (!msg->dn) {
ret = ENOMEM;
goto done;
}
/* Add Index for nameAlias */
ret = ldb_msg_add_empty(msg, "@IDXATTR", LDB_FLAG_MOD_ADD, NULL);
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_msg_add_string(msg, "@IDXATTR", "nameAlias");
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_modify(sysdb->ldb, msg);
if (ret != LDB_SUCCESS) {
ret = sysdb_error_to_errno(ret);
goto done;
}
/* conversion done, update version number */
ret = update_version(ctx);
done:
ret = finish_upgrade(ret, &ctx, ver);
talloc_free(tmp_ctx);
return ret;
}
int sysdb_upgrade_08(struct sysdb_ctx *sysdb, const char **ver)
{
TALLOC_CTX *tmp_ctx;
int ret;
struct ldb_message *msg;
struct upgrade_ctx *ctx;
tmp_ctx = talloc_new(NULL);
if (!tmp_ctx) {
return ENOMEM;
}
ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_9, &ctx);
if (ret) {
return ret;
}
/* Add new indexes */
msg = ldb_msg_new(tmp_ctx);
if (!msg) {
ret = ENOMEM;
goto done;
}
msg->dn = ldb_dn_new(tmp_ctx, sysdb->ldb, "@INDEXLIST");
if (!msg->dn) {
ret = ENOMEM;
goto done;
}
/* Add Index for servicePort and serviceProtocol */
ret = ldb_msg_add_empty(msg, "@IDXATTR", LDB_FLAG_MOD_ADD, NULL);
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_msg_add_string(msg, "@IDXATTR", "servicePort");
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_msg_add_string(msg, "@IDXATTR", "serviceProtocol");
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_modify(sysdb->ldb, msg);
if (ret != LDB_SUCCESS) {
ret = sysdb_error_to_errno(ret);
goto done;
}
/* conversion done, update version number */
ret = update_version(ctx);
done:
ret = finish_upgrade(ret, &ctx, ver);
talloc_free(tmp_ctx);
return ret;
}
int sysdb_upgrade_09(struct sysdb_ctx *sysdb, const char **ver)
{
TALLOC_CTX *tmp_ctx;
int ret;
struct ldb_message *msg;
struct upgrade_ctx *ctx;
tmp_ctx = talloc_new(NULL);
if (!tmp_ctx) {
return ENOMEM;
}
ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_10, &ctx);
if (ret) {
return ret;
}
/* Add new indexes */
msg = ldb_msg_new(tmp_ctx);
if (!msg) {
ret = ENOMEM;
goto done;
}
msg->dn = ldb_dn_new(tmp_ctx, sysdb->ldb, "@INDEXLIST");
if (!msg->dn) {
ret = ENOMEM;
goto done;
}
/* Add Index for servicePort and serviceProtocol */
ret = ldb_msg_add_empty(msg, "@IDXATTR", LDB_FLAG_MOD_ADD, NULL);
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_msg_add_string(msg, "@IDXATTR", "sudoUser");
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_modify(sysdb->ldb, msg);
if (ret != LDB_SUCCESS) {
ret = sysdb_error_to_errno(ret);
goto done;
}
/* conversion done, update version number */
ret = update_version(ctx);
done:
ret = finish_upgrade(ret, &ctx, ver);
talloc_free(tmp_ctx);
return ret;
}
int sysdb_upgrade_10(struct sysdb_ctx *sysdb, struct sss_domain_info *domain,
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_el;
const char *name;
struct ldb_dn *basedn;
const char *filter = "(&(objectClass=user)(!(uidNumber=*))(memberOf=*))";
const char *attrs[] = { "name", "memberof", NULL };
struct upgrade_ctx *ctx;
int i, j;
tmp_ctx = talloc_new(NULL);
if (tmp_ctx == NULL) {
return ENOMEM;
}
ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_11, &ctx);
if (ret) {
return ret;
}
basedn = sysdb_user_base_dn(tmp_ctx, domain);
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;
}
for (i = 0; i < res->count; i++) {
user = res->msgs[i];
memberof_el = ldb_msg_find_element(user, "memberof");
name = ldb_msg_find_attr_as_string(user, "name", NULL);
if (name == NULL) {
ret = EIO;
goto done;
}
DEBUG(SSSDBG_TRACE_LIBS, "User [%s] is a member of %d groups\n",
name, memberof_el->num_values);
for (j = 0; j < memberof_el->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_el->values[j]);
if (msg->dn == NULL) {
ret = ENOMEM;
goto done;
}
if (!ldb_dn_validate(msg->dn)) {
DEBUG(SSSDBG_MINOR_FAILURE, "DN validation failed during "
"upgrade: [%s]\n",
memberof_el->values[j].data);
talloc_zfree(msg);
continue;
}
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", name);
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
DEBUG(SSSDBG_TRACE_FUNC, "Adding ghost [%s] to entry [%s]\n",
name, ldb_dn_get_linearized(msg->dn));
ret = sss_ldb_modify_permissive(sysdb->ldb, msg);
talloc_zfree(msg);
if (ret == LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS) {
/* If we failed adding the ghost user(s) because the values already
* exist, they were probably propagated from a parent that was
* upgraded before us. Mark the group as expired so that it is
* refreshed on next request.
*/
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_el->values[j]);
if (msg->dn == NULL) {
ret = ENOMEM;
goto done;
}
ret = ldb_msg_add_empty(msg, SYSDB_CACHE_EXPIRE,
LDB_FLAG_MOD_REPLACE, NULL);
if (ret != LDB_SUCCESS) {
goto done;
}
ret = ldb_msg_add_string(msg, SYSDB_CACHE_EXPIRE, "1");
if (ret != LDB_SUCCESS) {
goto done;
}
ret = sss_ldb_modify_permissive(sysdb->ldb, msg);
talloc_zfree(msg);
if (ret != LDB_SUCCESS) {
goto done;
}
} else if (ret != LDB_SUCCESS) {
ret = sysdb_error_to_errno(ret);
goto done;
}
}
DEBUG(SSSDBG_TRACE_FUNC, "Removing fake user [%s]\n",
ldb_dn_get_linearized(user->dn));
ret = ldb_delete(sysdb->ldb, user->dn);
if (ret != LDB_SUCCESS) {
ret = sysdb_error_to_errno(ret);
goto done;
}
}
/* conversion done, update version number */
ret = update_version(ctx);
done:
ret = finish_upgrade(ret, &ctx, ver);
talloc_free(tmp_ctx);
return ret;
}
int sysdb_upgrade_11(struct sysdb_ctx *sysdb, struct sss_domain_info *domain,
const char **ver)
{
TALLOC_CTX *tmp_ctx;
errno_t ret;
struct ldb_result *res;
struct ldb_message *entry;
const char *key;
const char *value;
struct ldb_message_element *memberof_el;
struct ldb_dn *memberof_dn;
struct ldb_dn *basedn;
const struct ldb_val *val;
const char *attrs[] = { SYSDB_AUTOFS_ENTRY_KEY,
SYSDB_AUTOFS_ENTRY_VALUE,
SYSDB_MEMBEROF,
NULL };
struct upgrade_ctx *ctx;
size_t i, j;
tmp_ctx = talloc_new(NULL);
if (!tmp_ctx) {
return ENOMEM;
}
ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_12, &ctx);
if (ret) {
return ret;
}
basedn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, SYSDB_TMPL_CUSTOM_SUBTREE,
AUTOFS_ENTRY_SUBDIR, domain->name);
if (basedn == NULL) {
ret = ENOMEM;
goto done;
}
ret = ldb_search(sysdb->ldb, tmp_ctx, &res, basedn, LDB_SCOPE_SUBTREE,
attrs, "(objectClass=%s)", SYSDB_AUTOFS_ENTRY_OC);
if (ret != LDB_SUCCESS) {
ret = EIO;
goto done;
}
DEBUG(SSSDBG_TRACE_LIBS, "Found %d autofs entries\n", res->count);
for (i = 0; i < res->count; i++) {
entry = res->msgs[i];
key = ldb_msg_find_attr_as_string(entry,
SYSDB_AUTOFS_ENTRY_KEY, NULL);
value = ldb_msg_find_attr_as_string(entry,
SYSDB_AUTOFS_ENTRY_VALUE, NULL);
memberof_el = ldb_msg_find_element(entry, SYSDB_MEMBEROF);
if (key && value && memberof_el) {
for (j = 0; j < memberof_el->num_values; j++) {
memberof_dn = ldb_dn_from_ldb_val(tmp_ctx, sysdb->ldb,
&(memberof_el->values[j]));
if (!memberof_dn) {
DEBUG(SSSDBG_OP_FAILURE, "Cannot convert memberof into DN, skipping\n");
continue;
}
val = ldb_dn_get_rdn_val(memberof_dn);
if (!val) {
DEBUG(SSSDBG_OP_FAILURE, "Cannot get map name from map DN\n");
continue;
}
ret = sysdb_save_autofsentry(domain,
(const char *) val->data,
key, value, NULL);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Cannot save autofs entry [%s]-[%s] into map %s\n",
key, value, val->data);
continue;
}
}
}
/* Delete the old entry if it was either processed or incomplete */
DEBUG(SSSDBG_TRACE_LIBS, "Deleting [%s]\n",
ldb_dn_get_linearized(entry->dn));
ret = ldb_delete(sysdb->ldb, entry->dn);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "Cannot delete old autofs entry %s\n",
ldb_dn_get_linearized(entry->dn));
continue;
}
}
/* conversion done, update version number */
ret = update_version(ctx);
done:
ret = finish_upgrade(ret, &ctx, ver);
talloc_free(tmp_ctx);
return ret;
}
int sysdb_upgrade_12(struct sysdb_ctx *sysdb, const char **ver)
{
TALLOC_CTX *tmp_ctx;
int ret;
struct ldb_message *msg;
struct upgrade_ctx *ctx;
tmp_ctx = talloc_new(NULL);
if (!tmp_ctx) {
return ENOMEM;
}
ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_13, &ctx);
if (ret) {
return ret;
}
/* add new indexes */
msg = ldb_msg_new(tmp_ctx);
if (!msg) {
ret = ENOMEM;
goto done;
}
msg->dn = ldb_dn_new(tmp_ctx, sysdb->ldb, "@INDEXLIST");
if (!msg->dn) {
ret = ENOMEM;
goto done;
}
/* add index for sshKnownHostsExpire */
ret = ldb_msg_add_empty(msg, "@IDXATTR", LDB_FLAG_MOD_ADD, NULL);
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_msg_add_string(msg, "@IDXATTR", "sshKnownHostsExpire");
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_modify(sysdb->ldb, msg);
if (ret != LDB_SUCCESS) {
ret = sysdb_error_to_errno(ret);
goto done;
}
/* conversion done, update version number */
ret = update_version(ctx);
done:
ret = finish_upgrade(ret, &ctx, ver);
talloc_free(tmp_ctx);
return ret;
}
int sysdb_upgrade_13(struct sysdb_ctx *sysdb, const char **ver)
{
struct upgrade_ctx *ctx;
struct ldb_result *dom_res;
struct ldb_result *res;
struct ldb_dn *basedn;
const char *attrs[] = { "cn", "name", NULL };
const char *tmp_str;
errno_t ret;
int i, j, l, n;
ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_14, &ctx);
if (ret) {
return ret;
}
basedn = ldb_dn_new(ctx, sysdb->ldb, SYSDB_BASE);
if (!basedn) {
DEBUG(SSSDBG_OP_FAILURE, "Failed to build base dn\n");
ret = EIO;
goto done;
}
ret = ldb_search(sysdb->ldb, ctx, &dom_res,
basedn, LDB_SCOPE_ONELEVEL,
attrs, "objectclass=%s", SYSDB_SUBDOMAIN_CLASS);
if (ret != LDB_SUCCESS) {
DEBUG(SSSDBG_OP_FAILURE, "Failed to search subdomains\n");
ret = EIO;
goto done;
}
for (i = 0; i < dom_res->count; i++) {
tmp_str = ldb_msg_find_attr_as_string(dom_res->msgs[i], "cn", NULL);
if (tmp_str == NULL) {
DEBUG(SSSDBG_MINOR_FAILURE,
"The object [%s] doesn't have a name\n",
ldb_dn_get_linearized(dom_res->msgs[i]->dn));
continue;
}
basedn = ldb_dn_new_fmt(ctx, sysdb->ldb, SYSDB_DOM_BASE, tmp_str);
if (!basedn) {
DEBUG(SSSDBG_OP_FAILURE,
"Failed to build base dn for subdomain %s\n", tmp_str);
continue;
}
ret = ldb_search(sysdb->ldb, ctx, &res,
basedn, LDB_SCOPE_SUBTREE, attrs, NULL);
if (ret != LDB_SUCCESS) {
DEBUG(SSSDBG_OP_FAILURE,
"Failed to search subdomain %s\n", tmp_str);
talloc_free(basedn);
continue;
}
l = ldb_dn_get_comp_num(basedn);
for (j = 0; j < res->count; j++) {
n = ldb_dn_get_comp_num(res->msgs[j]->dn);
if (n <= l + 1) {
/* Do not remove subdomain containers, only their contents */
continue;
}
ret = ldb_delete(sysdb->ldb, res->msgs[j]->dn);
if (ret) {
DEBUG(SSSDBG_OP_FAILURE,
"Failed to delete %s\n",
ldb_dn_get_linearized(res->msgs[j]->dn));
continue;
}
}
talloc_free(basedn);
talloc_free(res);
}
talloc_free(dom_res);
/* conversion done, update version number */
ret = update_version(ctx);
done:
ret = finish_upgrade(ret, &ctx, ver);
return ret;
}
int sysdb_upgrade_14(struct sysdb_ctx *sysdb, const char **ver)
{
struct upgrade_ctx *ctx;
struct ldb_message *msg;
struct ldb_result *res;
struct ldb_dn *basedn;
struct ldb_dn *newdn;
const char *attrs[] = { SYSDB_NAME, NULL };
const char *tmp_str;
errno_t ret;
int i;
ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_15, &ctx);
if (ret) {
return ret;
}
basedn = ldb_dn_new(ctx, sysdb->ldb, SYSDB_BASE);
if (!basedn) {
DEBUG(SSSDBG_OP_FAILURE, "Failed to build base dn\n");
ret = EIO;
goto done;
}
/* create base ranges container */
msg = ldb_msg_new(ctx);
if (!msg) {
ret = ENOMEM;
goto done;
}
msg->dn = ldb_dn_new(msg, sysdb->ldb, SYSDB_TMPL_RANGE_BASE);
if (!msg->dn) {
ret = ENOMEM;
goto done;
}
ret = ldb_msg_add_string(msg, "cn", "ranges");
if (ret != LDB_SUCCESS) {
ret = EIO;
goto done;
}
/* do a synchronous add */
ret = ldb_add(sysdb->ldb, msg);
if (ret != LDB_SUCCESS) {
DEBUG(SSSDBG_FATAL_FAILURE,
"Failed to upgrade DB (%d, [%s])!\n",
ret, ldb_errstring(sysdb->ldb));
ret = EIO;
goto done;
}
talloc_zfree(msg);
ret = ldb_search(sysdb->ldb, ctx, &res,
basedn, LDB_SCOPE_SUBTREE, attrs,
"objectclass=%s", SYSDB_ID_RANGE_CLASS);
if (ret != LDB_SUCCESS) {
DEBUG(SSSDBG_OP_FAILURE, "Failed to search range objects\n");
ret = EIO;
goto done;
}
/* Failure to convert any range is not fatal. As long as there are no
* left-over objects we can fail to move them around, as they will be
* recreated on the next online access */
for (i = 0; i < res->count; i++) {
tmp_str = ldb_msg_find_attr_as_string(res->msgs[i], SYSDB_NAME, NULL);
if (tmp_str == NULL) {
DEBUG(SSSDBG_OP_FAILURE,
"The object [%s] doesn't have a name\n",
ldb_dn_get_linearized(res->msgs[i]->dn));
ret = ldb_delete(sysdb->ldb, res->msgs[i]->dn);
if (ret) {
DEBUG(SSSDBG_OP_FAILURE,
"Failed to delete %s\n",
ldb_dn_get_linearized(res->msgs[i]->dn));
ret = EIO;
goto done;
}
continue;
}
newdn = ldb_dn_new_fmt(ctx, sysdb->ldb, SYSDB_TMPL_RANGE, tmp_str);
if (!newdn) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Failed to create new DN to move [%s]\n",
ldb_dn_get_linearized(res->msgs[i]->dn));
ret = ENOMEM;
goto done;
}
ret = ldb_rename(sysdb->ldb, res->msgs[i]->dn, newdn);
if (ret != LDB_SUCCESS) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Failed to move [%s] to [%s]\n",
ldb_dn_get_linearized(res->msgs[i]->dn),
ldb_dn_get_linearized(newdn));
ret = ldb_delete(sysdb->ldb, res->msgs[i]->dn);
if (ret) {
DEBUG(SSSDBG_OP_FAILURE,
"Failed to delete %s\n",
ldb_dn_get_linearized(res->msgs[i]->dn));
ret = EIO;
goto done;
}
}
talloc_zfree(newdn);
}
/* conversion done, update version number */
ret = update_version(ctx);
done:
ret = finish_upgrade(ret, &ctx, ver);
return ret;
}
int sysdb_upgrade_15(struct sysdb_ctx *sysdb, const char **ver)
{
TALLOC_CTX *tmp_ctx;
int ret;
struct ldb_message *msg;
struct upgrade_ctx *ctx;
tmp_ctx = talloc_new(NULL);
if (!tmp_ctx) {
return ENOMEM;
}
ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_16, &ctx);
if (ret) {
return ret;
}
/* Add new indexes */
msg = ldb_msg_new(tmp_ctx);
if (!msg) {
ret = ENOMEM;
goto done;
}
msg->dn = ldb_dn_new(tmp_ctx, sysdb->ldb, "@ATTRIBUTES");
if (!msg->dn) {
ret = ENOMEM;
goto done;
}
/* Case insensitive search for canonicalUserPrincipalName */
ret = ldb_msg_add_empty(msg, SYSDB_CANONICAL_UPN, LDB_FLAG_MOD_ADD, NULL);
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_msg_add_string(msg, SYSDB_CANONICAL_UPN, "CASE_INSENSITIVE");
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_modify(sysdb->ldb, msg);
if (ret != LDB_SUCCESS) {
ret = sysdb_error_to_errno(ret);
goto done;
}
/* conversion done, update version number */
ret = update_version(ctx);
done:
ret = finish_upgrade(ret, &ctx, ver);
talloc_free(tmp_ctx);
return ret;
}
int sysdb_upgrade_16(struct sysdb_ctx *sysdb, const char **ver)
{
struct ldb_message *msg;
struct upgrade_ctx *ctx;
errno_t ret;
ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_17, &ctx);
if (ret) {
return ret;
}
msg = ldb_msg_new(ctx);
if (msg == NULL) {
ret = ENOMEM;
goto done;
}
msg->dn = ldb_dn_new(msg, sysdb->ldb, "@INDEXLIST");
if (msg->dn == NULL) {
ret = ENOMEM;
goto done;
}
/* add index for objectSIDString */
ret = ldb_msg_add_empty(msg, "@IDXATTR", LDB_FLAG_MOD_ADD, NULL);
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_msg_add_string(msg, "@IDXATTR", "objectSIDString");
if (ret != LDB_SUCCESS) {
ret = ENOMEM;
goto done;
}
ret = ldb_modify(sysdb->ldb, msg);
if (ret != LDB_SUCCESS) {
ret = sysdb_error_to_errno(ret);
goto done;
}
/* conversion done, update version number */
ret = update_version(ctx);
done:
ret = finish_upgrade(ret, &ctx, ver);
return ret;
}
/*
* Example template for future upgrades.
* Copy and change version numbers as appropriate.
*/
#if 0
int sysdb_upgrade_13(struct sysdb_ctx *sysdb, const char **ver)
{
struct upgrade_ctx *ctx;
errno_t ret;
ret = commence_upgrade(sysdb, sysdb->ldb, SYSDB_VERSION_0_14, &ctx);
if (ret) {
return ret;
}
/* DO STUFF HERE (use ctx, as the local temporary memory context) */
/* conversion done, update version number */
ret = update_version(ctx);
done:
ret = finish_upgrade(ret, &ctx, ver);
return ret;
}
#endif