summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am4
-rw-r--r--common/sock_any.c99
-rw-r--r--common/sock_any.h2
-rw-r--r--configure.in4
-rw-r--r--daemon/defaults.h2
-rw-r--r--daemon/digest.c87
-rw-r--r--daemon/digest.h4
-rw-r--r--daemon/httpauthd.c10
-rw-r--r--daemon/httpauthd.h3
-rw-r--r--daemon/ldap.c20
-rw-r--r--daemon/simple.c14
-rw-r--r--doc/Makefile.am4
-rw-r--r--doc/httpauth.conf.582
-rw-r--r--doc/httpauthd.845
-rw-r--r--doc/httpauthd.conf.5316
-rw-r--r--doc/httpauthd.conf.sample34
-rw-r--r--sample/httpauthd.conf24
17 files changed, 592 insertions, 162 deletions
diff --git a/Makefile.am b/Makefile.am
index 96afc15..47af21f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,6 +1,6 @@
-EXTRA_DIST = doc apache1x common config.sub
-SUBDIRS = daemon
+EXTRA_DIST = apache1x common config.sub
+SUBDIRS = daemon doc
dist-hook:
rm -rf `find $(distdir)/ -name CVS`
diff --git a/common/sock_any.c b/common/sock_any.c
index b2bde01..f01933b 100644
--- a/common/sock_any.c
+++ b/common/sock_any.c
@@ -3,6 +3,7 @@
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
+#include <netdb.h>
#include "sock_any.h"
@@ -11,20 +12,47 @@
int sock_any_pton(const char* addr, struct sockaddr_any* any, int defport)
{
size_t l;
- const char* t;
+ char buf[256]; /* TODO: Use a constant */
+ char* t;
+ char* t2;
memset(any, 0, sizeof(*any));
+ /* Just a port? */
+ do
+ {
+ #define PORT_CHARS "0123456789"
+ #define PORT_MIN 1
+ #define PORT_MAX 5
+
+ int port = 0;
+
+ l = strspn(addr, PORT_CHARS);
+ if(l < PORT_MIN || l > PORT_MAX || addr[l] != 0)
+ break;
+
+ port = strtol(t, &t2, 10);
+ if(*t2 || port <= 0 || port >= 65536)
+ break;
+
+ any->s.in.sin_family = AF_INET;
+ any->s.in.sin_port = htons((unsigned short)(port <= 0 ? defport : port));
+ any->s.in.sin_addr.s_addr = 0;
+
+ any->namelen = sizeof(any->s.in);
+ return AF_INET;
+ }
+ while(0);
+
/* Look and see if we can parse an ipv4 address */
do
{
+ #define IPV4_PORT_CHARS
#define IPV4_CHARS "0123456789."
#define IPV4_MIN 3
#define IPV4_MAX 18
int port = 0;
- char buf[IPV4_MAX + 1];
-
t = NULL;
l = strlen(addr);
@@ -51,7 +79,6 @@ int sock_any_pton(const char* addr, struct sockaddr_any* any, int defport)
if(t)
{
- char* t2;
port = strtol(t, &t2, 10);
if(*t2 || port <= 0 || port >= 65536)
break;
@@ -76,8 +103,6 @@ int sock_any_pton(const char* addr, struct sockaddr_any* any, int defport)
#define IPV6_MAX 48
int port = -1;
- char buf[IPV6_MAX + 1];
-
t = NULL;
l = strlen(addr);
@@ -131,9 +156,11 @@ int sock_any_pton(const char* addr, struct sockaddr_any* any, int defport)
while(0);
#endif
+ /* A unix socket path */
do
{
- if(strchr(addr, ':'))
+ /* No colon and must have a path component */
+ if(strchr(addr, ':') || !strchr(addr, '/'))
break;
l = strlen(addr);
@@ -148,6 +175,64 @@ int sock_any_pton(const char* addr, struct sockaddr_any* any, int defport)
}
while(0);
+ /* A DNS name and a port? */
+ do
+ {
+ struct addrinfo* res;
+ int port = 0;
+ t = NULL;
+
+ l = strlen(addr);
+ if(l < 255 && isalpha(addr[0]))
+ break;
+
+ /* Some basic illegal character checks */
+ if(strspn(addr, " /\\") != l)
+ break;
+
+ strcpy(buf, addr);
+
+ /* Find the last set that contains just numbers */
+ t = strchr(buf, ':');
+ if(t)
+ {
+ *t = 0;
+ t++;
+ }
+
+ if(t)
+ {
+ port = strtol(t, &t2, 10);
+ if(*t2 || port <= 0 || port >= 65536)
+ break;
+ }
+
+ /* Try and resolve the domain name */
+ if(getaddrinfo(buf, NULL, NULL, &res) != 0 || !res)
+ break;
+
+ memcpy(&(any->s.a), res->ai_addr, sizeof(struct sockaddr));
+ any->namelen = res->ai_addrlen;
+ freeaddrinfo(res);
+
+ port = htons((unsigned short)(port <= 0 ? defport : port));
+
+ switch(any->s.a.sa_family)
+ {
+ case PF_INET:
+ any->s.in.sin_port = port;
+ break;
+#ifdef HAVE_INET6
+ case PF_INET6:
+ any->s.in6.sin6_port = port;
+ break;
+#endif
+ };
+
+ return any->s.a.sa_family;
+ }
+ while(0);
+
return -1;
}
diff --git a/common/sock_any.h b/common/sock_any.h
index f281020..5d733b1 100644
--- a/common/sock_any.h
+++ b/common/sock_any.h
@@ -8,7 +8,6 @@
struct sockaddr_any
{
- size_t namelen;
union _sockaddr_any
{
/* The header */
@@ -21,6 +20,7 @@ struct sockaddr_any
struct sockaddr_in6 in6;
#endif
} s;
+ size_t namelen;
};
#define SANY_ADDR(any) (&((any).s.a))
diff --git a/configure.in b/configure.in
index b3ecf3d..c70ae2b 100644
--- a/configure.in
+++ b/configure.in
@@ -92,9 +92,9 @@ AC_CHECK_DECL(PTHREAD_MUTEX_ERRORCHECK_NP, [AC_DEFINE(HAVE_ERR_MUTEX, 1, "Error
[ #include <pthread.h> ])], [ #include <pthread.h> ])
# Required Functions
-AC_CHECK_FUNCS([memset strerror malloc realloc getopt strchr tolower], ,
+AC_CHECK_FUNCS([memset strerror malloc realloc getopt strchr tolower getaddrinfo], ,
[echo "ERROR: Required function missing"; exit 1])
AC_CHECK_FUNCS([strlwr])
-AC_CONFIG_FILES([Makefile daemon/Makefile])
+AC_CONFIG_FILES([Makefile daemon/Makefile doc/Makefile])
AC_OUTPUT
diff --git a/daemon/defaults.h b/daemon/defaults.h
index 828bec1..b3c6fcf 100644
--- a/daemon/defaults.h
+++ b/daemon/defaults.h
@@ -3,7 +3,7 @@
#define __DEFAULTS_H__
#define DEFAULT_PENDING_MAX 16
-#define DEFAULT_PENDING_TIMEOUT 60
+#define DEFAULT_PENDING_TIMEOUT 20
#define DEFAULT_TIMEOUT 900
#define DEFAULT_CACHEMAX 1024
#define DEFAULT_PORT 8020
diff --git a/daemon/digest.c b/daemon/digest.c
index 0bd6398..5c53191 100644
--- a/daemon/digest.c
+++ b/daemon/digest.c
@@ -242,7 +242,7 @@ int digest_parse(char* header, ha_buffer_t* buf, digest_header_t* rec,
if(rec->nonce)
{
size_t len = DIGEST_NONCE_LEN;
- void* d = ha_bufdec64(buf, rec->nonce, &len);
+ void* d = ha_bufdechex(buf, rec->nonce, &len);
if(d && len == DIGEST_NONCE_LEN)
memcpy(nonce, d, DIGEST_NONCE_LEN);
@@ -252,15 +252,15 @@ int digest_parse(char* header, ha_buffer_t* buf, digest_header_t* rec,
return HA_OK;
}
-int digest_check(const char* realm, const char* method, const char* uri,
- ha_buffer_t* buf, digest_header_t* dg, digest_record_t* rec)
+int digest_check(digest_header_t* dg, digest_record_t* rec, ha_options_t* opts,
+ ha_buffer_t* buf, const char* method, const char* uri)
{
unsigned char hash[MD5_LEN];
md5_ctx_t md5;
const char* digest;
const char* t;
- ASSERT(realm && method && uri && buf && dg && rec);
+ ASSERT(opts && method && buf && dg && rec);
/* TODO: Many of these should somehow communicate BAD REQ back to the client */
@@ -272,21 +272,31 @@ int digest_check(const char* realm, const char* method, const char* uri,
}
/* Username */
- if(!dg->username || !dg->username[0] ||
- md5_strcmp(rec->userhash, dg->username) != 0)
+ if(!dg->username || !dg->username[0])
{
ha_messagex(LOG_WARNING, "digest response missing username");
return HA_BADREQ;
}
+ if(md5_strcmp(rec->userhash, dg->username) != 0)
+ {
+ ha_messagex(LOG_ERR, "digest response contains invalid username");
+ return HA_FALSE;
+ }
+
/* The realm */
- if(!dg->realm || strcmp(dg->realm, realm) != 0)
+ if(!dg->realm)
{
- ha_messagex(LOG_WARNING, "digest response contains invalid realm: '%s'",
- dg->realm ? dg->realm : "");
+ ha_messagex(LOG_WARNING, "digest response contains missing realm");
return HA_BADREQ;
}
+ if(strcmp(dg->realm, opts->realm) != 0)
+ {
+ ha_messagex(LOG_ERR, "digest response contains invalid realm: %s", dg->realm);
+ return HA_FALSE;
+ }
+
/* Components in the new RFC */
if(dg->qop)
{
@@ -308,25 +318,38 @@ int digest_check(const char* realm, const char* method, const char* uri,
return HA_BADREQ;
}
- /* The nonce count */
- if(!dg->nc || !dg->nc[0])
+ if(!opts->digest_ignorenc)
{
- ha_messagex(LOG_WARNING, "digest response is missing nc value");
- return HA_BADREQ;
- }
-
- /* Validate the nc */
- else
- {
- char* e;
- long nc = strtol(dg->nc, &e, 10);
-
- if(*e || nc != rec->nc)
+ /* The nonce count */
+ if(!dg->nc || !dg->nc[0])
{
- ha_messagex(LOG_WARNING, "digest response has invalid nc value: %s",
- dg->nc);
+ ha_messagex(LOG_WARNING, "digest response is missing nc value");
return HA_BADREQ;
}
+
+ /* Validate the nc */
+ else
+ {
+ char* e;
+ long nc = strtol(dg->nc, &e, 16);
+
+ if(*e)
+ {
+ ha_messagex(LOG_ERR, "digest response has invalid nc value: %s", dg->nc);
+ return HA_BADREQ;
+ }
+
+ /* If we didn't a nc then save it away */
+ if(!*e && rec->nc == 0)
+ rec->nc = nc;
+
+ if(*e || nc != rec->nc)
+ {
+ ha_messagex(LOG_WARNING, "digest response has wrong nc value. "
+ "possible replay attack: %s", dg->nc);
+ return HA_FALSE;
+ }
+ }
}
}
@@ -345,7 +368,7 @@ int digest_check(const char* realm, const char* method, const char* uri,
return HA_BADREQ;
}
- if(strcmp(dg->uri, uri) != 0)
+ if(!opts->digest_ignoreuri && strcmp(dg->uri, uri) != 0)
{
ha_uri_t d_uri;
ha_uri_t s_uri;
@@ -370,9 +393,9 @@ int digest_check(const char* realm, const char* method, const char* uri,
if(ha_uricmp(&d_uri, &s_uri) != 0)
{
- ha_messagex(LOG_WARNING, "digest response contains wrong uri: %s "
- "(should be %s)", dg->uri, uri);
- return HA_BADREQ;
+ ha_messagex(LOG_ERR, "digest response contains wrong uri: %s "
+ "(should be %s)", dg->uri, uri);
+ return HA_FALSE;
}
}
@@ -523,14 +546,14 @@ const char* digest_respond(ha_buffer_t* buf, digest_header_t* dg,
return NULL;
ha_bufmcat(buf, "rspauth=\"", t, "\"",
- " qop=", dg->qop,
- " nc=", dg->nc,
- " cnonce=\"", dg->cnonce, "\"", NULL);
+ ", qop=", dg->qop,
+ ", nc=", dg->nc,
+ ", cnonce=\"", dg->cnonce, "\"", NULL);
if(nextnonce)
{
ha_bufjoin(buf);
- ha_bufmcat(buf, " nextnonce=\"", nextnonce, "\"", NULL);
+ ha_bufmcat(buf, ", nextnonce=\"", nextnonce, "\"", NULL);
}
return ha_bufdata(buf);
diff --git a/daemon/digest.h b/daemon/digest.h
index 0f5e6b2..8eec901 100644
--- a/daemon/digest.h
+++ b/daemon/digest.h
@@ -42,8 +42,8 @@ int ha_digestparse(char* header, ha_buffer_t* buf, digest_header_t* rec,
int ha_digestnonce(time_t* tm, unsigned char* nonce);
-int ha_digestcheck(const char* realm, const char* method, const char* uri,
- ha_buffer_t* buf, digest_header_t* header, digest_record_t* rec);
+int digest_check(digest_header_t* dg, digest_record_t* rec, ha_options_t* opts,
+ ha_buffer_t* buf, const char* method, const char* uri);
const char* digest_respond(ha_buffer_t* buf, digest_header_t* dg,
digest_record_t* rec, unsigned char* next);
diff --git a/daemon/httpauthd.c b/daemon/httpauthd.c
index 7c74754..55092db 100644
--- a/daemon/httpauthd.c
+++ b/daemon/httpauthd.c
@@ -1314,6 +1314,16 @@ static int config_parse(const char* file, ha_buffer_t* buf)
recog = 1;
}
+ else if(strcmp(name, "digestignorenc") == 0)
+ {
+ int v;
+ if(ha_confbool(name, value, &v) < 0)
+ exit(1); /* Message already printed */
+
+ opts->digest_ignorenc = v;
+ recog = 1;
+ }
+
else if(strcmp(name, "digestdomains") == 0)
{
opts->digest_domains = value;
diff --git a/daemon/httpauthd.h b/daemon/httpauthd.h
index cd1d39e..d225e4b 100644
--- a/daemon/httpauthd.h
+++ b/daemon/httpauthd.h
@@ -188,7 +188,8 @@ typedef struct ha_options
const char* realm;
/* For digest auth: */
- int digest_ignoreuri;
+ unsigned int digest_ignoreuri : 1;
+ unsigned int digest_ignorenc : 1;
const char* digest_domains;
const char* digest_debugnonce;
}
diff --git a/daemon/ldap.c b/daemon/ldap.c
index 78e048f..1e03c32 100644
--- a/daemon/ldap.c
+++ b/daemon/ldap.c
@@ -981,7 +981,7 @@ static int digest_ldap_challenge(ldap_context_t* ctx, ha_response_t* resp,
if(ctx->opts->digest_debugnonce)
{
nonce_str = ctx->opts->digest_debugnonce;
- ha_messagex(LOG_WARNING, "simple: using debug nonce. security non-existant.");
+ ha_messagex(LOG_WARNING, "ldap: using debug nonce. security non-existant.");
}
else
#endif
@@ -1036,7 +1036,6 @@ static int digest_ldap_response(ldap_context_t* ctx, const char* header,
if(dg.nonce && strcmp(dg.nonce, ctx->opts->digest_debugnonce) != 0)
{
ret = HA_FALSE;
- resp->code = HA_SERVER_BADREQ;
ha_messagex(LOG_WARNING, "ldap: digest response contains invalid nonce");
goto finally;
}
@@ -1054,10 +1053,7 @@ static int digest_ldap_response(ldap_context_t* ctx, const char* header,
if(r != HA_OK)
{
if(r == HA_FALSE)
- {
- resp->code = HA_SERVER_BADREQ;
ha_messagex(LOG_WARNING, "ldap: digest response contains invalid nonce");
- }
goto finally;
}
@@ -1099,11 +1095,13 @@ static int digest_ldap_response(ldap_context_t* ctx, const char* header,
}
}
- /* Increment our nonce count */
- rec->nc++;
+ /* We had a record so ... */
+ else
+ {
+ rec->nc++;
+ }
- ret = digest_check(ctx->opts->realm, method,
- ctx->opts->digest_ignoreuri ? NULL : uri, buf, &dg, rec);
+ ret = digest_check(&dg, rec, ctx->opts, buf, method, uri);
if(ret == HA_BADREQ)
{
@@ -1391,7 +1389,7 @@ int ldap_process(ha_context_t* context, ha_request_t* req,
header = ha_getheader(req, "Authorization", HA_PREFIX_DIGEST);
if(header)
{
- ha_messagex(LOG_DEBUG, "ldap: processing basic auth header");
+ ha_messagex(LOG_DEBUG, "ldap: processing digest auth header");
ret = digest_ldap_response(ctx, header, req->args[AUTH_ARG_METHOD],
req->args[AUTH_ARG_URI], resp, buf);
if(ret < 0)
@@ -1405,7 +1403,7 @@ int ldap_process(ha_context_t* context, ha_request_t* req,
header = ha_getheader(req, "Authorization", HA_PREFIX_BASIC);
if(header)
{
- ha_messagex(LOG_DEBUG, "ldap: processing digest auth header");
+ ha_messagex(LOG_DEBUG, "ldap: processing basic auth header");
ret = basic_ldap_response(ctx, header, resp, buf);
if(ret < 0)
return ret;
diff --git a/daemon/simple.c b/daemon/simple.c
index 5b4eb60..d45668f 100644
--- a/daemon/simple.c
+++ b/daemon/simple.c
@@ -458,7 +458,6 @@ static int simple_digest_response(simple_context_t* ctx, const char* header,
{
if(dg.nonce && strcmp(dg.nonce, ctx->opts->digest_debugnonce) != 0)
{
- resp->code = HA_SERVER_BADREQ;
ret = HA_FALSE;
ha_messagex(LOG_WARNING, "simple: digest response contains invalid nonce");
goto finally;
@@ -477,10 +476,7 @@ static int simple_digest_response(simple_context_t* ctx, const char* header,
if(r != HA_OK)
{
if(r == HA_FALSE)
- {
- resp->code = HA_SERVER_BADREQ;
ha_messagex(LOG_WARNING, "simple: digest response contains invalid nonce");
- }
ret = r;
goto finally;
@@ -523,11 +519,13 @@ static int simple_digest_response(simple_context_t* ctx, const char* header,
}
}
- /* Increment our nonce count */
- rec->nc++;
+ /* We had a record so ... */
+ else
+ {
+ rec->nc++;
+ }
- ret = digest_check(ctx->opts->realm, method,
- ctx->opts->digest_ignoreuri ? NULL : uri, buf, &dg, rec);
+ ret = digest_check(&dg, rec, ctx->opts, buf, method, uri);
if(ret == HA_BADREQ)
{
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644
index 0000000..56ca28e
--- /dev/null
+++ b/doc/Makefile.am
@@ -0,0 +1,4 @@
+
+man_MANS = httpauthd.8 httpauthd.conf.5
+
+EXTRA_DIST = protocol.txt httpauthd.conf.sample ${man_MANS}
diff --git a/doc/httpauth.conf.5 b/doc/httpauth.conf.5
deleted file mode 100644
index fd26c7b..0000000
--- a/doc/httpauth.conf.5
+++ /dev/null
@@ -1,82 +0,0 @@
-.Dd April, 2004
-.Dt HTTPAUTH.CONF 5
-.Os httpauth
-.Sh NAME
-.Nm httpauth.conf
-.Nd the configuration file for
-.Em httpauthd
-.Sh DESCRIPTION
-The XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx
-.Nm
-scripting language is a regular expression language used for fine grained,
-buffer based search and replace. It is not limited to lines. A full description
-of what
-.Nm
-is capable of is outside the scope of this document.
-.Pp
-.Ar script
-is a text or compiled
-.Nm
-script. For details see the language documentation that came along with the distribution.
-.Pp
-When used with the
-.Fl f
-argument
-.Nm
-replaces files in place. Otherwise it reads from
-.Ar infile
-and writes to
-.Ar outfile
-\&. If either infile or outfile are missing or are equal to a dash
-.Sq Li -
-, then rep processes
-.Em stdin
-or
-.Em stdout
-respectively.
-.Sh OPTIONS
-The options are as follows:
-.Bl -tag -width Fl
-.It Fl b
-Backup files where replacements have occurred. The backup files have an
-.Sq x_r
-extension appended to their filename.
-.It Fl i
-Prompt for confirmation before each replacement.
-.It Fl p
-Only output replaced text. Can be used as a rudimentary parser.
-.It Fl q
-Supress status messages. Only errors will be sent to stderr.
-.It Fl z
-Set the replacement buffer size to
-.Ar buffsize .
-This speeds up execution as regular expressions only have to act on a small
-portion of the whole file at once. However the largest match will be limited to
-roughly
-.Ar buffsize
-, so use this option with care. The script loops over each buffer until no more
-matches are found within it. Care is taken to overlap the buffers as much as
-possible to ensure that any match smaller than
-.Ar buffsize
-can be matched.
-.Sh NOTE
-The
-.Nm
-command uses
-.Xr getopt 3
-to parse it's arguments, which allows it to accept
-the
-.Sq Li --
-option which will stop processing of flag options at that point. This allows
-the processing of files with names that begin with a dash
-.Pq Sq - .
-.Sh BUGS
-When reading from
-.Em stdin
-you must specify a buffer size.
-.Sh SEE ALSO
-.Xr repc 1 ,
-.Xr rlib 3 ,
-.Xr pcre 3
-.Sh AUTHOR
-.An Nate Nielsen Aq nielsen@memberwebs.com \ No newline at end of file
diff --git a/doc/httpauthd.8 b/doc/httpauthd.8
new file mode 100644
index 0000000..afdf753
--- /dev/null
+++ b/doc/httpauthd.8
@@ -0,0 +1,45 @@
+.Dd April, 2004
+.Dt httpauthd 8
+.Os httpauth
+.Sh NAME
+.Nm httpauthd
+.Nd a daemon which performs HTTP authentication
+.Sh DESCRIPTION
+.Xr httpauthd 8
+is a daemon that performs HTTP authentication for a variety of HTTP servers.
+Callers send it HTTP headers, which it then processes and returns responses
+destined for the client.
+.Pp
+It listens on unix or IP sockets, allowing for centralization of HTTP
+authentication and an extra layer security if necessary.
+.Pp
+.Nm httpauthd
+can perform a variety of different types of authentication, and is built
+in an extensible manner so more can be added in the future.
+.Pp
+.Bl -bullet -compact
+.It
+Basic and Digest authentication against a file.
+.It
+Basic and Digest authentication against an LDAP server.
+.It
+Basic and NTLM authentication against a SMB domain server.
+.El
+.Sh OPTIONS
+The options are as follows:
+.Bl -tag -width Fl
+.It Fl d
+Don't detach from the console and run as a daemon. In addition the
+.Ar level
+argument specifies what level of error messages to display. 0 being
+the least, 4 the most.
+.It Fl f
+Specify an alternate location for the configuration file.
+.It Fl X
+Process stdin and stdout instead of listening for connections on a
+socket. Useful for troubleshooting problems.
+.El
+.Sh SEE ALSO
+.Xr httpauth.conf 8
+.Sh AUTHOR
+.An Nate Nielsen Aq nielsen@memberwebs.com
diff --git a/doc/httpauthd.conf.5 b/doc/httpauthd.conf.5
new file mode 100644
index 0000000..2e3c49e
--- /dev/null
+++ b/doc/httpauthd.conf.5
@@ -0,0 +1,316 @@
+.Dd April, 2004
+.Dt httpauthd.conf 5
+.Os httpauth
+.Sh NAME
+.Nm httpauthd.conf
+.Nd the configuration file for
+.Xr httpauthd 8
+.Sh DESCRIPTION
+.Xr httpauthd 8
+reads it's configuration from this file when starting up. It contains global
+settings followed by the various authentication methods and their settings.
+.Sh SYNTAX
+The settings are specified one per line. The setting name comes first
+followed by a colon, and the value for that setting. Authentication method
+sections are prefixed with a '[method]' on a line of it's own.
+.Pp
+Lines beginning with a # mark are comments. An example:
+.Bd -literal -offset indent
+# Sample Configuration File
+Socket: 0.0.0.0:8020
+AuthTypes: Basic Digest
+
+[Simple]
+Alias: MyAuth
+PasswordFile: /srv/passwd.file
+.Ed
+.Sh AUTHENTICATION METHODS
+Methods are the various ways
+.Xr httpauthd
+can authenticate a user. A method will use either LDAP, a file or some
+other means to determine if a user is valid. The methods currently
+implemented are:
+.Ar Simple LDAP NTLM
+.Pp
+A method block in the configuration file needs to contain one of the
+above method names as the header for it's section (ie: [LDAP]). It
+can be given another name by specifying an
+.Em Alias
+for it.
+.Pp
+This allows for the creation of various configurations with purpose
+specific names. These names are used by callers of
+.Xr httpauthd 8
+to identify how to authenticate a given HTTP connection.
+.Sh GLOBAL OPTIONS
+These options affect httpauthd as a whole. They should be placed before the
+beginning of the first authentication method section. In addition certain
+options can be placed in this section which affect all the authentication
+methods. These are outlined under the
+.Em METHOD OPTIONS
+heading further below.
+.Bl -hang
+.It Cd Socket
+This is where httpauthd listens for connections. It can either be a unix
+type socket by specifying a file path (eg: /var/run/ha.sock), a port number
+(eg: 8030) or a IP address with optional port number (eg: 192.168.2.38:8200).
+If you specify an IP address without a port,
+.Em 8020
+will be used.
+.Pp
+[ Default:
+.Em /var/run/httpauthd.sock
+]
+.It Cd MaxThreads
+This equals the amount of authentication connections that
+.Xr httpauthd 8
+will be able to have open at once.
+.Pp
+[ Default:
+.Em 32
+]
+.El
+
+.Sh METHOD OPTIONS
+These options change settings in how the various methods handle authentication.
+When they appear after a method section, they only affect that method. Most of
+them can also appear in the inital section of the configuration file in which
+case they're used as defaults.
+.Bl -hang
+.It Cd Alias
+Change the name of the current authentication method. This is necessary when
+you're using a certain method twice (eg: LDAP) with different settings. This
+option can only be used in a method section.
+.It Cd AuthTypes
+The allowed HTTP authentication types, separated by spaces. Any combination of:
+.Ar Basic Digest NTLM
+.Pp
+[ Default:
+.Ar Basic Digest NTLM
+]
+.It Cd CacheMax
+The maximum amount of successful authentication requests a method can cache.
+.Pp
+[ Default:
+.Em 1024
+]
+.It Cd CacheTimeout
+The length of time in seconds that a successful authentication remains cached.
+How this exactly works depends on the method it applies to.
+.Pp
+[ Default:
+.Em 900
+]
+.It Cd DigestDomains
+The
+.Em domains
+setting used with
+.Em Digest
+authentication. This allows you to specify one or more URIs which are in the
+same authentication space. The specified URIs are prefixes, i.e. the
+client will assume that all URIs "below" these are also protected by the
+same username/password. The URIs may be either absolute URIs (i.e. inluding
+a scheme, host, port, etc) or relative URIs. Separated by spaces.
+.Pp
+[ Default:
+.Em (none)
+]
+.It Cd DigestIgnoreNC
+When set to
+.Em True
+allows the NC value in
+.Em Digest
+authentication to be incorrect. This opens up various replay attacks.
+.Pp
+[ Default:
+.Em False
+]
+.It Cd DigestIgnoreURI
+When set to
+.Em True
+allows the URI value in
+.Em Digest
+authentication to be mismatched with the URI requested. This opens up
+a variety of replay attacks, but may be necessary in some cases.
+.Pp
+[ Default:
+.Em False
+]
+.It Cd Realm
+The realm used in
+.Em Basic
+and
+.Em Digest
+authentication.
+.Pp
+[ Default:
+.Em (none)
+]
+.El
+.Sh SIMPLE METHOD OPTIONS
+These are settings for the
+.Em Simple
+authentication method. This method authenticates against password hashes in a file.
+.Bl -hang
+.It Cd PasswordFile
+The path of the file that contains the password hashes. This file can be in either
+the format created by
+.Xr htpasswd 1
+or
+.Xr htdigest 1
+(tools that come with apache).
+.Pp
+[ Required ]
+.El
+.Sh LDAP METHOD OPTIONS
+Settings for the
+.Em LDAP
+authentication method. This method authenticates users against an LDAP server.
+.Bl -hang
+.It Cd LDAPBase
+The base DN to use in the search for a user. This only applies when no
+LDAPDNMap is specified.
+.Pp
+[ Required when
+.Em LDAPDNMap
+is missing ]
+.It Cd LDAPDNMap
+Specifies the DN for a user name. The
+.Em %u
+and
+.Em %r
+flags can be used in the DN, which will substitute the user and realm
+respectively.
+.Pp
+[ Optional ]
+.It Cd LDAPDoBind
+When performing Basic authentication,
+.Xr httpauthd
+can try to bind to the LDAP server as the user in question. This
+allows authentication even when no access to cleartext passwords
+is available. Note that this does not apply to Digest authentication.
+.Pp
+[ Default:
+.Em True
+]
+.It Cd LDAPFilter
+The LDAP filter to use when querying the server. The
+.Em %u
+and
+.Em %r
+flags can be used in the filter, which will substitute the user and realm
+respectively. When used without a
+.Em LDAPDNMap
+then this is used to identify the LDAP entry for the user. In this case care
+should be taken that the filter only returns one record.
+.Pp
+[ Required when
+.Em LDAPDNMap
+is missing ]
+.It Cd LDAPHA1Attr
+A HA1 is a special kind of digest containing the user name, realm and
+password. This can be used in place of cleartext passwords when doing
+Digest authentication. This setting specifies the attribute on the
+LDAP server that the hash can be found in.
+.Pp
+.Xr httpauthd 8
+can perform both Basic and Digest authentication against this attribute.
+Note that the realm however is stored in the hash and must match the
+realm being sent to the client in the
+.Em Realm
+setting.
+.Pp
+[ Optional ]
+.It Cd LDAPMax
+The maximum amount of connections to make to the LDAP server.
+.Pp
+[ Default:
+.Em 10
+]
+.It Cd LDAPPasswsord
+The password to use with
+.Em LDAPUser
+.Pp
+[ Optional ]
+.It Cd LDAPPwAttr
+The name of the attribute on the LDAP server that contains the user's
+password. This can be for Basic authentication (when
+.Em LDAPDoBind
+is off) or Digest authentication. When used with Digest Auth (and no
+.Em LDAPHA1Attr
+is specified) it needs to contain a cleartext password.
+.Pp
+[ Default:
+.Em userPassword
+]
+.It Cd LDAPScope
+When searching the LDAP for a user (ie:
+.Em LDAPDNMap
+is not specified) this is the scope for the search. Specify one of the
+following:
+.Ar sub base one
+.Pp
+[ Default:
+.Em sub
+]
+.It Cd LDAPServers
+The host names or IP addresses of the LDAP servers to authenticate against.
+Separated by spaces. More than one can be specified for failover capability.
+.Pp
+[ Required ]
+.It Cd LDAPTimeout
+The timeout for searches on the LDAP server (in seconds).
+.Pp
+[ Default:
+.Em 30
+]
+.It Cd LDAPUser
+When specified
+.Xr httpauthd
+will bind as this user after connecting to the LDAP server. This is useful
+in the case where anonymous users can't perform LDAP searches, for example.
+.Pp
+[ Optional ]
+.El
+.Sh NTLM METHOD OPTIONS
+Settings for the
+.Em NTLM
+authentication method. This method authenticates users against NT domain
+server.
+.Bl -hang
+.It Cd NTLMBackup
+The backup domain server to authenticate against. Used when
+.Em NTLMServer
+is not available.
+.Pp
+[ Optional ]
+.It Cd NTLMDomain
+The domain which contains the users that will be authenticated. This is
+the NT domain, not the DNS domain.
+.Pp
+[ Required ]
+.It Cd NTLMServer
+The domain server to authenticate against. You should specify a name here
+not an IP address.
+.Pp
+[ Required ]
+.It Cd PendingMax
+The maximum amount of halfway authenticated NTLM connections allowed.
+This corresponds directly to the amount of concurrent connections made to
+.Em NTLMServer
+.Pp
+[ Default:
+.Em 16
+]
+.It Cd PendingTimeout
+The maximum time a halfway authenticated NTLM connection is allowed to
+remain that way (in seconds).
+.Pp
+[ Default:
+.Em 20
+]
+.El
+.Sh SEE ALSO
+.Xr httpauthd 8
+.Sh AUTHOR
+.An Nate Nielsen Aq nielsen@memberwebs.com
diff --git a/doc/httpauthd.conf.sample b/doc/httpauthd.conf.sample
new file mode 100644
index 0000000..f5e1e87
--- /dev/null
+++ b/doc/httpauthd.conf.sample
@@ -0,0 +1,34 @@
+
+# Sample HTTPAUTH configuration file
+
+# Listen on a TCP port
+Socket: 0.0.0.0
+
+# Only perform Digest authentication
+AuthTypes: Digest
+
+# ----------------------------------------------------
+# This is the beginning of an Simple method section
+[Simple]
+
+# Give the method a name
+Alias: LocalUsers
+
+# The file
+PasswordFile: /usr/data/localusers.pw
+
+
+
+
+# ----------------------------------------------------
+# This is the beginning of an LDAP method section
+[LDAP]
+
+# Give the method a name
+Alias: AllUsers
+
+LDAPServers: ldap.test.com
+LDAPDoBind: True
+LDAPDNMap: cn=%u,dc=test,dc=com
+LDAPFilter: (objectClass=person)
+
diff --git a/sample/httpauthd.conf b/sample/httpauthd.conf
index ae59ce2..f11f48e 100644
--- a/sample/httpauthd.conf
+++ b/sample/httpauthd.conf
@@ -3,31 +3,29 @@
# and blank lines
MaxThreads: 18
CacheTimeout: 300
-AuthTypes: Basic Digest
+AuthTypes: Digest
+# AuthTypes: Basic Digest
Socket: 0.0.0.0:8020
+DigestIgnoreNC: True
+
[Simple]
Realm: blah
PasswordFile: /data/projects/httpauth/sample/passwd.file
-DigestDebugNonce: AkCLQA==560f26e24db2d4cecbe5d6e24d958377ab73def9
[LDAP]
Realm: blah
LDAPServers: authdev.ws.local
LDAPDoBind: False
-LDAPDNMap: cn=%u,ou=test,dc=fam
-DigestDomains: http://test.ws.local/
+# LDAPDNMap: cn=%u,ou=test,dc=fam
+# DigestDomains: http://test.ws.local/
LDAPUser: cn=root,dc=fam
LDAPPassword: ldaptest@@password
-LDAPHA1Attr: login
-# LDAPPWAttr: clearPassword
-
-LDAPFilter: (&(comment=%r)(&(cn=%u)(objectClass=contactInfo)))
-LDAPBase: ou=test,dc=fam
-LDAPScope: one
+# LDAPHA1Attr: login
+LDAPPWAttr: clearPassword
-DigestDebugNonce: AkCLQA==560f26e24db2d4cecbe5d6e24d958377ab73def9
+LDAPFilter: (cn=%u)
+LDAPBase: dc=fam
+LDAPScope: sub
-# LDAPMax:
-# LDAPTimeout: