Remove second account nukes main account

REPRODUCIBILITY: no
OS VERSION: 4.5.26
HARDWARE: xperia 10 ii
UI LANGUAGE: german
REGRESSION: unknown

DESCRIPTION:

main accounts data is wiped (contacts, images, sms, accounts).

Device on next boot shows tutorial.

PRECONDITIONS:

have a second account created

STEPS TO REPRODUCE:

  1. delete the second account
  2. reboot

EXPECTED RESULT:

main account is untouched

ACTUAL RESULT:

main account is ground zero-ed / all data is removed, device is like resetted.

MODIFICATIONS:

ADDITIONAL INFORMATION:

this is extremely serious as all data is gone.

2 Likes

Yes, this should really be repaired. But till now, the experiences and researches gave no results.
This problem was reported already, e.g. here:[Multi-user][Android] User data get deleted and lost, device reset
My temporary conclusion was that it is related to Android start/stop near the moment of changing user.

1 Like

Oh, thinking now: weren’t there some files in /home/defaultuser/ that, when deleted, trigger a reset?

That I haven’t heard, but there was a while ago an RPM package that deleted everything when uninstalled.

It was due to a busybox/bash incompatibility, and has long since been fixed (in the package).

Yes, but this was on the native side, i.e. not android.

I had only done user removal; no extra stuff added (apart perhaps some android apps).
And when everything went I really mean everything, two years of contacts, pictures, messaging…

