[git commit] httpd: make it possible to use system passwords for auth

Denys Vlasenko vda.linux at googlemail.com
Tue Nov 29 12:51:11 UTC 2011


commit: http://git.busybox.net/busybox/commit/?id=7291755439ad2f400df51a74b4e9a31a48f484b1
branch: http://git.busybox.net/busybox/commit/?id=refs/heads/master

function                                             old     new   delta
check_user_passwd                                    320     467    +147
httpd_main                                           760     757      -3

Signed-off-by: Pascal Bellard <pascal.bellard at ads-lu.com>
Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 networking/httpd.c |  175 +++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 147 insertions(+), 28 deletions(-)

diff --git a/networking/httpd.c b/networking/httpd.c
index ecdf5b5..c66e0f6 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -54,6 +54,8 @@
  * /cgi-bin:foo:bar  # Require user foo, pwd bar on urls starting with /cgi-bin/
  * /adm:admin:setup  # Require user admin, pwd setup on urls starting with /adm/
  * /adm:toor:PaSsWd  # or user toor, pwd PaSsWd on urls starting with /adm/
+ * /adm:root:*       # or user root, pwd from /etc/passwd on urls starting with /adm/
+ * /wiki:*:*         # or any user from /etc/passwd with according pwd on urls starting with /wiki/
  * .au:audio/basic   # additional mime type for audio.au files
  * *.php:/path/php   # run xxx.php through an interpreter
  *
@@ -123,6 +125,14 @@
 //usage:     "\n	-d STRING	URL decode STRING"
 
 #include "libbb.h"
+#if ENABLE_PAM
+/* PAM may include <locale.h>. We may need to undefine bbox's stub define: */
+# undef setlocale
+/* For some obscure reason, PAM is not in pam/xxx, but in security/xxx.
+ * Apparently they like to confuse people. */
+# include <security/pam_appl.h>
+# include <security/pam_misc.h>
+#endif
 #if ENABLE_FEATURE_HTTPD_USE_SENDFILE
 # include <sys/sendfile.h>
 #endif
@@ -1658,6 +1668,56 @@ static int checkPermIP(void)
 }
 
 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+
+# if ENABLE_FEATURE_HTTPD_AUTH_MD5 && ENABLE_PAM
+struct pam_userinfo {
+	const char *name;
+	const char *pw;
+};
+
+static int pam_talker(int num_msg,
+		const struct pam_message **msg,
+		struct pam_response **resp,
+		void *appdata_ptr)
+{
+	int i;
+	struct pam_userinfo *userinfo = (struct pam_userinfo *) appdata_ptr;
+	struct pam_response *response;
+
+	if (!resp || !msg || !userinfo)
+		return PAM_CONV_ERR;
+
+	/* allocate memory to store response */
+	response = xzalloc(num_msg * sizeof(*response));
+
+	/* copy values */
+	for (i = 0; i < num_msg; i++) {
+		const char *s;
+
+		switch (msg[i]->msg_style) {
+		case PAM_PROMPT_ECHO_ON:
+			s = userinfo->name;
+			break;
+		case PAM_PROMPT_ECHO_OFF:
+			s = userinfo->pw;
+			break;
+	        case PAM_ERROR_MSG:
+        	case PAM_TEXT_INFO:
+        		s = "";
+			break;
+		default:
+			free(response);
+			return PAM_CONV_ERR;
+		}
+		response[i].resp = xstrdup(s);
+		if (PAM_SUCCESS != 0)
+			response[i].resp_retcode = PAM_SUCCESS;
+	}
+	*resp = response;
+	return PAM_SUCCESS;
+}
+# endif
+
 /*
  * Config file entries are of the form "/<path>:<user>:<passwd>".
  * If config file has no prefix match for path, access is allowed.
@@ -1667,7 +1727,7 @@ static int checkPermIP(void)
  *
  * Returns 1 if user_and_passwd is OK.
  */
