/* On some linux this is required to get crypt to show itself */ #define _XOPEN_SOURCE #include "usuals.h" #include "httpauthd.h" #include "defaults.h" #include "hash.h" #include "bd.h" #include "md5.h" #include #include #define SIMPLE_MAXLINE 256 /* ------------------------------------------------------------------------------- * Structures */ typedef struct simple_context { /* Base Handler ------------------------------------------------------ */ bd_context_t bd; /* Settings ---------------------------------------------------------- */ const char* filename; /* The file name with the user names */ } simple_context_t; /* Forward declarations for callbacks */ static int complete_digest(const ha_request_t* req, const char* user, unsigned char* ha1); static int validate_basic(const ha_request_t* req, const char* user, const char* password); /* The defaults for the context */ static const simple_context_t simple_defaults = { BD_CALLBACKS(complete_digest, validate_basic), NULL /* filename */ }; /* ------------------------------------------------------------------------------- * Internal Functions */ static int complete_digest(const ha_request_t* req, const char* user, unsigned char* ha1) { simple_context_t* ctx = (simple_context_t*)req->context->ctx_data; FILE* f; char* t; char* t2; size_t len; char line[SIMPLE_MAXLINE]; int ret = HA_FALSE; ASSERT(ctx && req && user && user[0]); ha_messagex(LOG_DEBUG, "searching password file for user's ha1: %s", user); f = fopen(ctx->filename, "r"); if(!f) { ha_message(LOG_ERR, "simple: can't open file for basic auth: %s", ctx->filename); return HA_FAILED; } /* * Note: There should be no returns or jumps between * this point and the closing of the file below. */ /* Now look through the whole file */ while(!feof(f)) { fgets(line, SIMPLE_MAXLINE, f); if(ferror(f)) { ha_message(LOG_ERR, "simple: error reading basic password file"); ret = HA_FAILED; break; } t = strchr(line, ':'); if(t) { /* Split the line */ *t = 0; t++; /* Check the user */ if(strcmp(line, user) == 0) { /* Otherwise it might be a digest type ha1. */ t2 = strchr(t, ':'); if(t2) { *t2 = 0; t2++; /* Check the realm */ if(strcmp(t, req->context->realm) == 0) { len = MD5_LEN; /* Now try and decode the ha1 */ t = ha_bufdechex(req->buf, t2, &len); if(t && len == MD5_LEN) { ha_messagex(LOG_DEBUG, "simple: found ha1 for user: %s", user); memcpy(ha1, t, MD5_LEN); ret = HA_OK; break; } } } if(!t2 || ret != HA_OK) ha_messagex(LOG_WARNING, "simple: user '%s' found in file, but password not in digest format", user); } } } fclose(f); if(ha_buferr(req->buf)) return HA_CRITERROR; return ret; } static int validate_basic(const ha_request_t* req, const char* user, const char* password) { simple_context_t* ctx = (simple_context_t*)req->context->ctx_data; FILE* f; char line[SIMPLE_MAXLINE]; unsigned char ha1[MD5_LEN]; char* t; char* t2; size_t len; int ret = HA_FALSE; ASSERT(ctx && req); ASSERT(user && user[0] && password); ha_messagex(LOG_DEBUG, "simple: validating user against password file: %s", user); f = fopen(ctx->filename, "r"); if(!f) { ha_message(LOG_ERR, "simple: can't open file for basic auth: %s", ctx->filename); return HA_FAILED; } digest_makeha1(ha1, user, req->context->realm, password); /* * Note: There should be no returns or jumps between * this point and the closing of the file below. */ /* Now look through the whole file */ while(!feof(f)) { fgets(line, SIMPLE_MAXLINE, f); if(ferror(f)) { ha_message(LOG_ERR, "simple: error reading basic password file"); ret = HA_FAILED; break; } /* Take white space off end of line */ trim_end(line); t = strchr(line, ':'); if(t) { /* Split the line */ *t = 0; t++; /* Check the user */ if(strcmp(line, user) == 0) { /* Not sure if crypt is thread safe so we lock */ ha_lock(NULL); /* Check the password */ t2 = crypt(password, t); ha_unlock(NULL); if(strcmp(t2, t) == 0) { ha_messagex(LOG_DEBUG, "simple: found valid crypt password for user: %s", user); ret = HA_OK; break; } /* Otherwise it might be a digest type ha1. */ t2 = strchr(t, ':'); if(t2) { *t2 = 0; t2++; /* Check the realm */ if(strcmp(t, req->context->realm) == 0) { len = MD5_LEN; /* Now try antd decode the ha1 */ t = ha_bufdechex(req->buf, t2, &len); if(t && len == MD5_LEN && memcmp(ha1, t, MD5_LEN) == 0) { ha_messagex(LOG_DEBUG, "simple: found valid ha1 for user: %s", user); ret = HA_OK; break; } } } if(ha_buferr(req->buf)) break; } } } fclose(f); if(ha_buferr(req->buf)) return HA_CRITERROR; return ret; } /* ------------------------------------------------------------------------------- * Handler Functions */ int simple_config(ha_context_t* context, const char* name, const char* value) { simple_context_t* ctx = (simple_context_t*)(context->ctx_data); ASSERT(name && name[0] && value && value[0]); if(strcmp(name, "passwordfile") == 0) { ctx->filename = value; return HA_OK; } return HA_FALSE; } int simple_init(ha_context_t* context) { int r; if((r = bd_init(context)) != HA_OK) return r; if(context) { simple_context_t* ctx = (simple_context_t*)(context->ctx_data); int fd; ASSERT(ctx); /* Check to make sure the file exists */ if(!ctx->filename) { ha_messagex(LOG_ERR, "simple: configuration incomplete. " "Must have a PasswordFile configured."); return HA_FAILED; } fd = open(ctx->filename, O_RDONLY); if(fd == -1) { ha_message(LOG_ERR, "simple: can't open file for authentication: %s", ctx->filename); return HA_FAILED; } close(fd); ha_messagex(LOG_INFO, "simple: initialized handler"); } return HA_OK; } /* ------------------------------------------------------------------------------- * Handler Definition */ ha_handler_t simple_handler = { "SIMPLE", /* The type */ simple_init, /* Initialization function */ bd_destroy, /* Uninitialization routine */ simple_config, /* Config routine */ bd_process, /* Processing routine */ NULL, /* A default context */ sizeof(simple_context_t) /* Size of the context */ };