Changeset 8fb7cef1d61f328d6af32a69fc72f3b4c66ea154

Show
Ignore:
Timestamp:
09/29/09 05:29:23 (5 years ago)
Author:
Theo Schlossnagle <jesus@omniti.com>
git-committer:
Theo Schlossnagle <jesus@omniti.com> 1254202163 +0000
git-parent:

[2ea74a7d99056374e198cf3c9e124c223ddbc963]

git-author:
Theo Schlossnagle <jesus@omniti.com> 1254202163 +0000
Message:

Support Digest and auto-detection, fixes #186

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • src/modules-lua/noit/module/http.lua

    r6009418 r8fb7cef  
    5252    <parameter name="auth_method" 
    5353               required="optional" 
    54                allowed="^Basic$">HTTP Authentication method to use.</parameter> 
     54               allowed="^(?:Basic|Digest|Auto)$">HTTP Authentication method to use.</parameter> 
    5555    <parameter name="auth_user" 
    5656               required="optional" 
     
    127127end 
    128128 
     129function rand_string(t, l) 
     130    local n = table.getn(t) 
     131    local o = '' 
     132    while l > 0 do 
     133      o = o .. t[math.random(1,n)] 
     134      l = l - 1 
     135    end 
     136    return o 
     137end 
     138 
     139function auth_digest(method, uri, user, pass, challenge) 
     140    local c = ', ' .. challenge 
     141    local nc = '00000001' 
     142    local cnonce = 
     143        rand_string({'a','b','c','d','e','f','g','h','i','j','k','l','m', 
     144                     'n','o','p','q','r','s','t','u','v','x','y','z','A', 
     145                     'B','C','D','E','F','G','H','I','J','K','L','M','N', 
     146                     'O','P','Q','R','S','T','U','V','W','X','Y','Z','0', 
     147                     '1','2','3','4','5','6','7','8','9'}, 8) 
     148    local p = {} 
     149    for k,v in string.gmatch(c, ',%s+(%a+)="([^"]+)"') do p[k] = v end 
     150    for k,v in string.gmatch(c, ',%s+(%a+)=([^",][^,]*)') do p[k] = v end 
     151 
     152    -- qop can be a list 
     153    for q in string.gmatch(p.qop, '([^,]+)') do 
     154        if q == "auth" then p.qop = "auth" end 
     155    end 
     156 
     157    -- calculate H(A1) 
     158    local ha1 = noit.md5_hex(user .. ':' .. p.realm .. ':' .. pass) 
     159    if string.lower(p.qop or '') == 'md5-sess' then 
     160        ha1 = noit.md5_hex(ha1 .. ':' .. p.nonce .. ':' .. cnonce) 
     161    end 
     162    -- calculate H(A2) 
     163    local ha2 = '' 
     164    if p.qop == "auth" or p.qop == nil then 
     165        ha2 = noit.md5_hex(method .. ':' .. uri) 
     166    else 
     167        -- we don't support auth-int 
     168        error("qop=" .. p.qop .. " is unsupported") 
     169    end 
     170    local resp = '' 
     171    if p.qop == "auth" then 
     172        resp = noit.md5_hex(ha1 .. ':' .. p.nonce .. ':' .. nc 
     173                                .. ':' .. cnonce .. ':' .. p.qop 
     174                                .. ':' .. ha2) 
     175    else 
     176        resp = noit.md5_hex(ha1 .. ':' .. p.nonce .. ':' .. ha2) 
     177    end 
     178    local o = {} 
     179    o.username = user 
     180    o.realm = p.realm 
     181    o.nonce = p.nonce 
     182    o.uri = uri 
     183    o.cnonce = cnonce 
     184    o.qop = p.qop 
     185    o.response = resp 
     186    o.algorithm = p.algorithm 
     187    if p.opaque then o.opaque = p.opaque end 
     188    local hdr = '' 
     189    for k,v in pairs(o) do 
     190      if hdr == '' then hdr = k .. '="' .. v .. '"'  
     191      else hdr = hdr .. ', ' .. k .. '="' .. v .. '"' end 
     192    end 
     193    hdr = hdr .. ', nc=' .. nc 
     194noit.log("error", "Authorization: Digest " .. hdr .. "\n") 
     195    return hdr 
     196end 
     197 
    129198function initiate(module, check) 
    130199    local url = check.config.url or 'http:///' 
     
    137206    local method = check.config.method or "GET" 
    138207 
     208    -- expect the worst 
     209    check.bad() 
     210    check.unavailable() 
     211 
    139212    if host == nil then host = check.target end 
    140213    if schema == nil then 
     
    172245    end 
    173246    callbacks.ciphers = function () return check.config.ciphers end 
    174     local client = HttpClient:new(callbacks) 
    175     local rv, err = client:connect(check.target, port, use_ssl) 
    176     
    177     if rv ~= 0 then 
    178         check.bad() 
    179         check.unavailable() 
    180         check.status(str or "unknown error") 
    181         return 
    182     end 
    183  
    184     -- perform the request 
     247 
     248    -- set the stage 
    185249    local headers = {} 
    186     if check.config.auth_method == "Basic" then 
    187       local user = check.config.auth_user or '' 
    188       local password = check.config.auth_password or '' 
    189       local encoded = noit.base64_encode(user .. ':' .. password) 
    190       headers["Authorization"] = "Basic " .. encoded 
    191     elseif check.config.auth_method ~= nil then 
    192       check.status("Unknown auth method: " .. check.config.auth_method) 
    193       return 
    194     end 
    195250    headers.Host = host 
    196251    for header, value in pairs(check.config) do 
     
    200255        end 
    201256    end 
     257    if check.config.auth_method == "Basic" then 
     258        local user = check.config.auth_user or '' 
     259        local password = check.config.auth_password or '' 
     260        local encoded = noit.base64_encode(user .. ':' .. password) 
     261        headers["Authorization"] = "Basic " .. encoded 
     262    elseif check.config.auth_method == "Digest" or  
     263           check.config.auth_method == "Auto" then 
     264        -- this is handled later as we need our challenge. 
     265        local client = HttpClient:new() 
     266        local rv, err = client:connect(check.target, port, use_ssl) 
     267        if rv ~= 0 then 
     268            check.status(str or "unknown error") 
     269            return 
     270        end 
     271        local headers_firstpass = {} 
     272        for k,v in pairs(headers) do 
     273            headers_firstpass[k] = v 
     274        end 
     275        client:do_request(method, uri, headers_firstpass) 
     276        client:get_response() 
     277        if client.code ~= 401 or 
     278           client.headers["www-authenticate"] == nil then 
     279            check.status("expected digest challenge, got " .. client.code) 
     280            return 
     281        end 
     282        local user = check.config.auth_user or '' 
     283        local password = check.config.auth_password or '' 
     284        local ameth, challenge = 
     285            string.match(client.headers["www-authenticate"], '^(%S+)%s+(.+)$') 
     286        if check.config.auth_method == "Auto" and ameth == "Basic" then 
     287            local encoded = noit.base64_encode(user .. ':' .. password) 
     288            headers["Authorization"] = "Basic " .. encoded 
     289        elseif ameth == "Digest" then 
     290            headers["Authorization"] = 
     291                "Digest " .. auth_digest(method, uri, 
     292                                         user, password, challenge) 
     293        else 
     294            check.status("Unexpected auth '" .. ameth .. "' in challenge") 
     295            return 
     296        end 
     297    elseif check.config.auth_method ~= nil then 
     298      check.status("Unknown auth method: " .. check.config.auth_method) 
     299      return 
     300    end 
     301 
     302    -- perform the request 
     303    local client = HttpClient:new(callbacks) 
     304    local rv, err = client:connect(check.target, port, use_ssl) 
     305    
     306    if rv ~= 0 then 
     307        check.status(str or "unknown error") 
     308        return 
     309    end 
    202310    client:do_request(method, uri, headers) 
    203311    client:get_response() 
     312 
    204313    local endtime = noit.timeval.now() 
    205314    check.available()