Skip to content
Fetching contributors…
Cannot retrieve contributors at this time
289 lines (229 sloc) 6.98 KB
/*
* Copyright (C) 2004-2012 See the AUTHORS file for details.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#define REQUIRESSL
#include <znc/Modules.h>
#include <znc/User.h>
#include <znc/Listener.h>
#include <znc/znc.h>
using std::map;
using std::vector;
using std::set;
using std::pair;
class CSSLClientCertMod : public CModule {
public:
MODCONSTRUCTOR(CSSLClientCertMod) {
AddHelpCommand();
AddCommand("Add", static_cast<CModCommand::ModCmdFunc>(&CSSLClientCertMod::HandleAddCommand),
"[pubkey]", "If pubkey is not provided will use the current key");
AddCommand("Del", static_cast<CModCommand::ModCmdFunc>(&CSSLClientCertMod::HandleDelCommand),
"id");
AddCommand("List", static_cast<CModCommand::ModCmdFunc>(&CSSLClientCertMod::HandleListCommand));
AddCommand("Show", static_cast<CModCommand::ModCmdFunc>(&CSSLClientCertMod::HandleShowCommand),
"", "Print your current key");
}
virtual ~CSSLClientCertMod() {}
virtual bool OnBoot() {
const vector<CListener*>& vListeners = CZNC::Get().GetListeners();
vector<CListener*>::const_iterator it;
// We need the SSL_VERIFY_PEER flag on all listeners, or else
// the client doesn't send a ssl cert
for (it = vListeners.begin(); it != vListeners.end(); it++)
(*it)->GetRealListener()->SetRequireClientCertFlags(SSL_VERIFY_PEER);
MCString::iterator it1;
for (it1 = BeginNV(); it1 != EndNV(); it1++) {
VCString vsKeys;
VCString::iterator it2;
if (CZNC::Get().FindUser(it1->first) == NULL) {
DEBUG("Unknown user in saved data [" + it1->first + "]");
continue;
}
it1->second.Split(" ", vsKeys, false);
for (it2 = vsKeys.begin(); it2 != vsKeys.end(); it2++) {
m_PubKeys[it1->first].insert(*it2);
}
}
return true;
}
virtual void OnPostRehash() {
OnBoot();
}
virtual bool OnLoad(const CString& sArgs, CString& sMessage) {
OnBoot();
return true;
}
bool Save() {
MSCString::iterator it;
ClearNV(false);
for (it = m_PubKeys.begin(); it != m_PubKeys.end(); it++) {
CString sVal;
SCString::iterator it2;
for (it2 = it->second.begin(); it2 != it->second.end(); it2++) {
sVal += *it2 + " ";
}
if (!sVal.empty())
SetNV(it->first, sVal, false);
}
return SaveRegistry();
}
bool AddKey(CUser *pUser, CString sKey) {
pair<SCString::iterator, bool> pair = m_PubKeys[pUser->GetUserName()].insert(sKey);
if (pair.second) {
Save();
}
return pair.second;
}
virtual EModRet OnLoginAttempt(CSmartPtr<CAuthBase> Auth) {
CString sUser = Auth->GetUsername();
Csock *pSock = Auth->GetSocket();
CUser *pUser = CZNC::Get().FindUser(sUser);
if (pSock == NULL || pUser == NULL)
return CONTINUE;
CString sPubKey = GetKey(pSock);
DEBUG("User: " << sUser << " Key: " << sPubKey);
if (sPubKey.empty()) {
DEBUG("Peer got no public key, ignoring");
return CONTINUE;
}
MSCString::iterator it = m_PubKeys.find(sUser);
if (it == m_PubKeys.end()) {
DEBUG("No saved pubkeys for this client");
return CONTINUE;
}
SCString::iterator it2 = it->second.find(sPubKey);
if (it2 == it->second.end()) {
DEBUG("Invalid pubkey");
return CONTINUE;
}
// This client uses a valid pubkey for this user, let them in
DEBUG("Accepted pubkey auth");
Auth->AcceptLogin(*pUser);
return HALT;
}
void HandleShowCommand(const CString& sLine) {
CString sPubKey = GetKey(m_pClient);
if (sPubKey.empty()) {
PutModule("You are not connected with any valid public key");
} else {
PutModule("Your current public key is: " + sPubKey);
}
}
void HandleAddCommand(const CString& sLine) {
CString sPubKey = sLine.Token(1);
if (sPubKey.empty()) {
sPubKey = GetKey(m_pClient);
}
if (sPubKey.empty()) {
PutModule("You did not supply a public key or connect with one.");
} else {
if (AddKey(m_pUser, sPubKey)) {
PutModule("'" + sPubKey + "' added.");
} else {
PutModule("The key '" + sPubKey + "' is already added.");
}
}
}
void HandleListCommand(const CString& sLine) {
CTable Table;
Table.AddColumn("Id");
Table.AddColumn("Key");
MSCString::iterator it = m_PubKeys.find(m_pUser->GetUserName());
if (it == m_PubKeys.end()) {
PutModule("No keys set for your user");
return;
}
SCString::iterator it2;
unsigned int id = 1;
for (it2 = it->second.begin(); it2 != it->second.end(); it2++) {
Table.AddRow();
Table.SetCell("Id", CString(id++));
Table.SetCell("Key", *it2);
}
if (PutModule(Table) == 0) {
// This double check is necessary, because the
// set could be empty.
PutModule("No keys set for your user");
}
}
void HandleDelCommand(const CString& sLine) {
unsigned int id = sLine.Token(1, true).ToUInt();
MSCString::iterator it = m_PubKeys.find(m_pUser->GetUserName());
if (it == m_PubKeys.end()) {
PutModule("No keys set for your user");
return;
}
if (id == 0 || id > it->second.size()) {
PutModule("Invalid #, check \"list\"");
return;
}
SCString::iterator it2 = it->second.begin();
while (id > 1) {
it2++;
id--;
}
it->second.erase(it2);
if (it->second.size() == 0)
m_PubKeys.erase(it);
PutModule("Removed");
Save();
}
CString GetKey(Csock *pSock) {
CString sRes;
long int res = pSock->GetPeerFingerprint(sRes);
DEBUG("GetKey() returned status " << res << " with key " << sRes);
// This is 'inspired' by charybdis' libratbox
switch (res) {
case X509_V_OK:
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
return sRes;
default:
return "";
}
}
virtual CString GetWebMenuTitle() { return "certauth"; }
virtual bool OnWebRequest(CWebSock& WebSock, const CString& sPageName, CTemplate& Tmpl) {
CUser *pUser = WebSock.GetSession()->GetUser();
if (sPageName == "index") {
MSCString::iterator it = m_PubKeys.find(pUser->GetUserName());
if (it != m_PubKeys.end()) {
SCString::iterator it2;
for (it2 = it->second.begin(); it2 != it->second.end(); it2++) {
CTemplate& row = Tmpl.AddRow("KeyLoop");
row["Key"] = *it2;
}
}
return true;
} else if (sPageName == "add") {
AddKey(pUser, WebSock.GetParam("key"));
WebSock.Redirect(GetWebPath());
return true;
} else if (sPageName == "delete") {
MSCString::iterator it = m_PubKeys.find(pUser->GetUserName());
if (it != m_PubKeys.end()) {
if (it->second.erase(WebSock.GetParam("key", false))) {
if (it->second.size() == 0) {
m_PubKeys.erase(it);
}
Save();
}
}
WebSock.Redirect(GetWebPath());
return true;
}
return false;
}
private:
// Maps user names to a list of allowed pubkeys
typedef map<CString, set<CString> > MSCString;
MSCString m_PubKeys;
};
template<> void TModInfo<CSSLClientCertMod>(CModInfo& Info) {
Info.SetWikiPage("certauth");
}
GLOBALMODULEDEFS(CSSLClientCertMod, "Allow users to authenticate via SSL client certificates")
Something went wrong with that request. Please try again.