make rpc do basic auth and shit like that

pull/395/head
Jeff Becker 5 years ago
parent 5d3833ef1a
commit 990049f423
No known key found for this signature in database
GPG Key ID: F357B3B42F6F9B05

@ -316,6 +316,7 @@ function(add_log_tag target)
endfunction()
set(ABYSS_SRC
${ABYSS}/src/md5.cpp
${ABYSS}/src/http.cpp
${ABYSS}/src/client.cpp
${ABYSS}/src/server.cpp)

@ -6,10 +6,10 @@ set(ABYSS_LIB abyss)
include_directories(include)
set(ABYSS_SRC
${ABYSS}/md5.cpp
${ABYSS}/http.cpp
${ABYSS}/client.cpp
${ABYSS}/server.cpp
${ABYSS}/json.cpp
)
${ABYSS}/json.cpp)
add_library(${ABYSS_LIB} ${ABYSS_SRC})

@ -4,6 +4,7 @@
#include <ev/ev.h>
#include <util/json.hpp>
#include <util/string_view.hpp>
#include <abyss/http.hpp>
#include <deque>
#include <functional>
@ -21,6 +22,7 @@ namespace abyss
using RPC_Params = nlohmann::json;
using RPC_Response = nlohmann::json;
using Headers_t = std::unordered_multimap< std::string, std::string >;
using Response = RequestHeader;
struct ConnImpl;
/// jsonrpc response handler for client
@ -90,6 +92,9 @@ namespace abyss
void
Flush();
std::string username;
std::string password;
private:
struct Call
{
@ -119,7 +124,6 @@ namespace abyss
std::deque< Call > m_PendingCalls;
std::list< std::unique_ptr< IRPCClientHandler > > m_Conns;
};
} // namespace http
} // namespace abyss

@ -0,0 +1,40 @@
#ifndef MD5_HPP
#define MD5_HPP
#include <array>
#include <algorithm>
#include <string>
struct MD5
{
MD5();
void
Update(const unsigned char* ptr, uint32_t len);
void
Final(uint8_t* digest);
uint32_t i[2]; /* number of _bits_ handled mod 2^64 */
uint32_t buf[4]; /* scratch buffer */
unsigned char in[64]; /* input buffer */
/// do md5(str) and return hex encoded digest
static std::string
SumHex(const std::string& str)
{
std::array< uint8_t, 16 > digest;
auto dist = str.size();
MD5 m;
m.Update((const unsigned char*)str.c_str(), dist);
m.Final(digest.data());
std::string hex;
std::for_each(digest.begin(), digest.end(),
[&hex](const unsigned char& ch) {
char buf[4] = {0};
std::snprintf(buf, sizeof(buf), "%.2x", ch);
hex += std::string(buf);
});
return hex;
}
};
#endif

@ -2,4 +2,18 @@
#define __LIB_ABYSS_HPP__
#include <abyss/server.hpp>
#include <abyss/client.hpp>
namespace abyss
{
struct Globals
{
Globals()
{
}
~Globals()
{
}
};
} // namespace abyss
#endif