-static int check_user_passwd(const char *path, const char *user_and_passwd)
+static int check_user_passwd(const char *path, char *user_and_passwd)
 {
 	Htaccess *cur;
 	const char *prev = NULL;
@@ -1675,6 +1735,7 @@ static int check_user_passwd(const char *path, const char *user_and_passwd)
 	for (cur = g_auth; cur; cur = cur->next) {
 		const char *dir_prefix;
 		size_t len;
+		int r;
 
 		dir_prefix = cur->before_colon;
 
@@ -1690,7 +1751,8 @@ static int check_user_passwd(const char *path, const char *user_and_passwd)
 		len = strlen(dir_prefix);
 		if (len != 1 /* dir_prefix "/" matches all, don't need to check */
 		 && (strncmp(dir_prefix, path, len) != 0
-		    || (path[len] != '/' && path[len] != '\0'))
+		    || (path[len] != '/' && path[len] != '\0')
+		    )
 		) {
 			continue;
 		}
@@ -1699,38 +1761,95 @@ static int check_user_passwd(const char *path, const char *user_and_passwd)
 		prev = dir_prefix;
 
 		if (ENABLE_FEATURE_HTTPD_AUTH_MD5) {
-			char *md5_passwd;
-
-			md5_passwd = strchr(cur->after_colon, ':');
-			if (md5_passwd && md5_passwd[1] == '$' && md5_passwd[2] == '1'
-			 && md5_passwd[3] == '$' && md5_passwd[4]
-			) {
-				char *encrypted;
-				int r, user_len_p1;
-
-				md5_passwd++;
-				user_len_p1 = md5_passwd - cur->after_colon;
-				/* comparing "user:" */
-				if (strncmp(cur->after_colon, user_and_passwd, user_len_p1) != 0) {
+			char *colon_after_user;
+			const char *passwd;
+
+			colon_after_user = strchr(user_and_passwd, ':');
+			if (!colon_after_user)
+				goto bad_input;
+			passwd = strchr(cur->after_colon, ':');
+			if (!passwd)
+				goto bad_input;
+			passwd++;
+			if (passwd[0] == '*') {
+# if ENABLE_PAM
+				struct pam_userinfo userinfo;
+				struct pam_conv conv_info = { &pam_talker, (void *) &userinfo };
+				pam_handle_t *pamh;
+
+				/* compare "user:" */
+				if (cur->after_colon[0] != '*'
+				 && strncmp(cur->after_colon, user_and_passwd, colon_after_user - user_and_passwd + 1) != 0
+				) {
+					continue;
+				}
+				/* this cfg entry is '*' or matches username from peer */
+				*colon_after_user = '\0';
+				userinfo.name = user_and_passwd;
+				userinfo.pw = colon_after_user + 1;
+				r = pam_start("httpd", user_and_passwd, &conv_info, &pamh) != PAM_SUCCESS
+				 || pam_authenticate(pamh, PAM_DISALLOW_NULL_AUTHTOK) != PAM_SUCCESS
+				 || pam_acct_mgmt(pamh, PAM_DISALLOW_NULL_AUTHTOK)    != PAM_SUCCESS
+				;
+				pam_end(pamh, PAM_SUCCESS);
+				*colon_after_user = ':';
+				goto end_check_passwd;
+# else
+#  if ENABLE_FEATURE_SHADOWPASSWDS
+				/* Using _r function to avoid pulling in static buffers */
+				struct spwd spw;
+				char buffer[256];
+#  endif
+				struct passwd *pw;
+
+				*colon_after_user = '\0';
+				pw = getpwnam(user_and_passwd);
+				*colon_after_user = ':';
+				if (!pw || !pw->pw_passwd)
 					continue;
+				passwd = pw->pw_passwd;
+#  if ENABLE_FEATURE_SHADOWPASSWDS
+				if ((passwd[0] == 'x' || passwd[0] == '*') && !passwd[1]) {
+					/* getspnam_r may return 0 yet set result to NULL.
+					 * At least glibc 2.4 does this. Be extra paranoid here. */
+					struct spwd *result = NULL;
+					r = getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result);
+					if (r == 0 && result)
+						passwd = result->sp_pwdp;
 				}
+#  endif
+# endif /* ENABLE_PAM */
+			}
 
-				encrypted = pw_encrypt(
-					user_and_passwd + user_len_p1 /* cleartext pwd from user */,
-					md5_passwd /*salt */, 1 /* cleanup */);
-				r = strcmp(encrypted, md5_passwd);
-				free(encrypted);
-				if (r == 0)
-					goto set_remoteuser_var; /* Ok */
+			/* compare "user:" */
+			if (cur->after_colon[0] != '*'
+			 && strncmp(cur->after_colon, user_and_passwd, colon_after_user - user_and_passwd + 1) != 0
+			) {
 				continue;
 			}
+			/* this cfg entry is '*' or matches username from peer */
+
+			/* encrypt pwd from peer and check match with local one */
+			{
+				char *encrypted = pw_encrypt(
+					/* pwd: */  colon_after_user + 1,
+					/* salt: */ passwd,
+					/* cleanup: */ 0
+				);
+				r = strcmp(encrypted, passwd);
+				free(encrypted);
+				goto end_check_passwd;
+			}
+ bad_input: ;
 		}
 
 		/* Comparing plaintext "user:pass" in one go */
-		if (strcmp(cur->after_colon, user_and_passwd) == 0) {
- set_remoteuser_var:
+		r = strcmp(cur->after_colon, user_and_passwd);
+ end_check_passwd:
+		if (r == 0) {
 			remoteuser = xstrndup(user_and_passwd,
-					strchrnul(user_and_passwd, ':') - user_and_passwd);
+				strchrnul(user_and_passwd, ':') - user_and_passwd
+			);
 			return 1; /* Ok */
 		}
 	} /* for */
@@ -2067,10 +2186,10 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
 	}
 
 #if ENABLE_FEATURE_HTTPD_BASIC_AUTH
-	/* Case: no "Authorization:" was seen, but page does require passwd.
+	/* Case: no "Authorization:" was seen, but page might require passwd.
 	 * Check that with dummy user:pass */
 	if (authorized < 0)
-		authorized = check_user_passwd(urlcopy, ":");
+		authorized = check_user_passwd(urlcopy, (char *) "");
 	if (!authorized)
 		send_headers_and_exit(HTTP_UNAUTHORIZED);
 #endif
@@ -2353,7 +2472,7 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
 		salt[1] = '1';
 		salt[2] = '$';
 		crypt_make_salt(salt + 3, 4);
-		puts(pw_encrypt(pass, salt, 1));
+		puts(pw_encrypt(pass, salt, /*cleanup:*/ 0));
 		return 0;
 	}
 #endif


More information about the busybox-cvs mailing list