package devkit

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file digest_auth.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
open Printf
open Prelude
open Httpev_common

type t = { mutable stamp : Time.t; mutable index : int; realm : string; user : string; password : string; }

type digest_request = {
    name:string;
    crealm:string;
    nonce:string;
    uri:string;
    qop:[`Auth | `Authi | `Unknown];
    nc:string;
    cnonce:string;
    response:string;
    opaque:string;
}

module Parse = struct (* awful *)

let appendlst lst elem =
   lst := List.append !lst [elem]

let appendstr str elem =
  str := ((!str) ^ elem)

let lowparse elem curstr curlist =
  if elem = ',' then begin
    if (String.length !curstr) > 0 then begin
    appendlst curlist !curstr ; end;
    curstr:="";
  end else if (elem <>  ' ')&&(elem <> '"')&&(elem<>'\n')&&(elem<>'\r') then appendstr curstr (Char.escaped elem)

let make_tuple a b = (a,b)

let highparse str curlist  =
  let first_equal = try String.index str '='with Not_found -> Exn.fail "symbol = not found in %s" str in
  appendlst curlist (make_tuple (String.sub str 0 first_equal) (String.sub str (first_equal+1) (String.length(str)-1-first_equal)))

let digest_request_from_string s =
   if String.length s < 6 then Exn.fail "Digest string too short";
   let s1 = String.sub s 0 6 in
   if String.lowercase_ascii s1 <> "digest" then Exn.fail "Authorization fail - non-digest trying to connect";
   let str = String.sub s 6 ((String.length s) - 6) in
   let tmpstr = ref "" in
   let a = str^"," in
   let tmplist = ref [] in
   String.iter (fun a -> lowparse a tmpstr tmplist) a;
   let resultlist = ref [] in
   List.iter (fun a -> highparse a resultlist) !tmplist;
   let get k = try List.assoc k !resultlist with Not_found -> "" in
   {
      name     = get "username";
      crealm   = get "realm";
      nonce    = get "nonce";
      uri      = get "uri";
      qop      = (match get "qop" with "auth" -> `Auth | "auth-int" -> `Authi | _ -> `Unknown);
      nc       = get "nc";
      cnonce   = get "cnonce";
      response = get "response";
      opaque   = get "opaque";
   }

let _string_from_digest_request p =
   let s = "Digest username=\""^p.name^"\", realm=\""^p.crealm^"\", nonce=\""^p.nonce^"\", uri=\""^p.uri^"\", qop=" in
   let a = match p.qop with
           | `Auth -> s^"auth"
           | `Authi -> s^"auth-int"
           | `Unknown -> s^"unknown" in
   let a2 = a^", nc="^p.nc^", cnonce=\""^p.cnonce^"\", response=\""^p.response^"\", opaque=\""^p.opaque^"\"" in
   a2

end (* Parse *)

let md5_hex_string = Digest.(to_hex $ string)
let hash l = md5_hex_string @@ String.concat ":" l

let digest_opaque = md5_hex_string @@ Action.random_bytes 64

let init ~realm ~user ~password () = { realm; user; password; stamp = Time.now (); index = 1; }

let check t req =
  if Time.now () -. t.stamp > 300. then
  begin
    t.stamp <- Time.now ();
    t.index <- t.index + 1;
  end;
  let nonce = hash [Unix.string_of_inet_addr @@ client_ip req; string_of_float t.stamp; string_of_int t.index] in
  try
     let dig = List.assoc "authorization" req.headers |> Parse.digest_request_from_string in
     match dig.nonce = nonce with
     | false -> raise Not_found
     | true -> (* Nonce is ok, checking another params *)
        let ha1 = hash [t.user; t.realm; t.password] in
        let ha2 = hash [show_method req.meth; dig.uri] in
        let response =
          match dig.qop with
          | `Authi |`Auth -> hash [ha1; dig.nonce; dig.nc; dig.cnonce; "auth"; ha2]
          | `Unknown -> hash [ha1; dig.nonce; ha2]
        in
        if dig.opaque <> digest_opaque || dig.response <> response then raise Not_found;
        `Ok
  with
  | _ ->
    let v = sprintf "Digest realm=\"%s\", qop=\"auth\", nonce=\"%s\", opaque=\"%s\"" t.realm nonce digest_opaque in
    `Unauthorized ("WWW-Authenticate", v)
OCaml

Innovation. Community. Security.