diff --git a/plugins/router_basicauth/router_basicauth.c b/plugins/router_basicauth/router_basicauth.c new file mode 100644 index 00000000..9f8957cb --- /dev/null +++ b/plugins/router_basicauth/router_basicauth.c @@ -0,0 +1,216 @@ +#include "../../uwsgi.h" + +#ifdef UWSGI_ROUTING + +#ifdef __linux__ +#include +#endif + +extern struct uwsgi_server uwsgi; + +// this algo is based on nginx one (the fastest i have tested) +static char *http_basic_auth_get(char *authorization, uint16_t len) { + + uint16_t i; + static uint8_t table64[] = { + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, 77, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77, + 77, 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, 77, 77, 77, 77, 77, + 77, 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, 77, 77, 77, 77, 77, + + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 + }; + + for (i = 0; i < len; i++) { + if (authorization[i] == '=') + break; + + // check for invalid content + if (table64[ (uint8_t) authorization[i] ] == 77) { + return NULL; + } + } + + // check for invalid length + if (i % 4 == 1) + return NULL; + + uint16_t dst_len = (((len+3)/4) * 3) + 1; + char *dst = uwsgi_malloc(dst_len); + + char *ptr = dst; + uint8_t *src = (uint8_t *) authorization; + while(i > 3) { + *ptr++= (char) ( table64[src[0]] << 2 | table64[src[1]] >> 4); + *ptr++= (char) ( table64[src[1]] << 4 | table64[src[2]] >> 2); + *ptr++= (char) ( table64[src[2]] << 6 | table64[src[3]]); + + src+=4; + i-=4; + } + + if (i > 1) { + *ptr++= (char) ( table64[src[0]] << 2 | table64[src[1]] >> 4); + } + + if (i > 2) { + *ptr++= (char) ( table64[src[1]] << 4 | table64[src[2]] >> 2); + } + + *ptr++= 0; + + return dst; + +} + +uint16_t check_htpasswd(char *filename, char *auth) { + + char line[1024]; + + char *colon = strchr(auth, ':'); + if (!colon) return 0; + + FILE *htpasswd = fopen(filename, "r"); + if (!htpasswd) { + return 0; + } + while(fgets(line, 1024, htpasswd)) { + char *colon2 = strchr(line, ':'); + if (!colon2) break; + + char *cpwd = colon2+1; + size_t clen = strlen(cpwd); + if (clen < 13) break; + + if (clen > 13) cpwd[13] = 0; + +#ifdef __linux__ + struct crypt_data cd; + cd.initialized = 0; + // we do as nginx here + cd.current_salt[0] = ~cpwd[0]; + char *crypted = crypt_r( colon+1, cpwd, &cd); +#else + char *crypted = crypt( colon+1, cpwd); +#endif + if (!crypted) continue; + + if (!strcmp( crypted, cpwd )) { + fclose(htpasswd); + return colon-auth; + } + } + + fclose(htpasswd); + + return 0; +} + +int uwsgi_routing_func_basicauth(struct wsgi_request *wsgi_req, struct uwsgi_route *ur) { + + struct iovec iov[4]; + + if (wsgi_req->protocol_len > 0) { + iov[0].iov_base = wsgi_req->protocol; + iov[0].iov_len = wsgi_req->protocol_len; + } + else { + iov[0].iov_base = "HTTP/1.0"; + iov[0].iov_len = 8; + } + + // chec for "Basic =" string at least + if (wsgi_req->authorization_len > 7) { + if (strncmp(wsgi_req->authorization, "Basic ", 6)) + goto forbidden; + + char *auth = http_basic_auth_get(wsgi_req->authorization+6, wsgi_req->authorization_len-6); + if (auth) { + if (ur->custom) { + // check htpasswd-like file + } + else { + if (!strcmp(auth, ur->data2)) { + free(auth); + return UWSGI_ROUTE_CONTINUE; + } + } + free(auth); + if (ur->is_last) + goto forbidden; + return UWSGI_ROUTE_NEXT; + } + } + +forbidden: + iov[1].iov_base = " 401 Authorization Required\r\nWWW-Authenticate: Basic realm=\""; + iov[1].iov_len = 60 ; + + iov[2].iov_base = ur->data; + iov[2].iov_len = ur->data_len; + + iov[3].iov_base = "\"\r\n\r\n"; + iov[3].iov_len = 5; + + wsgi_req->headers_size = wsgi_req->socket->proto_writev_header(wsgi_req, iov, 4); + + wsgi_req->response_size = wsgi_req->socket->proto_write(wsgi_req,"Unauthorized", 12); + + return UWSGI_ROUTE_BREAK; +} + + +int uwsgi_router_basicauth(struct uwsgi_route *ur, char *args) { + + ur->func = uwsgi_routing_func_basicauth; + + char *comma = strchr(args, ','); + if (!comma) { + uwsgi_log("invalid route syntax: %s\n", args); + exit(1); + } + + *comma = 0; + + char *colon = strchr(comma+1, ':'); + // is an htpasswd-like file ? + if (!colon) { + ur->custom = 1; + } + + ur->data = args; + ur->data_len = strlen(args); + + ur->data2 = comma+1; + ur->data2_len = strlen(ur->data2); + + return 0; +} + + +void router_basicauth_register(void) { + + uwsgi_register_router("basicauth", uwsgi_router_basicauth); +} + +struct uwsgi_plugin router_basicauth_plugin = { + + .name = "router_redirect", + .on_load = router_basicauth_register, +}; +#else +struct uwsgi_plugin router_basicauth_plugin = { + .name = "router_basicauth", +}; +#endif diff --git a/plugins/router_basicauth/uwsgiplugin.py b/plugins/router_basicauth/uwsgiplugin.py new file mode 100644 index 00000000..5fce6158 --- /dev/null +++ b/plugins/router_basicauth/uwsgiplugin.py @@ -0,0 +1,6 @@ +NAME='router_basicauth' + +CFLAGS = [] +LDFLAGS = [] +LIBS = ['-lcrypt'] +GCC_LIST = ['router_basicauth']