I don’t want to make false hopes but perhaps is it worth it to try to look into /home/ from the pc by booting on the recovery image (one of the how-to here: Delete a corrupted file - #12 by ric9k) and if it appears to be really empty, maybe try to use some ext undeleting softwares (was that photorec?)

1 Like

Yes, but I remember it seemed to happen more often if Android Support was enabled/disabled right before changing user.

Hm, yes I remember. I might have mixed with .jolla-startupwizard* files flags, which suppression triggers some kind of reset of basic settings and make feel the device is reset. (while it is not reset.)

Looking into this a bit:

So, the Settings application, in the page /usr/share/jolla-settings/pages/users/users.qml mainly uses something called UserModel to add or remote user accounts.

The QML UserModel is implemented from nemo-qml-plugin-systemsettings, the source code for that is at nemo-qml-plugin-systemsettings.

The plugin talks via DBus to something called jolla-usermanagerd. Surprisingly, sources are again available, at jolla-usermanagerd.

The function to delete files of a (deleted) user: https://github.com/sailfishos/user-managerd/blob/master/src/sailfishusermanager.cpp#L401

[…to be continued…]

[EDIT:]

Meh, all this gumshoeing before looking for the obvious.

It seems there is a fix, or something related to this, in master, but not tagged:

The latest tag of usermanagerd is 0.8.7, and this is what I have on my device. That commit is on top of that.

2 Likes

/usr/sbin/userdel_local.sh and /usr/bin/user-managerd --removeUserFiles $1


TL;DR: VERDICT: likely harmless, but the scripts must be looked into.

jolla-usermanagerd, through its script /usr/sbin/userdel_local.sh calls /usr/bin/user-managerd --removeUserFiles $1

which does this:

QDir dir(USER_ENVIRONMENT_DIR.arg(uid));
    if (dir.removeRecursively())

Where USER_ENVIRONMENT_DIR is defined a bit higher in the file as:

const auto USER_ENVIRONMENT_DIR = QStringLiteral("/home/.system/var/lib/environment/%1");

So, IF uid is defined (e.g. as 999), dir will be /home/.system/var/lib/environment/9999 BUT if uid null or empty, dir will be /home/.system/var/lib/environment/, and the call will remove ALL subdirs from ALL users.

AFAICS, uid is not checked for null or empty.

However, even if there were a bug here, the effects would be small, as /home/.system/var/lib/environment/ only contains some files setting user environment, like the selected locale options.

Still, in that same function, after we did dir.removeRecursively(), we proceed to execute the scripts from executeScripts(uid, USER_REMOVE_SCRIPT_DIR);, where USER_REMOVE_SCRIPT_DIR is the one mentioned above:

const auto USER_REMOVE_SCRIPT_DIR = QStringLiteral("/usr/share/user-managerd/remove.d");

user-managerd::removeUser


TL;DR: VERDICT: inconclusive

So lets look at user-managerd::removeUser:

{
    if (!checkAccessRights(uid))
        return;

    if (uid == OWNER_USER_UID) {
        auto message = QStringLiteral("Can not remove device owner");
        qCWarning(lcSUM) << message;
        sendErrorReply(QDBusError::InvalidArgs, message);
        return;
    }

    if (uid == currentUser()) {
        auto message = QStringLiteral("Can not remove current user");
        qCWarning(lcSUM) << message;
        sendErrorReply(QDBusError::InvalidArgs, message);
        return;
    }

    m_exitTimer->start();

    if (uid != SAILFISH_USERMANAGER_GUEST_UID && !removeHome(uid)) {
        qCWarning(lcSUM) << "Removing user home failed";
    }

    removeUserFiles(uid);

    if (!m_lu->removeUser(uid)) {
        auto message = QStringLiteral("User remove failed");
        qCWarning(lcSUM) << message;
        sendErrorReply(QStringLiteral(SailfishUserManagerErrorUserRemoveFailed), message);
    } else {
        emit userRemoved(uid);
    }

Interesting, there is a check for OWNER - so this is meant to prevent exactly what we see in the bug report.

Does this work right: if (uid == currentUser()) ?

Hm:

uint SailfishUserManager::currentUser()
{
    m_exitTimer->start();
    uid_t uid;
    if (sd_seat_get_active("seat0", nullptr, &uid) < 0) {
        auto message = QStringLiteral("Failed to get current user id");
        qCWarning(lcSUM) << message;
        sendErrorReply(QStringLiteral(SailfishUserManagerErrorGetUidFailed), message);
        return SAILFISH_UNDEFINED_UID;
    }
    return uid;
}

Assuming systemd can actually correctly find out the uid from seat0 through sd_seat_get_active (big assumption here!), we MIGHT get back SAILFISH_UNDEFINED_UID. AFAICS that is used in various sanity checking functions, including checkAccessRights.

I could not find where SAILFISH_UNDEFINED_UID is actually defined - does this mean it is indeed “undefined” everywhere? (Similarly there is no definition for OWNER_USER_ID. These must be set in the build environment beforehand? No idea.)

But assuming those checks are all sound, what’s this?

 if (uid != SAILFISH_USERMANAGER_GUEST_UID && !removeHome(uid)) {
bool SailfishUserManager::removeHome(uint uid)
{
    QString home = m_lu->homeDir(uid);
    if (home.isEmpty())
        return false;

    return removeDir(home);
}
bool SailfishUserManager::removeDir(const QString &dir)
{
    QDir directory(dir);
    if (!directory.removeRecursively()) {
        qCWarning(lcSUM) << "Removing directory failed";
        return false;
    }
    return true;
}

Okay, here we actually delete stuff so this looks dangerous.
AFAICS it is also the only place where user homes are deleted so far.

What’s m_lu, which resolves the actual dir to remove? It’s an instance of libuser, via m_lu(new LibUserHelper()).We find homeDir in src/libuserhelper.cpp which in turn uses libuser. lu_user_lookup_id and lu_ent_get_first_string_in are used to retrieve the home directory from passwd.

4 Likes

Scripts in /usr/share/user-managerd/remove.d

Tl;DR: VERDICT: likely harmless, but the fingerprint tool might do something wrong, we can’t tell.

There’s
/usr/share/user-managerd/remove.d
where packages can place their cleanup scripts. On my system, there are three scripts in there, owned by the oneshot, sailfish-fpd, and usb-moded packages.

From looking at all three, they appear to be reasonably sane, all of them do NOT do anything when their “UID” parameter is empty, or one of a nonexisting user account. but rather error out. Good.

NOTE Maybe there are packages which install other scripts into this dir. Users affected by the original Bug Report should investigate this.

One interesing thing is the fingerprint script, it uses a tool called /usr/libexec/sailfish-fpd/fpslave from package sailfish-fpd-slave-binder. No idea what it does exactly, but there might be a connection to Android here, the "binder"hints to that. I might be completely off here though.
Unfortunately, the code for this does not seem to be available, so we can’t look into this.

The script from usb-moded calls usb_moded-util, which “clears user config” though this: https://github.com/sailfishos/usb-moded/blob/master/src/usb_moded-util.c#L363, where we issue a dbus call again, the method (via https://github.com/sailfishos/usb-moded/blob/master/src/usb_moded-dbus.h) called is clear_config. AFAICS this ends up executing https://github.com/sailfishos/usb-moded/blob/master/src/usb_moded-config.c#L1120:

/**
 * Remove user configs
 */
bool config_user_clear(uid_t uid)
{
#ifdef SAILFISH_ACCESS_CONTROL
    if (uid < MIN_ADDITIONAL_USER || uid > MAX_ADDITIONAL_USER) {
        log_err("Invalid uid value: %d\n", uid);
        return false;
    }
#endif

    GKeyFile *active_ini = g_key_file_new();
    config_load_dynamic_config(active_ini);

    char *key = config_make_user_key_string(MODE_SETTING_KEY, uid);
    if (key) {
        if (g_key_file_remove_key(active_ini, MODE_SETTING_ENTRY, key, NULL))
            config_save_dynamic_config(active_ini);
        g_free(key);
    }

    g_key_file_free(active_ini);
    return true;
}

Fine, I guess…

4 Likes

Good hints, there was indeed some binder issues flying by in the journalctl logs I was looking at,
but I cant recall (and now there isnt any anymore).
Its interesting, how comes however a configuration reset would affect data ?

Also, in which case would the user be in a state of not having a uid?
Deleting the user account would not mean that I am doing it from the account itself (id assume this would be covered by the if (uid == currentUser()) above )

I deleted my secondary account a few weeks ago without any noticeable consequences to the main account. This is on the latest release on the 10 III.

One thing that might a differentiator is that I created the secondary probably over a year ago, and hadn’t logged into for a while, and definitely not since the bootup prior to deletion. Perhaps that might be something different from the session history the OP had before their catastrophe.

Did you happen to install android apps for it?

No I don’t think I did.

@nephros it still puzzles me, on linux a process just cannot not have a uid;
so how could that situation come up?
Perhaps if the process itself has gone out of existance; and something that is supposed to extract the uid from the running process instance fails to do so because of it / its a race condition? :frowning:

No, I didn’t say that a process can’t have an UID.

What I was thinking about was the codepath: if for some reason either of the parts failed to determine the UID, or got a wrong one, would that lead to error later in the codepath.
But I couldn’t find a case where that would lead to a malfunction.

1 Like

So to follow the reverse logic path, assuming something REMOVED the whole of defaultuser’s home (and there is more “if”'s attached to this keep on reading);

  • my home got nuked
  • removeHome must have been pointing to /home/defaultuser
  • the UID MUST have been the one of the default owner → the same as the
    OWNER_USER_UID or something that differs from that, but still maps to the default owner… (is this possible, I cannot say; id assume not?)

First question : Where is the uid coming from?

  • the removeUser is a slot as defined here; to which signal is that connected into?
  • then there is this guy which seems to define the actual current user…, which might raise concerns on the race condition again (if this method is called on the bus before the removal, patatrac); unfortunately its impossible to know from here who called it, as its a DBus call…any documentation available on origin of these types of DBus calls?

Alternative possibility : something yanks the defaultuser path leaving only /home as the final one, therefore everything is nuked (unlikely)

There is also this one that seems to remove some stuff…