Skip to content

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also .

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also .
...
  • 3 commits
  • 2 files changed
  • 0 commit comments
  • 1 contributor
Showing with 170 additions and 53 deletions.
  1. +151 −46 modules/saslauth.cpp
  2. +19 −7 src/Client.cpp
View
197 modules/saslauth.cpp
@@ -13,36 +13,52 @@
#include <znc/Modules.h>
#include <znc/znc.h>
+#include <znc/User.h>
+
#include <sasl/sasl.h>
class CSASLAuthMod : public CModule {
public:
MODCONSTRUCTOR(CSASLAuthMod) {
m_Cache.SetTTL(60000/*ms*/);
+
+ AddHelpCommand();
+ AddCommand("CreateUser", static_cast<CModCommand::ModCmdFunc>(&CSASLAuthMod::CreateUser),
+ "[yes|no]");
+ AddCommand("CloneUser", static_cast<CModCommand::ModCmdFunc>(&CSASLAuthMod::CloneUser),
+ "[username]");
+ AddCommand("DisableCloneUser", static_cast<CModCommand::ModCmdFunc>(&CSASLAuthMod::DisableCloneUser));
}
- virtual ~CSASLAuthMod() {}
- virtual bool OnBoot() {
- return true;
+ virtual ~CSASLAuthMod() {
+ sasl_done();
+ }
+
+ void OnModCommand(const CString& sCommand) {
+ if (m_pUser->IsAdmin()) {
+ HandleCommand(sCommand);
+ } else {
+ PutModule("Access denied");
+ }
}
virtual bool OnLoad(const CString& sArgs, CString& sMessage) {
- VCString vsChans;
+ VCString vsArgs;
VCString::const_iterator it;
- sArgs.Split(" ", vsChans, false);
+ sArgs.Split(" ", vsArgs, false);
- for (it = vsChans.begin(); it != vsChans.end(); ++it) {
- if (it->StrCmp("saslauthd") || it->StrCmp("auxprop")) {
- method += *it + " ";
- }
- else {
+ for (it = vsArgs.begin(); it != vsArgs.end(); ++it) {
+ if (it->Equals("saslauthd") || it->Equals("auxprop")) {
+ m_sMethod += *it + " ";
+ } else {
CUtils::PrintError("Ignoring invalid SASL pwcheck method: " + *it);
sMessage = "Ignored invalid SASL pwcheck method";
}
}
- method.TrimRight();
- if (method.empty()) {
+ m_sMethod.TrimRight();
+
+ if (m_sMethod.empty()) {
sMessage = "Need a pwcheck method as argument (saslauthd, auxprop)";
return false;
}
@@ -52,60 +68,149 @@ class CSASLAuthMod : public CModule {
return false;
}
+ m_cbs[0].id = SASL_CB_GETOPT;
+ m_cbs[0].proc = reinterpret_cast<int(*)()>(CSASLAuthMod::getopt);
+ m_cbs[0].context = this;
+ m_cbs[1].id = SASL_CB_LIST_END;
+ m_cbs[1].proc = NULL;
+ m_cbs[1].context = NULL;
+
return true;
}
virtual EModRet OnLoginAttempt(CSmartPtr<CAuthBase> Auth) {
- CString const user(Auth->GetUsername());
- CString const pass(Auth->GetPassword());
- CUser* pUser(CZNC::Get().FindUser(user));
- sasl_conn_t *sasl_conn(0);
-
- if (!pUser) { // @todo Will want to do some sort of && !m_bAllowCreate in the future
- Auth->RefuseLogin("Invalid User - Halting SASL Authentication");
- return HALT;
+ const CString& sUsername = Auth->GetUsername();
+ const CString& sPassword = Auth->GetPassword();
+ CUser *pUser(CZNC::Get().FindUser(sUsername));
+ sasl_conn_t *sasl_conn(NULL);
+ bool bSuccess = false;
+
+ if (!pUser && !CreateUser()) {
+ return CONTINUE;
}
- CString const key(CString(user + ":" + pass).MD5());
- if (m_Cache.HasItem(key)) {
- Auth->AcceptLogin(*pUser);
- DEBUG("+++ Found in cache");
+ const CString sCacheKey(CString(sUsername + ":" + sPassword).MD5());
+ if (m_Cache.HasItem(sCacheKey)) {
+ bSuccess = true;
+ DEBUG("saslauth: Found [" + sUsername + "] in cache");
+ } else if (sasl_server_new("znc", NULL, NULL, NULL, NULL, m_cbs, 0, &sasl_conn) == SASL_OK &&
+ sasl_checkpass(sasl_conn, sUsername.c_str(), sUsername.size(), sPassword.c_str(), sPassword.size()) == SASL_OK) {
+ m_Cache.AddItem(sCacheKey);
+
+ DEBUG("saslauth: Successful SASL authentication [" + sUsername + "]");
+
+ bSuccess = true;
}
- else if (sasl_server_new("znc", NULL, NULL, NULL, NULL, cbs, 0, &sasl_conn) == SASL_OK &&
- sasl_checkpass(sasl_conn, user.c_str(), user.size(), pass.c_str(), pass.size()) == SASL_OK) {
- Auth->AcceptLogin(*pUser);
- m_Cache.AddItem(key);
- DEBUG("+++ Successful SASL password check");
+
+ sasl_dispose(&sasl_conn);
+
+ if (bSuccess) {
+ if (!pUser) {
+ CString sErr;
+ pUser = new CUser(sUsername);
+
+ if (ShouldCloneUser()) {
+ CUser *pBaseUser = CZNC::Get().FindUser(CloneUser());
+
+ if (!pBaseUser) {
+ DEBUG("saslauth: Clone User [" << CloneUser() << "] User not found");
+ delete pUser;
+ pUser = NULL;
+ }
+
+ if (pUser && !pUser->Clone(*pBaseUser, sErr)) {
+ DEBUG("saslauth: Clone User [" << CloneUser() << "] " << sErr);
+ delete pUser;
+ pUser = NULL;
+ }
+ }
+
+ if (pUser && !CZNC::Get().AddUser(pUser, sErr)) {
+ DEBUG("saslauth: Add user [" << sUsername << "] " << sErr);
+ delete pUser;
+ pUser = NULL;
+ }
+
+ if (pUser) {
+ pUser->SetPass("::", CUser::HASH_MD5, "::");
+ }
+ }
+
+ if (pUser) {
+ Auth->AcceptLogin(*pUser);
+ return HALT;
+ }
}
- else {
- Auth->RefuseLogin("SASL Authentication failed");
- DEBUG("--- FAILED SASL password check");
+
+ return CONTINUE;
+ }
+
+ const CString& GetMethod() const { return m_sMethod; }
+
+ void CreateUser(const CString &sLine) {
+ CString sCreate = sLine.Token(1);
+
+ if (!sCreate.empty()) {
+ SetNV("CreateUser", sCreate);
}
- sasl_dispose(&sasl_conn);
- return HALT;
+ if (CreateUser()) {
+ PutModule("We will create users on their first login");
+ } else {
+ PutModule("We will not create users on their first login");
+ }
+ }
+
+ void CloneUser(const CString &sLine) {
+ CString sUsername = sLine.Token(1);
+
+ if (!sUsername.empty()) {
+ SetNV("CloneUser", sUsername);
+ }
+
+ if (ShouldCloneUser()) {
+ PutModule("We will clone [" + CloneUser() + "]");
+ } else {
+ PutModule("We will not clone a user");
+ }
+ }
+
+ void DisableCloneUser(const CString &sLine) {
+ DelNV("CloneUser");
+ PutModule("Clone user disabled");
+ }
+
+ bool CreateUser() const {
+ return GetNV("CreateUser").ToBool();
+ }
+
+ CString CloneUser() const {
+ return GetNV("CloneUser");
}
-private:
+ bool ShouldCloneUser() {
+ return !GetNV("CloneUser").empty();
+ }
+
+protected:
TCacheMap<CString> m_Cache;
- static sasl_callback_t cbs[];
- static CString method;
+ sasl_callback_t m_cbs[2];
+ CString m_sMethod;
- static int getopt(void *context, const char *plugin_name, const char *option, const char **result, unsigned *len) {
- if (!method.empty() && strcmp(option, "pwcheck_method") == 0) {
- *result = method.c_str();
+ static int getopt(void *context, const char *plugin_name,
+ const char *option, const char **result, unsigned *len) {
+ if (CString(option).Equals("pwcheck_method")) {
+ *result = ((CSASLAuthMod*)context)->GetMethod().c_str();
return SASL_OK;
}
+
return SASL_CONTINUE;
}
};
-sasl_callback_t CSASLAuthMod::cbs[] = {
- { SASL_CB_GETOPT, reinterpret_cast<int(*)()>(CSASLAuthMod::getopt), NULL },
- { SASL_CB_LIST_END, NULL, NULL }
-};
-
-CString CSASLAuthMod::method;
+template<> void TModInfo<CSASLAuthMod>(CModInfo& Info) {
+ Info.SetWikiPage("saslauth");
+}
GLOBALMODULEDEFS(CSASLAuthMod, "Allow users to authenticate via SASL password verification method")
View
26 src/Client.cpp
@@ -410,20 +410,32 @@ void CClient::ReadLine(const CString& sData) {
sLine += " " + sKey;
}
} else if (sCommand.Equals("PART")) {
- CString sChan = sLine.Token(1).TrimPrefix_n();
+ CString sChans = sLine.Token(1).TrimPrefix_n();
CString sMessage = sLine.Token(2, true).TrimPrefix_n();
- NETWORKMODULECALL(OnUserPart(sChan, sMessage), m_pUser, m_pNetwork, this, return);
+ VCString vChans;
+ sChans.Split(",", vChans, false);
+ sChans.clear();
+
+ for (VCString::const_iterator it = vChans.begin(); it != vChans.end(); ++it) {
+ CString sChan = *it;
+ NETWORKMODULECALL(OnUserPart(sChan, sMessage), m_pUser, m_pNetwork, this, continue);
- CChan* pChan = m_pNetwork->FindChan(sChan);
+ CChan* pChan = m_pNetwork->FindChan(sChan);
- if (pChan && !pChan->IsOn()) {
- PutStatusNotice("Removing channel [" + sChan + "]");
- m_pNetwork->DelChan(sChan);
+ if (pChan && !pChan->IsOn()) {
+ PutStatusNotice("Removing channel [" + sChan + "]");
+ m_pNetwork->DelChan(sChan);
+ } else {
+ sChans += (sChans.empty()) ? sChan : CString("," + sChan);
+ }
+ }
+
+ if (sChans.empty()) {
return;
}
- sLine = "PART " + sChan;
+ sLine = "PART " + sChans;
if (!sMessage.empty()) {
sLine += " :" + sMessage;

No commit comments for this range

Something went wrong with that request. Please try again.