I can assure you it's possible... cause I've done it!

I'd have to do some serious editing to post a complete implementation, but here are a few of the important handlers I came up with. The checkPassword() handler has been edited heavily from my original code, so it may need some fixing. Also note that the nonce / opaque values are not implemented ideally in authenticateHeader(). You'll need a basic understanding of the RFC to make good use of these.

Hope this helps!

function checkPassword theSocket,requestHeader,@replyHeader,@userName
  global server_opaque,server_nonce,nonce_time, loggedInIDs, loggedInNames, xfactor

  put "WebCF Registered Users" into realm -- default
 
  put lineOffset("Authorization:", requestHeader) into theLine
 
  if (theLine > 0) then
    -- check the authorization
    put word 1 of requestHeader into method
    put line theLine of requestHeader into authLine
   
    replace "Authorization: Digest" with empty in authLine
    set the itemDelimiter to comma
    repeat for each item theItem in authLine
      set the itemDelimiter to "="
      put item 1 of theItem into theName
      repeat until (char 1 of theName <> " ")
        delete char 1 of theName
      end repeat
      replace (theName&"=") with (theName&"◊") in theItem
      set the itemDelimiter to "◊"
      put item 2 of theItem into theValue
      replace quote with empty in theValue
      set the itemDelimiter to comma
      switch(theName)
      case "userName"
        put theValue into userName
        exit switch
      case "realm"
        put theValue into realm
        exit switch
      case "nonce"
        put theValue into nonce
        exit switch
      case "uri"
        put theValue into theURL
        exit switch
      case "qop"
        put theValue into qop
        exit switch
      case "nc"
        put theValue into nc
        exit switch
      case "cnonce"
        put theValue into cnonce
        exit switch
      case "response"
        put theValue into digestValue
        exit switch
      case "opaque"
        put theValue into opaque
        exit switch
      end switch
    end repeat
  else
    put authenticateHeader(realm,"auth",empty,"default") into replyHeader
    return FALSE
  end if

  ## edit this to lookup the correct password
  put empty into userID
  put lookupPassword(userName, userID) into password
  if (password is empty) then
    put authenticateHeader(realm,"auth",empty,"default") into replyHeader
    return FALSE
  end if
 
  put makeDigest(userName,password,realm,method,theURL,nonce,nc,cnonce,qop) into actualValue
 
  put char 1 to length(actualValue) of digestValue into digestValue
  if ((digestValue = actualValue) AND (opaque = server_opaque)) then
      put authenticateInfoHeader(userName,password,realm,method,theURL,server_nonce[userName]) into replyHeader
      put userID into loggedInIDs[theSocket]
      put userName into loggedInNames[theSocket]
     
      put base64Encode(userName&tab&thePassword) into loginValue
      SetCookie theSocket,"webcf_entry",loginValue
      return TRUE
  else
    -- failed
    put authenticateHeader(realm,"auth",empty) into replyHeader
    return FALSE
  end if
end checkPassword


function authenticateInfoHeader userName,_password,realm,method,theURL,nonce
  put makeDigest(userName,_password,realm,empty,theURL,nonce) into response
  return "Authentication-Info: next-nonce="&quote&nonce&quote&comma&"qop="&quote&auth&quote&comma&"rspauth="&quote&response&quote
end authenticateInfoHeader

function authenticateHeader theRealm,authMethods,isStale,userName,forceNonce
  global server_opaque,server_nonce,cfLF
  if (userName is empty) then put "default" into userName
  if (server_opaque is empty) then put base64Encode(random(4*(the ticks))&md5Digest(the ticks)) into server_opaque
  if (server_nonce is empty) OR (forceNonce) then
    put base64Encode("Dummy") into server_nonce
  end if
  if (isStale is empty) then return "HTTP/1.1 401 Unauthorized"&crLF&"WWW-Authenticate: Digest"&&"realm="&quote&theRealm&quote&comma&"qop="&quote&authMethods&quote&comma&"nonce="&quote&server_nonce&quote&comma&"opaque="&quote&server_opaque&quote&comma&"algorithm="&quote&"MD5"&quote&crLF
  else
    return "HTTP/1.1 401 Unauthorized"&crLF&"Connection: close"&crLF&"WWW-Authenticate: Digest"&&"realm="&quote&theRealm&quote&comma&"qop="&quote&authMethods&quote&comma&"nonce="&quote&server_nonce&quote&comma&"opaque="&quote&server_opaque&quote&comma&"stale="&isStale&",algorithm="&quote&"MD5"&quote&crLF
  end if
end authenticateHeader

function makeDigest userName,_password,realm,method,theURL,nonce,nc,cnonce,qop
  put md5Digest(userName&colon&realm&colon&_password) into A1
  get binaryDecode("H*",A1,A1)
  put md5Digest(method&colon&theURL) into A2
  get binaryDecode("H*",A2,A2)
  if (qop is empty) then
    put md5Digest(A1&colon&nonce&colon&A2) into actualValue
  else
    put md5Digest(A1&colon&nonce&colon&nc&colon&cnonce&colon&qop&colon&A2) into actualValue
  end if
  get binaryDecode("H*",actualValue,actualValue)
  return actualvalue
end makeDigest



I'm sure it'll be a piece of cake, Rob. :)  But please let us know
what you come up with. The truth is I just want someone else to
interpret section 3.2.2 of the rfc on digest authorization. I'm
trying to preserve what few brain cells I have remaining.


Reply via email to