Skip to content

Commit

Permalink
Merge pull request circonus-labs#55 from TheTeaWeevil/master
Browse files Browse the repository at this point in the history
Auth Digest In HTTPClient
  • Loading branch information
postwait committed Mar 19, 2012
2 parents 55a9096 + 04d012f commit f2b23a1
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 83 deletions.
67 changes: 67 additions & 0 deletions src/modules-lua/noit/HttpClient.lua
Expand Up @@ -214,4 +214,71 @@ function HttpClient:get_response(read_limit)
return self:get_body(read_limit)
end

function HttpClient:auth_digest(method, uri, user, pass, challenge)
local c = ', ' .. challenge
local nc = '00000001'
local function rand_string(t, l)
local n = table.getn(t)
local o = ''
while l > 0 do
o = o .. t[math.random(1,n)]
l = l - 1
end
return o
end
local cnonce =
rand_string({'a','b','c','d','e','f','g','h','i','j','k','l','m',
'n','o','p','q','r','s','t','u','v','x','y','z','A',
'B','C','D','E','F','G','H','I','J','K','L','M','N',
'O','P','Q','R','S','T','U','V','W','X','Y','Z','0',
'1','2','3','4','5','6','7','8','9'}, 8)
local p = {}
for k,v in string.gmatch(c, ',%s+(%a+)="([^"]+)"') do p[k] = v end
for k,v in string.gmatch(c, ',%s+(%a+)=([^",][^,]*)') do p[k] = v end

-- qop can be a list
for q in string.gmatch(p.qop, '([^,]+)') do
if q == "auth" then p.qop = "auth" end
end

-- calculate H(A1)
local ha1 = noit.md5_hex(user .. ':' .. p.realm .. ':' .. pass)
if string.lower(p.qop or '') == 'md5-sess' then
ha1 = noit.md5_hex(ha1 .. ':' .. p.nonce .. ':' .. cnonce)
end
-- calculate H(A2)
local ha2 = ''
if p.qop == "auth" or p.qop == nil then
ha2 = noit.md5_hex(method .. ':' .. uri)
else
-- we don't support auth-int
error("qop=" .. p.qop .. " is unsupported")
end
local resp = ''
if p.qop == "auth" then
resp = noit.md5_hex(ha1 .. ':' .. p.nonce .. ':' .. nc
.. ':' .. cnonce .. ':' .. p.qop
.. ':' .. ha2)
else
resp = noit.md5_hex(ha1 .. ':' .. p.nonce .. ':' .. ha2)
end
local o = {}
o.username = user
o.realm = p.realm
o.nonce = p.nonce
o.uri = uri
o.cnonce = cnonce
o.qop = p.qop
o.response = resp
o.algorithm = p.algorithm
if p.opaque then o.opaque = p.opaque end
local hdr = ''
for k,v in pairs(o) do
if hdr == '' then hdr = k .. '="' .. v .. '"'
else hdr = hdr .. ', ' .. k .. '="' .. v .. '"' end
end
hdr = hdr .. ', nc=' .. nc
return hdr
end

return HttpClient
70 changes: 1 addition & 69 deletions src/modules-lua/noit/module/http.lua
Expand Up @@ -153,74 +153,6 @@ function elapsed(check, name, starttime, endtime)
return seconds
end

function rand_string(t, l)
local n = table.getn(t)
local o = ''
while l > 0 do
o = o .. t[math.random(1,n)]
l = l - 1
end
return o
end

function auth_digest(method, uri, user, pass, challenge)
local c = ', ' .. challenge
local nc = '00000001'
local cnonce =
rand_string({'a','b','c','d','e','f','g','h','i','j','k','l','m',
'n','o','p','q','r','s','t','u','v','x','y','z','A',
'B','C','D','E','F','G','H','I','J','K','L','M','N',
'O','P','Q','R','S','T','U','V','W','X','Y','Z','0',
'1','2','3','4','5','6','7','8','9'}, 8)
local p = {}
for k,v in string.gmatch(c, ',%s+(%a+)="([^"]+)"') do p[k] = v end
for k,v in string.gmatch(c, ',%s+(%a+)=([^",][^,]*)') do p[k] = v end

-- qop can be a list
for q in string.gmatch(p.qop, '([^,]+)') do
if q == "auth" then p.qop = "auth" end
end

