From: Jo-Philipp Wich Date: Sat, 20 Mar 2010 23:51:51 +0000 (+0000) Subject: uhttpd: add basic auth infrastructure X-Git-Tag: 0.10.0~799 X-Git-Url: https://git.archive.openwrt.org/?p=project%2Fluci.git;a=commitdiff_plain;h=95b9bb0f69fa4ed2bc2c414a614d1723b1e59e21 uhttpd: add basic auth infrastructure --- diff --git a/contrib/package/uhttpd/src/uhttpd-cgi.c b/contrib/package/uhttpd/src/uhttpd-cgi.c index a89dffa61..9e9e96410 100644 --- a/contrib/package/uhttpd/src/uhttpd-cgi.c +++ b/contrib/package/uhttpd/src/uhttpd-cgi.c @@ -248,6 +248,10 @@ void uh_cgi_request(struct client *cl, struct http_request *req, struct path_inf /* request url */ setenv("REQUEST_URI", req->url, 1); + /* remote user */ + if( req->realm ) + setenv("REMOTE_USER", req->realm->user, 1); + /* request message headers */ foreach_header(i, req->headers) { diff --git a/contrib/package/uhttpd/src/uhttpd-file.c b/contrib/package/uhttpd/src/uhttpd-file.c index 63c2d7e4f..e8ea17464 100644 --- a/contrib/package/uhttpd/src/uhttpd-file.c +++ b/contrib/package/uhttpd/src/uhttpd-file.c @@ -16,8 +16,8 @@ * limitations under the License. */ -#define _XOPEN_SOURCE 500 /* strptime() ... */ -#define _BSD_SOURCE /* scandir() ... */ +#define _XOPEN_SOURCE 500 /* strptime() */ +#define _BSD_SOURCE /* scandir(), timegm() */ #include "uhttpd.h" #include "uhttpd-utils.h" diff --git a/contrib/package/uhttpd/src/uhttpd-utils.c b/contrib/package/uhttpd/src/uhttpd-utils.c index 12d68ff86..19918da16 100644 --- a/contrib/package/uhttpd/src/uhttpd-utils.c +++ b/contrib/package/uhttpd/src/uhttpd-utils.c @@ -16,6 +16,9 @@ * limitations under the License. */ +#define _XOPEN_SOURCE 500 /* crypt() */ +#define _BSD_SOURCE /* strcasecmp(), strncasecmp() */ + #include "uhttpd.h" #include "uhttpd-utils.h" @@ -304,50 +307,52 @@ int uh_urlencode(char *buf, int blen, const char *src, int slen) return len; } -int uh_path_normalize(char *buf, int blen, const char *src, int slen) +int uh_b64decode(char *buf, int blen, const unsigned char *src, int slen) { - int i, skip; + int i = 0; int len = 0; - for( i = 0, skip = 1; (i <= slen) && (src[i] != 0); i++ ) - { - /* collapse multiple "/" into one */ - if( src[i] == '/' ) - { - /* collapse "/../" to "/" */ - if( ((i+2) <= slen) && (src[i+1] == '.') && (src[i+2] == '.') && - (((i+3) > slen) || (src[i+3] == '/')) - ) { - i += 2; - continue; - } + unsigned int cin = 0; + unsigned int cout = 0; - /* collapse "/./" to "/" */ - else if( ((i+1) <= slen) && (src[i+1] == '.') && - (((i+2) > slen) || (src[i+2] == '/')) - ) { - i += 1; - continue; - } - /* skip repeating "/" */ - else if( skip ) - { - continue; - } + for( i = 0; (i <= slen) && (src[i] != 0); i++ ) + { + cin = src[i]; + + if( (cin >= '0') && (cin <= '9') ) + cin = cin - '0' + 52; + else if( (cin >= 'A') && (cin <= 'Z') ) + cin = cin - 'A'; + else if( (cin >= 'a') && (cin <= 'z') ) + cin = cin - 'a' + 26; + else if( cin == '+' ) + cin = 62; + else if( cin == '/' ) + cin = 63; + else if( cin == '=' ) + cin = 0; + else + continue; - skip++; - } + cout = (cout << 6) | cin; - /* finally a harmless char */ - else + if( (i % 4) == 3 ) { - skip = 0; + if( (len + 3) < blen ) + { + buf[len++] = (char)(cout >> 16); + buf[len++] = (char)(cout >> 8); + buf[len++] = (char)(cout); + } + else + { + break; + } } - - buf[len++] = src[i]; } + buf[len++] = 0; return len; } @@ -372,7 +377,8 @@ struct path_info * uh_path_lookup(struct client *cl, const char *url) memset(&p, 0, sizeof(p)); /* copy docroot */ - memcpy(buffer, docroot, sizeof(buffer)); + memcpy(buffer, docroot, + min(strlen(docroot), sizeof(buffer) - 1)); /* separate query string from url */ if( (pathptr = strchr(url, '?')) != NULL ) @@ -473,6 +479,170 @@ struct path_info * uh_path_lookup(struct client *cl, const char *url) } +static char uh_realms[UH_LIMIT_AUTHREALMS * sizeof(struct auth_realm)] = { 0 }; +static int uh_realm_count = 0; + +struct auth_realm * uh_auth_add( + char *path, char *realm, char *user, char *pass +) { + struct auth_realm *new = NULL; + struct passwd *pwd; + struct spwd *spwd; + + if( uh_realm_count < UH_LIMIT_AUTHREALMS ) + { + new = (struct auth_realm *) + &uh_realms[uh_realm_count * sizeof(struct auth_realm)]; + + memset(new, 0, sizeof(struct auth_realm)); + + memcpy(new->realm, realm, + min(strlen(realm), sizeof(new->realm) - 1)); + + memcpy(new->path, path, + min(strlen(path), sizeof(new->path) - 1)); + + memcpy(new->user, user, + min(strlen(user), sizeof(new->user) - 1)); + + /* given password refers to a passwd entry */ + if( (strlen(pass) > 3) && !strncmp(pass, "$p$", 3) ) + { + /* try to resolve shadow entry */ + if( ((spwd = getspnam(&pass[3])) != NULL) && spwd->sp_pwdp ) + { + memcpy(new->pass, spwd->sp_pwdp, + min(strlen(spwd->sp_pwdp), sizeof(new->pass) - 1)); + } + + /* try to resolve passwd entry */ + else if( ((pwd = getpwnam(&pass[3])) != NULL) && pwd->pw_passwd && + (pwd->pw_passwd[0] != '!') && (pwd->pw_passwd[0] != 0) + ) { + memcpy(new->pass, pwd->pw_passwd, + min(strlen(pwd->pw_passwd), sizeof(new->pass) - 1)); + } + } + + /* ordinary pwd */ + else + { + memcpy(new->pass, pass, + min(strlen(pass), sizeof(new->pass) - 1)); + } + + uh_realm_count++; + } + + return new; +} + +int uh_auth_check( + struct client *cl, struct http_request *req, struct path_info *pi +) { + int i, plen, rlen, protected; + char buffer[UH_LIMIT_MSGHEAD]; + char *user = NULL; + char *pass = NULL; + + struct auth_realm *realm = NULL; + + plen = strlen(pi->name); + protected = 0; + + /* check whether at least one realm covers the requested url */ + for( i = 0; i < uh_realm_count; i++ ) + { + realm = (struct auth_realm *) + &uh_realms[i * sizeof(struct auth_realm)]; + + rlen = strlen(realm->path); + + if( (plen >= rlen) && !strncasecmp(pi->name, realm->path, rlen) ) + { + req->realm = realm; + protected = 1; + break; + } + } + + /* requested resource is covered by a realm */ + if( protected ) + { + /* try to get client auth info */ + foreach_header(i, req->headers) + { + if( !strcasecmp(req->headers[i], "Authorization") && + (strlen(req->headers[i+1]) > 6) && + !strncasecmp(req->headers[i+1], "Basic ", 6) + ) { + memset(buffer, 0, sizeof(buffer)); + uh_b64decode(buffer, sizeof(buffer) - 1, + (unsigned char *) &req->headers[i+1][6], + strlen(req->headers[i+1]) - 6); + + if( (pass = strchr(buffer, ':')) != NULL ) + { + user = buffer; + *pass++ = 0; + } + + break; + } + } + + /* have client auth */ + if( user && pass ) + { + /* find matching realm */ + for( i = 0, realm = NULL; i < uh_realm_count; i++ ) + { + realm = (struct auth_realm *) + &uh_realms[i * sizeof(struct auth_realm)]; + + rlen = strlen(realm->path); + + if( (plen >= rlen) && + !strncasecmp(pi->name, realm->path, rlen) && + !strcmp(user, realm->user) + ) { + req->realm = realm; + break; + } + + realm = NULL; + } + + /* found a realm matching the username */ + if( realm ) + { + /* is a crypt passwd */ + if( realm->pass[0] == '$' ) + pass = crypt(pass, realm->pass); + + /* check user pass */ + if( !strcmp(pass, realm->pass) ) + return 1; + } + } + + /* 401 */ + uh_http_sendf(cl, NULL, + "HTTP/%.1f 401 Authorization Required\r\n" + "WWW-Authenticate: Basic realm=\"%s\"\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: 23\r\n\r\n" + "Authorization Required\n", + req->version, realm ? realm->realm : "" + ); + + return 0; + } + + return 1; +} + + static char uh_listeners[UH_LIMIT_LISTENERS * sizeof(struct listener)] = { 0 }; static char uh_clients[UH_LIMIT_CLIENTS * sizeof(struct client)] = { 0 }; diff --git a/contrib/package/uhttpd/src/uhttpd-utils.h b/contrib/package/uhttpd/src/uhttpd-utils.h index 7d4bcf6f8..62731dcc1 100644 --- a/contrib/package/uhttpd/src/uhttpd-utils.h +++ b/contrib/package/uhttpd/src/uhttpd-utils.h @@ -20,6 +20,8 @@ #include #include +#include +#include #include #define min(x, y) (((x) < (y)) ? (x) : (y)) @@ -51,7 +53,10 @@ int uh_tcp_send(struct client *cl, const char *buf, int len); int uh_tcp_peek(struct client *cl, char *buf, int len); int uh_tcp_recv(struct client *cl, char *buf, int len); -int uh_http_sendhf(struct client *cl, int code, const char *summary, const char *fmt, ...); +int uh_http_sendhf( + struct client *cl, int code, const char *summary, + const char *fmt, ... +); #define uh_http_response(cl, code, message) \ uh_http_sendhf(cl, code, message, message) @@ -71,7 +76,17 @@ int uh_http_send( int uh_urldecode(char *buf, int blen, const char *src, int slen); int uh_urlencode(char *buf, int blen, const char *src, int slen); -int uh_path_normalize(char *buf, int blen, const char *src, int slen); +int uh_b64decode(char *buf, int blen, const unsigned char *src, int slen); + + +struct auth_realm * uh_auth_add( + char *path, char *realm, char *user, char *pass +); + +int uh_auth_check( + struct client *cl, struct http_request *req, struct path_info *pi +); + struct path_info * uh_path_lookup(struct client *cl, const char *url); diff --git a/contrib/package/uhttpd/src/uhttpd.c b/contrib/package/uhttpd/src/uhttpd.c index ccbc9efc6..7a5f42ad6 100644 --- a/contrib/package/uhttpd/src/uhttpd.c +++ b/contrib/package/uhttpd/src/uhttpd.c @@ -268,7 +268,7 @@ static struct http_request * uh_http_header_parse(struct client *cl, char *buffe static struct http_request * uh_http_header_recv(struct client *cl) { - char buffer[UH_LIMIT_MSGHEAD]; + static char buffer[UH_LIMIT_MSGHEAD]; char *bufptr = &buffer[0]; char *idxptr = NULL; @@ -670,7 +670,7 @@ int main (int argc, char **argv) { #ifdef HAVE_LUA /* Lua request? */ - if( strstr(req->url, conf.lua_prefix) == req->url ) + if( L && strstr(req->url, conf.lua_prefix) == req->url ) { uh_lua_request(cl, req, L); } @@ -679,15 +679,19 @@ int main (int argc, char **argv) /* dispatch request */ if( (pin = uh_path_lookup(cl, req->url)) != NULL ) { -#ifdef HAVE_CGI - if( strstr(pin->name, conf.cgi_prefix) == pin->name ) + /* auth ok? */ + if( uh_auth_check(cl, req, pin) ) { - uh_cgi_request(cl, req, pin); - } - else +#ifdef HAVE_CGI + if( strstr(pin->name, conf.cgi_prefix) == pin->name ) + { + uh_cgi_request(cl, req, pin); + } + else #endif - { - uh_file_request(cl, req, pin); + { + uh_file_request(cl, req, pin); + } } } diff --git a/contrib/package/uhttpd/src/uhttpd.h b/contrib/package/uhttpd/src/uhttpd.h index aa9186782..c6212109b 100644 --- a/contrib/package/uhttpd/src/uhttpd.h +++ b/contrib/package/uhttpd/src/uhttpd.h @@ -42,6 +42,7 @@ #define UH_LIMIT_LISTENERS 16 #define UH_LIMIT_CLIENTS 64 +#define UH_LIMIT_AUTHREALMS 8 #define UH_HTTP_MSG_GET 0 #define UH_HTTP_MSG_HEAD 1 @@ -85,11 +86,19 @@ struct client { #endif }; +struct auth_realm { + char path[PATH_MAX]; + char realm[128]; + char user[32]; + char pass[128]; +}; + struct http_request { int method; float version; char *url; char *headers[UH_LIMIT_HEADERS]; + struct auth_realm *realm; }; struct http_response {