[squid-users] ncsa_auth and md5 solution contained within

From: Daniel Barron <nettle@dont-contact.us>
Date: Wed, 14 May 2003 22:49:18 +0100

Hi, my day job I work for SmoothWall (http://www.smoothwall.co.uk) and we
need ncsa_auth with md5 support. So one of our developers (Martin Houston)
has made a patch to make it work with md5 and crypt passwords automatically.
It seems to be a FAF frequently Asked for Feature I thought I might as well
provide it here for those looking for a solution in future.

The problem is the md5 generated by htpasswd is different to that expected
so Martin Houston ported ncsa_auth to the Apache 1.3.27 tree so it could use
its libraries etc. The solution is not ideal but it does work.

Basically apply the below patch to the apache source and compile and then
copy out the ncsa_auth file and use as normal with squid - however it will
automatically work with md5 passwords. It's a drop in replacement.

I hope this helps some people:

diff -ruN apache_1.3.27/src/support/Makefile.tmpl ncsa_auth/apache_1.3.27/src/support/Makefile.tmpl
--- apache_1.3.27/src/support/Makefile.tmpl 2002-03-02 17:46:23.000000000 +0000
+++ ncsa_auth/apache_1.3.27/src/support/Makefile.tmpl 2003-05-13 12:32:48.000000000 +0100
@@ -12,9 +12,9 @@
 INCLUDES=$(INCLUDES1) $(INCLUDES0) $(EXTRA_INCLUDES)
 LDFLAGS=$(LDFLAGS1) $(EXTRA_LDFLAGS) -L$(OSDIR) -L$(SRCDIR)/ap
 
-TARGETS=htpasswd htdigest rotatelogs logresolve ab apxs checkgid
+TARGETS=htpasswd htdigest rotatelogs logresolve ab apxs checkgid ncsa_auth
 
-OBJS=htpasswd.o htdigest.o rotatelogs.o logresolve.o ab.o checkgid.o
+OBJS=htpasswd.o htdigest.o rotatelogs.o logresolve.o ab.o checkgid.o ncsa_auth.o
 
 .c.o:
         $(CC) -c $(INCLUDES) $(CFLAGS) $<
@@ -52,6 +52,10 @@
 suexec: suexec.o
         $(CC) $(CFLAGS) -o suexec $(LDFLAGS) suexec.o $(LIBS)
 
+ncsa_auth: ncsa_auth.o
+ $(CC) $(CFLAGS) -o ncsa_auth $(LDFLAGS) ncsa_auth.o $(LIBS)
+
+
 clean:
         rm -f $(TARGETS) *.o
 
diff -ruN apache_1.3.27/src/support/ncsa_auth.c ncsa_auth/apache_1.3.27/src/support/ncsa_auth.c
--- apache_1.3.27/src/support/ncsa_auth.c 1970-01-01 01:00:00.000000000 +0100
+++ ncsa_auth/apache_1.3.27/src/support/ncsa_auth.c 2003-05-13 11:41:23.000000000 +0100
@@ -0,0 +1,278 @@
+/*
+ * ncsa_auth.c
+ *
+ * AUTHOR: Arjan de Vet <Arjan.deVet@adv.iae.nl>
+ *
+ * Example authentication program for Squid, based on the original
+ * proxy_auth code from client_side.c, written by
+ * Jon Thackray <jrmt@uk.gdscorp.com>.
+ *
+ * Uses a NCSA httpd style password file for authentication with the
+ * following improvements suggested by various people:
+ *
+ * - comment lines are possible and should start with a '#';
+ * - empty or blank lines are possible;
+ * - extra fields in the password file are ignored; this makes it
+ * possible to use a Unix password file but I do not recommend that.
+ *
+ * This version taken from Squid and merged back into Apache source tree
+ * to enable Apache compatible md5 passwords to be used in the file as
+ * well as crypt passwords.
+ *
+ * By Martin Houston martin@smoothwall.co.uk May 2003.
+ * SmoothWall Ltd (www.smoothwall.co.uk)
+ *
+ */
+#include "ap_config.h"
+#ifndef NETWARE
+#include <sys/types.h>
+#endif
+#include <signal.h>
+#include <errno.h>
+#include "ap.h"
+#include "ap_md5.h"
+#include "ap_sha1.h"
+
+#ifdef HAVE_CRYPT_H
+#include <crypt.h>
+#endif
+
+#ifdef WIN32
+#include <conio.h>
+#include "../os/win32/getopt.h"
+#define unlink _unlink
+#endif
+
+
+/* We used to use the Squid hashing code but for now just do a linear
+ * linked list search for simplicity.
+ * Apache may have an equivalent feature to plug into.
+ */
+/*
+ * rfc1738_unescape() - Converts escaped characters (%xy numbers) in
+ * given the string. %% is a %. %ab is the 8-bit hexadecimal number "ab"
+ */
+void
+rfc1738_unescape(char *s)
+{
+ char hexnum[3];
+ int i, j; /* i is write, j is read */
+ unsigned int x;
+ for (i = j = 0; s[j]; i++, j++) {
+ s[i] = s[j];
+ if (s[i] != '%')
+ continue;
+ if (s[j + 1] == '%') { /* %% case */
+ j++;
+ continue;
+ }
+ if (s[j + 1] && s[j + 2]) {
+ hexnum[0] = s[j + 1];
+ hexnum[1] = s[j + 2];
+ hexnum[2] = '\0';
+ if (1 == sscanf(hexnum, "%x", &x)) {
+ s[i] = (char) (0x0ff & x);
+ j += 2;
+ }
+ }
+ }
+ s[i] = '\0';
+}
+
+#define USER_DATA_MAGIC "ncsa_au" /* 8 bytes including the null */
+
+typedef struct _user_data {
+ /* This has to be the right 8 byte string before we consider this a valid rec */
+ char magic[8];
+ char *user;
+ struct _user_data *next;
+ char *passwd;
+} user_data;
+
+user_data *data = (user_data *)NULL;
+
+/*
+ * free the linked list we set up but take care not to go into a loop.
+ * May leak memory but are guaranteed to finish execution
+ */
+static void
+data_free(user_data *u, int depth, int maxdepth, user_data** backtrace)
+{
+ int i;
+ int free_backtrace = 0;
+
+ /* get the null case out of the way first */
+ if(u == (user_data *)0) {
+ return;
+ }
+ if(strncmp(u->magic, USER_DATA_MAGIC, sizeof(USER_DATA_MAGIC))) {
+ printf("Bad magic - data corruption\n");
+ return;
+ }
+
+ if(backtrace == (user_data **)NULL) {
+ backtrace = (user_data **)malloc(sizeof(void *) * maxdepth+1);
+ if(backtrace == (user_data **)NULL) {
+ printf("malloc fail in data_free\n");
+ return; /* possible memory leak here! */
+ }
+ free_backtrace = 1; /* now need to free this also */
+ }
+ if(depth >= maxdepth) {
+ printf("Max depth of %d exceeded in data_free\n", maxdepth);
+ if(free_backtrace)
+ free(backtrace);
+ return; /* possible memory leak here but avoid infinite loop */
+ }
+
+ if(u) {
+ /* see if we encountered this before */
+ for(i = 0; i < depth; i++) {
+ if(backtrace[i] == u) { /* seen this address before */
+ printf("Loop encountered in linked list %d same as %d!\n", i, depth);
+
+ if(free_backtrace)
+ free(backtrace);
+ return;
+ }
+ }
+ backtrace[depth] = u;
+
+
+ if(u->next)
+ data_free(u->next,depth+1,maxdepth,backtrace);
+ free(u->user);
+ free(u->passwd);
+ free(u);
+ if(free_backtrace)
+ free(backtrace);
+ }
+}
+
+user_data *data_lookup(user_data *data, const char *key) {
+ user_data *u = data;
+
+ if(u == (user_data *)NULL) {
+ return(u);
+ }
+ do{
+ if(strncmp(u->magic, USER_DATA_MAGIC, sizeof(USER_DATA_MAGIC))) {
+ printf("user_data is corrupt\n");
+ return (user_data *)NULL;
+ }
+ if(!strcmp(key, u->user)) {
+ /* found it */
+ return u;
+ }
+ u = u->next;
+ }
+ while(u); /* follow the chain till NULL */
+ return(u);
+}
+
+static void
+read_passwd_file(const char *passwdfile)
+{
+ FILE *f;
+ char buf[8192];
+ user_data *u;
+ char *user;
+ char *passwd;
+ if (data != NULL) {
+ data_free(data,0,100000,0);
+ data = NULL;
+ }
+ /* initial setup */
+
+ f = fopen(passwdfile, "r");
+ while (fgets(buf, 8192, f) != NULL) {
+ if ((buf[0] == '#') || (buf[0] == ' ') || (buf[0] == '\t') ||
+ (buf[0] == '\n'))
+ continue;
+ user = strtok(buf, ":\n");
+ passwd = strtok(NULL, ":\n");
+ if ((strlen(user) > 0) && passwd) {
+ u = malloc(sizeof(*u));
+ strcpy(u->magic, USER_DATA_MAGIC);
+ u->user = strdup(user);
+ u->passwd = strdup(passwd);
+ u->next = data;
+ data = u; /* link to front of list - quickest */
+ }
+ }
+ fclose(f);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct stat sb;
+ time_t change_time = 0;
+ char buf[256];
+ char *user, *passwd, *p;
+ user_data *u;
+ setbuf(stdout, NULL);
+ if (argc != 2) {
+ fprintf(stderr, "Usage: ncsa_auth <passwordfile>\n");
+ exit(1);
+ }
+ if (stat(argv[1], &sb) != 0) {
+ fprintf(stderr, "cannot stat %s\n", argv[1]);
+ exit(1);
+ }
+ while (fgets(buf, 256, stdin) != NULL) {
+ if ((p = strchr(buf, '\n')) != NULL)
+ *p = '\0'; /* strip \n */
+ if (stat(argv[1], &sb) == 0) {
+ if (sb.st_mtime != change_time) {
+ read_passwd_file(argv[1]);
+ change_time = sb.st_mtime;
+ }
+ }
+ if ((user = strtok(buf, " ")) == NULL) {
+ printf("ERR\n");
+ continue;
+ }
+ if ((passwd = strtok(NULL, "")) == NULL) {
+ printf("ERR\n");
+ continue;
+ }
+ rfc1738_unescape(user);
+ rfc1738_unescape(passwd);
+ u = data_lookup(data, user);
+
+
+ if (u == NULL) {
+ printf("data_lookup fails for %s\n", user);
+ printf("ERR\n");
+ } else {
+
+ /* detect which sort of salt this is */
+ if(!strncmp(u->passwd, "$apr1$", strlen("$apr1$"))) {
+ char cpw[120];
+ /* apache generated salt */
+
+
+ ap_MD5Encode((const unsigned char *)passwd, (const unsigned char *)u->passwd,
+ cpw, sizeof(cpw));
+ /* printf("Trying %s against %s gives %s\n", passwd, u->passwd, cpw); */
+ if (strcmp(u->passwd,cpw)) {
+ printf("ERR\n");
+ } else {
+ printf("OK\n");
+ }
+ }
+ else {
+ /* plain old crypt */
+ if (strcmp(u->passwd, (char *)crypt(passwd, u->passwd))) {
+ printf("ERR\n");
+ } else {
+ printf("OK\n");
+ }
+ }
+ }
+ }
+ data_free(data,0,100000,0);
+ exit(0);
+}
+
+

-- 
Daniel Barron
(Visit http://dansguardian.org/ - True web content filtering for all)
----------- End forwarded message -----------
-- 
Daniel Barron
Senior Developer
Smoothwall Ltd. -  http://www.smoothwall.co.uk/
This email and any attachments transmitted with it are confidential to the
intended recipient(s) and may not be communicated to any other person or
published by any means without the express permission of SmoothWall Limited.
Any views expressed in this message are solely those of the author. See:
http://www.smoothwall.co.uk/emailnotice.html for the full text of this
notice.
GPG Public Key available - http://www.smoothwall.co.uk/~daniel/publickey.txt
----------- End forwarded message -----------
-- 
Daniel Barron
(Visit http://dansguardian.org/ - True web content filtering for all)
Received on Wed May 14 2003 - 15:49:26 MDT

This archive was generated by hypermail pre-2.1.9 : Tue Dec 09 2003 - 17:16:40 MST