-- calculate H(A1)
local ha1 = noit.md5_hex(user .. ':' .. p.realm .. ':' .. pass)
if string.lower(p.qop or '') == 'md5-sess' then
ha1 = noit.md5_hex(ha1 .. ':' .. p.nonce .. ':' .. cnonce)
end
-- calculate H(A2)
local ha2 = ''
if p.qop == "auth" or p.qop == nil then
ha2 = noit.md5_hex(method .. ':' .. uri)
else
-- we don't support auth-int
error("qop=" .. p.qop .. " is unsupported")
end
local resp = ''
if p.qop == "auth" then
resp = noit.md5_hex(ha1 .. ':' .. p.nonce .. ':' .. nc
.. ':' .. cnonce .. ':' .. p.qop
.. ':' .. ha2)
else
resp = noit.md5_hex(ha1 .. ':' .. p.nonce .. ':' .. ha2)
end
local o = {}
o.username = user
o.realm = p.realm
o.nonce = p.nonce
o.uri = uri
o.cnonce = cnonce
o.qop = p.qop
o.response = resp
o.algorithm = p.algorithm
if p.opaque then o.opaque = p.opaque end
local hdr = ''
for k,v in pairs(o) do
if hdr == '' then hdr = k .. '="' .. v .. '"'
else hdr = hdr .. ', ' .. k .. '="' .. v .. '"' end
end
hdr = hdr .. ', nc=' .. nc
return hdr
end

function populate_cookie_jar(cookies, host, hdr)
if hdr ~= nil then
local name, value, trailer =
Expand Down Expand Up @@ -388,7 +320,7 @@ function initiate(module, check)
headers["Authorization"] = "Basic " .. encoded
elseif ameth == "Digest" then
headers["Authorization"] =
"Digest " .. auth_digest(method, uri,
"Digest " .. client:auth_digest(method, uri,
user, password, challenge)
else
check.status("Unexpected auth '" .. ameth .. "' in challenge")
Expand Down
71 changes: 57 additions & 14 deletions src/modules-lua/noit/module/resmon.lua
Expand Up @@ -201,13 +201,6 @@ function initiate(module, check)
local starttime = noit.timeval.now()
local read_limit = tonumber(check.config.read_limit) or nil

local user = check.config.auth_user or nil
local pass = check.config.auth_password or nil
local encoded = nil
if (user ~= nil and pass ~= nil) then
encoded = noit.base64_encode(user .. ':' .. pass)
end

-- assume the worst.
check.bad()
check.unavailable()
Expand All @@ -233,20 +226,70 @@ function initiate(module, check)
local hdrs_in = { }
callbacks.consume = function (str) output = output .. str end
callbacks.headers = function (t) hdrs_in = t end

-- perform the request
local headers = {}
headers.Host = host

if check.config.auth_method == "Basic" or
(check.config.auth_method == nil and
check.config.auth_user ~= nil and
check.config.auth_password ~= nil) then
local user = check.config.auth_user or nil
local pass = check.config.auth_password or nil
local encoded = nil
if (user ~= nil and pass ~= nil) then
encoded = noit.base64_encode(user .. ':' .. pass)
headers["Authorization"] = "Basic " .. encoded
end
elseif check.config.auth_method == "Digest" or
check.config.auth_method == "Auto" then

-- this is handled later as we need our challenge.
local client = HttpClient:new()
local rv, err = client:connect(check.target_ip, port, use_ssl)
if rv ~= 0 then
check.status(str or "unknown error")
return
end
local headers_firstpass = {}
for k,v in pairs(headers) do
headers_firstpass[k] = v
end
client:do_request("GET", uri, headers_firstpass)
client:get_response(read_limit)
if client.code ~= 401 or
client.headers["www-authenticate"] == nil then
check.status("expected digest challenge, got " .. client.code)
return
end
local user = check.config.auth_user or ''
local password = check.config.auth_password or ''
local ameth, challenge =
string.match(client.headers["www-authenticate"], '^(%S+)%s+(.+)$')
if check.config.auth_method == "Auto" and ameth == "Basic" then
local encoded = noit.base64_encode(user .. ':' .. password)
headers["Authorization"] = "Basic " .. encoded
elseif ameth == "Digest" then
headers["Authorization"] =
"Digest " .. client:auth_digest("GET", uri,
user, password, challenge)
else
check.status("Unexpected auth '" .. ameth .. "' in challenge")
return
end
elseif check.config.auth_method ~= nil then
check.status("Unknown auth method: " .. check.config.auth_method)
return
end

local client = HttpClient:new(callbacks)
local rv, err = client:connect(check.target_ip, port, use_ssl)

if rv ~= 0 then
check.status(err or "unknown error")
return
end

-- perform the request
local headers = {}
headers.Host = host
if encoded ~= nil then
headers["Authorization"] = "Basic " .. encoded
end
client:do_request("GET", uri, headers)
client:get_response(read_limit)

Expand Down

0 comments on commit f2b23a1

Please sign in to comment.