From 9ef564513290a93e8f88236e5aadaa05e19d1030 Mon Sep 17 00:00:00 2001
From: Julien Muchembled <jm@jmuchemb.eu>
Date: Sun, 24 Jul 2011 18:29:28 +0200
Subject: [PATCH] mod_auth: ignore comments, trailing blanks and empty lines
 in ht{digest,passwd} files

---
 src/http_auth.c         |   76 ++++++++++++++++++++++++-----------------------
 tests/lighttpd.htpasswd |    4 ++-
 2 files changed, 42 insertions(+), 38 deletions(-)

diff --git a/src/http_auth.c b/src/http_auth.c
index e479683..1d8edbf 100644
--- a/src/http_auth.c
+++ b/src/http_auth.c
@@ -141,6 +141,22 @@ static unsigned char * base64_decode(buffer *out, const char *in) {
 	return result;
 }
 
+static char *parse_line(char *s, size_t *c) {
+	char *e, *comment;
+	size_t len = *c;
+
+	if (NULL != (e = memchr(s, '\n', len)))
+		len = e++ - s;
+	/* ignore comments */
+	if (NULL != (comment = memchr(s, '#', len)))
+		len = comment - s;
+	/* ignore trailing blanks */
+	while (len && isblank(s[len-1]))
+		len--;
+	*c = len;
+	return e;
+}
+
 static int http_auth_get_password(server *srv, mod_auth_plugin_data *p, buffer *username, buffer *realm, buffer *password) {
 	int ret = -1;
 
@@ -149,6 +165,7 @@ static int http_auth_get_password(server *srv, mod_auth_plugin_data *p, buffer *
 	if (p->conf.auth_backend == AUTH_BACKEND_HTDIGEST) {
 		stream f;
 		char * f_line;
+		size_t line_len;
 
 		if (buffer_is_empty(p->conf.auth_htdigest_userfile)) return -1;
 
@@ -160,9 +177,9 @@ static int http_auth_get_password(server *srv, mod_auth_plugin_data *p, buffer *
 
 		f_line = f.start;
 
-		while (f_line - f.start != f.size) {
-			char *f_user, *f_pwd, *e, *f_realm;
-			size_t u_len, pwd_len, r_len;
+		while (f_line && (line_len = f.size - (f_line - f.start))) {
+			char *f_user, *f_pwd, *f_realm;
+			size_t u_len, r_len;
 
 			f_user = f_line;
 
@@ -172,7 +189,11 @@ static int http_auth_get_password(server *srv, mod_auth_plugin_data *p, buffer *
 			 * user:realm:md5(user:realm:password)
 			 */
 
-			if (NULL == (f_realm = memchr(f_user, ':', f.size - (f_user - f.start) ))) {
+			f_line = parse_line(f_user, &line_len);
+			if (!line_len)
+				continue;
+
+			if (NULL == (f_realm = memchr(f_user, ':', line_len))) {
 				log_error_write(srv, __FILE__, __LINE__, "sbs",
 						"parsed error in", p->conf.auth_htdigest_userfile,
 						"expected 'username:realm:hashed password'");
@@ -181,8 +202,10 @@ static int http_auth_get_password(server *srv, mod_auth_plugin_data *p, buffer *
 
 				return -1;
 			}
+			u_len = f_realm - f_user;
+			f_realm++;
 
-			if (NULL == (f_pwd = memchr(f_realm + 1, ':', f.size - (f_realm + 1 - f.start)))) {
+			if (NULL == (f_pwd = memchr(f_realm, ':', line_len - u_len - 1))) {
 				log_error_write(srv, __FILE__, __LINE__, "sbs",
 						"parsed error in", p->conf.auth_plain_userfile,
 						"expected 'username:realm:hashed password'");
@@ -191,35 +214,20 @@ static int http_auth_get_password(server *srv, mod_auth_plugin_data *p, buffer *
 
 				return -1;
 			}
-
-			/* get pointers to the fields */
-			u_len = f_realm - f_user;
-			f_realm++;
 			r_len = f_pwd - f_realm;
 			f_pwd++;
 
-			if (NULL != (e = memchr(f_pwd, '\n', f.size - (f_pwd - f.start)))) {
-				pwd_len = e - f_pwd;
-			} else {
-				pwd_len = f.size - (f_pwd - f.start);
-			}
-
 			if (username->used - 1 == u_len &&
 			    (realm->used - 1 == r_len) &&
 			    (0 == strncmp(username->ptr, f_user, u_len)) &&
 			    (0 == strncmp(realm->ptr, f_realm, r_len))) {
 				/* found */
 
-				buffer_copy_string_len(password, f_pwd, pwd_len);
+				buffer_copy_string_len(password, f_pwd, line_len - (f_pwd - f_user));
 
 				ret = 0;
 				break;
 			}
-
-			/* EOL */
-			if (!e) break;
-
-			f_line = e + 1;
 		}
 
 		stream_close(&f);
@@ -228,6 +236,7 @@ static int http_auth_get_password(server *srv, mod_auth_plugin_data *p, buffer *
 		stream f;
 		char * f_line;
 		buffer *auth_fn;
+		size_t line_len;
 
 		auth_fn = (p->conf.auth_backend == AUTH_BACKEND_HTPASSWD) ? p->conf.auth_htpasswd_userfile : p->conf.auth_plain_userfile;
 
@@ -242,9 +251,9 @@ static int http_auth_get_password(server *srv, mod_auth_plugin_data *p, buffer *
 
 		f_line = f.start;
 
-		while (f_line - f.start != f.size) {
-			char *f_user, *f_pwd, *e;
-			size_t u_len, pwd_len;
+		while (f_line && (line_len = f.size - (f_line - f.start))) {
+			char *f_user, *f_pwd;
+			size_t u_len;
 
 			f_user = f_line;
 
@@ -254,7 +263,11 @@ static int http_auth_get_password(server *srv, mod_auth_plugin_data *p, buffer *
 			 * user:crypted passwd
 			 */
 
-			if (NULL == (f_pwd = memchr(f_user, ':', f.size - (f_user - f.start) ))) {
+			f_line = parse_line(f_user, &line_len);
+			if (!line_len)
+				continue;
+
+			if (NULL == (f_pwd = memchr(f_user, ':', line_len))) {
 				log_error_write(srv, __FILE__, __LINE__, "sbs",
 						"parsed error in", auth_fn,
 						"expected 'username:hashed password'");
@@ -268,26 +281,15 @@ static int http_auth_get_password(server *srv, mod_auth_plugin_data *p, buffer *
 			u_len = f_pwd - f_user;
 			f_pwd++;
 
-			if (NULL != (e = memchr(f_pwd, '\n', f.size - (f_pwd - f.start)))) {
-				pwd_len = e - f_pwd;
-			} else {
-				pwd_len = f.size - (f_pwd - f.start);
-			}
-
 			if (username->used - 1 == u_len &&
 			    (0 == strncmp(username->ptr, f_user, u_len))) {
 				/* found */
 
-				buffer_copy_string_len(password, f_pwd, pwd_len);
+				buffer_copy_string_len(password, f_pwd, line_len - (f_pwd - f_user));
 
 				ret = 0;
 				break;
 			}
-
-			/* EOL */
-			if (!e) break;
-
-			f_line = e + 1;
 		}
 
 		stream_close(&f);
diff --git a/tests/lighttpd.htpasswd b/tests/lighttpd.htpasswd
index 1faf25a..43cc6d3 100644
--- a/tests/lighttpd.htpasswd
+++ b/tests/lighttpd.htpasswd
@@ -1,2 +1,4 @@
+# some comment
 des:12tMnfw882VDQ
-md5:$1$md5$kIa7Juuiv8zja0ILQPR36/
+
+md5:$1$md5$kIa7Juuiv8zja0ILQPR36/  # another one
-- 
1.7.5.4.674.g69604.dirty

