uhttpd: add basic auth infrastructure
This commit is contained in:
parent
1ecf7ac9fa
commit
95b9bb0f69
6 changed files with 251 additions and 49 deletions
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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++ )
|
||||
unsigned int cin = 0;
|
||||
unsigned int cout = 0;
|
||||
|
||||
|
||||
for( i = 0; (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;
|
||||
}
|
||||
cin = src[i];
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
skip++;
|
||||
}
|
||||
|
||||
/* finally a harmless char */
|
||||
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
|
||||
{
|
||||
skip = 0;
|
||||
}
|
||||
continue;
|
||||
|
||||
buf[len++] = src[i];
|
||||
cout = (cout << 6) | cin;
|
||||
|
||||
if( (i % 4) == 3 )
|
||||
{
|
||||
if( (len + 3) < blen )
|
||||
{
|
||||
buf[len++] = (char)(cout >> 16);
|
||||
buf[len++] = (char)(cout >> 8);
|
||||
buf[len++] = (char)(cout);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 };
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
|
||||
#include <stdarg.h>
|
||||
#include <fcntl.h>
|
||||
#include <pwd.h>
|
||||
#include <shadow.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#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);
|
||||
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
/* auth ok? */
|
||||
if( uh_auth_check(cl, req, pin) )
|
||||
{
|
||||
#ifdef HAVE_CGI
|
||||
if( strstr(pin->name, conf.cgi_prefix) == pin->name )
|
||||
{
|
||||
uh_cgi_request(cl, req, pin);
|
||||
}
|
||||
else
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue