/* 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 validate_digest(ha_request_t* rq, const char* user, digest_context_t* dg); static int validate_basic(ha_request_t* rq, const char* user, const char* password); /* The defaults for the context */ static const simple_context_t simple_defaults = { BD_CALLBACKS(validate_digest, validate_basic, NULL), NULL /* filename */ }; /* ------------------------------------------------------------------------------- * Internal Functions */ static int validate_digest(ha_request_t* rq, const char* user, digest_context_t* dg) { simple_context_t* ctx = (simple_context_t*)rq->context->ctx_data; FILE* f; char* t; char* t2; size_t len; char line[SIMPLE_MAXLINE]; int ret = HA_FALSE; int foundinvalid = 0; int foundgood = 0; ASSERT(ctx && rq && user && user[0]); ha_messagex(rq, LOG_DEBUG, "searching password file for user's ha1: %s", user); f = fopen(ctx->filename, "r"); if(!f) { ha_message(rq, LOG_ERR, "can't open file for digest 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(rq, LOG_ERR, "error reading basic password file"); ret = HA_FAILED; break; } t = strchr(line, ':'); if(!t) continue; /* Split the line */ *t = 0; t++; /* Check the user */ if(strcmp(line, user) != 0) continue; t2 = strchr(t, ':'); if(!t2) { /* An invalid line without a realm */ foundinvalid = 1; continue; } /* Split the line */ *t2 = 0; t2++; /* Check the realm */ if(strcmp(t, rq->context->realm) != 0) continue; /* Now try and decode the ha1 */ len = MD5_LEN; t = ha_bufdechex(rq->buf, t2, &len); if(!t || len != MD5_LEN) { /* An invalid ha1 */ foundinvalid = 1; continue; } ha_messagex(rq, LOG_DEBUG, "found ha1 for user: %s", user); memcpy(dg->ha1, t, MD5_LEN); foundgood = 1; /* Try to do the validation */ ret = digest_complete_check(dg, rq->buf); /* If invalid then continue search */ if(ret == HA_FALSE) continue; /* Success or an error ends here */ break; } if(foundinvalid && !foundgood) ha_messagex(rq, LOG_WARNING, "user '%s' found in file, but password not in digest format", user); fclose(f); if(ha_buferr(rq->buf)) return HA_CRITERROR; return ret; } static int validate_basic(ha_request_t* rq, const char* user, const char* password) { simple_context_t* ctx = (simple_context_t*)rq->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 && rq); ASSERT(user && user[0] && password); ha_messagex(rq, LOG_DEBUG, "validating user against password file: %s", user); f = fopen(ctx->filename, "r"); if(!f) { ha_message(rq, LOG_ERR, "can't open file for basic auth: %s", ctx->filename); return HA_FAILED; } digest_makeha1(ha1, user, rq->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(rq, LOG_ERR, "error reading basic password file"); ret = HA_FAILED; break; } /* Take white space off end of line */ trim_end(line); t = strchr(line, ':'); if(!t) continue; /* Split the line */ *t = 0; t++; /* Check the user */ if(strcmp(line, user) != 0) continue; /* Not sure if crypt is thread safe so we lock */ ha_lock(NULL); /* Check the password */ t2 = (const char*)crypt(password, t); ha_unlock(NULL); if(strcmp(t2, t) == 0) { ha_messagex(rq, LOG_DEBUG, "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) continue; /* Split the line */ *t2 = 0; t2++; /* Check the realm */ if(strcmp(t, rq->context->realm) != 0) continue; /* Now try antd decode the ha1 */ len = MD5_LEN; t = ha_bufdechex(rq->buf, t2, &len); if(!t || len != MD5_LEN) continue; if(memcmp(ha1, t, MD5_LEN) == 0) { ha_messagex(rq, LOG_DEBUG, "found valid ha1 for user: %s", user); ret = HA_OK; break; } if(ha_buferr(rq->buf)) break; } fclose(f); if(ha_buferr(rq->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(NULL, LOG_ERR, "configuration incomplete. " "Must have a PasswordFile configured."); return HA_FAILED; } fd = open(ctx->filename, O_RDONLY); if(fd == -1) { ha_message(NULL, LOG_ERR, "can't open file for authentication: %s", ctx->filename); return HA_FAILED; } close(fd); ha_messagex(NULL, LOG_INFO, "initialized simple 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 */ &simple_defaults, /* A default context */ sizeof(simple_context_t) /* Size of the context */ };