--- src/http_auth.h.orig	2010-01-02 17:58:11.000000000 +0100
+++ src/http_auth.h	2010-01-02 17:59:17.000000000 +0100
@@ -14,7 +14,8 @@
 	AUTH_BACKEND_PLAIN,
 	AUTH_BACKEND_LDAP,
 	AUTH_BACKEND_HTPASSWD,
-	AUTH_BACKEND_HTDIGEST
+	AUTH_BACKEND_HTDIGEST,
+	AUTH_BACKEND_PROGRAM
 } auth_backend_t;
 
 typedef struct {
@@ -38,6 +39,8 @@
 	unsigned short auth_ldap_starttls;
 	unsigned short auth_ldap_allow_empty_pw;
 
+	buffer *auth_program_exec;
+
 	unsigned short auth_debug;
 
 	/* generated */
--- src/http_auth.c.orig	2016-07-31 08:42:39.000000000 -0400
+++ src/http_auth.c	2016-07-31 16:24:22.144575820 -0400
@@ -192,7 +192,8 @@
 		}
 
 		fclose(fp);
-	} else if (p->conf.auth_backend == AUTH_BACKEND_LDAP) {
+	} else if (p->conf.auth_backend == AUTH_BACKEND_LDAP ||
+	           p->conf.auth_backend == AUTH_BACKEND_PROGRAM) {
 		return 0;
 	}
 
@@ -711,6 +712,57 @@
 
 		return 0;
 #endif
+	} else if (p->conf.auth_backend == AUTH_BACKEND_PROGRAM) {
+		buffer    *progbuf = p->conf.auth_program_exec;
+		const char  *prog;
+		FILE    *pipe;
+		int   ret;
+
+		/*
+		 * This is tested when loading configuration,
+		 * but better be paranoid.
+		 */
+		if(!progbuf || progbuf->used == 0) {
+			log_error_write(srv, __FILE__, __LINE__, "s",
+					"Missing 'auth.backend.program.exec' directive");
+			return -1;
+		}
+		prog = progbuf->ptr;
+		/*
+		 * Preliminary check, so we can have better error reporting.
+		 * It was tested during configuration reading, but maybe
+		 * something happened to the program since that time.
+		 *
+		 * If someone mess with the program after this test, it
+		 * would simply fail in the popen()/pclose() which we check anyway.
+		 */
+		if(access(prog, F_OK | X_OK) < 0) {
+			log_error_write(srv, __FILE__, __LINE__, "ssss",
+					"auth.backend.program: Failed access(",
+					prog,
+					"): ",
+					strerror(errno));
+			return -1;
+		}
+		if((pipe = popen(prog, "w")) == NULL) {
+			log_error_write(srv, __FILE__, __LINE__, "ssss",
+					"Failed popen(",
+					prog,
+					"): ",
+					strerror(errno));
+			return -1;
+		}
+		fprintf(pipe, "%s:%s\n", username->ptr, pw);
+		if((ret = pclose(pipe)) != 0) {
+			log_error_write(srv, __FILE__, __LINE__, "sssds",
+					"Failed pclose(", prog, "):", ret, strerror(errno));
+			return -1;
+		}
+		if (p->conf.auth_debug) {
+			log_error_write(srv, __FILE__, __LINE__, "ss",
+					"auth.backend.program success for: ", username->ptr);
+		}
+		return 0;
 	}
 	return -1;
 }
--- src/mod_auth.c.orig	2010-01-02 17:58:11.000000000 +0100
+++ src/mod_auth.c	2010-01-02 18:08:14.000000000 +0100
@@ -82,6 +82,7 @@
 
 			if (s->ldap) ldap_unbind_s(s->ldap);
 #endif
+			buffer_free(s->auth_program_exec);
 
 			free(s);
 		}
@@ -119,6 +120,7 @@
 	PATCH(ldap_filter_pre);
 	PATCH(ldap_filter_post);
 #endif
+	PATCH(auth_program_exec);
 
 	/* skip the first, the global context */
 	for (i = 1; i < srv->config_context->used; i++) {
@@ -169,6 +171,8 @@
 				PATCH(auth_ldap_bindpw);
 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.allow-empty-pw"))) {
 				PATCH(auth_ldap_allow_empty_pw);
+			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.program.exec"))) {
+				PATCH(auth_program_exec);
 			}
 		}
 	}
@@ -326,7 +330,8 @@
 		{ "auth.backend.ldap.allow-empty-pw",     NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 11 */
 		{ "auth.backend.htdigest.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 12 */
 		{ "auth.backend.htpasswd.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 13 */
-		{ "auth.debug",                     NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },  /* 14 */
+		{ "auth.backend.program.exec",      NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 14 */
+		{ "auth.debug",                     NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },  /* 15 */
 		{ NULL,                             NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
 	};
 
@@ -352,6 +357,7 @@
 		s->auth_ldap_filter = buffer_init();
 		s->auth_ldap_cafile = buffer_init();
 		s->auth_ldap_starttls = 0;
+		s->auth_program_exec = buffer_init();
 		s->auth_debug = 0;
 
 		s->auth_require = array_init();
@@ -376,7 +382,8 @@
 		cv[11].destination = &(s->auth_ldap_allow_empty_pw);
 		cv[12].destination = s->auth_htdigest_userfile;
 		cv[13].destination = s->auth_htpasswd_userfile;
-		cv[14].destination = &(s->auth_debug);
+		cv[14].destination = s->auth_program_exec;
+		cv[15].destination = &(s->auth_debug);
 
 		p->config_storage[i] = s;
 		ca = ((data_config *)srv->config_context->data[i])->value;
@@ -394,6 +401,8 @@
 				s->auth_backend = AUTH_BACKEND_PLAIN;
 			} else if (0 == strcmp(s->auth_backend_conf->ptr, "ldap")) {
 				s->auth_backend = AUTH_BACKEND_LDAP;
+			} else if (0 == strcmp(s->auth_backend_conf->ptr, "program")) {
+				s->auth_backend = AUTH_BACKEND_PROGRAM;
 			} else {
 				log_error_write(srv, __FILE__, __LINE__, "sb", "auth.backend not supported:", s->auth_backend_conf);
 
@@ -534,6 +543,28 @@
 				return (ret);
 			break;
 		}
+		case AUTH_BACKEND_PROGRAM: {
+			 const char  *prog;
+			 /*
+				* Let's make some sanity checks so we detect them during
+				* startup and not only when trying to authenticate.
+				*/
+			 if(!(s->auth_program_exec) || s->auth_program_exec->used == 0) {
+				 log_error_write(srv, __FILE__, __LINE__, "s",
+						 "Missing or empty auth.backend.program.exec");
+				 return HANDLER_ERROR;
+			 }
+			 prog = s->auth_program_exec->ptr;
+			 if(access(prog, F_OK | X_OK) < 0) {
+				 log_error_write(srv, __FILE__, __LINE__, "ssss",
+						 "auth.backend.program: Failed access(",
+						 prog,
+						 "): ",
+						 strerror(errno));
+				 return HANDLER_ERROR;
+			 }
+			 break;
+		 }
 		default:
 			break;
 		}
