diff --git a/include/httpdconfig.h b/include/httpdconfig.h index 24fca58..56e02e4 100644 --- a/include/httpdconfig.h +++ b/include/httpdconfig.h @@ -4,3 +4,6 @@ //Pos of esp fs in flash #define ESPFS_POS 0x12000 + +//If you want, you can define a realm for the authentication system. +//#define HTTP_AUTH_REALM "MyRealm" \ No newline at end of file diff --git a/user/auth.c b/user/auth.c new file mode 100644 index 0000000..2281ae9 --- /dev/null +++ b/user/auth.c @@ -0,0 +1,68 @@ +/* +HTTP auth implementation. +*/ + +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * Jeroen Domburg wrote this file. As long as you retain + * this notice you can do whatever you want with this stuff. If we meet some day, + * and you think this stuff is worth it, you can buy me a beer in return. + * ---------------------------------------------------------------------------- + */ + + +#include +#include +#include "user_interface.h" +#include "mem.h" +#include "httpd.h" +#include "cgi.h" +#include "auth.h" +#include "io.h" +#include "base64.h" +#include "espmissingincludes.h" +#include + +int ICACHE_FLASH_ATTR authBasic(HttpdConnData *connData) { + const char *forbidden="401 Forbidden."; + int no=0; + int r; + char hdr[(AUTH_MAX_USER_LEN+AUTH_MAX_PASS_LEN+2)*10]; + char userpass[AUTH_MAX_USER_LEN+AUTH_MAX_PASS_LEN+2]; + char user[AUTH_MAX_USER_LEN]; + char pass[AUTH_MAX_PASS_LEN]; + if (connData->conn==NULL) { + //Connection aborted. Clean up. + return HTTPD_CGI_DONE; + } + + r=httpdGetHeader(connData, "Authorization", hdr, sizeof(hdr)); + if (r && strncmp(hdr, "Basic", 5)==0) { + r=base64_decode(strlen(hdr)-6, hdr+6, sizeof(userpass), (unsigned char *)userpass); + if (r<0) r=0; + userpass[r]=0; + os_printf("Auth: %s\n", userpass); + while (((AuthGetUserPw)(connData->cgiArg))(connData, no, + user, AUTH_MAX_USER_LEN, pass, AUTH_MAX_PASS_LEN)) { + //Check user/pass against auth header + if (strlen(userpass)==strlen(user)+strlen(pass)+1 && + os_strncmp(userpass, user, strlen(user))==0 && + userpass[strlen(user)]==':' && + os_strcmp(userpass+strlen(user)+1, pass)==0) { + //Authenticated. Yay! + return HTTPD_CGI_AUTHENTICATED; + } + no++; + } + } + + //Not authenticated. Go bug user with login screen. + httpdStartResponse(connData, 401); + httpdHeader(connData, "Content-Type", "text/plain"); + httpdHeader(connData, "WWW-Authenticate", "Basic realm=\""HTTP_AUTH_REALM"\""); + httpdEndHeaders(connData); + espconn_sent(connData->conn, (uint8 *)forbidden, os_strlen(forbidden)); + return HTTPD_CGI_DONE; +} + diff --git a/user/auth.h b/user/auth.h new file mode 100644 index 0000000..98809b8 --- /dev/null +++ b/user/auth.h @@ -0,0 +1,22 @@ +#ifndef AUTH_H +#define AUTH_H + +#include "httpdconfig.h" + +#ifndef HTTP_AUTH_REALM +#define HTTP_AUTH_REALM "Protected" +#endif + +#define HTTPD_AUTH_SINGLE 0 +#define HTTPD_AUTH_CALLBACK 1 + +#define AUTH_MAX_USER_LEN 32 +#define AUTH_MAX_PASS_LEN 32 + +//Parameter given to authWhatever functions. This callback returns the usernames/passwords the device +//has. +typedef int (* AuthGetUserPw)(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen); + +int ICACHE_FLASH_ATTR authBasic(HttpdConnData *connData); + +#endif \ No newline at end of file diff --git a/user/base64.c b/user/base64.c new file mode 100644 index 0000000..34d1780 --- /dev/null +++ b/user/base64.c @@ -0,0 +1,117 @@ +/* base64.c : base-64 / MIME encode/decode */ +/* PUBLIC DOMAIN - Jon Mayo - November 13, 2003 */ +#include "espmissingincludes.h" +#include "c_types.h" +#include +#include +#include +#include +#include "base64.h" + +static const uint8_t base64dec_tab[256]= { + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255, + 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255, + 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +}; + +#if 0 +static int ICACHE_FLASH_ATTR base64decode(const char in[4], char out[3]) { + uint8_t v[4]; + + v[0]=base64dec_tab[(unsigned)in[0]]; + v[1]=base64dec_tab[(unsigned)in[1]]; + v[2]=base64dec_tab[(unsigned)in[2]]; + v[3]=base64dec_tab[(unsigned)in[3]]; + + out[0]=(v[0]<<2)|(v[1]>>4); + out[1]=(v[1]<<4)|(v[2]>>2); + out[2]=(v[2]<<6)|(v[3]); + return (v[0]|v[1]|v[2]|v[3])!=255 ? in[3]=='=' ? in[2]=='=' ? 1 : 2 : 3 : 0; +} +#endif + +/* decode a base64 string in one shot */ +int ICACHE_FLASH_ATTR base64_decode(size_t in_len, const char *in, size_t out_len, unsigned char *out) { + unsigned ii, io; + uint32_t v; + unsigned rem; + + for(io=0,ii=0,v=0,rem=0;ii=8) { + rem-=8; + if(io>=out_len) return -1; /* truncation is failure */ + out[io++]=(v>>rem)&255; + } + } + if(rem>=8) { + rem-=8; + if(io>=out_len) return -1; /* truncation is failure */ + out[io++]=(v>>rem)&255; + } + return io; +} + +//Only need decode functions for now. +#if 0 + +static const uint8_t base64enc_tab[64]= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +void base64encode(const unsigned char in[3], unsigned char out[4], int count) { + out[0]=base64enc_tab[(in[0]>>2)]; + out[1]=base64enc_tab[((in[0]&3)<<4)|(in[1]>>4)]; + out[2]=count<2 ? '=' : base64enc_tab[((in[1]&15)<<2)|(in[2]>>6)]; + out[3]=count<3 ? '=' : base64enc_tab[(in[2]&63)]; +} + + +int base64_encode(size_t in_len, const unsigned char *in, size_t out_len, char *out) { + unsigned ii, io; + uint_least32_t v; + unsigned rem; + + for(io=0,ii=0,v=0,rem=0;ii=6) { + rem-=6; + if(io>=out_len) return -1; /* truncation is failure */ + out[io++]=base64enc_tab[(v>>rem)&63]; + } + } + if(rem) { + v<<=(6-rem); + if(io>=out_len) return -1; /* truncation is failure */ + out[io++]=base64enc_tab[v&63]; + } + while(io&3) { + if(io>=out_len) return -1; /* truncation is failure */ + out[io++]='='; + } + if(io>=out_len) return -1; /* no room for null terminator */ + out[io]=0; + return io; +} + +#endif \ No newline at end of file diff --git a/user/base64.h b/user/base64.h new file mode 100644 index 0000000..c4ae0f2 --- /dev/null +++ b/user/base64.h @@ -0,0 +1 @@ +int base64_decode(size_t in_len, const char *in, size_t out_len, unsigned char *out); diff --git a/user/user_main.c b/user/user_main.c index 9b765e4..233b533 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -19,6 +19,24 @@ #include "cgi.h" #include "cgiwifi.h" #include "stdout.h" +#include "auth.h" + +//Function that tells the authentication system what users/passwords live on the system. +//This is disabled in the default build; if you want to try it, enable the authBasic line in +//the builtInUrls below. +int myPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen) { + if (no==0) { + os_strcpy(user, "admin"); + os_strcpy(pass, "s3cr3t"); + return 1; +//Add more users this way +// } else if (no==1) { +// os_strcpy(user, "user1"); +// os_strcpy(pass, "something"); +// return 1; + } + return 0; +} HttpdBuiltInUrl builtInUrls[]={ {"/", cgiRedirect, "/index.tpl"}, @@ -28,6 +46,10 @@ HttpdBuiltInUrl builtInUrls[]={ {"/led.cgi", cgiLed, NULL}, //Routines to make the /wifi URL and everything beneath it work. + +//Enable the line below to protect the WiFi configuration with an username/password combo. +// {"/wifi/*", authBasic, myPassFn}, + {"/wifi", cgiRedirect, "/wifi/wifi.tpl"}, {"/wifi/", cgiRedirect, "/wifi/wifi.tpl"}, {"/wifi/wifiscan.cgi", cgiWiFiScan, NULL}, @@ -35,7 +57,6 @@ HttpdBuiltInUrl builtInUrls[]={ {"/wifi/connect.cgi", cgiWiFiConnect, NULL}, {"/wifi/setmode.cgi", cgiWifiSetMode, NULL}, - {"*", cgiEspFsHook, NULL}, //Catch-all cgi function for the filesystem {NULL, NULL, NULL} };