Loading src/sharectl.cpp +13 −0 Changes for src/sharectl.cpp: 13 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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; } Loading src/smb.cpp +166 −17 Changes for src/smb.cpp: 166 added lines, 17 removed lines. Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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; Loading Loading @@ -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] == '*'); Loading @@ -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; Loading Loading @@ -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(); Loading @@ -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); } Loading Loading
src/sharectl.cpp +13 −0 Changes for src/sharectl.cpp: 13 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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; } Loading
src/smb.cpp +166 −17 Changes for src/smb.cpp: 166 added lines, 17 removed lines. Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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; Loading Loading @@ -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] == '*'); Loading @@ -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; Loading Loading @@ -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(); Loading @@ -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); } Loading