mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-21 11:30:01 +00:00
Moved auth code to separate file
This commit is contained in:
1 parent
199e0a330f
commit
d3577b7877
6 files changed
+998
-999
No files matched your search
+1
-1
@@ -27,7 +27,7 @@ VERSION = $(shell perl -lne \
|
||||
|
||||
# The order in which files are listed is important
|
||||
SOURCES = src/internal.h src/string.c src/parse_date.c src/options.c \
|
||||
src/mongoose.c
|
||||
src/crypto.c src/auth.c src/mongoose.c
|
||||
|
||||
TINY_SOURCES = ../mongoose.c main.c
|
||||
LUA_SOURCES = $(TINY_SOURCES) sqlite3.c lsqlite3.c lua_5.2.1.c
|
||||
|
||||
@@ -0,0 +1,301 @@
|
||||
#include "internal.h"
|
||||
|
||||
// Stringify binary data. Output buffer must be twice as big as input,
|
||||
// because each byte takes 2 bytes in string representation
|
||||
static void bin2str(char *to, const unsigned char *p, size_t len) {
|
||||
static const char *hex = "0123456789abcdef";
|
||||
|
||||
for (; len--; p++) {
|
||||
*to++ = hex[p[0] >> 4];
|
||||
*to++ = hex[p[0] & 0x0f];
|
||||
}
|
||||
*to = '\0';
|
||||
}
|
||||
|
||||
// Return stringified MD5 hash for list of strings. Buffer must be 33 bytes.
|
||||
char *mg_md5(char buf[33], ...) {
|
||||
unsigned char hash[16];
|
||||
const char *p;
|
||||
va_list ap;
|
||||
MD5_CTX ctx;
|
||||
|
||||
MD5Init(&ctx);
|
||||
|
||||
va_start(ap, buf);
|
||||
while ((p = va_arg(ap, const char *)) != NULL) {
|
||||
MD5Update(&ctx, (const unsigned char *) p, (unsigned) strlen(p));
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
MD5Final(hash, &ctx);
|
||||
bin2str(buf, hash, sizeof(hash));
|
||||
return buf;
|
||||
}
|
||||
|
||||
// Check the user's password, return 1 if OK
|
||||
static int check_password(const char *method, const char *ha1, const char *uri,
|
||||
const char *nonce, const char *nc, const char *cnonce,
|
||||
const char *qop, const char *response) {
|
||||
char ha2[32 + 1], expected_response[32 + 1];
|
||||
|
||||
// Some of the parameters may be NULL
|
||||
if (method == NULL || nonce == NULL || nc == NULL || cnonce == NULL ||
|
||||
qop == NULL || response == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// NOTE(lsm): due to a bug in MSIE, we do not compare the URI
|
||||
// TODO(lsm): check for authentication timeout
|
||||
if (// strcmp(dig->uri, c->ouri) != 0 ||
|
||||
strlen(response) != 32
|
||||
// || now - strtoul(dig->nonce, NULL, 10) > 3600
|
||||
) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
mg_md5(ha2, method, ":", uri, NULL);
|
||||
mg_md5(expected_response, ha1, ":", nonce, ":", nc,
|
||||
":", cnonce, ":", qop, ":", ha2, NULL);
|
||||
|
||||
return mg_strcasecmp(response, expected_response) == 0;
|
||||
}
|
||||
|
||||
// Use the global passwords file, if specified by auth_gpass option,
|
||||
// or search for .htpasswd in the requested directory.
|
||||
static FILE *open_auth_file(struct mg_connection *conn, const char *path) {
|
||||
char name[PATH_MAX];
|
||||
const char *p, *e, *gpass = conn->ctx->config[GLOBAL_PASSWORDS_FILE];
|
||||
struct file file = STRUCT_FILE_INITIALIZER;
|
||||
FILE *fp = NULL;
|
||||
|
||||
if (gpass != NULL) {
|
||||
// Use global passwords file
|
||||
fp = mg_fopen(gpass, "r");
|
||||
// Important: using local struct file to test path for is_directory flag.
|
||||
// If filep is used, mg_stat() makes it appear as if auth file was opened.
|
||||
} else if (mg_stat(path, &file) && file.is_directory) {
|
||||
mg_snprintf(name, sizeof(name), "%s%c%s",
|
||||
path, '/', PASSWORDS_FILE_NAME);
|
||||
fp = mg_fopen(name, "r");
|
||||
} else {
|
||||
// Try to find .htpasswd in requested directory.
|
||||
for (p = path, e = p + strlen(p) - 1; e > p; e--)
|
||||
if (e[0] == '/')
|
||||
break;
|
||||
mg_snprintf(name, sizeof(name), "%.*s%c%s",
|
||||
(int) (e - p), p, '/', PASSWORDS_FILE_NAME);
|
||||
fp = mg_fopen(name, "r");
|
||||
}
|
||||
|
||||
return fp;
|
||||
}
|
||||
|
||||
// Parsed Authorization header
|
||||
struct ah {
|
||||
char *user, *uri, *cnonce, *response, *qop, *nc, *nonce;
|
||||
};
|
||||
|
||||
// Return 1 on success. Always initializes the ah structure.
|
||||
static int parse_auth_header(struct mg_connection *conn, char *buf,
|
||||
size_t buf_size, struct ah *ah) {
|
||||
char *name, *value, *s;
|
||||
const char *auth_header;
|
||||
|
||||
(void) memset(ah, 0, sizeof(*ah));
|
||||
if ((auth_header = mg_get_header(conn, "Authorization")) == NULL ||
|
||||
mg_strncasecmp(auth_header, "Digest ", 7) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Make modifiable copy of the auth header
|
||||
(void) mg_strlcpy(buf, auth_header + 7, buf_size);
|
||||
s = buf;
|
||||
|
||||
// Parse authorization header
|
||||
for (;;) {
|
||||
// Gobble initial spaces
|
||||
while (isspace(* (unsigned char *) s)) {
|
||||
s++;
|
||||
}
|
||||
name = skip_quoted(&s, "=", " ", 0);
|
||||
// Value is either quote-delimited, or ends at first comma or space.
|
||||
if (s[0] == '\"') {
|
||||
s++;
|
||||
value = skip_quoted(&s, "\"", " ", '\\');
|
||||
if (s[0] == ',') {
|
||||
s++;
|
||||
}
|
||||
} else {
|
||||
value = skip_quoted(&s, ", ", " ", 0); // IE uses commas, FF uses spaces
|
||||
}
|
||||
if (*name == '\0') {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!strcmp(name, "username")) {
|
||||
ah->user = value;
|
||||
} else if (!strcmp(name, "cnonce")) {
|
||||
ah->cnonce = value;
|
||||
} else if (!strcmp(name, "response")) {
|
||||
ah->response = value;
|
||||
} else if (!strcmp(name, "uri")) {
|
||||
ah->uri = value;
|
||||
} else if (!strcmp(name, "qop")) {
|
||||
ah->qop = value;
|
||||
} else if (!strcmp(name, "nc")) {
|
||||
ah->nc = value;
|
||||
} else if (!strcmp(name, "nonce")) {
|
||||
ah->nonce = value;
|
||||
}
|
||||
}
|
||||
|
||||
// CGI needs it as REMOTE_USER
|
||||
if (ah->user != NULL) {
|
||||
conn->request_info.remote_user = mg_strdup(ah->user);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Authorize against the opened passwords file. Return 1 if authorized.
|
||||
static int authorize(struct mg_connection *conn, FILE *fp) {
|
||||
struct ah ah;
|
||||
char line[256], f_user[256], ha1[256], f_domain[256], buf[MG_BUF_LEN];
|
||||
|
||||
if (!parse_auth_header(conn, buf, sizeof(buf), &ah)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Loop over passwords file
|
||||
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||
if (sscanf(line, "%[^:]:%[^:]:%s", f_user, f_domain, ha1) != 3) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(ah.user, f_user) &&
|
||||
!strcmp(conn->ctx->config[AUTHENTICATION_DOMAIN], f_domain))
|
||||
return check_password(conn->request_info.request_method, ha1, ah.uri,
|
||||
ah.nonce, ah.nc, ah.cnonce, ah.qop, ah.response);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Return 1 if request is authorised, 0 otherwise.
|
||||
static int check_authorization(struct mg_connection *conn, const char *path) {
|
||||
char fname[PATH_MAX];
|
||||
struct vec uri_vec, filename_vec;
|
||||
const char *list;
|
||||
FILE *fp = NULL;
|
||||
int authorized = 1;
|
||||
|
||||
list = conn->ctx->config[PROTECT_URI];
|
||||
while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) {
|
||||
if (!memcmp(conn->request_info.uri, uri_vec.ptr, uri_vec.len)) {
|
||||
mg_snprintf(fname, sizeof(fname), "%.*s",
|
||||
(int) filename_vec.len, filename_vec.ptr);
|
||||
fp = mg_fopen(fname, "r");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fp == NULL) {
|
||||
fp = open_auth_file(conn, path);
|
||||
}
|
||||
|
||||
if (fp != NULL) {
|
||||
authorized = authorize(conn, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
return authorized;
|
||||
}
|
||||
|
||||
static void send_authorization_request(struct mg_connection *conn) {
|
||||
conn->status_code = 401;
|
||||
mg_printf(conn,
|
||||
"HTTP/1.1 401 Unauthorized\r\n"
|
||||
"Content-Length: 0\r\n"
|
||||
"WWW-Authenticate: Digest qop=\"auth\", "
|
||||
"realm=\"%s\", nonce=\"%lu\"\r\n\r\n",
|
||||
conn->ctx->config[AUTHENTICATION_DOMAIN],
|
||||
(unsigned long) time(NULL));
|
||||
}
|
||||
|
||||
static int is_authorized_for_put(struct mg_connection *conn) {
|
||||
const char *passfile = conn->ctx->config[PUT_DELETE_PASSWORDS_FILE];
|
||||
FILE *fp;
|
||||
int ret = 0;
|
||||
|
||||
if (passfile != NULL && (fp = mg_fopen(passfile, "r")) != NULL) {
|
||||
ret = authorize(conn, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mg_modify_passwords_file(const char *fname, const char *domain,
|
||||
const char *user, const char *pass) {
|
||||
int found;
|
||||
char line[512], u[512], d[512], ha1[33], tmp[PATH_MAX];
|
||||
FILE *fp, *fp2;
|
||||
|
||||
found = 0;
|
||||
fp = fp2 = NULL;
|
||||
|
||||
// Regard empty password as no password - remove user record.
|
||||
if (pass != NULL && pass[0] == '\0') {
|
||||
pass = NULL;
|
||||
}
|
||||
|
||||
(void) snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
|
||||
|
||||
// Create the file if does not exist
|
||||
if ((fp = fopen(fname, "a+")) != NULL) {
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
// Open the given file and temporary file
|
||||
if ((fp = fopen(fname, "r")) == NULL) {
|
||||
return 0;
|
||||
} else if ((fp2 = fopen(tmp, "w+")) == NULL) {
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Copy the stuff to temporary file
|
||||
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||
if (sscanf(line, "%[^:]:%[^:]:%*s", u, d) != 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(u, user) && !strcmp(d, domain)) {
|
||||
found++;
|
||||
if (pass != NULL) {
|
||||
mg_md5(ha1, user, ":", domain, ":", pass, NULL);
|
||||
fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
|
||||
}
|
||||
} else {
|
||||
fprintf(fp2, "%s", line);
|
||||
}
|
||||
}
|
||||
|
||||
// If new user, just add it
|
||||
if (!found && pass != NULL) {
|
||||
mg_md5(ha1, user, ":", domain, ":", pass, NULL);
|
||||
fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
|
||||
}
|
||||
|
||||
// Close files
|
||||
fclose(fp);
|
||||
fclose(fp2);
|
||||
|
||||
// Put the temp file in place of real file
|
||||
remove(fname);
|
||||
rename(tmp, fname);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
static int is_big_endian(void) {
|
||||
static const int n = 1;
|
||||
return ((char *) &n)[0] == 0;
|
||||
}
|
||||
|
||||
#ifndef HAVE_MD5
|
||||
typedef struct MD5Context {
|
||||
uint32_t buf[4];
|
||||
uint32_t bits[2];
|
||||
unsigned char in[64];
|
||||
} MD5_CTX;
|
||||
|
||||
static void byteReverse(unsigned char *buf, unsigned longs) {
|
||||
uint32_t t;
|
||||
|
||||
// Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN
|
||||
if (is_big_endian()) {
|
||||
do {
|
||||
t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
|
||||
((unsigned) buf[1] << 8 | buf[0]);
|
||||
* (uint32_t *) buf = t;
|
||||
buf += 4;
|
||||
} while (--longs);
|
||||
}
|
||||
}
|
||||
|
||||
#define F1(x, y, z) (z ^ (x & (y ^ z)))
|
||||
#define F2(x, y, z) F1(z, x, y)
|
||||
#define F3(x, y, z) (x ^ y ^ z)
|
||||
#define F4(x, y, z) (y ^ (x | ~z))
|
||||
|
||||
#define MD5STEP(f, w, x, y, z, data, s) \
|
||||
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
|
||||
|
||||
// Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
|
||||
// initialization constants.
|
||||
static void MD5Init(MD5_CTX *ctx) {
|
||||
ctx->buf[0] = 0x67452301;
|
||||
ctx->buf[1] = 0xefcdab89;
|
||||
ctx->buf[2] = 0x98badcfe;
|
||||
ctx->buf[3] = 0x10325476;
|
||||
|
||||
ctx->bits[0] = 0;
|
||||
ctx->bits[1] = 0;
|
||||
}
|
||||
|
||||
static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) {
|
||||
register uint32_t a, b, c, d;
|
||||
|
||||
a = buf[0];
|
||||
b = buf[1];
|
||||
c = buf[2];
|
||||
d = buf[3];
|
||||
|
||||
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
|
||||
|
||||
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
|
||||
|
||||
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
|
||||
|
||||
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
|
||||
|
||||
buf[0] += a;
|
||||
buf[1] += b;
|
||||
buf[2] += c;
|
||||
buf[3] += d;
|
||||
}
|
||||
|
||||
static void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len) {
|
||||
uint32_t t;
|
||||
|
||||
t = ctx->bits[0];
|
||||
if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
|
||||
ctx->bits[1]++;
|
||||
ctx->bits[1] += len >> 29;
|
||||
|
||||
t = (t >> 3) & 0x3f;
|
||||
|
||||
if (t) {
|
||||
unsigned char *p = (unsigned char *) ctx->in + t;
|
||||
|
||||
t = 64 - t;
|
||||
if (len < t) {
|
||||
memcpy(p, buf, len);
|
||||
return;
|
||||
}
|
||||
memcpy(p, buf, t);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
|
||||
buf += t;
|
||||
len -= t;
|
||||
}
|
||||
|
||||
while (len >= 64) {
|
||||
memcpy(ctx->in, buf, 64);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
|
||||
buf += 64;
|
||||
len -= 64;
|
||||
}
|
||||
|
||||
memcpy(ctx->in, buf, len);
|
||||
}
|
||||
|
||||
static void MD5Final(unsigned char digest[16], MD5_CTX *ctx) {
|
||||
unsigned count;
|
||||
unsigned char *p;
|
||||
uint32_t *a;
|
||||
|
||||
count = (ctx->bits[0] >> 3) & 0x3F;
|
||||
|
||||
p = ctx->in + count;
|
||||
*p++ = 0x80;
|
||||
count = 64 - 1 - count;
|
||||
if (count < 8) {
|
||||
memset(p, 0, count);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
|
||||
memset(ctx->in, 0, 56);
|
||||
} else {
|
||||
memset(p, 0, count - 8);
|
||||
}
|
||||
byteReverse(ctx->in, 14);
|
||||
|
||||
a = (uint32_t *)ctx->in;
|
||||
a[14] = ctx->bits[0];
|
||||
a[15] = ctx->bits[1];
|
||||
|
||||
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
|
||||
byteReverse((unsigned char *) ctx->buf, 4);
|
||||
memcpy(digest, ctx->buf, 16);
|
||||
memset((char *) ctx, 0, sizeof(*ctx));
|
||||
}
|
||||
#endif // !HAVE_MD5
|
||||
|
||||
|
||||
@@ -458,3 +458,5 @@ struct de {
|
||||
struct file file;
|
||||
};
|
||||
|
||||
static FILE *mg_fopen(const char *path, const char *mode);
|
||||
static int mg_stat(const char *path, struct file *filep);
|
||||
@@ -1205,505 +1205,6 @@ static void get_mime_type(struct mg_context *ctx, const char *path,
|
||||
vec->len = strlen(vec->ptr);
|
||||
}
|
||||
|
||||
static int is_big_endian(void) {
|
||||
static const int n = 1;
|
||||
return ((char *) &n)[0] == 0;
|
||||
}
|
||||
|
||||
#ifndef HAVE_MD5
|
||||
typedef struct MD5Context {
|
||||
uint32_t buf[4];
|
||||
uint32_t bits[2];
|
||||
unsigned char in[64];
|
||||
} MD5_CTX;
|
||||
|
||||
static void byteReverse(unsigned char *buf, unsigned longs) {
|
||||
uint32_t t;
|
||||
|
||||
// Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN
|
||||
if (is_big_endian()) {
|
||||
do {
|
||||
t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
|
||||
((unsigned) buf[1] << 8 | buf[0]);
|
||||
* (uint32_t *) buf = t;
|
||||
buf += 4;
|
||||
} while (--longs);
|
||||
}
|
||||
}
|
||||
|
||||
#define F1(x, y, z) (z ^ (x & (y ^ z)))
|
||||
#define F2(x, y, z) F1(z, x, y)
|
||||
#define F3(x, y, z) (x ^ y ^ z)
|
||||
#define F4(x, y, z) (y ^ (x | ~z))
|
||||
|
||||
#define MD5STEP(f, w, x, y, z, data, s) \
|
||||
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
|
||||
|
||||
// Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
|
||||
// initialization constants.
|
||||
static void MD5Init(MD5_CTX *ctx) {
|
||||
ctx->buf[0] = 0x67452301;
|
||||
ctx->buf[1] = 0xefcdab89;
|
||||
ctx->buf[2] = 0x98badcfe;
|
||||
ctx->buf[3] = 0x10325476;
|
||||
|
||||
ctx->bits[0] = 0;
|
||||
ctx->bits[1] = 0;
|
||||
}
|
||||
|
||||
static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) {
|
||||
register uint32_t a, b, c, d;
|
||||
|
||||
a = buf[0];
|
||||
b = buf[1];
|
||||
c = buf[2];
|
||||
d = buf[3];
|
||||
|
||||
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
|
||||
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
|
||||
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
|
||||
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
|
||||
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
|
||||
|
||||
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
|
||||
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
|
||||
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
|
||||
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
|
||||
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
|
||||
|
||||
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
|
||||
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
|
||||
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
|
||||
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
|
||||
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
|
||||
|
||||
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
|
||||
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
|
||||
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
|
||||
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
|
||||
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
|
||||
|
||||
buf[0] += a;
|
||||
buf[1] += b;
|
||||
buf[2] += c;
|
||||
buf[3] += d;
|
||||
}
|
||||
|
||||
static void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len) {
|
||||
uint32_t t;
|
||||
|
||||
t = ctx->bits[0];
|
||||
if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
|
||||
ctx->bits[1]++;
|
||||
ctx->bits[1] += len >> 29;
|
||||
|
||||
t = (t >> 3) & 0x3f;
|
||||
|
||||
if (t) {
|
||||
unsigned char *p = (unsigned char *) ctx->in + t;
|
||||
|
||||
t = 64 - t;
|
||||
if (len < t) {
|
||||
memcpy(p, buf, len);
|
||||
return;
|
||||
}
|
||||
memcpy(p, buf, t);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
|
||||
buf += t;
|
||||
len -= t;
|
||||
}
|
||||
|
||||
while (len >= 64) {
|
||||
memcpy(ctx->in, buf, 64);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
|
||||
buf += 64;
|
||||
len -= 64;
|
||||
}
|
||||
|
||||
memcpy(ctx->in, buf, len);
|
||||
}
|
||||
|
||||
static void MD5Final(unsigned char digest[16], MD5_CTX *ctx) {
|
||||
unsigned count;
|
||||
unsigned char *p;
|
||||
uint32_t *a;
|
||||
|
||||
count = (ctx->bits[0] >> 3) & 0x3F;
|
||||
|
||||
p = ctx->in + count;
|
||||
*p++ = 0x80;
|
||||
count = 64 - 1 - count;
|
||||
if (count < 8) {
|
||||
memset(p, 0, count);
|
||||
byteReverse(ctx->in, 16);
|
||||
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
|
||||
memset(ctx->in, 0, 56);
|
||||
} else {
|
||||
memset(p, 0, count - 8);
|
||||
}
|
||||
byteReverse(ctx->in, 14);
|
||||
|
||||
a = (uint32_t *)ctx->in;
|
||||
a[14] = ctx->bits[0];
|
||||
a[15] = ctx->bits[1];
|
||||
|
||||
MD5Transform(ctx->buf, (uint32_t *) ctx->in);
|
||||
byteReverse((unsigned char *) ctx->buf, 4);
|
||||
memcpy(digest, ctx->buf, 16);
|
||||
memset((char *) ctx, 0, sizeof(*ctx));
|
||||
}
|
||||
#endif // !HAVE_MD5
|
||||
|
||||
// Stringify binary data. Output buffer must be twice as big as input,
|
||||
// because each byte takes 2 bytes in string representation
|
||||
static void bin2str(char *to, const unsigned char *p, size_t len) {
|
||||
static const char *hex = "0123456789abcdef";
|
||||
|
||||
for (; len--; p++) {
|
||||
*to++ = hex[p[0] >> 4];
|
||||
*to++ = hex[p[0] & 0x0f];
|
||||
}
|
||||
*to = '\0';
|
||||
}
|
||||
|
||||
// Return stringified MD5 hash for list of strings. Buffer must be 33 bytes.
|
||||
char *mg_md5(char buf[33], ...) {
|
||||
unsigned char hash[16];
|
||||
const char *p;
|
||||
va_list ap;
|
||||
MD5_CTX ctx;
|
||||
|
||||
MD5Init(&ctx);
|
||||
|
||||
va_start(ap, buf);
|
||||
while ((p = va_arg(ap, const char *)) != NULL) {
|
||||
MD5Update(&ctx, (const unsigned char *) p, (unsigned) strlen(p));
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
MD5Final(hash, &ctx);
|
||||
bin2str(buf, hash, sizeof(hash));
|
||||
return buf;
|
||||
}
|
||||
|
||||
// Check the user's password, return 1 if OK
|
||||
static int check_password(const char *method, const char *ha1, const char *uri,
|
||||
const char *nonce, const char *nc, const char *cnonce,
|
||||
const char *qop, const char *response) {
|
||||
char ha2[32 + 1], expected_response[32 + 1];
|
||||
|
||||
// Some of the parameters may be NULL
|
||||
if (method == NULL || nonce == NULL || nc == NULL || cnonce == NULL ||
|
||||
qop == NULL || response == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// NOTE(lsm): due to a bug in MSIE, we do not compare the URI
|
||||
// TODO(lsm): check for authentication timeout
|
||||
if (// strcmp(dig->uri, c->ouri) != 0 ||
|
||||
strlen(response) != 32
|
||||
// || now - strtoul(dig->nonce, NULL, 10) > 3600
|
||||
) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
mg_md5(ha2, method, ":", uri, NULL);
|
||||
mg_md5(expected_response, ha1, ":", nonce, ":", nc,
|
||||
":", cnonce, ":", qop, ":", ha2, NULL);
|
||||
|
||||
return mg_strcasecmp(response, expected_response) == 0;
|
||||
}
|
||||
|
||||
// Use the global passwords file, if specified by auth_gpass option,
|
||||
// or search for .htpasswd in the requested directory.
|
||||
static FILE *open_auth_file(struct mg_connection *conn, const char *path) {
|
||||
char name[PATH_MAX];
|
||||
const char *p, *e, *gpass = conn->ctx->config[GLOBAL_PASSWORDS_FILE];
|
||||
struct file file = STRUCT_FILE_INITIALIZER;
|
||||
FILE *fp = NULL;
|
||||
|
||||
if (gpass != NULL) {
|
||||
// Use global passwords file
|
||||
if ((fp = mg_fopen(gpass, "r")) == NULL) {
|
||||
cry(conn, "fopen(%s): %s", gpass, strerror(ERRNO));
|
||||
}
|
||||
// Important: using local struct file to test path for is_directory flag.
|
||||
// If filep is used, mg_stat() makes it appear as if auth file was opened.
|
||||
} else if (mg_stat(path, &file) && file.is_directory) {
|
||||
mg_snprintf(name, sizeof(name), "%s%c%s",
|
||||
path, '/', PASSWORDS_FILE_NAME);
|
||||
fp = mg_fopen(name, "r");
|
||||
} else {
|
||||
// Try to find .htpasswd in requested directory.
|
||||
for (p = path, e = p + strlen(p) - 1; e > p; e--)
|
||||
if (e[0] == '/')
|
||||
break;
|
||||
mg_snprintf(name, sizeof(name), "%.*s%c%s",
|
||||
(int) (e - p), p, '/', PASSWORDS_FILE_NAME);
|
||||
fp = mg_fopen(name, "r");
|
||||
}
|
||||
|
||||
return fp;
|
||||
}
|
||||
|
||||
// Parsed Authorization header
|
||||
struct ah {
|
||||
char *user, *uri, *cnonce, *response, *qop, *nc, *nonce;
|
||||
};
|
||||
|
||||
// Return 1 on success. Always initializes the ah structure.
|
||||
static int parse_auth_header(struct mg_connection *conn, char *buf,
|
||||
size_t buf_size, struct ah *ah) {
|
||||
char *name, *value, *s;
|
||||
const char *auth_header;
|
||||
|
||||
(void) memset(ah, 0, sizeof(*ah));
|
||||
if ((auth_header = mg_get_header(conn, "Authorization")) == NULL ||
|
||||
mg_strncasecmp(auth_header, "Digest ", 7) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Make modifiable copy of the auth header
|
||||
(void) mg_strlcpy(buf, auth_header + 7, buf_size);
|
||||
s = buf;
|
||||
|
||||
// Parse authorization header
|
||||
for (;;) {
|
||||
// Gobble initial spaces
|
||||
while (isspace(* (unsigned char *) s)) {
|
||||
s++;
|
||||
}
|
||||
name = skip_quoted(&s, "=", " ", 0);
|
||||
// Value is either quote-delimited, or ends at first comma or space.
|
||||
if (s[0] == '\"') {
|
||||
s++;
|
||||
value = skip_quoted(&s, "\"", " ", '\\');
|
||||
if (s[0] == ',') {
|
||||
s++;
|
||||
}
|
||||
} else {
|
||||
value = skip_quoted(&s, ", ", " ", 0); // IE uses commas, FF uses spaces
|
||||
}
|
||||
if (*name == '\0') {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!strcmp(name, "username")) {
|
||||
ah->user = value;
|
||||
} else if (!strcmp(name, "cnonce")) {
|
||||
ah->cnonce = value;
|
||||
} else if (!strcmp(name, "response")) {
|
||||
ah->response = value;
|
||||
} else if (!strcmp(name, "uri")) {
|
||||
ah->uri = value;
|
||||
} else if (!strcmp(name, "qop")) {
|
||||
ah->qop = value;
|
||||
} else if (!strcmp(name, "nc")) {
|
||||
ah->nc = value;
|
||||
} else if (!strcmp(name, "nonce")) {
|
||||
ah->nonce = value;
|
||||
}
|
||||
}
|
||||
|
||||
// CGI needs it as REMOTE_USER
|
||||
if (ah->user != NULL) {
|
||||
conn->request_info.remote_user = mg_strdup(ah->user);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Authorize against the opened passwords file. Return 1 if authorized.
|
||||
static int authorize(struct mg_connection *conn, FILE *fp) {
|
||||
struct ah ah;
|
||||
char line[256], f_user[256], ha1[256], f_domain[256], buf[MG_BUF_LEN];
|
||||
|
||||
if (!parse_auth_header(conn, buf, sizeof(buf), &ah)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Loop over passwords file
|
||||
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||
if (sscanf(line, "%[^:]:%[^:]:%s", f_user, f_domain, ha1) != 3) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(ah.user, f_user) &&
|
||||
!strcmp(conn->ctx->config[AUTHENTICATION_DOMAIN], f_domain))
|
||||
return check_password(conn->request_info.request_method, ha1, ah.uri,
|
||||
ah.nonce, ah.nc, ah.cnonce, ah.qop, ah.response);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Return 1 if request is authorised, 0 otherwise.
|
||||
static int check_authorization(struct mg_connection *conn, const char *path) {
|
||||
char fname[PATH_MAX];
|
||||
struct vec uri_vec, filename_vec;
|
||||
const char *list;
|
||||
FILE *fp = NULL;
|
||||
int authorized = 1;
|
||||
|
||||
list = conn->ctx->config[PROTECT_URI];
|
||||
while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) {
|
||||
if (!memcmp(conn->request_info.uri, uri_vec.ptr, uri_vec.len)) {
|
||||
mg_snprintf(fname, sizeof(fname), "%.*s",
|
||||
(int) filename_vec.len, filename_vec.ptr);
|
||||
if ((fp = mg_fopen(fname, "r")) == NULL) {
|
||||
cry(conn, "%s: cannot open %s: %s", __func__, fname, strerror(errno));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fp == NULL) {
|
||||
fp = open_auth_file(conn, path);
|
||||
}
|
||||
|
||||
if (fp != NULL) {
|
||||
authorized = authorize(conn, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
return authorized;
|
||||
}
|
||||
|
||||
static void send_authorization_request(struct mg_connection *conn) {
|
||||
conn->status_code = 401;
|
||||
mg_printf(conn,
|
||||
"HTTP/1.1 401 Unauthorized\r\n"
|
||||
"Content-Length: 0\r\n"
|
||||
"WWW-Authenticate: Digest qop=\"auth\", "
|
||||
"realm=\"%s\", nonce=\"%lu\"\r\n\r\n",
|
||||
conn->ctx->config[AUTHENTICATION_DOMAIN],
|
||||
(unsigned long) time(NULL));
|
||||
}
|
||||
|
||||
static int is_authorized_for_put(struct mg_connection *conn) {
|
||||
const char *passfile = conn->ctx->config[PUT_DELETE_PASSWORDS_FILE];
|
||||
FILE *fp;
|
||||
int ret = 0;
|
||||
|
||||
if (passfile != NULL && (fp = mg_fopen(passfile, "r")) != NULL) {
|
||||
ret = authorize(conn, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mg_modify_passwords_file(const char *fname, const char *domain,
|
||||
const char *user, const char *pass) {
|
||||
int found;
|
||||
char line[512], u[512], d[512], ha1[33], tmp[PATH_MAX];
|
||||
FILE *fp, *fp2;
|
||||
|
||||
found = 0;
|
||||
fp = fp2 = NULL;
|
||||
|
||||
// Regard empty password as no password - remove user record.
|
||||
if (pass != NULL && pass[0] == '\0') {
|
||||
pass = NULL;
|
||||
}
|
||||
|
||||
(void) snprintf(tmp, sizeof(tmp), "%s.tmp", fname);
|
||||
|
||||
// Create the file if does not exist
|
||||
if ((fp = fopen(fname, "a+")) != NULL) {
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
// Open the given file and temporary file
|
||||
if ((fp = fopen(fname, "r")) == NULL) {
|
||||
return 0;
|
||||
} else if ((fp2 = fopen(tmp, "w+")) == NULL) {
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Copy the stuff to temporary file
|
||||
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||
if (sscanf(line, "%[^:]:%[^:]:%*s", u, d) != 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(u, user) && !strcmp(d, domain)) {
|
||||
found++;
|
||||
if (pass != NULL) {
|
||||
mg_md5(ha1, user, ":", domain, ":", pass, NULL);
|
||||
fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
|
||||
}
|
||||
} else {
|
||||
fprintf(fp2, "%s", line);
|
||||
}
|
||||
}
|
||||
|
||||
// If new user, just add it
|
||||
if (!found && pass != NULL) {
|
||||
mg_md5(ha1, user, ":", domain, ":", pass, NULL);
|
||||
fprintf(fp2, "%s:%s:%s\n", user, domain, ha1);
|
||||
}
|
||||
|
||||
// Close files
|
||||
fclose(fp);
|
||||
fclose(fp2);
|
||||
|
||||
// Put the temp file in place of real file
|
||||
remove(fname);
|
||||
rename(tmp, fname);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SOCKET conn2(const char *host, int port, int use_ssl,
|
||||
char *ebuf, size_t ebuf_len) {
|
||||
struct sockaddr_in sin;
|
||||
|
||||
+498
-499
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user