@ -1,5 +1,6 @@
#include <abyss/client.hpp>
#include <abyss/http.hpp>
#include <abyss/md5.hpp>
#include <crypto/crypto.hpp>
#include <util/buffer.hpp>
#include <util/logger.hpp>
@ -14,12 +15,14 @@ namespace abyss
// big
static const size_t MAX_BODY_SIZE = (1024 * 1024);
llarp_tcp_conn* m_Conn;
JSONRPC* m_Parent;
nlohmann::json m_RequestBody;
Headers_t m_SendHeaders;
IRPCClientHandler* handler;
std::unique_ptr< json::IParser > m_BodyParser;
nlohmann::json m_Response;
uint16_t m_AuthTries;
bool m_ShouldAuth;
enum State
{
eInitial,
@ -31,14 +34,17 @@ namespace abyss
State state;
ConnImpl(llarp_tcp_conn* conn, const RPC_Method_t& method,
const RPC_Params& params, JSONRPC::HandlerFactory factory)
ConnImpl(llarp_tcp_conn* conn, JSONRPC* parent,
const RPC_Method_t& method, const RPC_Params& params,
JSONRPC::HandlerFactory factory)
: m_Conn(conn)
, m_Parent(parent)
, m_RequestBody(nlohmann::json::object())
, m_Response(nlohmann::json::object())
, m_AuthTries(0)
, m_ShouldAuth(false)
, state(eInitial)
{
srand(time(nullptr));
conn->user = this;
conn->closed = &ConnImpl::OnClosed;
conn->read = &ConnImpl::OnRead;
@ -77,7 +83,7 @@ namespace abyss
}
bool
ProcessStatusLine(string_view line) const
ProcessStatusLine(string_view line)
{
auto idx = line.find_first_of(' ');
if(idx == string_view::npos)
@ -95,19 +101,121 @@ namespace abyss
ShouldProcessHeader(const llarp::string_view& name) const
{
return name == llarp::string_view("content-length")
|| name == llarp::string_view("content-type");
|| name == llarp::string_view("content-type")
|| name == llarp::string_view("www-authenticate");
}
/// return true if we get a 200 status code
bool
HandleStatusCode(string_view code) const
HandleStatusCode(string_view code)
{
return code == string_view("200");
if(code == string_view("200"))
return true;
if(code == string_view("401"))
{
m_ShouldAuth = true;
return true;
}
return false;
}
bool
RetryWithAuth(const std::string& auth)
{
m_ShouldAuth = false;
auto idx = auth.find_first_of(' ');
if(idx == std::string::npos)
return false;
std::istringstream info(auth.substr(1 + idx));
std::unordered_map< std::string, std::string > opts;
std::string part;
while(std::getline(info, part, ','))
{
idx = part.find_first_of('=');
if(idx == std::string::npos)
return false;
std::string k = part.substr(0, idx);
std::string val;
++idx;
while(idx < part.size())
{
const char ch = part.at(idx);
val += ch;
++idx;
}
opts[k] = val;
}
auto itr = opts.find("algorithm");
if(itr != opts.end() && itr->second == "MD5-sess")
return false;
std::stringstream authgen;
auto strip = [&opts](const std::string& name) -> std::string {
std::string val;
std::for_each(opts[name].begin(), opts[name].end(),
[&val](const char& ch) {
if(ch != '"')
val += ch;
});
return val;
};
const auto realm = strip("realm");
const auto nonce = strip("nonce");
const auto qop = strip("qop");
std::string nonceCount = "0000000" + std::to_string(m_AuthTries);
std::string str =
m_Parent->username + ":" + realm + ":" + m_Parent->password;
std::string h1 = MD5::SumHex(str);
str = "POST:/json_rpc";
std::string h2 = MD5::SumHex(str);
llarp::AlignedBuffer< 8 > n;
n.Randomize();
std::string cnonce = n.ToHex();
str = h1 + ":" + nonce + ":" + nonceCount + ":" + cnonce + ":" + qop
+ ":" + h2;
auto responseH = MD5::SumHex(str);
authgen << "Digest username=\"" << m_Parent->username + "\", realm=\""
<< realm
<< "\", uri=\"/json_rpc\", algorithm=MD5, qop=auth, nonce=\""
<< nonce << "\", response=\"" << responseH
<< "\", nc=" << nonceCount << ", cnonce=\"" << cnonce << "\"";
for(const auto& opt : opts)
{
if(opt.first == "algorithm" || opt.first == "realm"
|| opt.first == "qop" || opt.first == "nonce"
|| opt.first == "stale")
continue;
authgen << ", " << opt.first << "=" << opt.second;
}
m_SendHeaders.clear();
m_SendHeaders.emplace("Authorization", authgen.str());
SendRequest();
return true;
}
bool
ProcessBody(const char* buf, size_t sz)
{
// we got 401 ?
if(m_ShouldAuth && m_AuthTries < 9)
{
m_AuthTries++;
auto range = Header.Headers.equal_range("www-authenticate");
auto itr = range.first;
while(itr != range.second)
{
if(RetryWithAuth(itr->second))
return true;
else
++itr;
}
return false;
}
// init parser
if(m_BodyParser == nullptr)
{
@ -222,7 +330,7 @@ namespace abyss
m_SendHeaders.emplace("Content-Length", std::to_string(body.size()));
m_SendHeaders.emplace("Accept", "application/json");
std::stringstream request;
request << "POST /json_rpc HTTP/1.0\r\n";
request << "POST /json_rpc HTTP/1.1\r\n";
for(const auto& item : m_SendHeaders)
request << item.first << ": " << item.second << "\r\n";
request << "\r\n" << body;
@ -341,9 +449,9 @@ namespace abyss
llarp_tcp_conn_close(conn);
return;
}
auto& front = m_PendingCalls.front();
ConnImpl* connimpl =
new ConnImpl(conn, front.method, front.params, front.createHandler);
auto& front = m_PendingCalls.front();
ConnImpl* connimpl = new ConnImpl(conn, this, front.method, front.params,
front.createHandler);
m_PendingCalls.pop_front();
m_Conns.emplace_back(connimpl->handler);
connimpl->SendRequest();

@ -33,4 +33,4 @@ namespace abyss
return true;
}
} // namespace http
} // namespace abyss
} // namespace abyss

