Commit 2286da34 authored by jan.koester's avatar jan.koester
Browse files

test

parent 2a4b491f
Loading
Loading
Loading
Loading
+13 −0
Changes for src/sharectl.cpp: 13 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -37,6 +37,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <vector>

#include <pwd.h>
#if __has_include(<sys/uid_r.h>)
#include <sys/uid_r.h>
#endif
#include <shadow.h>
#include <termios.h>
#include <unistd.h>
@@ -345,12 +348,22 @@ int smbpasswd(int argc, char **argv) {
        std::cerr << "sharectl: smbpasswd needs root\n";
        return 1;
    }
#if __has_include(<sys/uid_r.h>)
    // also a UUID or GUID user (getpwnam() doesn't find those)
    struct passwd_id_r *account = getpwnam_id_r(user.c_str());
    if (!account) {
        std::cerr << "sharectl: no user " << user << "\n";
        return 1;
    }
    if (uid_r_is_legacy(&account->pw_uid) && account->pw_uid.legacy == 0) {
#else
    struct passwd *account = getpwnam(user.c_str());
    if (!account) {
        std::cerr << "sharectl: no user " << user << "\n";
        return 1;
    }
    if (account->pw_uid == 0) {
#endif
        std::cerr << "sharectl: root can't log in over SMB\n";
        return 1;
    }
+166 −17
Changes for src/smb.cpp: 166 added lines, 17 removed lines.
Original line number Diff line number Diff line
@@ -44,6 +44,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <sys/socket.h>
#include <unistd.h>

#if __has_include(<sys/uid_r.h>)
#include <sys/uid_r.h>  // systempp: UUID and GUID users
#define SHAREDD_UID_R 1
#endif

#include <linux/genetlink.h>
#include <linux/netlink.h>

@@ -125,11 +130,22 @@ struct LoginResponseMsg {
    uint32_t reserved[16];
};

// ksmbd_login_response_ext; flags/uid_r/gid_r were reserved before the
// kernel knew UUID and GUID users (FenrirOS uid_r)
struct IdR {
    uint32_t type;
    unsigned char id[16];
};
struct LoginResponseExtMsg {
    uint32_t handle;
    int32_t ngroups;
    char reserved[128];
    uint32_t flags;
    IdR uid_r;
    IdR gid_r;
    char reserved[84];
};
constexpr uint32_t LoginExtIdR = 0x1;      // uid_r/gid_r are the user's ids
constexpr uint32_t LoginExtGroupsR = 0x2;  // the payload is IdR groups, not uint32_t

struct ShareConfigRequestMsg {
    uint32_t handle;
@@ -286,6 +302,61 @@ struct passwd *lookupUser(const std::string &account) {
    return l != account ? getpwnam(l.c_str()) : nullptr;
}

// An SMB account: classic ids, or (FenrirOS uid_r) a UUID or GUID user or
// group, which only a kernel with the *_r calls -- and ksmbd's login
// extension for them -- can run as; elsewhere the classic overflow id
// would make it "nobody", so such an account isn't there.
struct Account {
    std::string name;
    uint32_t uid = 65534, gid = 65534;  // classic ids (the overflow id for others)
    bool idR = false;                   // the uid or gid is a UUID or GUID
#ifdef SHAREDD_UID_R
    uid_r_t ruid, rgid;
#endif
};

#ifdef SHAREDD_UID_R
bool kernelHasUidR() {
    static int have = -1;
    uid_r_t id;
    if (have < 0) have = getuid_r(&id) == 0;
    return have;
}

IdR toIdR(const uid_r_t &id) {
    IdR r;
    memset(&r, 0, sizeof(r));
    r.type = id.type;
    memcpy(r.id, &id.legacy, id.type == UID_R_LEGACY ? sizeof(id.legacy) : sizeof(r.id));
    return r;
}
#endif

bool lookupAccount(const std::string &account, Account &a) {
#ifdef SHAREDD_UID_R
    struct passwd_id_r *p = getpwnam_id_r(account.c_str());
    std::string l = lower(account);
    if (!p && l != account) p = getpwnam_id_r(l.c_str());
    if (!p) return false;
    a.name = p->pw_name;
    a.ruid = p->pw_uid;
    a.rgid = p->pw_gid;
    a.uid = uid_r_to_legacy(&p->pw_uid);
    a.gid = uid_r_to_legacy(&p->pw_gid);
    a.idR = !uid_r_is_legacy(&p->pw_uid) || !uid_r_is_legacy(&p->pw_gid);
    endpwent_id_r();
    return !a.idR || kernelHasUidR();
#else
    struct passwd *pw = lookupUser(account);
    if (!pw) return false;
    a.name = pw->pw_name;
    a.uid = pw->pw_uid;
    a.gid = pw->pw_gid;
    endpwent();
    return true;
#endif
}

bool locked(const char *name) {
    struct spwd *sp = getspnam(name);
    bool l = sp && sp->sp_pwdp && (sp->sp_pwdp[0] == '!' || sp->sp_pwdp[0] == '*');
@@ -304,9 +375,53 @@ std::vector<gid_t> groupsOf(const char *name, gid_t gid) {
    return groups;
}

#ifdef SHAREDD_UID_R
// all groups of an account (primary first), UUID and GUID ones too
std::vector<gid_r_t> groupsOfR(const Account &a) {
    std::vector<gid_r_t> groups{a.rgid};
    setgrent_id_r();
    while (struct group_id_r *gr = getgrent_id_r()) {
        bool member = false, have = false;
        for (char **m = gr->gr_mem; m && *m; ++m) member = member || a.name == *m;
        for (auto &g : groups) have = have || uid_r_eq(&g, &gr->gr_gid);
        if (member && !have) groups.push_back(gr->gr_gid);
    }
    endgrent_id_r();
    return groups;
}

bool anyIdR(const std::vector<gid_r_t> &groups) {
    for (auto &g : groups)
        if (!uid_r_is_legacy(&g)) return true;
    return false;
}
#endif

// whether user may use share (its user list; "@group" for groups)
bool userAllowed(const Share &share, const std::string &account) {
    if (share.users.empty()) return true;
#ifdef SHAREDD_UID_R
    Account a;
    if (!lookupAccount(account, a)) return false;
    std::vector<gid_r_t> groups;
    bool haveGroups = false;
    for (const std::string &u : share.users) {
        if (u[0] != '@') {
            if (lower(u) == lower(a.name)) return true;
            continue;
        }
        struct group_id_r *gr = getgrnam_id_r(u.c_str() + 1);
        if (!gr) continue;
        gid_r_t want = gr->gr_gid;
        if (!haveGroups) {
            groups = groupsOfR(a);
            haveGroups = true;
        }
        for (auto &g : groups)
            if (uid_r_eq(&g, &want)) return true;
    }
    return false;
#endif
    struct passwd *pw = lookupUser(account);
    if (!pw) return false;
    std::string name = pw->pw_name;
@@ -540,30 +655,36 @@ void SmbServer::login(const unsigned char *payload, size_t len) {
    resp.handle = req.handle;
    copy(resp.account, sizeof(resp.account), account);

    struct passwd *pw = lookupUser(account);
    Account acct;
    bool found = lookupAccount(account, acct);
    unsigned char hash[16];
    bool root = pw && pw->pw_uid == 0;  // never over SMB
    if (pw && !root && !locked(pw->pw_name) && shadow_get_nthash(pw->pw_name, hash) == 1) {
        std::string name = pw->pw_name;
        resp.uid = pw->pw_uid;
        resp.gid = pw->pw_gid;
    bool root = found && !acct.idR && acct.uid == 0;  // never over SMB
    if (found && !root && !locked(acct.name.c_str()) && shadow_get_nthash(acct.name.c_str(), hash) == 1) {
        resp.uid = acct.uid;
        resp.gid = acct.gid;
        resp.status = UserOk;
        resp.hash_sz = 16;
        memcpy(resp.hash, hash, 16);
        memset(hash, 0, sizeof(hash));
        if (groupsOf(name.c_str(), pw->pw_gid).size() > 1) resp.status |= UserExtension;
        copy(resp.account, sizeof(resp.account), name);
    } else if (!pw && _cfg.settings.mapToGuest == "bad_user") {
#ifdef SHAREDD_UID_R
        // a UUID or GUID user always needs the extension: its real ids
        if (acct.idR || groupsOfR(acct).size() > 1) resp.status |= UserExtension;
#else
        if (groupsOf(acct.name.c_str(), acct.gid).size() > 1) resp.status |= UserExtension;
#endif
        copy(resp.account, sizeof(resp.account), acct.name);
    } else if (!found && _cfg.settings.mapToGuest == "bad_user") {
        struct passwd *g = getpwnam(_cfg.settings.guestAccount.c_str());
        resp.uid = g ? g->pw_uid : 65534;
        resp.gid = g ? g->pw_gid : 65534;
        resp.status = UserOk | UserGuest;
        copy(resp.account, sizeof(resp.account), _cfg.settings.guestAccount);
    } else {
        resp.status = pw ? UserBadPassword : UserBadUser;
        resp.status = found ? UserBadPassword : UserBadUser;
        std::cerr << "sharedd: SMB: login as " << account << " refused ("
                  << (!pw ? "no such user" : root ? "root may not log in over SMB"
                      : locked(pw->pw_name) ? "account locked" : "no SMB password") << ")"
                  << (!found ? "no such user (or a UUID/GUID user and a kernel without uid_r)"
                      : root ? "root may not log in over SMB"
                      : locked(acct.name.c_str()) ? "account locked" : "no SMB password") << ")"
                  << std::endl;
    }
    endpwent();
@@ -574,22 +695,50 @@ void SmbServer::loginExt(const unsigned char *payload, size_t len) {
    if (len < sizeof(LoginRequestMsg)) return;
    LoginRequestMsg req;
    memcpy(&req, payload, sizeof(req));
    LoginResponseExtMsg resp;
    memset(&resp, 0, sizeof(resp));
    resp.handle = req.handle;
#ifdef SHAREDD_UID_R
    Account a;
    std::vector<gid_r_t> groups;
    if (lookupAccount(text(req.account, sizeof(req.account)), a)) {
        groups = groupsOfR(a);
        if (a.idR) {
            resp.flags |= LoginExtIdR;
            resp.uid_r = toIdR(a.ruid);
            resp.gid_r = toIdR(a.rgid);
        }
    }
    bool groupsR = anyIdR(groups);
    if (groupsR) resp.flags |= LoginExtGroupsR;
    size_t each = groupsR ? sizeof(IdR) : sizeof(uint32_t);
    resp.ngroups = (int32_t)groups.size();
    std::vector<unsigned char> out(sizeof(resp) + groups.size() * each, 0);
    memcpy(out.data(), &resp, sizeof(resp));
    for (size_t i = 0; i < groups.size(); ++i) {
        if (groupsR) {
            IdR g = toIdR(groups[i]);
            memcpy(out.data() + sizeof(resp) + i * each, &g, sizeof(g));
        } else {
            uint32_t g = groups[i].legacy;
            memcpy(out.data() + sizeof(resp) + i * each, &g, sizeof(g));
        }
    }
#else
    std::vector<gid_t> groups;
    if (struct passwd *pw = lookupUser(text(req.account, sizeof(req.account)))) {
        std::string name = pw->pw_name;
        groups = groupsOf(name.c_str(), pw->pw_gid);
    }
    endpwent();
    std::vector<unsigned char> out(sizeof(LoginResponseExtMsg) + groups.size() * sizeof(uint32_t), 0);
    LoginResponseExtMsg resp;
    memset(&resp, 0, sizeof(resp));
    resp.handle = req.handle;
    resp.ngroups = (int32_t)groups.size();
    std::vector<unsigned char> out(sizeof(resp) + groups.size() * sizeof(uint32_t), 0);
    memcpy(out.data(), &resp, sizeof(resp));
    for (size_t i = 0; i < groups.size(); ++i) {
        uint32_t g = groups[i];
        memcpy(out.data() + sizeof(resp) + i * sizeof(uint32_t), &g, sizeof(g));
    }
#endif
    send(LoginResponseExt, out.data(), out.size(), false);
}