Commit 747dfdbf authored by jan.koester's avatar jan.koester
Browse files

test

parent 49a0bed0
Loading
Loading
Loading
Loading
+11 −1
Changes for modules/shares/shares.cpp: 11 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -60,6 +60,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include <grp.h>
#include <pwd.h>
#if __has_include(<sys/uid_r.h>)
#include <sys/uid_r.h>
#endif
#include <shadow.h>

#include <sharedd/api.h>
@@ -236,10 +239,17 @@ json_object *settings(json_object *a) {

json_object *smbPassword(json_object *a) {
    std::string name = webctl::str(a, "name"), password = webctl::str(a, "password");
#if __has_include(<sys/uid_r.h>)
    // also a UUID or GUID user, which getpwnam() doesn't find
    struct passwd_id_r *pw = getpwnam_id_r(name.c_str());
    bool root = pw && uid_r_is_legacy(&pw->pw_uid) && pw->pw_uid.legacy == 0;
#else
    struct passwd *pw = getpwnam(name.c_str());
    bool root = pw && pw->pw_uid == 0;
#endif
    endpwent();
    if (!pw) return error(tr("no user %1", "kein Benutzer %1", {name}), 404);
    if (pw->pw_uid == 0) return error(tr("root can't log in over SMB", "root kann sich nicht über SMB anmelden"), 403);
    if (root) return error(tr("root can't log in over SMB", "root kann sich nicht über SMB anmelden"), 403);
    if (!password.empty() && password.size() < 8)
        return error(tr("the password needs at least %1 characters", "das Passwort braucht mindestens %1 Zeichen", {"8"}));
    if (lckpwdf() != 0)
+47 −8
Changes for modules/users/users.cpp: 47 added lines, 8 removed lines.
Original line number Diff line number Diff line
@@ -50,7 +50,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Users and groups with a UUID or GUID id (systempp's <sys/uid_r.h>) are
 * read and written with the *_id_r functions, so a rewrite keeps them;
 * "list" shows such an id as text. Accounts made here get classic ids. */
 * "list" shows such an id as text. Accounts made here get a new UUID as
 * uid and gid (their own group), when the kernel has the *_r calls; with
 * "uid" (or without them) a classic one. */

#include <algorithm>
#include <cerrno>
@@ -132,6 +134,28 @@ uid_r_t idOf(unsigned long id, const std::string &r) {
}
#endif

#ifdef WEBCTL_UID_R
// the kernel has the *_r calls: UUID users can run
bool kernelHasUidR() {
    static int have = -1;
    uid_r_t id;
    if (have < 0) have = getuid_r(&id) == 0;
    return have;
}
#endif

// fchownat() for a user's ids (a UUID or GUID one too)
int chownIds(int dir, const char *name, const std::string &uidR, uid_t uid, const std::string &gidR, gid_t gid,
             int flags) {
#ifdef WEBCTL_UID_R
    if (!uidR.empty() || !gidR.empty()) {
        uid_r_t u = idOf(uid, uidR), g = idOf(gid, gidR);
        return fchownat_r(dir, name, &u, &g, flags);
    }
#endif
    return fchownat(dir, name, uid, gid, flags);
}

// the owner of path (not following a symlink), in idKey() form
bool ownerKey(const std::string &path, std::string &key) {
#ifdef WEBCTL_UID_R
@@ -403,7 +427,7 @@ std::vector<std::string> shells(const Db &db) {

// ---- home directories: never following symlinks ----

bool copyTree(int src, int dst, uid_t uid, gid_t gid) {
bool copyTree(int src, int dst, const User &u) {
    DIR *d = fdopendir(dup(src));
    if (!d) return false;
    bool ok = true;
@@ -415,7 +439,7 @@ bool copyTree(int src, int dst, uid_t uid, gid_t gid) {
            if (mkdirat(dst, e->d_name, st.st_mode & 07777) != 0) { ok = false; continue; }
            int s = openat(src, e->d_name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
            int t = openat(dst, e->d_name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
            ok = s >= 0 && t >= 0 && copyTree(s, t, uid, gid) && ok;
            ok = s >= 0 && t >= 0 && copyTree(s, t, u) && ok;
            if (s >= 0) close(s);
            if (t >= 0) close(t);
        } else if (S_ISREG(st.st_mode)) {
@@ -437,7 +461,7 @@ bool copyTree(int src, int dst, uid_t uid, gid_t gid) {
        } else {
            continue;  // no devices or fifos from a skeleton
        }
        if (fchownat(dst, e->d_name, uid, gid, AT_SYMLINK_NOFOLLOW) != 0) ok = false;
        if (chownIds(dst, e->d_name, u.uidR, u.uid, u.gidR, u.gid, AT_SYMLINK_NOFOLLOW) != 0) ok = false;
    }
    closedir(d);
    return ok;
@@ -452,10 +476,10 @@ json_object *createHome(const User &u) {
    }
    int home = open(u.dir.c_str(), O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
    if (home < 0) return error(u.dir + ": " + strerror(errno), 500);
    bool ok = fchown(home, u.uid, u.gid) == 0;
    bool ok = chownIds(home, "", u.uidR, u.uid, u.gidR, u.gid, AT_EMPTY_PATH) == 0;
    int skel = open("/etc/skel", O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
    if (skel >= 0) {
        ok = copyTree(skel, home, u.uid, u.gid) && ok;
        ok = copyTree(skel, home, u) && ok;
        close(skel);
    }
    close(home);
@@ -609,6 +633,15 @@ json_object *create(json_object *a) {
        for (auto &u : db.users)
            if (u.uidR.empty() && u.uid == (uid_t)uidArg) return error(tr("uid %1 belongs to %2", "UID %1 gehört bereits %2", {std::to_string(uidArg), u.name}), 409);
    uid_t uid = uidArg != -1 ? (uid_t)uidArg : freeId(db, false, FirstUid);
    std::string uidR;  // a new UUID user (the default where the kernel can run one)
#ifdef WEBCTL_UID_R
    if (uidArg == -1 && kernelHasUidR()) {
        uid_r_t id;
        if (uid_r_uuidgen(&id) != 0) return error(tr("no UUID for the user", "keine UUID für den Benutzer"), 500);
        uidR = idText(id);
        uid = UID_R_OVERFLOW;
    }
#endif

    gid_t gid;
    std::string gidR;
@@ -620,11 +653,17 @@ json_object *create(json_object *a) {
        gidR = g->gidR;
    } else {
        if (db.group(name)) return error(tr("a group %1 exists already; choose it as the primary group", "eine Gruppe %1 gibt es schon; bitte als primäre Gruppe auswählen", {name}), 409);
        if (!uidR.empty()) {
            // its own group has the user's UUID, as with useradd --uuid
            gid = UID_R_OVERFLOW;
            gidR = uidR;
        } else {
            gid = freeId(db, true, uid);
        db.groups.push_back({name, "x", gid, {}, ""});
        }
        db.groups.push_back({name, "x", gid, {}, gidR});
    }

    User u{name, "x", gecos, home, shell, uid, gid, "", gidR};
    User u{name, "x", gecos, home, shell, uid, gid, uidR, gidR};
    db.users.push_back(u);
    db.shadow.push_back({name, "!", today(), 0, 99999, 7, -1, -1, 0});
    if (json_object *e = setMemberships(db, name, groups)) return e;
+2 −2
Changes for www/modules/users.js: 2 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@
    'No groups.': 'Keine Gruppen.', 'No users.': 'Keine Benutzer.',
    'New user': 'Neuer Benutzer', 'Create': 'Anlegen', 'User name': 'Benutzername',
    'Lower case letters, digits, _ and -': 'Kleinbuchstaben, Ziffern, _ und -',
    'UID (optional)': 'UID (optional)', 'GID (optional)': 'GID (optional)', 'Full name': 'Voller Name',
    'UID (optional)': 'UID (optional)', 'empty: a new UUID': 'leer: eine neue UUID', 'GID (optional)': 'GID (optional)', 'Full name': 'Voller Name',
    'Without a password the account stays locked until one is set.':
      'Ohne Passwort bleibt das Konto gesperrt, bis eines gesetzt ist.',
    'Primary group': 'Primäre Gruppe', 'create its own group': 'eigene Gruppe anlegen', 'Shell': 'Shell',
@@ -91,7 +91,7 @@
        h('div', {class: 'row'},
          h('label', {}, t('User name'), h('input', {name: 'name', required: true, pattern: '[a-z_][a-z0-9_\\-]{0,31}',
                                                       autocomplete: 'off', title: t('Lower case letters, digits, _ and -')})),
          h('label', {}, t('UID (optional)'), h('input', {name: 'uid', type: 'number', min: 1, max: 60000}))),
          h('label', {}, t('UID (optional)'), h('input', {name: 'uid', type: 'number', min: 1, max: 60000, placeholder: t('empty: a new UUID')}))),
        h('label', {}, t('Full name'), h('input', {name: 'gecos', autocomplete: 'off'})),
        ...passwordFields(false),
        ...smbField(null),