@ -0,0 +1,234 @@
#include <abyss/md5.hpp>
using UINT4 = uint32_t;
/* forward declaration */
void
Transform(uint32_t *buf, uint32_t *in);
static unsigned char PADDING[64] = {
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
/* F, G and H are basic MD5 functions: selection, majority, parity */
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))
/* ROTATE_LEFT rotates x left n bits */
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */
/* Rotation is separate from addition to prevent recomputation */
#define FF(a, b, c, d, x, s, ac) \
{ \
(a) += F((b), (c), (d)) + (x) + (UINT4)(ac); \
(a) = ROTATE_LEFT((a), (s)); \
(a) += (b); \
}
#define GG(a, b, c, d, x, s, ac) \
{ \
(a) += G((b), (c), (d)) + (x) + (UINT4)(ac); \
(a) = ROTATE_LEFT((a), (s)); \
(a) += (b); \
}
#define HH(a, b, c, d, x, s, ac) \
{ \
(a) += H((b), (c), (d)) + (x) + (UINT4)(ac); \
(a) = ROTATE_LEFT((a), (s)); \
(a) += (b); \
}
#define II(a, b, c, d, x, s, ac) \
{ \
(a) += I((b), (c), (d)) + (x) + (UINT4)(ac); \
(a) = ROTATE_LEFT((a), (s)); \
(a) += (b); \
}
MD5::MD5()
{
i[0] = i[1] = (UINT4)0;
/* Load magic initialization constants.
*/
buf[0] = (UINT4)0x67452301;
buf[1] = (UINT4)0xefcdab89;
buf[2] = (UINT4)0x98badcfe;
buf[3] = (UINT4)0x10325476;
}
void
MD5::Update(const unsigned char *inBuf, uint32_t inLen)
{
UINT4 in[16];
int mdi;
unsigned int i, ii;
/* compute number of bytes mod 64 */
mdi = (int)((this->i[0] >> 3) & 0x3F);
/* update number of bits */
if((this->i[0] + ((UINT4)inLen << 3)) < this->i[0])
this->i[1]++;
this->i[0] += ((UINT4)inLen << 3);
this->i[1] += ((UINT4)inLen >> 29);
while(inLen--)
{
/* add new character to buffer, increment mdi */
this->in[mdi++] = *inBuf++;
/* transform if necessary */
if(mdi == 0x40)
{
for(i = 0, ii = 0; i < 16; i++, ii += 4)
in[i] = (((UINT4)this->in[ii + 3]) << 24)
| (((UINT4)this->in[ii + 2]) << 16)
| (((UINT4)this->in[ii + 1]) << 8) | ((UINT4)this->in[ii]);
Transform(this->buf, in);
mdi = 0;
}
}
}
void
MD5::Final(uint8_t *digest)
{
UINT4 in[16];
int mdi;
unsigned int i, ii;
unsigned int padLen;
/* save number of bits */
in[14] = this->i[0];
in[15] = this->i[1];
/* compute number of bytes mod 64 */
mdi = (int)((this->i[0] >> 3) & 0x3F);
/* pad out to 56 mod 64 */
padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi);
this->Update(PADDING, padLen);
/* append length in bits and transform */
for(i = 0, ii = 0; i < 14; i++, ii += 4)
in[i] = (((UINT4)this->in[ii + 3]) << 24)
| (((UINT4)this->in[ii + 2]) << 16) | (((UINT4)this->in[ii + 1]) << 8)
| ((UINT4)this->in[ii]);
Transform(this->buf, in);
/* store buffer in digest */
for(i = 0, ii = 0; i < 4; i++, ii += 4)
{
digest[ii] = (unsigned char)(this->buf[i] & 0xFF);
digest[ii + 1] = (unsigned char)((this->buf[i] >> 8) & 0xFF);
digest[ii + 2] = (unsigned char)((this->buf[i] >> 16) & 0xFF);
digest[ii + 3] = (unsigned char)((this->buf[i] >> 24) & 0xFF);
}
}
/* Basic MD5 step. Transform buf based on in.
*/
void
Transform(UINT4 *buf, UINT4 *in)
{
UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
/* Round 1 */
#define S11 7
#define S12 12
#define S13 17
#define S14 22
FF(a, b, c, d, in[0], S11, 3614090360); /* 1 */
FF(d, a, b, c, in[1], S12, 3905402710); /* 2 */
FF(c, d, a, b, in[2], S13, 606105819); /* 3 */
FF(b, c, d, a, in[3], S14, 3250441966); /* 4 */
FF(a, b, c, d, in[4], S11, 4118548399); /* 5 */
FF(d, a, b, c, in[5], S12, 1200080426); /* 6 */
FF(c, d, a, b, in[6], S13, 2821735955); /* 7 */
FF(b, c, d, a, in[7], S14, 4249261313); /* 8 */
FF(a, b, c, d, in[8], S11, 1770035416); /* 9 */
FF(d, a, b, c, in[9], S12, 2336552879); /* 10 */
FF(c, d, a, b, in[10], S13, 4294925233); /* 11 */
FF(b, c, d, a, in[11], S14, 2304563134); /* 12 */
FF(a, b, c, d, in[12], S11, 1804603682); /* 13 */
FF(d, a, b, c, in[13], S12, 4254626195); /* 14 */
FF(c, d, a, b, in[14], S13, 2792965006); /* 15 */
FF(b, c, d, a, in[15], S14, 1236535329); /* 16 */
/* Round 2 */
#define S21 5
#define S22 9
#define S23 14
#define S24 20
GG(a, b, c, d, in[1], S21, 4129170786); /* 17 */
GG(d, a, b, c, in[6], S22, 3225465664); /* 18 */
GG(c, d, a, b, in[11], S23, 643717713); /* 19 */
GG(b, c, d, a, in[0], S24, 3921069994); /* 20 */
GG(a, b, c, d, in[5], S21, 3593408605); /* 21 */
GG(d, a, b, c, in[10], S22, 38016083); /* 22 */
GG(c, d, a, b, in[15], S23, 3634488961); /* 23 */
GG(b, c, d, a, in[4], S24, 3889429448); /* 24 */
GG(a, b, c, d, in[9], S21, 568446438); /* 25 */
GG(d, a, b, c, in[14], S22, 3275163606); /* 26 */
GG(c, d, a, b, in[3], S23, 4107603335); /* 27 */
GG(b, c, d, a, in[8], S24, 1163531501); /* 28 */
GG(a, b, c, d, in[13], S21, 2850285829); /* 29 */
GG(d, a, b, c, in[2], S22, 4243563512); /* 30 */
GG(c, d, a, b, in[7], S23, 1735328473); /* 31 */
GG(b, c, d, a, in[12], S24, 2368359562); /* 32 */
/* Round 3 */
#define S31 4
#define S32 11
#define S33 16
#define S34 23
HH(a, b, c, d, in[5], S31, 4294588738); /* 33 */
HH(d, a, b, c, in[8], S32, 2272392833); /* 34 */
HH(c, d, a, b, in[11], S33, 1839030562); /* 35 */
HH(b, c, d, a, in[14], S34, 4259657740); /* 36 */
HH(a, b, c, d, in[1], S31, 2763975236); /* 37 */
HH(d, a, b, c, in[4], S32, 1272893353); /* 38 */
HH(c, d, a, b, in[7], S33, 4139469664); /* 39 */
HH(b, c, d, a, in[10], S34, 3200236656); /* 40 */
HH(a, b, c, d, in[13], S31, 681279174); /* 41 */
HH(d, a, b, c, in[0], S32, 3936430074); /* 42 */
HH(c, d, a, b, in[3], S33, 3572445317); /* 43 */
HH(b, c, d, a, in[6], S34, 76029189); /* 44 */
HH(a, b, c, d, in[9], S31, 3654602809); /* 45 */
HH(d, a, b, c, in[12], S32, 3873151461); /* 46 */
HH(c, d, a, b, in[15], S33, 530742520); /* 47 */
HH(b, c, d, a, in[2], S34, 3299628645); /* 48 */
/* Round 4 */
#define S41 6
#define S42 10
#define S43 15
#define S44 21
II(a, b, c, d, in[0], S41, 4096336452); /* 49 */
II(d, a, b, c, in[7], S42, 1126891415); /* 50 */
II(c, d, a, b, in[14], S43, 2878612391); /* 51 */
II(b, c, d, a, in[5], S44, 4237533241); /* 52 */
II(a, b, c, d, in[12], S41, 1700485571); /* 53 */
II(d, a, b, c, in[3], S42, 2399980690); /* 54 */
II(c, d, a, b, in[10], S43, 4293915773); /* 55 */
II(b, c, d, a, in[1], S44, 2240044497); /* 56 */
II(a, b, c, d, in[8], S41, 1873313359); /* 57 */
II(d, a, b, c, in[15], S42, 4264355552); /* 58 */
II(c, d, a, b, in[6], S43, 2734768916); /* 59 */
II(b, c, d, a, in[13], S44, 1309151649); /* 60 */
II(a, b, c, d, in[4], S41, 4149444226); /* 61 */
II(d, a, b, c, in[11], S42, 3174756917); /* 62 */
II(c, d, a, b, in[2], S43, 718787259); /* 63 */
II(b, c, d, a, in[9], S44, 3951481745); /* 64 */
buf[0] += a;
buf[1] += b;
buf[2] += c;
buf[3] += d;
}

