/* 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 #include #include #define BASIC_MAXLINE 128 /* This needs to be the same as an MD5 hash length */ #define BASIC_HASH_KEY_LEN 16 #define BASIC_ESTABLISHED (void*)1 /* ------------------------------------------------------------------------------- * Structures */ typedef struct basic_context { const char* filename; /* The file name with the user names */ const char* realm; /* The realm for basic authentication */ /* Context ----------------------------------------------------------- */ hash_t* established; /* Established connections */ } basic_context_t; /* ------------------------------------------------------------------------------- * Handler Functions */ int basic_config(ha_context_t* context, const char* name, const char* value) { basic_context_t* ctx = (basic_context_t*)(context.data); if(strcmp(name, "basicfile") == 0) { ctx->filename = value; return HA_OK; } else if(strcmp(name, "realm") == 0) { ctx->realm = value; return HA_OK; } return HA_FALSE; } int basic_init(ha_context_t* context) { /* Don't do global init */ if(!context) return HA_OK; basic_context_t* ctx = (basic_context_t*)(context.data); int fd; /* Make sure there are some types of authentication we can do */ if(!(context->types & HA_TYPE_BASIC)) { ha_messagex(LOG_ERR, "Basic module configured, but does not implement any " "configured authentication type."); return HA_ERROR; } /* Check to make sure the file exists */ if(!ctx->filename) { ha_messagex(LOG_ERR, "Basic configuration incomplete. " "Must have a BasicFile configured."); return HA_ERROR; } fd = open(ctx->filename, O_RDONLY); if(fd == -1) { ha_message(LOG_ERR, "can't open file for basic authentication: %s", ctx->filename); return HA_ERROR; } close(fd); /* Initialize our cache table */ if(!(ctx->established = hash_create(BASIC_HASH_KEY_LEN))) { ha_messagex(LOG_CRIT, "out of memory"); return HA_ERROR; } return HA_OK; } int basic_process(ha_context_t* context, ha_request_t* req, ha_response_t* resp, ha_buffer_t* buf) { basic_context_t* ctx = (basic_context_t*)(context.data); const char* header; char* t; int found = 0; ha_basic_header_t basic; memset(&basic, 0, sizeof(basic)); ha_lock(NULL); /* Purge the cache */ hash_purge(ctx->established, time(NULL) - context->timeout); ha_unlock(NULL); /* * Init checked and made sure basic auth is enabled, so we * can take that for granted here. */ header = ha_getheader(req, "Authorization", BASIC_PREFIX); if(header) { if(ha_parsebasic(header, buf, &basic) == HA_ERROR) return HA_ERROR; /* Check and see if this connection is in the cache */ ha_lock(NULL); if(hash_get(ctx->established, basic.key) == BASIC_ESTABLISHED) { hash_touch(ctx->established, basic.key); found = 1; } ha_unlock(NULL); } /* If we have a user name and password that wasn't in the cache */ if(!found && basic.user && basic.user[0] && basic.password && basic.password[0]) { FILE* f; char line[BASIC_MAXLINE]; f = fopen(ctx->filename, "r"); if(!f) { ha_message(LOG_ERR, "can't open file for basic auth: %s", ctx->filename); resp->code = HA_SERVER_ERROR; return HA_FALSE; } /* * 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, BASIC_MAXLINE, f); if(ferror(f)) ha_message(LOG_ERR, "error reading basic password file"); t = strchr(line, ':'); if(t) { /* Split the line */ *t = 0; t++; /* Check the user */ if(strcmp(line, basic.user) == 0) { /* Not sure if crypt is thread safe so we lock */ ha_lock(); /* Check the password */ if(strcmp(crypt(basic.password, t), t) == 0) found = 1; ha_unlock(); if(found) break; } } } fclose(f); } /* If we found a user then tell them what it was */ if(found) { resp->code = HA_SERVER_ACCEPT; resp->detail = basic.user; /* We put this connection into the successful connections */ if(!hash_set(ctx->established, basic.key, BASIC_ESTABLISHED)) { ha_messagex(LOG_CRIT, "out of memory"); return HA_ERROR; } } /* Otherwise make the browser authenticate */ else { resp->code = HA_SERVER_DECLINE; ha_bufnext(buf); ha_bufcat(buf, "BASIC realm=\"", ctx->realm ? ctx->realm : "", "\"", NULL); ha_addheader(resp, "WWW-Authenticate", ha_bufdata(buf)); } return HA_OK; } /* ------------------------------------------------------------------------------- * Handler Definition */ ha_handler_t basic_handler = { "Basic", /* The type */ basic_init, /* Initialization function */ basic_destroy, /* Uninitialization routine */ basic_config, /* Config routine */ basic_process, /* Processing routine */ NULL, /* A default context */ sizeof(basic_context_t) /* Size of the context */ };