SensESP 3.0.0-beta.6
Universal Signal K sensor toolkit ESP32
Loading...
Searching...
No Matches
http_authenticator.cpp
Go to the documentation of this file.
2
3namespace sensesp {
4
6 char buffer[33]; // buffer to hold 32 Hex Digit + /0
7 int i;
8 for (i = 0; i < 4; i++) {
9 sprintf(buffer + (i * 8), "%08x", static_cast<unsigned int>(esp_random()));
10 }
11 return String(buffer);
12}
13
15 int result = authenticate_digest(req);
16
17 if (result == 1) {
18 return true;
19 } else if (result == 0) {
20 request_authentication(req, false);
21 return 0;
22 } else {
23 request_authentication(req, true);
24 return 0;
25 }
26}
27
29 char auth_header[1024];
30 if (httpd_req_get_hdr_value_str(req, "Authorization", auth_header, 1024) !=
31 ESP_OK) {
32 return 0;
33 }
34
35 String auth_header_str(auth_header);
36 int space_pos = auth_header_str.indexOf(' ');
37 if (space_pos == -1) {
38 return 0;
39 }
40
41 String auth_type = auth_header_str.substring(0, space_pos);
42 if (auth_type != "Digest") {
43 return 0;
44 }
45
46 String auth_str = auth_header_str.substring(space_pos + 1);
47
48 String username = extract_param("username", auth_str);
49 String userhash = extract_param("userhash", auth_str, false);
50
51 if (userhash == "true") {
52 String ref_username = MD5(username_ + ":" + realm_);
53 if (username != ref_username) {
54 return 0;
55 }
56 } else if (username != username_) {
57 // FIXME: Should we disallow unhashed usernames?
58 return 0;
59 }
60
61 // Extract required parameters from the auth string
62 String realm = extract_param("realm", auth_str);
63 String nonce = extract_param("nonce", auth_str);
64 String uri = extract_param("uri", auth_str);
65 String algorithm = extract_param("algorithm", auth_str, false);
66 String qop = extract_param("qop", auth_str, false);
67 String response = extract_param("response", auth_str);
68 String opaque = extract_param("opaque", auth_str);
69 String nc = extract_param("nc", auth_str, false);
70 String cnonce = extract_param("cnonce", auth_str);
71
72 // check that all required parameters are present
73 if (realm == "" || nonce == "" || uri == "" || response == "" ||
74 opaque == "" || nc == "" || cnonce == "") {
75 return 0;
76 }
77
78 // Get the nonce count as an integer
79 int count = strtol(nc.c_str(), NULL, 16);
80
81 // Check if the nonce is valid
82 int nonce_status = find_nonce(nonce, count);
83 if (nonce_status == 0) {
84 return 0;
85 } else if (nonce_status == -1) {
86 return -1;
87 }
88
89 // Check that the realm matches
90 if (realm != realm_) {
91 return 0;
92 }
93
94 int method = req->method;
95 String method_str;
96 switch (method) {
97 case HTTP_GET:
98 method_str = "GET";
99 break;
100 case HTTP_POST:
101 method_str = "POST";
102 break;
103 case HTTP_PUT:
104 method_str = "PUT";
105 break;
106 case HTTP_DELETE:
107 method_str = "DELETE";
108 break;
109 default:
110 return 0;
111 }
112
113 // Calculate the expected response
114 String a1 = username_ + ":" + realm_ + ":" + password_;
115 String a2 = method_str + ":" + uri;
116 String expected_response = MD5(MD5(a1) + ":" + nonce + ":" + nc + ":" +
117 cnonce + ":" + qop + ":" + MD5(a2));
118
119 return response == expected_response ? 1 : 0;
120}
121
123 bool stale) {
124 String server_nonce = create_nonce();
125 String opaque = get_random_hex_string();
126
127 httpd_resp_set_status(req, "401 Unauthorized");
128
129 String header = "Digest realm=\"" + realm_ +
130 "\", qop=\"auth\", "
131 "userhash=true, "
132 "algorithm=\"MD5\", nonce=\"" +
133 server_nonce + "\", opaque=\"" + opaque + "\"";
134
135 if (stale) {
136 header += ", stale=true";
137 }
138
139 httpd_resp_set_hdr(req, "WWW-Authenticate", header.c_str());
140 httpd_resp_send(req, "401 Unauthorized", 0);
141 return ESP_OK;
142}
143
145 // The nonce to return is of the form:
146 // "timestamp MD5(timestamp:secret)"
147
148 String timestamp_str = String(millis());
149 String to_hash = timestamp_str + ":" + secret_;
150 String hash = MD5(to_hash);
151 String nonce = timestamp_str + " " + hash;
152
153 // The nonce should be base64-encoded
154 char encoded[64];
155 size_t output_length;
156 mbedtls_base64_encode((unsigned char*)encoded, sizeof(encoded),
157 &output_length, (uint8_t*)nonce.c_str(),
158 nonce.length());
159 encoded[output_length] = '\0';
160 String nonce_str(encoded);
161 // Add un-encoded nonce to the list of nonces
162 nonces_.push_front({nonce, 1});
163 return nonce_str;
164}
165
166int HTTPDigestAuthenticator::find_nonce(String nonce, int count) {
167 std::list<NonceData>::iterator it;
168 String decoded_nonce;
169 // Decode the base64 encoding
170 size_t output_length;
171 char decoded[64];
172 mbedtls_base64_decode((unsigned char*)decoded, sizeof(decoded),
173 &output_length, (uint8_t*)nonce.c_str(),
174 nonce.length());
175 decoded[output_length] = '\0';
176 decoded_nonce = String(decoded);
177 for (it = nonces_.begin(); it != nonces_.end(); it++) {
178 if (it->nonce == decoded_nonce) {
179 // Disable nonce count checking; at least Chrome sends NC values out of
180 // order if (it->count > count) {
181 // return 0;
182 // }
183 it->count++;
184 // Check if the nonce is stale
185 String timestamp_str = decoded_nonce.substring(0, nonce.indexOf(' '));
186 unsigned long timestamp = timestamp_str.toInt();
187 if (millis() - timestamp > nonce_max_age_) {
188 nonces_.erase(it);
189 return -1;
190 }
191 it->count = count + 1;
192 return 1;
193 }
194 // If the nonce age is 4*nonce_max_age_, remove it from the list
195 String timestamp_str = it->nonce.substring(0, it->nonce.indexOf(' '));
196 unsigned long timestamp = timestamp_str.toInt();
197 if (millis() - timestamp > 4 * nonce_max_age_) {
198 it = nonces_.erase(it);
199 }
200 }
201 return 0;
202}
203
204String HTTPDigestAuthenticator::extract_param(String param, String auth_str,
205 bool quoted) {
206 String quote = quoted ? "\"" : "";
207 int start = auth_str.indexOf(param + "=" + quote);
208 if (start == -1) {
209 return "";
210 }
211 start += param.length() + 1 + (quoted ? 1 : 0);
212 int end = auth_str.indexOf(quoted ? quote : ",", start);
213 if (end == -1) {
214 end = auth_str.length();
215 }
216 return auth_str.substring(start, end);
217}
218
219} // namespace sensesp
int authenticate_digest(httpd_req_t *req)
int find_nonce(String nonce, int count)
Find a nonce in the list of nonces.
virtual bool authenticate_request(httpd_req_t *req) override
Authenticate an incoming request.
esp_err_t request_authentication(httpd_req_t *req, bool stale=false)
String extract_param(String param, String auth_str, bool quoted=true)
String MD5(const String &payload_str)
MD5 hash function.
Definition hash.cpp:43
String get_random_hex_string()