Mercurial > ecos
diff packages/net/athttpd/current/src/auth.c @ 2250:0830ae6acddc
Add ATHTTPD server from Anthony Tonizzo.
| author | jlarmour |
|---|---|
| date | Tue, 18 Jul 2006 16:37:23 +0000 |
| parents | |
| children | b5670f3c40f2 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/net/athttpd/current/src/auth.c @@ -0,0 +1,504 @@ +/* ================================================================= + * + * auth.c + * + * Handles basic authentication. + * + * ================================================================= + * ####ECOSGPLCOPYRIGHTBEGIN#### + * ------------------------------------------- + * This file is part of eCos, the Embedded Configurable Operating + * System. + * Copyright (C) 2005 eCosCentric Ltd. + * + * eCos is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 or (at your option) + * any later version. + * + * eCos is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eCos; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * As a special exception, if other files instantiate templates or + * use macros or inline functions from this file, or you compile this + * file and link it with other works to produce a work based on this + * file, this file does not by itself cause the resulting work to be + * covered by the GNU General Public License. However the source code + * for this file must still be made available in accordance with + * section (3) of the GNU General Public License. + * + * This exception does not invalidate any other reasons why a work + * based on this file might be covered by the GNU General Public + * License. + * + * ------------------------------------------- + * ####ECOSGPLCOPYRIGHTEND#### + * ================================================================= + * #####DESCRIPTIONBEGIN#### + * + * Author(s): Anthony Tonizzo (atonizzo@gmail.com) + * Contributors: + * Date: 2006-06-12 + * Purpose: + * Description: + * + * ####DESCRIPTIONEND#### + * + * ================================================================= + */ +#include <pkgconf/hal.h> +#include <pkgconf/kernel.h> +#include <cyg/kernel/kapi.h> // Kernel API. +#include <cyg/hal/hal_tables.h> + +#include <stdio.h> + +#include <network.h> +#include <string.h> + +#include <cyg/athttpd/http.h> +#include <cyg/athttpd/md5.h> + +// This is the domain that is currently authorized. +cyg_uint8 *cyg_httpd_current_authName; + +CYG_HAL_TABLE_BEGIN(cyg_httpd_auth_table, httpd_auth_table ); +CYG_HAL_TABLE_END(cyg_httpd_auth_table_end, httpd_auth_table ); + +__externC cyg_httpd_auth_table_entry cyg_httpd_auth_table[]; +__externC cyg_httpd_auth_table_entry cyg_httpd_auth_table_end[]; + +// Variables used for authorization. +char cyg_httpd_md5_nonce[33]; +char cyg_httpd_md5_digest[33]; +char cyg_httpd_md5_response[33]; +char cyg_httpd_md5_cnonce[33]; +char cyg_httpd_md5_noncecount[9]; +char cyg_httpd_md5_ha2[HASHHEXLEN+1] = {'\0'}; +char cyg_httpd_md5_ha1[HASHHEXLEN+1]; + +char b64string[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +cyg_httpd_auth_table_entry* +cyg_httpd_auth_entry_from_path(char *authPath) +{ + cyg_httpd_auth_table_entry *entry = cyg_httpd_auth_table; + if (strcmp(authPath, "/") != 0) + while (entry != cyg_httpd_auth_table_end) + { + if (strncmp(entry->auth_dirname, + authPath, + strlen(entry->auth_dirname)) == 0) + return entry; + entry++; + } + else + while (entry != cyg_httpd_auth_table_end) + { + if (strcmp(entry->auth_dirname, authPath) == 0) + return entry; + entry++; + } + + return (cyg_httpd_auth_table_entry *)0; +} + +cyg_httpd_auth_table_entry* +cyg_httpd_auth_entry_from_domain(char *authDomain) +{ + cyg_httpd_auth_table_entry *entry = cyg_httpd_auth_table; + while (entry != cyg_httpd_auth_table_end) + { + if (!strncmp((const char*)authDomain, + entry->auth_domainname, + strlen(entry->auth_domainname))) + return entry; + entry++; + } + + return (cyg_httpd_auth_table_entry *)0; +} + +cyg_int32 +cyg_httpd_base64_encode(char* to, char* from, cyg_uint32 len ) +{ + char *fromp = from; + char *top = to; + char cbyte; + char obyte; + cyg_int8 end[3]; + + for (; len >= 3; len -= 3) + { + cbyte = *fromp++; + *top++ = b64string[(int)(cbyte >> 2)]; + obyte = (cbyte << 4) & 0x30; + + cbyte = *fromp++; + obyte |= (cbyte >> 4); + *top++ = b64string[(cyg_int32)obyte]; + obyte = (cbyte << 2) & 0x3C; + + cbyte = *fromp++; + obyte |= (cbyte >> 6); + *top++ = b64string[(cyg_int32)obyte]; + *top++ = b64string[(cyg_int32)(cbyte & 0x3F)]; + } + + if (len) + { + end[0] = *fromp++; + if (--len ) + end[1] = *fromp++; + else + end[1] = 0; + end[2] = 0; + + cbyte = end[0]; + *top++ = b64string[(cyg_int32)(cbyte >> 2)]; + obyte = (cbyte << 4) & 0x30; + + cbyte = end[1]; + obyte |= (cbyte >> 4); + *top++ = b64string[(cyg_int32)obyte]; + obyte = (cbyte << 2) & 0x3C; + + if (len ) + *top++ = b64string[(cyg_int32)obyte]; + else + *top++ = '='; + *top++ = '='; + } + *top = 0; + return top - to; +} + +cyg_int32 +cyg_httpd_base64_decode(char* to, char* from, cyg_uint32 len ) +{ + char *fromp = from; + char *top = to; + char *p; + char cbyte; + char obyte; + cyg_int32 padding = 0; + + for (; len >= 4; len -= 4) + { + if ((cbyte = *fromp++) == '=') + cbyte = 0; + else + { + if (badchar(cbyte, p ) ) + return -1; + cbyte = (p - b64string); + } + obyte = cbyte << 2; + + if ((cbyte = *fromp++) == '=') + cbyte = 0; + else + { + if (badchar(cbyte, p)) + return -1; + cbyte = p - b64string; + } + obyte |= cbyte >> 4; + *top++ = obyte; + + obyte = cbyte << 4; + if ((cbyte = *fromp++) == '=') + { + cbyte = 0; + padding++; + } + else + { + padding = 0; + if (badchar(cbyte, p)) + return -1; + cbyte = p - b64string; + } + obyte |= cbyte >> 2; + *top++ = obyte; + + obyte = cbyte << 6; + if ((cbyte = *fromp++) == '=') + { + cbyte = 0; + padding++; + } + else + { + padding = 0; + if (badchar(cbyte, p)) + return -1; + cbyte = p - b64string; + } + obyte |= cbyte; + *top++ = obyte; + } + + *top = 0; + if (len) + return -1; + return (top - to) - padding; +} + +cyg_httpd_auth_table_entry* +cyg_httpd_verify_auth(char* username, char* password) +{ + if ((strcmp(httpstate.needs_auth->auth_username, username) == 0) && + (strcmp(httpstate.needs_auth->auth_password, password) == 0)) + return httpstate.needs_auth; + else + return (cyg_httpd_auth_table_entry*)0; +} + +// The following code is a slightly modified version of those available at the +// end of RFC1270. +void cyg_httpd_cvthex(HASH Bin, HASHHEX Hex) +{ + unsigned short i; + unsigned char j; + + for (i = 0; i < HASHLEN; i++) + { + j = (Bin[i] >> 4) & 0xf; + if (j <= 9) + Hex[i*2] = (j + '0'); + else + Hex[i*2] = (j + 'a' - 10); + j = Bin[i] & 0xf; + if (j <= 9) + Hex[i*2+1] = (j + '0'); + else + Hex[i*2+1] = (j + 'a' - 10); + }; + Hex[HASHHEXLEN] = '\0'; +}; + +// Calculate H(A1) as per spec. +void +cyg_httpd_digest_calc_HA1( char *pszAlg, + char *pszUserName, + char *pszRealm, + char *pszPassword, + char *pszNonce, + char *pszCNonce, + HASHHEX SessionKey ) +{ + MD5_CTX Md5Ctx; + HASH HA1; + + MD5Init(&Md5Ctx); + MD5Update(&Md5Ctx, (unsigned char*)pszUserName, strlen(pszUserName)); + MD5Update(&Md5Ctx, (unsigned char*)":", 1); + MD5Update(&Md5Ctx, (unsigned char*)pszRealm, strlen(pszRealm)); + MD5Update(&Md5Ctx, (unsigned char*)":", 1); + MD5Update(&Md5Ctx, (unsigned char*)pszPassword, strlen(pszPassword)); + MD5Final((unsigned char*)HA1, &Md5Ctx); + if (strcmp(pszAlg, "md5-sess") == 0) + { + MD5Init(&Md5Ctx); + MD5Update(&Md5Ctx, (unsigned char*)HA1, HASHLEN); + MD5Update(&Md5Ctx, (unsigned char*)":", 1); + MD5Update(&Md5Ctx, (unsigned char*)pszNonce, strlen(pszNonce)); + MD5Update(&Md5Ctx, (unsigned char*)":", 1); + MD5Update(&Md5Ctx, (unsigned char*)pszCNonce, strlen(pszCNonce)); + MD5Final((unsigned char*)HA1, &Md5Ctx); + }; + cyg_httpd_cvthex(HA1, SessionKey); +}; + +// Calculate request-digest/response-digest as per HTTP Digest spec. +void +cyg_httpd_digest_calc_response(HASHHEX HA1, + char *pszNonce, + char *pszNonceCount, + char *pszCNonce, + char *pszQop, + char *pszMethod, + char *pszDigestUri, + HASHHEX HEntity, + HASHHEX Response) +{ + MD5_CTX Md5Ctx; + HASH HA2; + HASH RespHash; + HASHHEX HA2Hex; + + // Calculate H(A2). + MD5Init(&Md5Ctx); + MD5Update(&Md5Ctx, (unsigned char*)pszMethod, strlen(pszMethod)); + MD5Update(&Md5Ctx, (unsigned char*)":", 1); + MD5Update(&Md5Ctx, (unsigned char*)pszDigestUri, strlen(pszDigestUri)); + if (strcmp(pszQop, "auth-int") == 0) { + MD5Update(&Md5Ctx, (unsigned char*)":", 1); + MD5Update(&Md5Ctx, (unsigned char*)HEntity, HASHHEXLEN); + }; + MD5Final((unsigned char*)HA2, &Md5Ctx); + cyg_httpd_cvthex(HA2, HA2Hex); + + // calculate response + MD5Init(&Md5Ctx); + MD5Update(&Md5Ctx, (unsigned char*)HA1, HASHHEXLEN); + MD5Update(&Md5Ctx, (unsigned char*)":", 1); + MD5Update(&Md5Ctx, (unsigned char*)pszNonce, strlen(pszNonce)); + MD5Update(&Md5Ctx, (unsigned char*)":", 1); + if (*pszQop) + { + MD5Update(&Md5Ctx, (unsigned char*)pszNonceCount, strlen(pszNonceCount)); + MD5Update(&Md5Ctx, (unsigned char*)":", 1); + MD5Update(&Md5Ctx, (unsigned char*)pszCNonce, strlen(pszCNonce)); + MD5Update(&Md5Ctx, (unsigned char*)":", 1); + MD5Update(&Md5Ctx, (unsigned char*)pszQop, strlen(pszQop)); + MD5Update(&Md5Ctx, (unsigned char*)":", 1); + }; + MD5Update(&Md5Ctx, (unsigned char*)HA2Hex, HASHHEXLEN); + MD5Final((unsigned char*)RespHash, &Md5Ctx); + cyg_httpd_cvthex(RespHash, Response); +}; + +cyg_httpd_auth_table_entry* +cyg_httpd_is_authenticated(char* fname) +{ + // Let's check if the directory access needs authorization. The + // authentication is done on the directory name. + cyg_httpd_auth_table_entry* entry = + cyg_httpd_auth_entry_from_path(fname); + if (entry != 0) + { + if (entry->auth_mode == CYG_HTTPD_AUTH_BASIC) + { + cyg_httpd_base64_decode(cyg_httpd_md5_response, + cyg_httpd_md5_digest, + strlen(cyg_httpd_md5_digest)); + char *extension = rindex(cyg_httpd_md5_response, ':'); + if (extension == NULL) + { + return (httpstate.needs_auth = entry); + } + else + { + *extension = '\0'; // Crypto now has the username. + + // In the case of a 'Basic" authentication, the HTTP header + // did not return to us the domain name that we sent when we + // challenged the request: The only things that are returned + // are the username:password duo. In this case I will just + // compare the entry's username/password to those read from + // the header. + if ((strcmp(entry->auth_username, + cyg_httpd_md5_response) != 0) || + (strcmp(entry->auth_password, + ++extension) != 0)) + return (httpstate.needs_auth = entry); + } + } + else + { + char *cyg_httpd_md5_method; + + switch (httpstate.method) + { + case CYG_HTTPD_METHOD_GET: + cyg_httpd_md5_method = "GET"; + break; + case CYG_HTTPD_METHOD_POST: + cyg_httpd_md5_method = "POST"; + break; + default: + cyg_httpd_md5_method = "HEAD"; + break; + } + cyg_httpd_digest_calc_HA1(CYG_HTTPD_MD5_AUTH_NAME, + entry->auth_username, + entry->auth_domainname, + entry->auth_password, + cyg_httpd_md5_nonce, + cyg_httpd_md5_cnonce, + cyg_httpd_md5_ha1); + cyg_httpd_digest_calc_response(cyg_httpd_md5_ha1, + cyg_httpd_md5_nonce, + cyg_httpd_md5_noncecount, + cyg_httpd_md5_cnonce, + CYG_HTTPD_MD5_AUTH_QOP, + cyg_httpd_md5_method, + httpstate.url, + cyg_httpd_md5_ha2, + cyg_httpd_md5_digest); + if (strcmp(cyg_httpd_md5_response, cyg_httpd_md5_digest) != 0) + return (httpstate.needs_auth = entry); + } + } + // No need for authentication... + return (cyg_httpd_auth_table_entry*)0; +} + +char* +cyg_httpd_digest_data(char *dest, char *src) +{ + int exit = 0; + while (exit == 0) + { + switch (*src ) + { + case '\r': + case '\n': + *dest = '\0'; + exit = 1; + break; + case ' ': + src++; + *dest = '\0'; + exit = 1; + break; + case '"': + case ',': + src++; + break; + default: + *dest++ = *src++; + } + } + return src; +} + +// Skips through fields we do not need. +char* +cyg_httpd_digest_skip(char *p) +{ + if (*p == '"') + { + p++; + while ((*p != '"') && (*p != '\n')) + p++; + p++; + if (*p == ',') + p++; + if (*p == ' ') + p++; + if (*p == '\n') + p++; + } + else + { + while ((*p != ' ') && (*p != '\n')) + p++; + if (*p == ',') + p++; + if (*p == ' ') + p++; + if (*p == '\n') + p++; + } + return p; +}
