diff --git a/include/znc/Listener.h b/include/znc/Listener.h index 383547a2..dcb0fc9d 100644 --- a/include/znc/Listener.h +++ b/include/znc/Listener.h @@ -97,9 +97,7 @@ class CTCPListener : public CListener { class CUnixListener : public CListener { public: CUnixListener(const CString& sPath, const CString& sURIPrefix, bool bSSL, - EAcceptType eAccept) - : CListener(sURIPrefix, bSSL, eAccept), - m_sPath(sPath) {} + EAcceptType eAccept, const CString& sGid, const CString& sMode); ~CUnixListener(); CUnixListener(const CUnixListener&) = delete; @@ -107,6 +105,8 @@ class CUnixListener : public CListener { // Getters const CString& GetPath() const { return m_sPath; } + const CString& GetGroup() const { return m_sGid; } + CString GetMode() const; // !Getters bool Listen() override; @@ -114,6 +114,8 @@ class CUnixListener : public CListener { protected: CString m_sPath; + CString m_sGid; + int m_iMode; }; class CRealListener : public CZNCSock { diff --git a/include/znc/znc.h b/include/znc/znc.h index 1365d518..6e7f0f95 100644 --- a/include/znc/znc.h +++ b/include/znc/znc.h @@ -209,8 +209,9 @@ class CZNC : private CCoreTranslationMixin { bool AddTCPListener(unsigned short uPort, const CString& sBindHost, const CString& sURIPrefix, bool bSSL, EAddrType eAddr, CListener::EAcceptType eAccept, CString& sError); - bool AddUnixListener(const CString& sPath, const CString& sURIPrefix, bool bSSL, - CListener::EAcceptType eAccept, CString& sError); + bool AddUnixListener(const CString& sPath, const CString& sURIPrefix, + bool bSSL, CListener::EAcceptType eAccept, + const CString& sGroup, const CString& sMode, CString& sError); bool DelListener(CListener*); // For backwards-compatibility TODO: Remove diff --git a/modules/data/webadmin/tmpl/settings.tmpl b/modules/data/webadmin/tmpl/settings.tmpl index 4822cd7d..1443dd96 100644 --- a/modules/data/webadmin/tmpl/settings.tmpl +++ b/modules/data/webadmin/tmpl/settings.tmpl @@ -21,10 +21,9 @@ - - + + - @@ -33,9 +32,6 @@
checked="checked"/>
- - unix: -
checked="checked"/>
@@ -51,14 +47,10 @@
- - - - "/>
@@ -66,7 +58,8 @@ - + +
@@ -82,11 +75,58 @@ "/>
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + diff --git a/modules/webadmin.cpp b/modules/webadmin.cpp index feacc452..0b724138 100644 --- a/modules/webadmin.cpp +++ b/modules/webadmin.cpp @@ -1910,8 +1910,10 @@ class CWebAdminMod : public CModule { eAddr, eAccept, sMessage); } else { CString sPath = WebSock.GetParam("path"); + CString sMode = WebSock.GetParam("mode"); + CString sGroup = WebSock.GetParam("group"); bResult = CZNC::Get().AddUnixListener(sPath, sURIPrefix, bSSL, - eAccept, sMessage); + eAccept, sGroup, sMode, sMessage); } if (bResult) { @@ -2030,6 +2032,8 @@ class CWebAdminMod : public CModule { dynamic_cast(pListener)) { l["Type"] = "Unix"; l["Path"] = pUnixListener->GetPath(); + l["Mode"] = pUnixListener->GetMode(); + l["Group"] = pUnixListener->GetGroup(); // We can't determine whether it's the same port, as it's // always "localhost". Just assume the user knows what he's // doing. Unix sockets are advanced topic anyway. diff --git a/src/ClientCommand.cpp b/src/ClientCommand.cpp index 8ccbd963..b372d5db 100644 --- a/src/ClientCommand.cpp +++ b/src/ClientCommand.cpp @@ -1641,29 +1641,39 @@ void CClient::UserPortCommand(CString& sLine) { const CString sCommand = sLine.Token(0); if (sCommand.Equals("LISTPORTS")) { - CTable Table; - Table.AddColumn(t_s("Port", "listports")); - Table.AddColumn(t_s("BindHost", "listports")); - Table.AddColumn(t_s("SSL", "listports")); - Table.AddColumn(t_s("Protocol", "listports")); - Table.AddColumn(t_s("IRC", "listports")); - Table.AddColumn(t_s("Web", "listports")); + CTable TableT; + TableT.AddColumn(t_s("Port", "listports")); + TableT.AddColumn(t_s("BindHost", "listports")); + TableT.AddColumn(t_s("Protocol", "listports")); + TableT.AddColumn(t_s("SSL", "listports")); + TableT.AddColumn(t_s("IRC", "listports")); + TableT.AddColumn(t_s("Web", "listports")); + + CTable TableU; + TableU.AddColumn(t_s("Path", "listports")); + TableU.AddColumn(t_s("Mode", "listports")); + TableU.AddColumn(t_s("Group", "listports")); + TableU.AddColumn(t_s("SSL", "listports")); + TableU.AddColumn(t_s("IRC", "listports")); + TableU.AddColumn(t_s("Web", "listports")); const vector& vpListeners = CZNC::Get().GetListeners(); for (const CListener* pListener : vpListeners) { - Table.AddRow(); + CTable* pTable; if (const CTCPListener* pTCPListener = dynamic_cast(pListener)) { - Table.SetCell(t_s("Port", "listports"), - CString(pTCPListener->GetPort())); - Table.SetCell(t_s("BindHost", "listports"), - (pTCPListener->GetBindHost().empty() - ? CString("*") - : pTCPListener->GetBindHost())); + TableT.AddRow(); + pTable = &TableT; + TableT.SetCell(t_s("Port", "listports"), + CString(pTCPListener->GetPort())); + TableT.SetCell(t_s("BindHost", "listports"), + (pTCPListener->GetBindHost().empty() + ? CString("*") + : pTCPListener->GetBindHost())); EAddrType eAddr = pTCPListener->GetAddrType(); - Table.SetCell( + TableT.SetCell( t_s("Protocol", "listports"), eAddr == ADDR_ALL ? t_s("IPv4 and IPv6", "listports") @@ -1671,28 +1681,37 @@ void CClient::UserPortCommand(CString& sLine) { : t_s("IPv6", "listports"))); } else if (const CUnixListener* pUnixListener = dynamic_cast(pListener)) { - Table.SetCell(t_s("Port", "listports"), - pUnixListener->GetPath()); + TableU.AddRow(); + pTable = &TableU; + TableU.SetCell(t_s("Path", "listports"), + pUnixListener->GetPath()); + TableU.SetCell(t_s("Mode", "listports"), + pUnixListener->GetMode()); + TableU.SetCell(t_s("Group", "listports"), + pUnixListener->GetGroup()); + } else { + continue; } - Table.SetCell(t_s("SSL", "listports"), - pListener->IsSSL() ? t_s("yes", "listports|ssl") - : t_s("no", "listports|ssl")); + pTable->SetCell(t_s("SSL", "listports"), + pListener->IsSSL() ? t_s("yes", "listports|ssl") + : t_s("no", "listports|ssl")); CListener::EAcceptType eAccept = pListener->GetAcceptType(); - Table.SetCell(t_s("IRC", "listports"), - eAccept == CListener::ACCEPT_ALL || - eAccept == CListener::ACCEPT_IRC - ? t_s("yes", "listports|irc") - : t_s("no", "listports|irc")); - Table.SetCell(t_s("Web", "listports"), - eAccept == CListener::ACCEPT_ALL || - eAccept == CListener::ACCEPT_HTTP - ? t_f("yes, on {1}", "listports|irc")( - pListener->GetURIPrefix() + "/") - : t_s("no", "listports|web")); + pTable->SetCell(t_s("IRC", "listports"), + eAccept == CListener::ACCEPT_ALL || + eAccept == CListener::ACCEPT_IRC + ? t_s("yes", "listports|irc") + : t_s("no", "listports|irc")); + pTable->SetCell(t_s("Web", "listports"), + eAccept == CListener::ACCEPT_ALL || + eAccept == CListener::ACCEPT_HTTP + ? t_f("yes, on {1}", "listports|irc")( + pListener->GetURIPrefix() + "/") + : t_s("no", "listports|web")); } - PutStatus(Table); + PutStatus(TableT); + PutStatus(TableU); return; } @@ -1732,12 +1751,30 @@ void CClient::UserPortCommand(CString& sLine) { std::unique_ptr pListener; if (sPort.TrimPrefix("unix:")) { - bool bSSL = sPort.TrimPrefix("ssl:"); + bool bSSL = false; + CString sMode; + CString sGroup; + if (auto colon = sPort.find_first_of(':'); colon != std::string::npos) { + VCString vsOpts; + CString(sPort.substr(0, colon)).Split(",", vsOpts, false); + for (CString& sOpt : vsOpts) { + if (sOpt == "ssl") { + bSSL = true; + } else if (sOpt.TrimPrefix("mode=")) { + sMode = sOpt; + } else if (sOpt.TrimPrefix("group=")) { + sGroup = sOpt; + } else { + throw PortCommandUsage{}; + } + } + sPort = sPort.substr(colon + 1); + } const CString& sPath = sPort; CListener::EAcceptType eAccept = ParseEAccept(sLine.Token(2)); CString sURIPrefix = sLine.Token(3); - pListener.reset(new CUnixListener(sPath, sURIPrefix, bSSL, eAccept)); + pListener.reset(new CUnixListener(sPath, sURIPrefix, bSSL, eAccept, sGroup, sMode)); } else { bool bSSL = sPort.StartsWith("+"); EAddrType eAddr = ParseEAddr(sLine.Token(2)); @@ -1765,7 +1802,7 @@ void CClient::UserPortCommand(CString& sLine) { "[bindhost [uriprefix]]")); PutStatus(t_s("+ means SSL")); PutStatus( - t_s("Or: AddPort unix:[ssl:]/path/to/socket " + t_s("Or: AddPort unix:[ssl,mode=NNN,group=foo]:/path/to/socket " "[uriprefix]")); } } else if (sCommand.Equals("DELPORT")) { diff --git a/src/Listener.cpp b/src/Listener.cpp index bc763fbf..0b9dd187 100644 --- a/src/Listener.cpp +++ b/src/Listener.cpp @@ -14,6 +14,12 @@ * limitations under the License. */ +#include +#include +#include +#include +#include + #include #include #include @@ -85,9 +91,21 @@ CConfig CTCPListener::ToConfig() const { return listenerConfig; } -CUnixListener::~CUnixListener() { +CUnixListener::CUnixListener(const CString& sPath, const CString& sURIPrefix, + bool bSSL, EAcceptType eAccept, + const CString& sGid, const CString& sMode) + : CListener(sURIPrefix, bSSL, eAccept), + m_sPath(sPath), + m_sGid(sGid), + m_iMode(-1) { + if (!sMode.empty()) { + std::istringstream s(sMode); + s >> std::oct >> m_iMode; + } } +CUnixListener::~CUnixListener() {} + bool CUnixListener::Listen() { if (m_pListener) { errno = EINVAL; @@ -97,18 +115,85 @@ bool CUnixListener::Listen() { m_pListener = new CRealListener(*this); SetupSSL(); - return CZNC::Get().GetManager().ListenUnix("UNIX_LISTENER", m_sPath, - m_pListener); + if (!CZNC::Get().GetManager().ListenUnix("UNIX_LISTENER", m_sPath, + m_pListener)) + return false; + + if (!m_sGid.empty()) { + bool bSuccess = [&]() -> bool { + std::vector buffer(100); + group gr{}; + group* result; + retrysize: + int err = getgrnam_r(m_sGid.c_str(), &gr, buffer.data(), + buffer.size(), &result); + switch (err) { + case ERANGE: { + if (buffer.size() > 10000) { + DEBUG("Can't get gid due to memory size"); + return false; + } + buffer.resize(buffer.size() + 100); + goto retrysize; + } + case 0: { + if (!result) { + DEBUG("Group not found"); + return false; + } + if (chown(m_sPath.c_str(), -1, result->gr_gid)) { + char* e = strerror(errno); + DEBUG("Can't chmod: " << e); + return false; + } + break; + } + default: { + char* e = strerror(err); + DEBUG("Error while getting gid " << e); + return false; + } + } + return true; + }(); + if (!bSuccess) { + m_pListener->Close(); + return false; + } + } + + if (m_iMode != -1) { + if (chmod(m_sPath.c_str(), m_iMode)) { + char* e = strerror(errno); + DEBUG("Error while chmod " << e); + m_pListener->Close(); + return false; + } + } + + return true; } CConfig CUnixListener::ToConfig() const { CConfig listenerConfig = CListener::ToConfig(); listenerConfig.AddKeyValuePair("Path", GetPath()); + if (!m_sGid.empty()) listenerConfig.AddKeyValuePair("Group", m_sGid); + if (m_iMode != -1) { + listenerConfig.AddKeyValuePair("Mode", GetMode()); + } return listenerConfig; } +CString CUnixListener::GetMode() const { + std::ostringstream s; + if (m_iMode != -1) { + s << std::oct << m_iMode; + } + return s.str(); +} + void CListener::ResetRealListener() { m_pListener = nullptr; } CRealListener::~CRealListener() { m_Listener.ResetRealListener(); } diff --git a/src/znc.cpp b/src/znc.cpp index 2b2888a5..a0c9edc9 100644 --- a/src/znc.cpp +++ b/src/znc.cpp @@ -1744,13 +1744,16 @@ bool CZNC::AddTCPListener(unsigned short uPort, const CString& sBindHost, bool CZNC::AddUnixListener(const CString& sPath, const CString& sURIPrefix, bool bSSL, CListener::EAcceptType eAccept, - CString& sError) { - CUtils::PrintAction("Binding to path [" + sPath + "]" + (bSSL ? " with SSL" : "")); + const CString& sGroup, const CString& sMode, CString& sError) { + CUtils::PrintAction("Binding to path [" + sPath + "]" + + (bSSL ? " with SSL" : "") + + (sGroup.empty() ? CString() : " gid=" + sGroup) + + (sMode.empty() ? CString() : " mode=" + sMode)); if (!CheckSslAndPemFile(bSSL, sError)) return false; CListener* pListener = - new CUnixListener(sPath, sURIPrefix, bSSL, eAccept); + new CUnixListener(sPath, sURIPrefix, bSSL, eAccept, sGroup, sMode); return FinishAddingListener(pListener, sError); } @@ -1841,8 +1844,13 @@ bool CZNC::AddListener(CConfig* pConfig, CString& sError) { return AddTCPListener(uPort, sBindHost, sURIPrefix, bSSL, eAddr, eAccept, sError); + } else { + CString sGroup; + CString sMode; + pConfig->FindStringEntry("group", sGroup); + pConfig->FindStringEntry("mode", sMode); + return AddUnixListener(sPath, sURIPrefix, bSSL, eAccept, sGroup, sMode, sError); } - return AddUnixListener(sPath, sURIPrefix, bSSL, eAccept, sError); } bool CZNC::AddListener(CListener* pListener) {
unix: +
checked="checked"/>
+
+
checked="checked"/>
+
+
checked="checked"/>
+
+
+ + + + "/> +
+
unix:unix:"/>