mirror of
https://github.com/cesanta/mongoose.git
synced 2024-12-26 22:41:03 +08:00
parent
6fa305e36d
commit
a350208f35
222
mongoose.c
222
mongoose.c
@ -1554,6 +1554,7 @@ struct mg_str mg_mk_str_n(const char *s, size_t len) {
|
||||
int mg_vcmp(const struct mg_str *str1, const char *str2) WEAK;
|
||||
int mg_vcmp(const struct mg_str *str1, const char *str2) {
|
||||
size_t n2 = strlen(str2), n1 = str1->len;
|
||||
if (n1 == 0) return (n2 > 0 ? -1 : 0);
|
||||
int r = strncmp(str1->p, str2, (n1 < n2) ? n1 : n2);
|
||||
if (r == 0) {
|
||||
return n1 - n2;
|
||||
@ -5756,6 +5757,7 @@ struct mg_http_endpoint {
|
||||
struct mg_str uri_pattern; /* owned */
|
||||
char *auth_domain; /* owned */
|
||||
char *auth_file; /* owned */
|
||||
enum mg_auth_algo auth_algo;
|
||||
|
||||
mg_event_handler_t handler;
|
||||
#if MG_ENABLE_CALLBACK_USERDATA
|
||||
@ -7494,10 +7496,10 @@ extern void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[],
|
||||
const size_t *msg_lens, uint8_t *digest);
|
||||
#endif
|
||||
|
||||
void cs_md5(char buf[33], ...) {
|
||||
unsigned char hash[16];
|
||||
const uint8_t *msgs[20], *p;
|
||||
size_t msg_lens[20];
|
||||
static void cs_digest(enum mg_auth_algo algo, char buf[65], ...) {
|
||||
unsigned char hash[32];
|
||||
const uint8_t *msgs[16], *p;
|
||||
size_t msg_lens[16];
|
||||
size_t num_msgs = 0;
|
||||
va_list ap;
|
||||
|
||||
@ -7509,44 +7511,61 @@ void cs_md5(char buf[33], ...) {
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
mg_hash_md5_v(num_msgs, msgs, msg_lens, hash);
|
||||
cs_to_hex(buf, hash, sizeof(hash));
|
||||
if (algo == MG_AUTH_ALGO_MD5) {
|
||||
mg_hash_md5_v(num_msgs, msgs, msg_lens, hash);
|
||||
cs_to_hex(buf, hash, 16);
|
||||
} else {
|
||||
mg_hash_sha256_v(num_msgs, msgs, msg_lens, hash);
|
||||
cs_to_hex(buf, hash, 32);
|
||||
}
|
||||
}
|
||||
|
||||
static void mg_mkmd5resp(const char *method, size_t method_len, const char *uri,
|
||||
size_t uri_len, const char *ha1, size_t ha1_len,
|
||||
const char *nonce, size_t nonce_len, const char *nc,
|
||||
size_t nc_len, const char *cnonce, size_t cnonce_len,
|
||||
const char *qop, size_t qop_len, char *resp) {
|
||||
static const char colon[] = ":";
|
||||
static const size_t one = 1;
|
||||
char ha2[33];
|
||||
cs_md5(ha2, method, method_len, colon, one, uri, uri_len, NULL);
|
||||
cs_md5(resp, ha1, ha1_len, colon, one, nonce, nonce_len, colon, one, nc,
|
||||
nc_len, colon, one, cnonce, cnonce_len, colon, one, qop, qop_len,
|
||||
colon, one, ha2, sizeof(ha2) - 1, NULL);
|
||||
static void mg_mk_digest_resp(const char *method, size_t method_len,
|
||||
const char *uri, size_t uri_len, const char *ha1,
|
||||
size_t ha1_len, const char *nonce,
|
||||
size_t nonce_len, const char *nc, size_t nc_len,
|
||||
const char *cnonce, size_t cnonce_len,
|
||||
const char *qop, size_t qop_len,
|
||||
enum mg_auth_algo algo, char *resp) {
|
||||
size_t one = 1;
|
||||
char ha2[65];
|
||||
size_t ha2_size = (algo == MG_AUTH_ALGO_MD5 ? 32 : 64);
|
||||
cs_digest(algo, ha2, method, method_len, ":", one, uri, uri_len, NULL);
|
||||
cs_digest(algo, resp, ha1, ha1_len, ":", one, nonce, nonce_len, ":", one, nc,
|
||||
nc_len, ":", one, cnonce, cnonce_len, ":", one, qop, qop_len,
|
||||
":", one, ha2, ha2_size, NULL);
|
||||
}
|
||||
|
||||
int mg_http_create_digest_auth_header_algo(char *buf, size_t buf_len,
|
||||
const char *method, const char *uri,
|
||||
const char *auth_domain, const char *user,
|
||||
const char *passwd, const char *nonce,
|
||||
enum mg_auth_algo algo) {
|
||||
const char qop[] = "auth";
|
||||
const size_t one = 1;
|
||||
char ha1[65], resp[65], cnonce[40];
|
||||
|
||||
size_t ha1_size = (algo == MG_AUTH_ALGO_MD5 ? 32 : 64);
|
||||
snprintf(cnonce, sizeof(cnonce), "%lx", (unsigned long) mg_time());
|
||||
cs_digest(algo, ha1, user, (size_t) strlen(user), ":", one, auth_domain,
|
||||
(size_t) strlen(auth_domain), ":", one, passwd,
|
||||
(size_t) strlen(passwd), NULL);
|
||||
mg_mk_digest_resp(method, strlen(method), uri, strlen(uri), ha1, ha1_size,
|
||||
nonce, strlen(nonce), "1", one, cnonce, strlen(cnonce), qop,
|
||||
sizeof(qop) - 1, algo, resp);
|
||||
return snprintf(buf, buf_len,
|
||||
"Authorization: Digest username=\"%s\","
|
||||
"realm=\"%s\",uri=\"%s\",qop=%s,nc=1,cnonce=%s,"
|
||||
"nonce=%s,response=%s,algorithm=%s\r\n",
|
||||
user, auth_domain, uri, qop, cnonce, nonce, resp,
|
||||
(algo == MG_AUTH_ALGO_MD5 ? "MD5" : "SHA-256"));
|
||||
}
|
||||
|
||||
int mg_http_create_digest_auth_header(char *buf, size_t buf_len,
|
||||
const char *method, const char *uri,
|
||||
const char *auth_domain, const char *user,
|
||||
const char *passwd, const char *nonce) {
|
||||
static const char colon[] = ":", qop[] = "auth";
|
||||
static const size_t one = 1;
|
||||
char ha1[33], resp[33], cnonce[40];
|
||||
|
||||
snprintf(cnonce, sizeof(cnonce), "%lx", (unsigned long) mg_time());
|
||||
cs_md5(ha1, user, (size_t) strlen(user), colon, one, auth_domain,
|
||||
(size_t) strlen(auth_domain), colon, one, passwd,
|
||||
(size_t) strlen(passwd), NULL);
|
||||
mg_mkmd5resp(method, strlen(method), uri, strlen(uri), ha1, sizeof(ha1) - 1,
|
||||
nonce, strlen(nonce), "1", one, cnonce, strlen(cnonce), qop,
|
||||
sizeof(qop) - 1, resp);
|
||||
return snprintf(buf, buf_len,
|
||||
"Authorization: Digest username=\"%s\","
|
||||
"realm=\"%s\",uri=\"%s\",qop=%s,nc=1,cnonce=%s,"
|
||||
"nonce=%s,response=%s\r\n",
|
||||
user, auth_domain, uri, qop, cnonce, nonce, resp);
|
||||
return mg_http_create_digest_auth_header_algo(buf, buf_len, method, uri, auth_domain, user, passwd, nonce, MG_AUTH_ALGO_MD5);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -7561,15 +7580,17 @@ static int mg_check_nonce(const char *nonce) {
|
||||
return (now >= val) && (now - val < 60 * 60);
|
||||
}
|
||||
|
||||
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
|
||||
FILE *fp) {
|
||||
int mg_http_check_digest_auth_algo(struct http_message *hm, const char *auth_domain,
|
||||
enum mg_auth_algo fp_algo, FILE *fp) {
|
||||
int ret = 0;
|
||||
struct mg_str *hdr;
|
||||
char username_buf[50], cnonce_buf[64], response_buf[40], uri_buf[200],
|
||||
qop_buf[20], nc_buf[20], nonce_buf[16];
|
||||
char username_buf[50], cnonce_buf[64], response_buf[64], uri_buf[200],
|
||||
qop_buf[20], nc_buf[20], nonce_buf[16], algo_buf[16];
|
||||
|
||||
char *username = username_buf, *cnonce = cnonce_buf, *response = response_buf,
|
||||
*uri = uri_buf, *qop = qop_buf, *nc = nc_buf, *nonce = nonce_buf;
|
||||
*uri = uri_buf, *qop = qop_buf, *nc = nc_buf, *nonce = nonce_buf, *algo = algo_buf;
|
||||
|
||||
enum mg_auth_algo hm_algo = MG_AUTH_ALGO_MD5;
|
||||
|
||||
/* Parse "Authorization:" header, fail fast on parse error */
|
||||
if (hm == NULL || fp == NULL ||
|
||||
@ -7588,16 +7609,30 @@ int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
|
||||
goto clean;
|
||||
}
|
||||
|
||||
if (mg_http_parse_header2(hdr, "algorithm", &algo, sizeof(algo_buf)) != 0) {
|
||||
if (strcmp(algo, "MD5") == 0) {
|
||||
hm_algo = MG_AUTH_ALGO_MD5;
|
||||
} else if (strcmp(algo, "SHA-256") == 0) {
|
||||
hm_algo = MG_AUTH_ALGO_SHA256;
|
||||
} else {
|
||||
goto clean;
|
||||
}
|
||||
}
|
||||
|
||||
if (hm_algo != fp_algo) {
|
||||
goto clean;
|
||||
}
|
||||
|
||||
/* NOTE(lsm): due to a bug in MSIE, we do not compare URIs */
|
||||
|
||||
ret = mg_check_digest_auth(
|
||||
ret = mg_check_digest_auth_algo(
|
||||
hm->method,
|
||||
mg_mk_str_n(
|
||||
hm->uri.p,
|
||||
hm->uri.len + (hm->query_string.len ? hm->query_string.len + 1 : 0)),
|
||||
mg_mk_str(username), mg_mk_str(cnonce), mg_mk_str(response),
|
||||
mg_mk_str(qop), mg_mk_str(nc), mg_mk_str(nonce), mg_mk_str(auth_domain),
|
||||
fp);
|
||||
fp_algo, fp);
|
||||
|
||||
clean:
|
||||
if (username != username_buf) MG_FREE(username);
|
||||
@ -7607,17 +7642,23 @@ clean:
|
||||
if (qop != qop_buf) MG_FREE(qop);
|
||||
if (nc != nc_buf) MG_FREE(nc);
|
||||
if (nonce != nonce_buf) MG_FREE(nonce);
|
||||
if (algo != algo_buf) MG_FREE(algo);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
|
||||
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
|
||||
FILE *fp) {
|
||||
return mg_http_check_digest_auth_algo(hm, auth_domain, MG_AUTH_ALGO_MD5, fp);
|
||||
}
|
||||
|
||||
int mg_check_digest_auth_algo(struct mg_str method, struct mg_str uri,
|
||||
struct mg_str username, struct mg_str cnonce,
|
||||
struct mg_str response, struct mg_str qop,
|
||||
struct mg_str nc, struct mg_str nonce,
|
||||
struct mg_str auth_domain, FILE *fp) {
|
||||
struct mg_str auth_domain, enum mg_auth_algo algo, FILE *fp) {
|
||||
char buf[128], f_user[sizeof(buf)], f_ha1[sizeof(buf)], f_domain[sizeof(buf)];
|
||||
char exp_resp[33];
|
||||
char exp_resp[65] = {0};
|
||||
|
||||
/*
|
||||
* Read passwords file line by line. If should have htdigest format,
|
||||
@ -7629,9 +7670,9 @@ int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
|
||||
mg_vcmp(&username, f_user) == 0 &&
|
||||
mg_vcmp(&auth_domain, f_domain) == 0) {
|
||||
/* Username and domain matched, check the password */
|
||||
mg_mkmd5resp(method.p, method.len, uri.p, uri.len, f_ha1, strlen(f_ha1),
|
||||
nonce.p, nonce.len, nc.p, nc.len, cnonce.p, cnonce.len,
|
||||
qop.p, qop.len, exp_resp);
|
||||
mg_mk_digest_resp(method.p, method.len, uri.p, uri.len, f_ha1, strlen(f_ha1),
|
||||
nonce.p, nonce.len, nc.p, nc.len, cnonce.p, cnonce.len,
|
||||
qop.p, qop.len, algo, exp_resp);
|
||||
LOG(LL_DEBUG, ("%.*s %s %.*s %s", (int) username.len, username.p,
|
||||
f_domain, (int) response.len, response.p, exp_resp));
|
||||
return mg_ncasecmp(response.p, exp_resp, strlen(exp_resp)) == 0;
|
||||
@ -7642,6 +7683,16 @@ int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
|
||||
struct mg_str username, struct mg_str cnonce,
|
||||
struct mg_str response, struct mg_str qop,
|
||||
struct mg_str nc, struct mg_str nonce,
|
||||
struct mg_str auth_domain, FILE *fp) {
|
||||
return mg_check_digest_auth_algo(
|
||||
method, uri, username, cnonce, response, qop, nc, nonce, auth_domain,
|
||||
MG_AUTH_ALGO_MD5, fp);
|
||||
}
|
||||
|
||||
int mg_http_is_authorized(struct http_message *hm, struct mg_str path,
|
||||
const char *domain, const char *passwords_file,
|
||||
int flags) {
|
||||
@ -7650,27 +7701,30 @@ int mg_http_is_authorized(struct http_message *hm, struct mg_str path,
|
||||
FILE *fp;
|
||||
int authorized = 1;
|
||||
|
||||
if (domain != NULL && passwords_file != NULL) {
|
||||
if (flags & MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE) {
|
||||
fp = mg_fopen(passwords_file, "r");
|
||||
} else if (flags & MG_AUTH_FLAG_IS_DIRECTORY) {
|
||||
snprintf(buf, sizeof(buf), "%.*s%c%s", (int) path.len, path.p, DIRSEP,
|
||||
passwords_file);
|
||||
fp = mg_fopen(buf, "r");
|
||||
} else {
|
||||
p = strrchr(path.p, DIRSEP);
|
||||
if (p == NULL) p = path.p;
|
||||
snprintf(buf, sizeof(buf), "%.*s%c%s", (int) (p - path.p), path.p, DIRSEP,
|
||||
passwords_file);
|
||||
fp = mg_fopen(buf, "r");
|
||||
}
|
||||
if (domain == NULL || passwords_file == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (fp != NULL) {
|
||||
authorized = mg_http_check_digest_auth(hm, domain, fp);
|
||||
fclose(fp);
|
||||
} else if (!(flags & MG_AUTH_FLAG_ALLOW_MISSING_FILE)) {
|
||||
authorized = 0;
|
||||
}
|
||||
if (flags & MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE) {
|
||||
fp = mg_fopen(passwords_file, "r");
|
||||
} else if (flags & MG_AUTH_FLAG_IS_DIRECTORY) {
|
||||
snprintf(buf, sizeof(buf), "%.*s%c%s", (int) path.len, path.p, DIRSEP,
|
||||
passwords_file);
|
||||
fp = mg_fopen(buf, "r");
|
||||
} else {
|
||||
p = strrchr(path.p, DIRSEP);
|
||||
if (p == NULL) p = path.p;
|
||||
snprintf(buf, sizeof(buf), "%.*s%c%s", (int) (p - path.p), path.p, DIRSEP,
|
||||
passwords_file);
|
||||
fp = mg_fopen(buf, "r");
|
||||
}
|
||||
|
||||
if (fp != NULL) {
|
||||
enum mg_auth_algo fp_algo = (enum mg_auth_algo) ((flags >> 8) & 3);
|
||||
authorized = mg_http_check_digest_auth_algo(hm, domain, fp_algo, fp);
|
||||
fclose(fp);
|
||||
} else if (!(flags & MG_AUTH_FLAG_ALLOW_MISSING_FILE)) {
|
||||
authorized = 0;
|
||||
}
|
||||
|
||||
LOG(LL_DEBUG, ("%.*s %s %x %d", (int) path.len, path.p,
|
||||
@ -8220,14 +8274,21 @@ MG_INTERNAL int mg_is_not_modified(struct http_message *hm, cs_stat_t *st) {
|
||||
}
|
||||
}
|
||||
|
||||
void mg_http_send_digest_auth_request(struct mg_connection *c,
|
||||
const char *domain) {
|
||||
void mg_http_send_digest_auth_request_algo(struct mg_connection *c,
|
||||
const char *domain,
|
||||
enum mg_auth_algo algo) {
|
||||
mg_printf(c,
|
||||
"HTTP/1.1 401 Unauthorized\r\n"
|
||||
"WWW-Authenticate: Digest qop=\"auth\", "
|
||||
"realm=\"%s\", nonce=\"%lx\"\r\n"
|
||||
"realm=\"%s\", nonce=\"%lx\", algorithm=%s\r\n"
|
||||
"Content-Length: 0\r\n\r\n",
|
||||
domain, (unsigned long) mg_time());
|
||||
domain, (unsigned long) mg_time(),
|
||||
(algo == MG_AUTH_ALGO_MD5 ? "MD5" : "SHA-256"));
|
||||
}
|
||||
|
||||
void mg_http_send_digest_auth_request(struct mg_connection *c,
|
||||
const char *domain) {
|
||||
mg_http_send_digest_auth_request_algo(c, domain, MG_AUTH_ALGO_MD5);
|
||||
}
|
||||
|
||||
static void mg_http_send_options(struct mg_connection *nc,
|
||||
@ -8296,13 +8357,15 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
|
||||
hm, mg_mk_str(path), opts->auth_domain, opts->global_auth_file,
|
||||
((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
|
||||
MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE |
|
||||
MG_AUTH_FLAG_ALLOW_MISSING_FILE)) ||
|
||||
MG_AUTH_FLAG_ALLOW_MISSING_FILE |
|
||||
MG_AUTH_FLAG_ALGO(opts->auth_algo))) ||
|
||||
!mg_http_is_authorized(
|
||||
hm, mg_mk_str(path), opts->auth_domain,
|
||||
opts->per_directory_auth_file,
|
||||
((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
|
||||
MG_AUTH_FLAG_ALLOW_MISSING_FILE))) {
|
||||
mg_http_send_digest_auth_request(nc, opts->auth_domain);
|
||||
MG_AUTH_FLAG_ALLOW_MISSING_FILE |
|
||||
MG_AUTH_FLAG_ALGO(opts->auth_algo)))) {
|
||||
mg_http_send_digest_auth_request_algo(nc, opts->auth_domain, opts->auth_algo);
|
||||
} else if (is_cgi) {
|
||||
#if MG_ENABLE_HTTP_CGI
|
||||
mg_handle_cgi(nc, index_file ? index_file : path, path_info, hm, opts);
|
||||
@ -8325,7 +8388,7 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
|
||||
((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
|
||||
MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE |
|
||||
MG_AUTH_FLAG_ALLOW_MISSING_FILE))))) {
|
||||
mg_http_send_digest_auth_request(nc, opts->auth_domain);
|
||||
mg_http_send_digest_auth_request_algo(nc, opts->auth_domain, opts->aith_algo);
|
||||
#endif
|
||||
} else if (!mg_vcmp(&hm->method, "MKCOL")) {
|
||||
mg_handle_mkcol(nc, path, hm);
|
||||
@ -8388,9 +8451,6 @@ void mg_serve_http(struct mg_connection *nc, struct http_message *hm,
|
||||
if (opts.document_root == NULL) {
|
||||
opts.document_root = ".";
|
||||
}
|
||||
if (opts.per_directory_auth_file == NULL) {
|
||||
opts.per_directory_auth_file = ".htpasswd";
|
||||
}
|
||||
if (opts.enable_directory_listing == NULL) {
|
||||
opts.enable_directory_listing = "yes";
|
||||
}
|
||||
@ -8740,6 +8800,7 @@ void mg_register_http_endpoint_opt(struct mg_connection *nc,
|
||||
if (opts.auth_domain != NULL && opts.auth_file != NULL) {
|
||||
new_ep->auth_domain = strdup(opts.auth_domain);
|
||||
new_ep->auth_file = strdup(opts.auth_file);
|
||||
new_ep->auth_algo = opts.auth_algo;
|
||||
}
|
||||
new_ep->handler = handler;
|
||||
#if MG_ENABLE_CALLBACK_USERDATA
|
||||
@ -8764,8 +8825,9 @@ static void mg_http_call_endpoint_handler(struct mg_connection *nc, int ev,
|
||||
if (ep != NULL) {
|
||||
#if MG_ENABLE_FILESYSTEM && !MG_DISABLE_HTTP_DIGEST_AUTH
|
||||
if (!mg_http_is_authorized(hm, hm->uri, ep->auth_domain, ep->auth_file,
|
||||
MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE)) {
|
||||
mg_http_send_digest_auth_request(nc, ep->auth_domain);
|
||||
(MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE |
|
||||
MG_AUTH_FLAG_ALGO(ep->auth_algo)))) {
|
||||
mg_http_send_digest_auth_request_algo(nc, ep->auth_domain, ep->auth_algo);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
46
mongoose.h
46
mongoose.h
@ -4704,6 +4704,8 @@ extern void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[],
|
||||
const size_t *msg_lens, uint8_t *digest);
|
||||
extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[],
|
||||
const size_t *msg_lens, uint8_t *digest);
|
||||
extern void mg_hash_sha256_v(size_t num_msgs, const uint8_t *msgs[],
|
||||
const size_t *msg_lens, uint8_t *digest);
|
||||
|
||||
/*
|
||||
* Flags for `mg_http_is_authorized()`.
|
||||
@ -4711,6 +4713,7 @@ extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[],
|
||||
#define MG_AUTH_FLAG_IS_DIRECTORY (1 << 0)
|
||||
#define MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE (1 << 1)
|
||||
#define MG_AUTH_FLAG_ALLOW_MISSING_FILE (1 << 2)
|
||||
#define MG_AUTH_FLAG_ALGO(algo) ((((int) algo) & 3) << 8)
|
||||
|
||||
/*
|
||||
* Checks whether an http request is authorized. `domain` is the authentication
|
||||
@ -4723,12 +4726,6 @@ int mg_http_is_authorized(struct http_message *hm, struct mg_str path,
|
||||
const char *domain, const char *passwords_file,
|
||||
int flags);
|
||||
|
||||
/*
|
||||
* Sends 401 Unauthorized response.
|
||||
*/
|
||||
void mg_http_send_digest_auth_request(struct mg_connection *c,
|
||||
const char *domain);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
@ -4879,6 +4876,14 @@ size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name,
|
||||
int mg_get_http_var(const struct mg_str *buf, const char *name, char *dst,
|
||||
size_t dst_len);
|
||||
|
||||
/*
|
||||
* Supported digest auth algorithms.
|
||||
*/
|
||||
enum mg_auth_algo {
|
||||
MG_AUTH_ALGO_MD5 = 0,
|
||||
MG_AUTH_ALGO_SHA256 = 1,
|
||||
};
|
||||
|
||||
#if MG_ENABLE_FILESYSTEM
|
||||
/*
|
||||
* This structure defines how `mg_serve_http()` works.
|
||||
@ -4915,6 +4920,11 @@ struct mg_serve_http_opts {
|
||||
*/
|
||||
const char *global_auth_file;
|
||||
|
||||
/*
|
||||
* Password hashing algorithm used by the password files.
|
||||
*/
|
||||
enum mg_auth_algo auth_algo;
|
||||
|
||||
/* Set to "no" to disable directory listing. Enabled by default. */
|
||||
const char *enable_directory_listing;
|
||||
|
||||
@ -5166,6 +5176,7 @@ struct mg_http_endpoint_opts {
|
||||
/* Authorization domain (realm) */
|
||||
const char *auth_domain;
|
||||
const char *auth_file;
|
||||
enum mg_auth_algo auth_algo;
|
||||
};
|
||||
|
||||
void mg_register_http_endpoint_opt(struct mg_connection *nc,
|
||||
@ -5173,12 +5184,23 @@ void mg_register_http_endpoint_opt(struct mg_connection *nc,
|
||||
mg_event_handler_t handler,
|
||||
struct mg_http_endpoint_opts opts);
|
||||
|
||||
/*
|
||||
* Sends 401 Unauthorized response.
|
||||
*/
|
||||
void mg_http_send_digest_auth_request(struct mg_connection *c,
|
||||
const char *domain);
|
||||
void mg_http_send_digest_auth_request_algo(struct mg_connection *c,
|
||||
const char *domain,
|
||||
enum mg_auth_algo algo);
|
||||
|
||||
/*
|
||||
* Authenticates a HTTP request against an opened password file.
|
||||
* Returns 1 if authenticated, 0 otherwise.
|
||||
*/
|
||||
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
|
||||
FILE *fp);
|
||||
int mg_http_check_digest_auth_algo(struct http_message *hm, const char *auth_domain,
|
||||
enum mg_auth_algo fp_algo, FILE *fp);
|
||||
|
||||
/*
|
||||
* Authenticates given response params against an opened password file.
|
||||
@ -5191,6 +5213,12 @@ int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
|
||||
struct mg_str response, struct mg_str qop,
|
||||
struct mg_str nc, struct mg_str nonce,
|
||||
struct mg_str auth_domain, FILE *fp);
|
||||
int mg_check_digest_auth_algo(struct mg_str method, struct mg_str uri,
|
||||
struct mg_str username, struct mg_str cnonce,
|
||||
struct mg_str response, struct mg_str qop,
|
||||
struct mg_str nc, struct mg_str nonce,
|
||||
struct mg_str auth_domain, enum mg_auth_algo algo,
|
||||
FILE *fp);
|
||||
|
||||
/*
|
||||
* Sends buffer `buf` of size `len` to the client using chunked HTTP encoding.
|
||||
@ -5360,6 +5388,12 @@ int mg_http_create_digest_auth_header(char *buf, size_t buf_len,
|
||||
const char *method, const char *uri,
|
||||
const char *auth_domain, const char *user,
|
||||
const char *passwd, const char *nonce);
|
||||
int mg_http_create_digest_auth_header_algo(char *buf, size_t buf_len,
|
||||
const char *method, const char *uri,
|
||||
const char *auth_domain,
|
||||
const char *user, const char *passwd,
|
||||
const char *nonce,
|
||||
enum mg_auth_algo algo);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ struct mg_str mg_mk_str_n(const char *s, size_t len) {
|
||||
int mg_vcmp(const struct mg_str *str1, const char *str2) WEAK;
|
||||
int mg_vcmp(const struct mg_str *str1, const char *str2) {
|
||||
size_t n2 = strlen(str2), n1 = str1->len;
|
||||
if (n1 == 0) return (n2 > 0 ? -1 : 0);
|
||||
int r = strncmp(str1->p, str2, (n1 < n2) ? n1 : n2);
|
||||
if (r == 0) {
|
||||
return n1 - n2;
|
||||
|
221
src/mg_http.c
221
src/mg_http.c
@ -112,6 +112,7 @@ struct mg_http_endpoint {
|
||||
struct mg_str uri_pattern; /* owned */
|
||||
char *auth_domain; /* owned */
|
||||
char *auth_file; /* owned */
|
||||
enum mg_auth_algo auth_algo;
|
||||
|
||||
mg_event_handler_t handler;
|
||||
#if MG_ENABLE_CALLBACK_USERDATA
|
||||
@ -1850,10 +1851,10 @@ extern void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[],
|
||||
const size_t *msg_lens, uint8_t *digest);
|
||||
#endif
|
||||
|
||||
void cs_md5(char buf[33], ...) {
|
||||
unsigned char hash[16];
|
||||
const uint8_t *msgs[20], *p;
|
||||
size_t msg_lens[20];
|
||||
static void cs_digest(enum mg_auth_algo algo, char buf[65], ...) {
|
||||
unsigned char hash[32];
|
||||
const uint8_t *msgs[16], *p;
|
||||
size_t msg_lens[16];
|
||||
size_t num_msgs = 0;
|
||||
va_list ap;
|
||||
|
||||
@ -1865,44 +1866,61 @@ void cs_md5(char buf[33], ...) {
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
mg_hash_md5_v(num_msgs, msgs, msg_lens, hash);
|
||||
cs_to_hex(buf, hash, sizeof(hash));
|
||||
if (algo == MG_AUTH_ALGO_MD5) {
|
||||
mg_hash_md5_v(num_msgs, msgs, msg_lens, hash);
|
||||
cs_to_hex(buf, hash, 16);
|
||||
} else {
|
||||
mg_hash_sha256_v(num_msgs, msgs, msg_lens, hash);
|
||||
cs_to_hex(buf, hash, 32);
|
||||
}
|
||||
}
|
||||
|
||||
static void mg_mkmd5resp(const char *method, size_t method_len, const char *uri,
|
||||
size_t uri_len, const char *ha1, size_t ha1_len,
|
||||
const char *nonce, size_t nonce_len, const char *nc,
|
||||
size_t nc_len, const char *cnonce, size_t cnonce_len,
|
||||
const char *qop, size_t qop_len, char *resp) {
|
||||
static const char colon[] = ":";
|
||||
static const size_t one = 1;
|
||||
char ha2[33];
|
||||
cs_md5(ha2, method, method_len, colon, one, uri, uri_len, NULL);
|
||||
cs_md5(resp, ha1, ha1_len, colon, one, nonce, nonce_len, colon, one, nc,
|
||||
nc_len, colon, one, cnonce, cnonce_len, colon, one, qop, qop_len,
|
||||
colon, one, ha2, sizeof(ha2) - 1, NULL);
|
||||
static void mg_mk_digest_resp(const char *method, size_t method_len,
|
||||
const char *uri, size_t uri_len, const char *ha1,
|
||||
size_t ha1_len, const char *nonce,
|
||||
size_t nonce_len, const char *nc, size_t nc_len,
|
||||
const char *cnonce, size_t cnonce_len,
|
||||
const char *qop, size_t qop_len,
|
||||
enum mg_auth_algo algo, char *resp) {
|
||||
size_t one = 1;
|
||||
char ha2[65];
|
||||
size_t ha2_size = (algo == MG_AUTH_ALGO_MD5 ? 32 : 64);
|
||||
cs_digest(algo, ha2, method, method_len, ":", one, uri, uri_len, NULL);
|
||||
cs_digest(algo, resp, ha1, ha1_len, ":", one, nonce, nonce_len, ":", one, nc,
|
||||
nc_len, ":", one, cnonce, cnonce_len, ":", one, qop, qop_len,
|
||||
":", one, ha2, ha2_size, NULL);
|
||||
}
|
||||
|
||||
int mg_http_create_digest_auth_header_algo(char *buf, size_t buf_len,
|
||||
const char *method, const char *uri,
|
||||
const char *auth_domain, const char *user,
|
||||
const char *passwd, const char *nonce,
|
||||
enum mg_auth_algo algo) {
|
||||
const char qop[] = "auth";
|
||||
const size_t one = 1;
|
||||
char ha1[65], resp[65], cnonce[40];
|
||||
|
||||
size_t ha1_size = (algo == MG_AUTH_ALGO_MD5 ? 32 : 64);
|
||||
snprintf(cnonce, sizeof(cnonce), "%lx", (unsigned long) mg_time());
|
||||
cs_digest(algo, ha1, user, (size_t) strlen(user), ":", one, auth_domain,
|
||||
(size_t) strlen(auth_domain), ":", one, passwd,
|
||||
(size_t) strlen(passwd), NULL);
|
||||
mg_mk_digest_resp(method, strlen(method), uri, strlen(uri), ha1, ha1_size,
|
||||
nonce, strlen(nonce), "1", one, cnonce, strlen(cnonce), qop,
|
||||
sizeof(qop) - 1, algo, resp);
|
||||
return snprintf(buf, buf_len,
|
||||
"Authorization: Digest username=\"%s\","
|
||||
"realm=\"%s\",uri=\"%s\",qop=%s,nc=1,cnonce=%s,"
|
||||
"nonce=%s,response=%s,algorithm=%s\r\n",
|
||||
user, auth_domain, uri, qop, cnonce, nonce, resp,
|
||||
(algo == MG_AUTH_ALGO_MD5 ? "MD5" : "SHA-256"));
|
||||
}
|
||||
|
||||
int mg_http_create_digest_auth_header(char *buf, size_t buf_len,
|
||||
const char *method, const char *uri,
|
||||
const char *auth_domain, const char *user,
|
||||
const char *passwd, const char *nonce) {
|
||||
static const char colon[] = ":", qop[] = "auth";
|
||||
static const size_t one = 1;
|
||||
char ha1[33], resp[33], cnonce[40];
|
||||
|
||||
snprintf(cnonce, sizeof(cnonce), "%lx", (unsigned long) mg_time());
|
||||
cs_md5(ha1, user, (size_t) strlen(user), colon, one, auth_domain,
|
||||
(size_t) strlen(auth_domain), colon, one, passwd,
|
||||
(size_t) strlen(passwd), NULL);
|
||||
mg_mkmd5resp(method, strlen(method), uri, strlen(uri), ha1, sizeof(ha1) - 1,
|
||||
nonce, strlen(nonce), "1", one, cnonce, strlen(cnonce), qop,
|
||||
sizeof(qop) - 1, resp);
|
||||
return snprintf(buf, buf_len,
|
||||
"Authorization: Digest username=\"%s\","
|
||||
"realm=\"%s\",uri=\"%s\",qop=%s,nc=1,cnonce=%s,"
|
||||
"nonce=%s,response=%s\r\n",
|
||||
user, auth_domain, uri, qop, cnonce, nonce, resp);
|
||||
return mg_http_create_digest_auth_header_algo(buf, buf_len, method, uri, auth_domain, user, passwd, nonce, MG_AUTH_ALGO_MD5);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1917,15 +1935,17 @@ static int mg_check_nonce(const char *nonce) {
|
||||
return (now >= val) && (now - val < 60 * 60);
|
||||
}
|
||||
|
||||
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
|
||||
FILE *fp) {
|
||||
int mg_http_check_digest_auth_algo(struct http_message *hm, const char *auth_domain,
|
||||
enum mg_auth_algo fp_algo, FILE *fp) {
|
||||
int ret = 0;
|
||||
struct mg_str *hdr;
|
||||
char username_buf[50], cnonce_buf[64], response_buf[40], uri_buf[200],
|
||||
qop_buf[20], nc_buf[20], nonce_buf[16];
|
||||
char username_buf[50], cnonce_buf[64], response_buf[64], uri_buf[200],
|
||||
qop_buf[20], nc_buf[20], nonce_buf[16], algo_buf[16];
|
||||
|
||||
char *username = username_buf, *cnonce = cnonce_buf, *response = response_buf,
|
||||
*uri = uri_buf, *qop = qop_buf, *nc = nc_buf, *nonce = nonce_buf;
|
||||
*uri = uri_buf, *qop = qop_buf, *nc = nc_buf, *nonce = nonce_buf, *algo = algo_buf;
|
||||
|
||||
enum mg_auth_algo hm_algo = MG_AUTH_ALGO_MD5;
|
||||
|
||||
/* Parse "Authorization:" header, fail fast on parse error */
|
||||
if (hm == NULL || fp == NULL ||
|
||||
@ -1944,16 +1964,30 @@ int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
|
||||
goto clean;
|
||||
}
|
||||
|
||||
if (mg_http_parse_header2(hdr, "algorithm", &algo, sizeof(algo_buf)) != 0) {
|
||||
if (strcmp(algo, "MD5") == 0) {
|
||||
hm_algo = MG_AUTH_ALGO_MD5;
|
||||
} else if (strcmp(algo, "SHA-256") == 0) {
|
||||
hm_algo = MG_AUTH_ALGO_SHA256;
|
||||
} else {
|
||||
goto clean;
|
||||
}
|
||||
}
|
||||
|
||||
if (hm_algo != fp_algo) {
|
||||
goto clean;
|
||||
}
|
||||
|
||||
/* NOTE(lsm): due to a bug in MSIE, we do not compare URIs */
|
||||
|
||||
ret = mg_check_digest_auth(
|
||||
ret = mg_check_digest_auth_algo(
|
||||
hm->method,
|
||||
mg_mk_str_n(
|
||||
hm->uri.p,
|
||||
hm->uri.len + (hm->query_string.len ? hm->query_string.len + 1 : 0)),
|
||||
mg_mk_str(username), mg_mk_str(cnonce), mg_mk_str(response),
|
||||
mg_mk_str(qop), mg_mk_str(nc), mg_mk_str(nonce), mg_mk_str(auth_domain),
|
||||
fp);
|
||||
fp_algo, fp);
|
||||
|
||||
clean:
|
||||
if (username != username_buf) MG_FREE(username);
|
||||
@ -1963,17 +1997,23 @@ clean:
|
||||
if (qop != qop_buf) MG_FREE(qop);
|
||||
if (nc != nc_buf) MG_FREE(nc);
|
||||
if (nonce != nonce_buf) MG_FREE(nonce);
|
||||
if (algo != algo_buf) MG_FREE(algo);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
|
||||
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
|
||||
FILE *fp) {
|
||||
return mg_http_check_digest_auth_algo(hm, auth_domain, MG_AUTH_ALGO_MD5, fp);
|
||||
}
|
||||
|
||||
int mg_check_digest_auth_algo(struct mg_str method, struct mg_str uri,
|
||||
struct mg_str username, struct mg_str cnonce,
|
||||
struct mg_str response, struct mg_str qop,
|
||||
struct mg_str nc, struct mg_str nonce,
|
||||
struct mg_str auth_domain, FILE *fp) {
|
||||
struct mg_str auth_domain, enum mg_auth_algo algo, FILE *fp) {
|
||||
char buf[128], f_user[sizeof(buf)], f_ha1[sizeof(buf)], f_domain[sizeof(buf)];
|
||||
char exp_resp[33];
|
||||
char exp_resp[65] = {0};
|
||||
|
||||
/*
|
||||
* Read passwords file line by line. If should have htdigest format,
|
||||
@ -1985,9 +2025,9 @@ int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
|
||||
mg_vcmp(&username, f_user) == 0 &&
|
||||
mg_vcmp(&auth_domain, f_domain) == 0) {
|
||||
/* Username and domain matched, check the password */
|
||||
mg_mkmd5resp(method.p, method.len, uri.p, uri.len, f_ha1, strlen(f_ha1),
|
||||
nonce.p, nonce.len, nc.p, nc.len, cnonce.p, cnonce.len,
|
||||
qop.p, qop.len, exp_resp);
|
||||
mg_mk_digest_resp(method.p, method.len, uri.p, uri.len, f_ha1, strlen(f_ha1),
|
||||
nonce.p, nonce.len, nc.p, nc.len, cnonce.p, cnonce.len,
|
||||
qop.p, qop.len, algo, exp_resp);
|
||||
LOG(LL_DEBUG, ("%.*s %s %.*s %s", (int) username.len, username.p,
|
||||
f_domain, (int) response.len, response.p, exp_resp));
|
||||
return mg_ncasecmp(response.p, exp_resp, strlen(exp_resp)) == 0;
|
||||
@ -1998,6 +2038,16 @@ int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
|
||||
struct mg_str username, struct mg_str cnonce,
|
||||
struct mg_str response, struct mg_str qop,
|
||||
struct mg_str nc, struct mg_str nonce,
|
||||
struct mg_str auth_domain, FILE *fp) {
|
||||
return mg_check_digest_auth_algo(
|
||||
method, uri, username, cnonce, response, qop, nc, nonce, auth_domain,
|
||||
MG_AUTH_ALGO_MD5, fp);
|
||||
}
|
||||
|
||||
int mg_http_is_authorized(struct http_message *hm, struct mg_str path,
|
||||
const char *domain, const char *passwords_file,
|
||||
int flags) {
|
||||
@ -2006,27 +2056,30 @@ int mg_http_is_authorized(struct http_message *hm, struct mg_str path,
|
||||
FILE *fp;
|
||||
int authorized = 1;
|
||||
|
||||
if (domain != NULL && passwords_file != NULL) {
|
||||
if (flags & MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE) {
|
||||
fp = mg_fopen(passwords_file, "r");
|
||||
} else if (flags & MG_AUTH_FLAG_IS_DIRECTORY) {
|
||||
snprintf(buf, sizeof(buf), "%.*s%c%s", (int) path.len, path.p, DIRSEP,
|
||||
passwords_file);
|
||||
fp = mg_fopen(buf, "r");
|
||||
} else {
|
||||
p = strrchr(path.p, DIRSEP);
|
||||
if (p == NULL) p = path.p;
|
||||
snprintf(buf, sizeof(buf), "%.*s%c%s", (int) (p - path.p), path.p, DIRSEP,
|
||||
passwords_file);
|
||||
fp = mg_fopen(buf, "r");
|
||||
}
|
||||
if (domain == NULL || passwords_file == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (fp != NULL) {
|
||||
authorized = mg_http_check_digest_auth(hm, domain, fp);
|
||||
fclose(fp);
|
||||
} else if (!(flags & MG_AUTH_FLAG_ALLOW_MISSING_FILE)) {
|
||||
authorized = 0;
|
||||
}
|
||||
if (flags & MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE) {
|
||||
fp = mg_fopen(passwords_file, "r");
|
||||
} else if (flags & MG_AUTH_FLAG_IS_DIRECTORY) {
|
||||
snprintf(buf, sizeof(buf), "%.*s%c%s", (int) path.len, path.p, DIRSEP,
|
||||
passwords_file);
|
||||
fp = mg_fopen(buf, "r");
|
||||
} else {
|
||||
p = strrchr(path.p, DIRSEP);
|
||||
if (p == NULL) p = path.p;
|
||||
snprintf(buf, sizeof(buf), "%.*s%c%s", (int) (p - path.p), path.p, DIRSEP,
|
||||
passwords_file);
|
||||
fp = mg_fopen(buf, "r");
|
||||
}
|
||||
|
||||
if (fp != NULL) {
|
||||
enum mg_auth_algo fp_algo = (enum mg_auth_algo) ((flags >> 8) & 3);
|
||||
authorized = mg_http_check_digest_auth_algo(hm, domain, fp_algo, fp);
|
||||
fclose(fp);
|
||||
} else if (!(flags & MG_AUTH_FLAG_ALLOW_MISSING_FILE)) {
|
||||
authorized = 0;
|
||||
}
|
||||
|
||||
LOG(LL_DEBUG, ("%.*s %s %x %d", (int) path.len, path.p,
|
||||
@ -2576,14 +2629,21 @@ MG_INTERNAL int mg_is_not_modified(struct http_message *hm, cs_stat_t *st) {
|
||||
}
|
||||
}
|
||||
|
||||
void mg_http_send_digest_auth_request(struct mg_connection *c,
|
||||
const char *domain) {
|
||||
void mg_http_send_digest_auth_request_algo(struct mg_connection *c,
|
||||
const char *domain,
|
||||
enum mg_auth_algo algo) {
|
||||
mg_printf(c,
|
||||
"HTTP/1.1 401 Unauthorized\r\n"
|
||||
"WWW-Authenticate: Digest qop=\"auth\", "
|
||||
"realm=\"%s\", nonce=\"%lx\"\r\n"
|
||||
"realm=\"%s\", nonce=\"%lx\", algorithm=%s\r\n"
|
||||
"Content-Length: 0\r\n\r\n",
|
||||
domain, (unsigned long) mg_time());
|
||||
domain, (unsigned long) mg_time(),
|
||||
(algo == MG_AUTH_ALGO_MD5 ? "MD5" : "SHA-256"));
|
||||
}
|
||||
|
||||
void mg_http_send_digest_auth_request(struct mg_connection *c,
|
||||
const char *domain) {
|
||||
mg_http_send_digest_auth_request_algo(c, domain, MG_AUTH_ALGO_MD5);
|
||||
}
|
||||
|
||||
static void mg_http_send_options(struct mg_connection *nc,
|
||||
@ -2652,13 +2712,15 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
|
||||
hm, mg_mk_str(path), opts->auth_domain, opts->global_auth_file,
|
||||
((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
|
||||
MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE |
|
||||
MG_AUTH_FLAG_ALLOW_MISSING_FILE)) ||
|
||||
MG_AUTH_FLAG_ALLOW_MISSING_FILE |
|
||||
MG_AUTH_FLAG_ALGO(opts->auth_algo))) ||
|
||||
!mg_http_is_authorized(
|
||||
hm, mg_mk_str(path), opts->auth_domain,
|
||||
opts->per_directory_auth_file,
|
||||
((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
|
||||
MG_AUTH_FLAG_ALLOW_MISSING_FILE))) {
|
||||
mg_http_send_digest_auth_request(nc, opts->auth_domain);
|
||||
MG_AUTH_FLAG_ALLOW_MISSING_FILE |
|
||||
MG_AUTH_FLAG_ALGO(opts->auth_algo)))) {
|
||||
mg_http_send_digest_auth_request_algo(nc, opts->auth_domain, opts->auth_algo);
|
||||
} else if (is_cgi) {
|
||||
#if MG_ENABLE_HTTP_CGI
|
||||
mg_handle_cgi(nc, index_file ? index_file : path, path_info, hm, opts);
|
||||
@ -2681,7 +2743,7 @@ MG_INTERNAL void mg_send_http_file(struct mg_connection *nc, char *path,
|
||||
((is_directory ? MG_AUTH_FLAG_IS_DIRECTORY : 0) |
|
||||
MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE |
|
||||
MG_AUTH_FLAG_ALLOW_MISSING_FILE))))) {
|
||||
mg_http_send_digest_auth_request(nc, opts->auth_domain);
|
||||
mg_http_send_digest_auth_request_algo(nc, opts->auth_domain, opts->aith_algo);
|
||||
#endif
|
||||
} else if (!mg_vcmp(&hm->method, "MKCOL")) {
|
||||
mg_handle_mkcol(nc, path, hm);
|
||||
@ -2744,9 +2806,6 @@ void mg_serve_http(struct mg_connection *nc, struct http_message *hm,
|
||||
if (opts.document_root == NULL) {
|
||||
opts.document_root = ".";
|
||||
}
|
||||
if (opts.per_directory_auth_file == NULL) {
|
||||
opts.per_directory_auth_file = ".htpasswd";
|
||||
}
|
||||
if (opts.enable_directory_listing == NULL) {
|
||||
opts.enable_directory_listing = "yes";
|
||||
}
|
||||
@ -3096,6 +3155,7 @@ void mg_register_http_endpoint_opt(struct mg_connection *nc,
|
||||
if (opts.auth_domain != NULL && opts.auth_file != NULL) {
|
||||
new_ep->auth_domain = strdup(opts.auth_domain);
|
||||
new_ep->auth_file = strdup(opts.auth_file);
|
||||
new_ep->auth_algo = opts.auth_algo;
|
||||
}
|
||||
new_ep->handler = handler;
|
||||
#if MG_ENABLE_CALLBACK_USERDATA
|
||||
@ -3120,8 +3180,9 @@ static void mg_http_call_endpoint_handler(struct mg_connection *nc, int ev,
|
||||
if (ep != NULL) {
|
||||
#if MG_ENABLE_FILESYSTEM && !MG_DISABLE_HTTP_DIGEST_AUTH
|
||||
if (!mg_http_is_authorized(hm, hm->uri, ep->auth_domain, ep->auth_file,
|
||||
MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE)) {
|
||||
mg_http_send_digest_auth_request(nc, ep->auth_domain);
|
||||
(MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE |
|
||||
MG_AUTH_FLAG_ALGO(ep->auth_algo)))) {
|
||||
mg_http_send_digest_auth_request_algo(nc, ep->auth_domain, ep->auth_algo);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
@ -333,6 +333,8 @@ extern void mg_hash_md5_v(size_t num_msgs, const uint8_t *msgs[],
|
||||
const size_t *msg_lens, uint8_t *digest);
|
||||
extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[],
|
||||
const size_t *msg_lens, uint8_t *digest);
|
||||
extern void mg_hash_sha256_v(size_t num_msgs, const uint8_t *msgs[],
|
||||
const size_t *msg_lens, uint8_t *digest);
|
||||
|
||||
/*
|
||||
* Flags for `mg_http_is_authorized()`.
|
||||
@ -340,6 +342,7 @@ extern void mg_hash_sha1_v(size_t num_msgs, const uint8_t *msgs[],
|
||||
#define MG_AUTH_FLAG_IS_DIRECTORY (1 << 0)
|
||||
#define MG_AUTH_FLAG_IS_GLOBAL_PASS_FILE (1 << 1)
|
||||
#define MG_AUTH_FLAG_ALLOW_MISSING_FILE (1 << 2)
|
||||
#define MG_AUTH_FLAG_ALGO(algo) ((((int) algo) & 3) << 8)
|
||||
|
||||
/*
|
||||
* Checks whether an http request is authorized. `domain` is the authentication
|
||||
@ -352,12 +355,6 @@ int mg_http_is_authorized(struct http_message *hm, struct mg_str path,
|
||||
const char *domain, const char *passwords_file,
|
||||
int flags);
|
||||
|
||||
/*
|
||||
* Sends 401 Unauthorized response.
|
||||
*/
|
||||
void mg_http_send_digest_auth_request(struct mg_connection *c,
|
||||
const char *domain);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
@ -56,6 +56,12 @@ int mg_http_create_digest_auth_header(char *buf, size_t buf_len,
|
||||
const char *method, const char *uri,
|
||||
const char *auth_domain, const char *user,
|
||||
const char *passwd, const char *nonce);
|
||||
int mg_http_create_digest_auth_header_algo(char *buf, size_t buf_len,
|
||||
const char *method, const char *uri,
|
||||
const char *auth_domain,
|
||||
const char *user, const char *passwd,
|
||||
const char *nonce,
|
||||
enum mg_auth_algo algo);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -139,6 +139,14 @@ size_t mg_parse_multipart(const char *buf, size_t buf_len, char *var_name,
|
||||
int mg_get_http_var(const struct mg_str *buf, const char *name, char *dst,
|
||||
size_t dst_len);
|
||||
|
||||
/*
|
||||
* Supported digest auth algorithms.
|
||||
*/
|
||||
enum mg_auth_algo {
|
||||
MG_AUTH_ALGO_MD5 = 0,
|
||||
MG_AUTH_ALGO_SHA256 = 1,
|
||||
};
|
||||
|
||||
#if MG_ENABLE_FILESYSTEM
|
||||
/*
|
||||
* This structure defines how `mg_serve_http()` works.
|
||||
@ -175,6 +183,11 @@ struct mg_serve_http_opts {
|
||||
*/
|
||||
const char *global_auth_file;
|
||||
|
||||
/*
|
||||
* Password hashing algorithm used by the password files.
|
||||
*/
|
||||
enum mg_auth_algo auth_algo;
|
||||
|
||||
/* Set to "no" to disable directory listing. Enabled by default. */
|
||||
const char *enable_directory_listing;
|
||||
|
||||
@ -426,6 +439,7 @@ struct mg_http_endpoint_opts {
|
||||
/* Authorization domain (realm) */
|
||||
const char *auth_domain;
|
||||
const char *auth_file;
|
||||
enum mg_auth_algo auth_algo;
|
||||
};
|
||||
|
||||
void mg_register_http_endpoint_opt(struct mg_connection *nc,
|
||||
@ -433,12 +447,23 @@ void mg_register_http_endpoint_opt(struct mg_connection *nc,
|
||||
mg_event_handler_t handler,
|
||||
struct mg_http_endpoint_opts opts);
|
||||
|
||||
/*
|
||||
* Sends 401 Unauthorized response.
|
||||
*/
|
||||
void mg_http_send_digest_auth_request(struct mg_connection *c,
|
||||
const char *domain);
|
||||
void mg_http_send_digest_auth_request_algo(struct mg_connection *c,
|
||||
const char *domain,
|
||||
enum mg_auth_algo algo);
|
||||
|
||||
/*
|
||||
* Authenticates a HTTP request against an opened password file.
|
||||
* Returns 1 if authenticated, 0 otherwise.
|
||||
*/
|
||||
int mg_http_check_digest_auth(struct http_message *hm, const char *auth_domain,
|
||||
FILE *fp);
|
||||
int mg_http_check_digest_auth_algo(struct http_message *hm, const char *auth_domain,
|
||||
enum mg_auth_algo fp_algo, FILE *fp);
|
||||
|
||||
/*
|
||||
* Authenticates given response params against an opened password file.
|
||||
@ -451,6 +476,12 @@ int mg_check_digest_auth(struct mg_str method, struct mg_str uri,
|
||||
struct mg_str response, struct mg_str qop,
|
||||
struct mg_str nc, struct mg_str nonce,
|
||||
struct mg_str auth_domain, FILE *fp);
|
||||
int mg_check_digest_auth_algo(struct mg_str method, struct mg_str uri,
|
||||
struct mg_str username, struct mg_str cnonce,
|
||||
struct mg_str response, struct mg_str qop,
|
||||
struct mg_str nc, struct mg_str nonce,
|
||||
struct mg_str auth_domain, enum mg_auth_algo algo,
|
||||
FILE *fp);
|
||||
|
||||
/*
|
||||
* Sends buffer `buf` of size `len` to the client using chunked HTTP encoding.
|
||||
|
Loading…
x
Reference in New Issue
Block a user