@ -24,6 +24,7 @@ namespace llarp
{
// error
_shouldClose = true;
errno = 0;
return -1;
}
return 0;
@ -73,6 +74,7 @@ namespace llarp
// wtf?
llarp::LogError("error connecting ", strerror(errno));
_conn->error(_conn);
errno = 0;
}
}
@ -118,7 +120,10 @@ namespace llarp
sockaddr* addr = (sockaddr*)&src;
ssize_t ret = ::recvfrom(fd, b.base, sz, 0, addr, &slen);
if(ret < 0)
{
errno = 0;
return -1;
}
if(static_cast< size_t >(ret) > sz)
return -1;
b.sz = ret;
@ -334,10 +339,11 @@ llarp_epoll_loop::tick(int ms)
{
llarp::LogDebug(idx, " of ", result, " on ", ev->fd,
" events=", std::to_string(events[idx].events));
if(events[idx].events & EPOLLERR)
if(events[idx].events & EPOLLERR && errno)
{
llarp::LogDebug("epoll error");
ev->error();
errno = 0;
}
else
{
@ -361,6 +367,7 @@ llarp_epoll_loop::tick(int ms)
if(result != -1)
tick_listeners();
/// if we didn't get an io events we sleep to avoid 100% cpu use
errno = 0;
if(!didIO)
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
return result;
@ -539,4 +546,4 @@ llarp_epoll_loop::stop()
if(epollfd != -1)
close(epollfd);
epollfd = -1;
}
}

