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;
+}