Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
http_server_auth.c
00001 /** 00002 * @file http_server_auth.c 00003 * @brief HTTP authentication 00004 * 00005 * @section License 00006 * 00007 * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. 00008 * 00009 * This file is part of CycloneTCP Open. 00010 * 00011 * This program is free software; you can redistribute it and/or 00012 * modify it under the terms of the GNU General Public License 00013 * as published by the Free Software Foundation; either version 2 00014 * of the License, or (at your option) any later version. 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU General Public License for more details. 00020 * 00021 * You should have received a copy of the GNU General Public License 00022 * along with this program; if not, write to the Free Software Foundation, 00023 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00024 * 00025 * @author Oryx Embedded SARL (www.oryx-embedded.com) 00026 * @version 1.7.6 00027 **/ 00028 00029 //Switch to the appropriate trace level 00030 #define TRACE_LEVEL HTTP_TRACE_LEVEL 00031 00032 //Dependencies 00033 #include <stdlib.h> 00034 #include "core/net.h" 00035 #include "http/http_server.h" 00036 #include "http/http_server_auth.h" 00037 #include "http/http_server_misc.h" 00038 #include "str.h" 00039 #include "debug.h" 00040 00041 //Check TCP/IP stack configuration 00042 #if (HTTP_SERVER_SUPPORT == ENABLED) 00043 00044 00045 /** 00046 * @brief Password verification 00047 * @param[in] connection Structure representing an HTTP connection 00048 * @param[in] password NULL-terminated string containing the password to be checked 00049 * @param[in] mode HTTP authentication scheme to be used. Acceptable 00050 * values are HTTP_AUTH_MODE_BASIC or HTTP_AUTH_MODE_DIGEST 00051 * @return TRUE if the password is valid, else FALSE 00052 **/ 00053 00054 bool_t httpCheckPassword(HttpConnection *connection, 00055 const char_t *password, HttpAuthMode mode) 00056 { 00057 //This flag tells whether the password is valid 00058 bool_t status = FALSE; 00059 00060 //Debug message 00061 TRACE_DEBUG("HTTP password verification...\r\n"); 00062 00063 #if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED) 00064 //Basic authentication scheme? 00065 if(mode == HTTP_AUTH_MODE_BASIC) 00066 { 00067 //Point to the authentication credentials 00068 HttpAuthorizationHeader *auth = &connection->request.auth; 00069 00070 //Make sure authentication credentials have been found 00071 if(auth->found && auth->mode == HTTP_AUTH_MODE_BASIC) 00072 { 00073 //Sanity check 00074 if(auth->password != NULL) 00075 { 00076 //Check whether the password is valid 00077 if(!strcmp(password, auth->password)) 00078 status = TRUE; 00079 } 00080 } 00081 } 00082 #endif 00083 #if (HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED) 00084 //Digest authentication scheme? 00085 if(mode == HTTP_AUTH_MODE_DIGEST) 00086 { 00087 //Point to the authentication credentials 00088 HttpAuthorizationHeader *auth = &connection->request.auth; 00089 00090 //Make sure authentication credentials have been found 00091 if(auth->found && auth->mode == HTTP_AUTH_MODE_DIGEST) 00092 { 00093 //Sanity check 00094 if(auth->realm != NULL && auth->nonce != NULL && 00095 auth->uri != NULL && auth->qop != NULL && 00096 auth->nc != NULL && auth->cnonce != NULL && 00097 auth->response != NULL) 00098 { 00099 error_t error; 00100 Md5Context *md5Context; 00101 char_t ha1[2 * MD5_DIGEST_SIZE + 1]; 00102 char_t ha2[2 * MD5_DIGEST_SIZE + 1]; 00103 00104 //Allocate a memory buffer to hold the MD5 context 00105 md5Context = osAllocMem(sizeof(Md5Context)); 00106 00107 //MD5 context successfully allocated? 00108 if(md5Context != NULL) 00109 { 00110 //Compute HA1 = MD5(username : realm : password) 00111 md5Init(md5Context); 00112 md5Update(md5Context, auth->user, strlen(auth->user)); 00113 md5Update(md5Context, ":", 1); 00114 md5Update(md5Context, auth->realm, strlen(auth->realm)); 00115 md5Update(md5Context, ":", 1); 00116 md5Update(md5Context, password, strlen(password)); 00117 md5Final(md5Context, NULL); 00118 00119 //Convert MD5 hash to hex string 00120 httpConvertArrayToHexString(md5Context->digest, MD5_DIGEST_SIZE, ha1); 00121 //Debug message 00122 TRACE_DEBUG(" HA1: %s\r\n", ha1); 00123 00124 //Compute HA2 = MD5(method : uri) 00125 md5Init(md5Context); 00126 md5Update(md5Context, connection->request.method, strlen(connection->request.method)); 00127 md5Update(md5Context, ":", 1); 00128 md5Update(md5Context, auth->uri, strlen(auth->uri)); 00129 md5Final(md5Context, NULL); 00130 00131 //Convert MD5 hash to hex string 00132 httpConvertArrayToHexString(md5Context->digest, MD5_DIGEST_SIZE, ha2); 00133 //Debug message 00134 TRACE_DEBUG(" HA2: %s\r\n", ha2); 00135 00136 //Compute MD5(HA1 : nonce : nc : cnonce : qop : HA1) 00137 md5Init(md5Context); 00138 md5Update(md5Context, ha1, strlen(ha1)); 00139 md5Update(md5Context, ":", 1); 00140 md5Update(md5Context, auth->nonce, strlen(auth->nonce)); 00141 md5Update(md5Context, ":", 1); 00142 md5Update(md5Context, auth->nc, strlen(auth->nc)); 00143 md5Update(md5Context, ":", 1); 00144 md5Update(md5Context, auth->cnonce, strlen(auth->cnonce)); 00145 md5Update(md5Context, ":", 1); 00146 md5Update(md5Context, auth->qop, strlen(auth->qop)); 00147 md5Update(md5Context, ":", 1); 00148 md5Update(md5Context, ha2, strlen(ha2)); 00149 md5Final(md5Context, NULL); 00150 00151 //Convert MD5 hash to hex string 00152 httpConvertArrayToHexString(md5Context->digest, MD5_DIGEST_SIZE, ha1); 00153 //Debug message 00154 TRACE_DEBUG(" response: %s\r\n", ha1); 00155 00156 //Release MD5 context 00157 osFreeMem(md5Context); 00158 00159 //Check response 00160 if(!strcasecmp(auth->response, ha1)) 00161 { 00162 //Perform nonce verification 00163 error = httpVerifyNonce(connection->serverContext, auth->nonce, auth->nc); 00164 00165 //Valid nonce? 00166 if(!error) 00167 { 00168 //Access to the resource is granted 00169 status = TRUE; 00170 } 00171 else 00172 { 00173 //The client may wish to simply retry the request with a 00174 //new encrypted response, without re-prompting the user 00175 //for a new username and password 00176 connection->response.auth.stale = TRUE; 00177 } 00178 } 00179 } 00180 } 00181 } 00182 } 00183 #endif 00184 00185 //Return TRUE is the password is valid, else FALSE 00186 return status; 00187 } 00188 00189 00190 /** 00191 * @brief Parse Authorization header field 00192 * @param[in] connection Structure representing an HTTP connection 00193 * @param[in] value Authorization field value 00194 **/ 00195 00196 void httpParseAuthorizationField(HttpConnection *connection, char_t *value) 00197 { 00198 char_t *p; 00199 char_t *token; 00200 00201 //Retrieve the authentication scheme 00202 token = strtok_r(value, " \t", &p); 00203 00204 //Any parsing error? 00205 if(token == NULL) 00206 { 00207 //Exit immediately 00208 return; 00209 } 00210 #if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED) 00211 //Basic access authentication? 00212 else if(!strcasecmp(token, "Basic")) 00213 { 00214 error_t error; 00215 size_t n; 00216 char_t *separator; 00217 00218 //Use the relevant authentication scheme 00219 connection->request.auth.mode = HTTP_AUTH_MODE_BASIC; 00220 //Retrieve the credentials 00221 token = strtok_r(NULL, " \t", &p); 00222 00223 //Any parsing error? 00224 if(token != NULL) 00225 { 00226 //Decrypt the Base64 encoded string 00227 error = base64Decode(token, strlen(token), token, &n); 00228 00229 //Successful decoding? 00230 if(!error) 00231 { 00232 //Properly terminate the string 00233 token[n] = '\0'; 00234 //Check whether a separator is present 00235 separator = strchr(token, ':'); 00236 00237 //Separator found? 00238 if(separator != NULL) 00239 { 00240 //Split the line 00241 *separator = '\0'; 00242 00243 //Save user name 00244 strSafeCopy(connection->request.auth.user, 00245 token, HTTP_SERVER_USERNAME_MAX_LEN); 00246 00247 //Point to the password 00248 token = separator + 1; 00249 //Save password 00250 connection->request.auth.password = token; 00251 } 00252 } 00253 } 00254 00255 //Debug message 00256 TRACE_DEBUG("Authorization header:\r\n"); 00257 TRACE_DEBUG(" username: %s\r\n", connection->request.auth.user); 00258 TRACE_DEBUG(" password: %s\r\n", connection->request.auth.password); 00259 } 00260 #endif 00261 #if (HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED) 00262 //Digest access authentication? 00263 else if(!strcasecmp(token, "Digest")) 00264 { 00265 size_t n; 00266 char_t *separator; 00267 char_t *name; 00268 00269 //Use the relevant authentication scheme 00270 connection->request.auth.mode = HTTP_AUTH_MODE_DIGEST; 00271 //Get the first parameter 00272 token = strtok_r(NULL, ",", &p); 00273 00274 //Parse the Authorization header field 00275 while(token != NULL) 00276 { 00277 //Check whether a separator is present 00278 separator = strchr(token, '='); 00279 00280 //Separator found? 00281 if(separator != NULL) 00282 { 00283 //Split the string 00284 *separator = '\0'; 00285 00286 //Get field name and value 00287 name = strTrimWhitespace(token); 00288 value = strTrimWhitespace(separator + 1); 00289 00290 //Retrieve the length of the value field 00291 n = strlen(value); 00292 00293 //Discard the surrounding quotes 00294 if(n > 0 && value[n - 1] == '\"') 00295 value[n - 1] = '\0'; 00296 if(value[0] == '\"') 00297 value++; 00298 00299 //Check parameter name 00300 if(!strcasecmp(name, "username")) 00301 { 00302 //Save user name 00303 strSafeCopy(connection->request.auth.user, 00304 value, HTTP_SERVER_USERNAME_MAX_LEN); 00305 } 00306 else if(!strcasecmp(name, "realm")) 00307 { 00308 //Save realm 00309 connection->request.auth.realm = value; 00310 } 00311 else if(!strcasecmp(name, "nonce")) 00312 { 00313 //Save nonce parameter 00314 connection->request.auth.nonce = value; 00315 } 00316 else if(!strcasecmp(name, "uri")) 00317 { 00318 //Save uri parameter 00319 connection->request.auth.uri = value; 00320 } 00321 else if(!strcasecmp(name, "qop")) 00322 { 00323 //Save qop parameter 00324 connection->request.auth.qop = value; 00325 } 00326 else if(!strcasecmp(name, "nc")) 00327 { 00328 //Save nc parameter 00329 connection->request.auth.nc = value; 00330 } 00331 else if(!strcasecmp(name, "cnonce")) 00332 { 00333 //Save cnonce parameter 00334 connection->request.auth.cnonce = value; 00335 } 00336 else if(!strcasecmp(name, "response")) 00337 { 00338 //Save response parameter 00339 connection->request.auth.response = value; 00340 } 00341 else if(!strcasecmp(name, "opaque")) 00342 { 00343 //Save opaque parameter 00344 connection->request.auth.opaque = value; 00345 } 00346 00347 //Get next parameter 00348 token = strtok_r(NULL, ",", &p); 00349 } 00350 } 00351 00352 //Debug message 00353 TRACE_DEBUG("Authorization header:\r\n"); 00354 TRACE_DEBUG(" username: %s\r\n", connection->request.auth.user); 00355 TRACE_DEBUG(" realm: %s\r\n", connection->request.auth.realm); 00356 TRACE_DEBUG(" nonce: %s\r\n", connection->request.auth.nonce); 00357 TRACE_DEBUG(" uri: %s\r\n", connection->request.auth.uri); 00358 TRACE_DEBUG(" qop: %s\r\n", connection->request.auth.qop); 00359 TRACE_DEBUG(" nc: %s\r\n", connection->request.auth.nc); 00360 TRACE_DEBUG(" cnonce: %s\r\n", connection->request.auth.cnonce); 00361 TRACE_DEBUG(" response: %s\r\n", connection->request.auth.response); 00362 TRACE_DEBUG(" opaque: %s\r\n", connection->request.auth.opaque); 00363 } 00364 #endif 00365 else 00366 { 00367 //The specified authentication scheme is not supported 00368 return; 00369 } 00370 00371 #if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED || HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED) 00372 //The Authorization header has been found 00373 connection->request.auth.found = TRUE; 00374 00375 //Invoke user-defined callback, if any 00376 if(connection->settings->authCallback != NULL) 00377 { 00378 //Check whether the access to the specified URI is authorized 00379 connection->status = connection->settings->authCallback(connection, 00380 connection->request.auth.user, connection->request.uri); 00381 } 00382 else 00383 { 00384 //Access to the specified URI is allowed 00385 connection->status = HTTP_ACCESS_ALLOWED; 00386 } 00387 #endif 00388 } 00389 00390 00391 /** 00392 * @brief Format WWW-Authenticate header field 00393 * @param[in] connection Structure representing an HTTP connection 00394 * @param[out] output Buffer where to format the header field 00395 * @return Total length of the header field 00396 **/ 00397 00398 size_t httpAddAuthenticateField(HttpConnection *connection, char_t *output) 00399 { 00400 size_t n; 00401 00402 #if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED) 00403 //Basic authentication scheme? 00404 if(connection->response.auth.mode == HTTP_AUTH_MODE_BASIC) 00405 { 00406 //Set WWW-Authenticate field 00407 n = sprintf(output, "WWW-Authenticate: Basic realm=\"Protected Area\"\r\n"); 00408 } 00409 else 00410 #endif 00411 #if (HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED) 00412 //Digest authentication scheme? 00413 if(connection->response.auth.mode == HTTP_AUTH_MODE_DIGEST) 00414 { 00415 error_t error; 00416 size_t k; 00417 uint8_t opaque[16]; 00418 00419 //Set WWW-Authenticate field 00420 n = sprintf(output, "WWW-Authenticate: Digest\r\n"); 00421 n += sprintf(output + n, " realm=\"Protected Area\",\r\n"); 00422 n += sprintf(output + n, " qop=\"auth\",\r\n"); 00423 n += sprintf(output + n, " nonce=\""); 00424 00425 //The nonce is a server-specified data string which should be uniquely 00426 //generated each time a 401 response is made 00427 error = httpGenerateNonce(connection->serverContext, output + n, &k); 00428 //Any error to report? 00429 if(error) 00430 return error; 00431 00432 //Advance pointer 00433 n += k; 00434 //Properly terminate the nonce string 00435 n += sprintf(output + n, "\",\r\n"); 00436 00437 //Format opaque parameter 00438 n += sprintf(output + n, " opaque=\""); 00439 00440 //Generate a random value 00441 if(connection->settings->randCallback != NULL) 00442 error = connection->settings->randCallback(opaque, 16); 00443 else 00444 error = ERROR_FAILURE; 00445 00446 //Random number generation failed? 00447 if(error) 00448 return error; 00449 00450 //Convert the byte array to hex string 00451 httpConvertArrayToHexString(opaque, 16, output + n); 00452 00453 //Advance pointer 00454 n += 32; 00455 //Properly terminate the opaque string 00456 n += sprintf(output + n, "\""); 00457 00458 //The STALE flag indicates that the previous request from the client 00459 //was rejected because the nonce value was stale 00460 if(connection->response.auth.stale) 00461 n += sprintf(output + n, ",\r\n stale=TRUE"); 00462 00463 //Properly terminate the WWW-Authenticate field 00464 n += sprintf(output + n, "\r\n"); 00465 } 00466 else 00467 #endif 00468 //Unknown authentication scheme? 00469 { 00470 //No need to add the WWW-Authenticate header field 00471 n = 0; 00472 } 00473 00474 //Return the total length of the WWW-Authenticate header field 00475 return n; 00476 } 00477 00478 00479 /** 00480 * @brief Nonce generation 00481 * @param[in] context Pointer to the HTTP server context 00482 * @param[in] output NULL-terminated string containing the nonce 00483 * @param[in] length NULL-terminated string containing the nonce count 00484 * @return Error code 00485 **/ 00486 00487 error_t httpGenerateNonce(HttpServerContext *context, 00488 char_t *output, size_t *length) 00489 { 00490 #if (HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED) 00491 error_t error; 00492 uint_t i; 00493 HttpNonceCacheEntry *entry; 00494 HttpNonceCacheEntry *oldestEntry; 00495 uint8_t nonce[HTTP_SERVER_NONCE_SIZE]; 00496 00497 //Acquire exclusive access to the nonce cache 00498 osAcquireMutex(&context->nonceCacheMutex); 00499 00500 //Keep track of the oldest entry 00501 oldestEntry = &context->nonceCache[0]; 00502 00503 //Loop through nonce cache entries 00504 for(i = 0; i < HTTP_SERVER_NONCE_CACHE_SIZE; i++) 00505 { 00506 //Point to the current entry 00507 entry = &context->nonceCache[i]; 00508 00509 //Check whether the entry is currently in used or not 00510 if(!entry->count) 00511 break; 00512 00513 //Keep track of the oldest entry in the table 00514 if(timeCompare(entry->timestamp, oldestEntry->timestamp) < 0) 00515 oldestEntry = entry; 00516 } 00517 00518 //The oldest entry is removed whenever the table runs out of space 00519 if(i >= HTTP_SERVER_NONCE_CACHE_SIZE) 00520 entry = oldestEntry; 00521 00522 //Generate a new nonce 00523 if(context->settings.randCallback != NULL) 00524 error = context->settings.randCallback(nonce, HTTP_SERVER_NONCE_SIZE); 00525 else 00526 error = ERROR_FAILURE; 00527 00528 //Check status code 00529 if(!error) 00530 { 00531 //Convert the byte array to hex string 00532 httpConvertArrayToHexString(nonce, HTTP_SERVER_NONCE_SIZE, entry->nonce); 00533 //Clear nonce count 00534 entry->count = 1; 00535 //Save the time at which the nonce was generated 00536 entry->timestamp = osGetSystemTime(); 00537 00538 //Copy the nonce to the output buffer 00539 strcpy(output, entry->nonce); 00540 //Return the length of the nonce excluding the NULL character 00541 *length = HTTP_SERVER_NONCE_SIZE * 2; 00542 } 00543 else 00544 { 00545 //Random number generation failed 00546 memset(entry, 0, sizeof(HttpNonceCacheEntry)); 00547 } 00548 00549 //Release exclusive access to the nonce cache 00550 osReleaseMutex(&context->nonceCacheMutex); 00551 //Return status code 00552 return error; 00553 00554 #else 00555 //Not implemented 00556 return ERROR_NOT_IMPLEMENTED; 00557 #endif 00558 } 00559 00560 00561 /** 00562 * @brief Nonce verification 00563 * @param[in] context Pointer to the HTTP server context 00564 * @param[in] nonce NULL-terminated string containing the nonce 00565 * @param[in] nc NULL-terminated string containing the nonce count 00566 * @return Error code 00567 **/ 00568 00569 error_t httpVerifyNonce(HttpServerContext *context, 00570 const char_t *nonce, const char_t *nc) 00571 { 00572 #if (HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED) 00573 error_t error; 00574 uint_t i; 00575 uint32_t count; 00576 systime_t time; 00577 HttpNonceCacheEntry *entry; 00578 00579 //Check parameters 00580 if(nonce == NULL || nc == NULL) 00581 return ERROR_INVALID_PARAMETER; 00582 00583 //Convert the nonce count to integer 00584 count = strtoul(nc, NULL, 16); 00585 //Get current time 00586 time = osGetSystemTime(); 00587 00588 //Acquire exclusive access to the nonce cache 00589 osAcquireMutex(&context->nonceCacheMutex); 00590 00591 //Loop through nonce cache entries 00592 for(i = 0; i < HTTP_SERVER_NONCE_CACHE_SIZE; i++) 00593 { 00594 //Point to the current entry 00595 entry = &context->nonceCache[i]; 00596 00597 //Check nonce value 00598 if(!strcasecmp(entry->nonce, nonce)) 00599 { 00600 //Make sure the nonce timestamp has not expired 00601 if((time - entry->timestamp) < HTTP_SERVER_NONCE_LIFETIME) 00602 { 00603 //Check nonce count to prevent replay attacks 00604 if(count >= entry->count) 00605 { 00606 //Update nonce count to the next expected value 00607 entry->count = count + 1; 00608 //We are done 00609 break; 00610 } 00611 } 00612 } 00613 } 00614 00615 //Check whether the nonce is valid 00616 if(i < HTTP_SERVER_NONCE_CACHE_SIZE) 00617 error = NO_ERROR; 00618 else 00619 error = ERROR_NOT_FOUND; 00620 00621 //Release exclusive access to the nonce cache 00622 osReleaseMutex(&context->nonceCacheMutex); 00623 //Return status code 00624 return error; 00625 00626 #else 00627 //Not implemented 00628 return ERROR_NOT_IMPLEMENTED; 00629 #endif 00630 } 00631 00632 #endif 00633
Generated on Tue Jul 12 2022 17:10:13 by
1.7.2