@ -1309,7 +1309,7 @@ namespace llarp
if(whitelistRouters)
{
rpcCaller = std::make_unique< rpc::Caller >(this);
rpcCaller->SetBasicAuth(lokidRPCUser, lokidRPCPassword);
rpcCaller->SetAuth(lokidRPCUser, lokidRPCPassword);
while(!rpcCaller->Start(lokidRPCAddr))
{
LogError("failed to start jsonrpc caller to ", lokidRPCAddr);

@ -120,12 +120,9 @@ namespace llarp
{
AbstractRouter* router;
llarp_time_t m_NextKeyUpdate = 0;
const llarp_time_t KeyUpdateInterval = 1000 * 60 * 2;
const llarp_time_t KeyUpdateInterval = 5000;
using PubkeyList_t = GetServiceNodeListHandler::PubkeyList_t;
std::string username;
std::string password;
CallerImpl(AbstractRouter* r) : ::abyss::http::JSONRPC(), router(r)
{
}
@ -142,7 +139,7 @@ namespace llarp
}
void
SetBasicAuth(const std::string& user, const std::string& passwd)
SetAuth(const std::string& user, const std::string& passwd)
{
username = user;
password = passwd;
@ -191,13 +188,7 @@ namespace llarp
void
CallerHandler::PopulateReqHeaders(abyss::http::Headers_t& hdr)
{
if(m_Parent->username.empty() || m_Parent->password.empty())
return;
std::stringstream ss;
ss << "Basic ";
std::string cred = m_Parent->username + ":" + m_Parent->password;
llarp::Base64Encode(ss, (const byte_t*)cred.c_str(), cred.size());
hdr.emplace("Authorization", ss.str());
hdr.emplace("User-Agent", "lokinet rpc (YOLO)");
}
struct Handler : public ::abyss::httpd::IRPCHandler
@ -351,9 +342,9 @@ namespace llarp
}
void
Caller::SetBasicAuth(const std::string& user, const std::string& passwd)
Caller::SetAuth(const std::string& user, const std::string& passwd)
{
m_Impl->SetBasicAuth(user, passwd);
m_Impl->SetAuth(user, passwd);
}
Server::Server(AbstractRouter* r)

@ -43,7 +43,7 @@ namespace llarp
/// set http basic auth for use with remote rpc endpoint
void
SetBasicAuth(const std::string& user, const std::string& password);
SetAuth(const std::string& user, const std::string& password);
/// start with jsonrpc endpoint address
bool

@ -29,6 +29,7 @@ list(APPEND TEST_SRC
test_llarp_encrypted_frame.cpp
test_llarp_router_contact.cpp
test_llarp_router.cpp
test_md5.cpp
util/test_llarp_util_aligned.cpp
util/test_llarp_util_bencode.cpp
util/test_llarp_util_bits.cpp

Loading…
Cancel
Save