1 /**
2  * @file digest.c  HTTP Digest authentication (RFC 2617)
3  *
4  * Copyright (C) 2010 Creytiv.com
5  */
6 #include <string.h>
7 #include <re_types.h>
8 #include <re_fmt.h>
9 #include <re_mbuf.h>
10 #include <re_md5.h>
11 #include <re_sys.h>
12 #include <re_httpauth.h>
13 
14 
15 typedef void (digest_decode_h)(const struct pl *name, const struct pl *val,
16 			       void *arg);
17 
18 
19 static const struct pl param_algorithm = PL("algorithm");
20 static const struct pl param_cnonce    = PL("cnonce");
21 static const struct pl param_nc        = PL("nc");
22 static const struct pl param_nonce     = PL("nonce");
23 static const struct pl param_opaque    = PL("opaque");
24 static const struct pl param_qop       = PL("qop");
25 static const struct pl param_realm     = PL("realm");
26 static const struct pl param_response  = PL("response");
27 static const struct pl param_uri       = PL("uri");
28 static const struct pl param_username  = PL("username");
29 static const struct pl param_stale     = PL("stale");
30 
31 
32 static void challenge_decode(const struct pl *name, const struct pl *val,
33 			     void *arg)
34 {
35 	struct httpauth_digest_chall *chall = arg;
36 
37 	if (!pl_casecmp(name, &param_realm))
38 		chall->realm = *val;
39 	else if (!pl_casecmp(name, &param_nonce))
40 		chall->nonce = *val;
41 	else if (!pl_casecmp(name, &param_opaque))
42 		chall->opaque= *val;
43 	else if (!pl_casecmp(name, &param_stale))
44 		chall->stale = *val;
45 	else if (!pl_casecmp(name, &param_algorithm))
46 		chall->algorithm = *val;
47 	else if (!pl_casecmp(name, &param_qop))
48 		chall->qop = *val;
49 }
50 
51 
52 static void response_decode(const struct pl *name, const struct pl *val,
53 			    void *arg)
54 {
55 	struct httpauth_digest_resp *resp = arg;
56 
57 	if (!pl_casecmp(name, &param_realm))
58 		resp->realm = *val;
59 	else if (!pl_casecmp(name, &param_nonce))
60 		resp->nonce = *val;
61 	else if (!pl_casecmp(name, &param_response))
62 		resp->response = *val;
63 	else if (!pl_casecmp(name, &param_username))
64 		resp->username = *val;
65 	else if (!pl_casecmp(name, &param_uri))
66 		resp->uri = *val;
67 	else if (!pl_casecmp(name, &param_nc))
68 		resp->nc = *val;
69 	else if (!pl_casecmp(name, &param_cnonce))
70 		resp->cnonce = *val;
71 	else if (!pl_casecmp(name, &param_qop))
72 		resp->qop = *val;
73 }
74 
75 
76 static int digest_decode(const struct pl *hval, digest_decode_h *dech,
77 			 void *arg)
78 {
79 	struct pl r = *hval, start, end, name, val;
80 
81 	if (re_regex(r.p, r.l, "[ \t\r\n]*Digest[ \t\r\n]+", &start, &end) ||
82 	    start.p != r.p)
83 		return EBADMSG;
84 
85 	pl_advance(&r, end.p - r.p);
86 
87 	while (!re_regex(r.p, r.l,
88 			 "[ \t\r\n,]+[a-z]+[ \t\r\n]*=[ \t\r\n]*[~ \t\r\n,]*",
89 			 NULL, &name, NULL, NULL, &val)) {
90 
91 		pl_advance(&r, val.p + val.l - r.p);
92 
93 		dech(&name, &val, arg);
94 	}
95 
96 	return 0;
97 }
98 
99 
100 /**
101  * Decode a Digest challenge
102  *
103  * @param chall Digest challenge object to decode into
104  * @param hval  Header value to decode from
105  *
106  * @return 0 if successfully decoded, otherwise errorcode
107  */
108 int httpauth_digest_challenge_decode(struct httpauth_digest_chall *chall,
109 				     const struct pl *hval)
110 {
111 	int err;
112 
113 	if (!chall || !hval)
114 		return EINVAL;
115 
116 	memset(chall, 0, sizeof(*chall));
117 
118 	err = digest_decode(hval, challenge_decode, chall);
119 	if (err)
120 		return err;
121 
122 	if (!chall->realm.p || !chall->nonce.p)
123 		return EBADMSG;
124 
125 	return 0;
126 }
127 
128 
129 /**
130  * Decode a Digest response
131  *
132  * @param resp Digest response object to decode into
133  * @param hval Header value to decode from
134  *
135  * @return 0 if successfully decoded, otherwise errorcode
136  */
137 int httpauth_digest_response_decode(struct httpauth_digest_resp *resp,
138 				    const struct pl *hval)
139 {
140 	int err;
141 
142 	if (!resp || !hval)
143 		return EINVAL;
144 
145 	memset(resp, 0, sizeof(*resp));
146 
147 	err = digest_decode(hval, response_decode, resp);
148 	if (err)
149 		return err;
150 
151 	if (!resp->realm.p    ||
152 	    !resp->nonce.p    ||
153 	    !resp->response.p ||
154 	    !resp->username.p ||
155 	    !resp->uri.p)
156 		return EBADMSG;
157 
158 	return 0;
159 }
160 
161 
162 /**
163  * Authenticate a digest response
164  *
165  * @param resp   Digest response
166  * @param method Request method
167  * @param ha1    HA1 value from MD5(username:realm:password)
168  *
169  * @return 0 if successfully authenticated, otherwise errorcode
170  */
171 int httpauth_digest_response_auth(const struct httpauth_digest_resp *resp,
172 				  const struct pl *method, const uint8_t *ha1)
173 {
174 	uint8_t ha2[MD5_SIZE], digest[MD5_SIZE], response[MD5_SIZE];
175 	const char *p;
176 	uint32_t i;
177 	int err;
178 
179 	if (!resp || !method || !ha1)
180 		return EINVAL;
181 
182 	if (resp->response.l != 32)
183 		return EAUTH;
184 
185 	err = md5_printf(ha2, "%r:%r", method, &resp->uri);
186 	if (err)
187 		return err;
188 
189 	if (pl_isset(&resp->qop))
190 		err = md5_printf(digest, "%w:%r:%r:%r:%r:%w",
191 				 ha1, (size_t)MD5_SIZE,
192 				 &resp->nonce,
193 				 &resp->nc,
194 				 &resp->cnonce,
195 				 &resp->qop,
196 				 ha2, sizeof(ha2));
197 	else
198 		err = md5_printf(digest, "%w:%r:%w",
199 				 ha1, (size_t)MD5_SIZE,
200 				 &resp->nonce,
201 				 ha2, sizeof(ha2));
202 	if (err)
203 		return err;
204 
205 	for (i=0, p=resp->response.p; i<sizeof(response); i++) {
206 		response[i]  = ch_hex(*p++) << 4;
207 		response[i] += ch_hex(*p++);
208 	}
209 
210 	if (memcmp(digest, response, MD5_SIZE))
211 		return EAUTH;
212 
213 	return 0;
214 }
215