blob: 584511e6484c19bbb2bec2cd799ea34e447a1fe1 [file] [log] [blame]
James Kuszmaul82f6c042021-01-17 11:30:16 -08001/**
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
15typedef void (digest_decode_h)(const struct pl *name, const struct pl *val,
16 void *arg);
17
18
19static const struct pl param_algorithm = PL("algorithm");
20static const struct pl param_cnonce = PL("cnonce");
21static const struct pl param_nc = PL("nc");
22static const struct pl param_nonce = PL("nonce");
23static const struct pl param_opaque = PL("opaque");
24static const struct pl param_qop = PL("qop");
25static const struct pl param_realm = PL("realm");
26static const struct pl param_response = PL("response");
27static const struct pl param_uri = PL("uri");
28static const struct pl param_username = PL("username");
29static const struct pl param_stale = PL("stale");
30
31
32static 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
52static 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
76static 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 */
108int 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 */
137int 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 */
171int 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}