/* * HttpAuth * * Copyright (C) 2004 Stefan Walter * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */ /* 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 "stringx.h" #include #include #include #define SIMPLE_MAXLINE 256 /* ------------------------------------------------------------------------------- * Structures */ typedef struct simple_context { /* Base Handler ------------------------------------------------------ */ bd_context_t bd; /* Read Only --------------------------------------------------------- */ 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, char ***groups); static int validate_basic(ha_request_t* rq, const char* user, const char* password, char ***groups); /* 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, char ***groups) { 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->server_ha1, t, MD5_LEN); foundgood = 1; /* Try to do the validation */ ret = digest_complete_check(dg, rq->context, 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(CHECK_RBUF(rq)) return HA_CRITERROR; return ret; } static int validate_basic(ha_request_t* rq, const char* user, const char* password, char ***groups) { 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 = (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(CHECK_RBUF(rq)) break; } fclose(f); if(CHECK_RBUF(rq)) 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 */ };