pam-abl-code/0000755000175000017500000000000012136474316012036 5ustar toortoorpam-abl-code/typefun.c0000644000175000017500000002376612136474316013712 0ustar toortoor/* * pam_abl - a PAM module and program for automatic blacklisting of hosts and users * * Copyright (C) 2005-2012 * * 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, see . */ #include "typefun.h" #include #include #define BYTES_FREE(state) (state->m_usedSize - (size_t)((char*)state->m_current - (char*)state->m_data)) #define SIZE_SMALLER(state, size) BYTES_FREE(state) < (size) //the space in the buffer before the actual attempts start #define HEADER_SIZE (sizeof(int)+sizeof(unsigned int)) //when we allocate memory for the data, allocate this much more for future attempts // this will save us a realloc at the cost of this much more bytes #define SPARE_SPACE 100 #define INCREASE_NOFATTEMPTS(state, x) ((*((unsigned int *)(((int*)(state->m_data))+1))) += x) #define SET_NOFATTEMPTS(state, x) ((*((unsigned int *)(((int*)(state->m_data))+1))) = x) #define ATTEMPS_START(state) (((char*)state->m_data) + HEADER_SIZE) int createEmptyState(BlockState blockState, AuthState **state) { *state = NULL; AuthState *retValue = malloc(sizeof(AuthState)); if (!retValue) return 1; memset(retValue, 0, sizeof(AuthState)); //allocate some bytes more then we actually need //this way we can probably cope with an extra attempt without reallocating size_t bufferSize = HEADER_SIZE + SPARE_SPACE; void *allocatedData = malloc(bufferSize); if (!allocatedData) { free(retValue); return 1; } retValue->m_data = allocatedData; retValue->m_size = bufferSize; retValue->m_usedSize = HEADER_SIZE; char *bufferPtr = (char*)retValue->m_data; *((int*)bufferPtr) = blockState; bufferPtr += sizeof(int); *((unsigned int*)bufferPtr) = 0; bufferPtr += sizeof(int); firstAttempt(retValue); *state = retValue; return 0; } int createAuthState(void *data, size_t size, AuthState **state) { *state = NULL; if (!data || !size) return 1; if (size < HEADER_SIZE) return 1; AuthState *retValue = malloc(sizeof(AuthState)); if (!retValue) return 1; memset(retValue, 0, sizeof(AuthState)); //allocate some bytes more then we actually need //this way we can probably cope with an extra attempt without reallocating size_t bufferSize = size + SPARE_SPACE; void *allocatedData = malloc(bufferSize); if (!allocatedData) { free(retValue); return 1; } memcpy(allocatedData, data, size); retValue->m_data = allocatedData; retValue->m_size = bufferSize; retValue->m_usedSize = size; //the state is the first field, we need to skip this before the Attempts begin retValue->m_current = ATTEMPS_START(retValue); *state = retValue; return 0; } BlockState getState(AuthState *state) { if (!state || !state->m_data) return -1; int stateEnum = *((int*)(state->m_data)); return stateEnum; } int setState(AuthState *state, BlockState blockState) { if (!state || !state->m_data) return 1; *((int*)(state->m_data)) = blockState; return 0; } unsigned int getNofAttempts(AuthState *state) { if (!state || !state->m_data) return 0; unsigned int nofAttempts = *(unsigned int *)(((int*)(state->m_data))+1); return nofAttempts; } int firstAttempt(AuthState *state) { if (!state || !state->m_data) return 1; state->m_current = ATTEMPS_START(state); return 0; } /* What does the raw data look like, it should have the following structure int: State unsigned int: number of attempts int the bytes after this ( time_t the time of the failed authentication int blockreason zero terminated user or host zero terminated service )* */ //TODO difference between a faulty layout and the end of the chain int nextAttempt(AuthState *state, AuthAttempt *attempt) { if (!state || !state->m_current) return 1; //is there still an attempt left to read? if (SIZE_SMALLER(state, sizeof(time_t))) { return 1; } time_t t = *((time_t*)state->m_current); state->m_current = ((time_t*)state->m_current) + 1; if (SIZE_SMALLER(state, sizeof(int))) { state->m_current = NULL; return 1; } int reason = *((int*)state->m_current); state->m_current = ((int*)(state->m_current)) + 1; char *userOrHost = (char*)state->m_current; size_t bytesFree = BYTES_FREE(state); size_t strLen = strnlen(userOrHost, bytesFree); if (strLen == bytesFree) { state->m_current = NULL; return 1; } state->m_current = ((char*)state->m_current) + strLen + 1; char *service = (char*)state->m_current; bytesFree = BYTES_FREE(state); strLen = strnlen(service, bytesFree); if (strLen == bytesFree) { state->m_current = NULL; return 1; } state->m_current = ((char*)state->m_current) + strLen + 1; //OK state->m_current should point to the next Attempt if (attempt) { attempt->m_time = t; attempt->m_reason = reason; attempt->m_service = service; attempt->m_userOrHost = userOrHost; } return 0; } void destroyAuthState(AuthState *state) { if (!state) return; free(state->m_data); state->m_data = NULL; state->m_current = NULL; state->m_size = 0; state->m_usedSize = 0; free(state); } static int limitAttempts(AuthState *state, unsigned int limit) { if (!state) return 1; unsigned int nofAttempts = getNofAttempts(state); if (nofAttempts <= limit) return 0; if (firstAttempt(state)) return 1; if (limit == 0) { state->m_size = 0; state->m_usedSize = HEADER_SIZE; SET_NOFATTEMPTS(state, 0); firstAttempt(state); } else { unsigned int toRemove = nofAttempts - limit; AuthAttempt attempt; while (nextAttempt(state, &attempt) == 0) { --toRemove; if (toRemove == 0) break; } if (toRemove == 0) { //iterating went fine, just remove the extra attempts size_t bytesToMove = state->m_usedSize - (size_t)((char*)state->m_current - (char*)state->m_data); memmove(ATTEMPS_START(state), state->m_current, bytesToMove); SET_NOFATTEMPTS(state, limit); state->m_usedSize = bytesToMove + HEADER_SIZE; firstAttempt(state); } else { //iterating went wrong, pass the error along return 1; } } return 0; } int addAttempt(AuthState *state, BlockReason reason, time_t pTime, const char *userOrHost, const char *service, unsigned int lowerLimit, unsigned int upperLimit) { if (!userOrHost || !service || !state) return 1; if (upperLimit > 0 && getNofAttempts(state) + 1 > upperLimit) { if (limitAttempts(state, lowerLimit > 0 ? lowerLimit - 1 : 0)) return 1; } //calculate how many space we need, perhaps we need to reallocate size_t userOrHostSize = strlen(userOrHost) + 1; size_t serviceSize = strlen(service) + 1; size_t neededSize = userOrHostSize + serviceSize + sizeof(time_t) + sizeof(int); if (neededSize > state->m_size - state->m_usedSize) { //damn, space left is too smalll, reallocate and move all the data //calculate a good new size for the block of memory (let's leave some spare room, for further enlargements) size_t newSize = state->m_usedSize + neededSize + SPARE_SPACE; void *newMem = realloc(state->m_data, newSize); if (!newMem) return 1; state->m_current = (char*)newMem + (size_t)((char*)state->m_current - (char*)state->m_data); state->m_size = newSize; state->m_data = newMem; // state->m_usedSize remains the same } char *bufferPtr = (char*)state->m_data + state->m_usedSize; *((time_t*)bufferPtr) = pTime; bufferPtr += sizeof(time_t); *((int*)bufferPtr) = reason; bufferPtr += sizeof(int); memcpy(bufferPtr, userOrHost, userOrHostSize); bufferPtr += userOrHostSize; memcpy(bufferPtr, service, serviceSize); bufferPtr += serviceSize; state->m_current = bufferPtr; state->m_usedSize += neededSize; INCREASE_NOFATTEMPTS(state, 1); if (state->m_usedSize != (size_t)((char*)state->m_current - (char*)state->m_data)) { return 0; } return 0; } void purgeAuthState(AuthState *state, time_t purgeTime) { if (!state || !state->m_data || firstAttempt(state)) { return; } AuthAttempt attempt; int endReached = 1; int nofAttemptsToRemove = 0; void *lastOld = state->m_current; while (!nextAttempt(state, &attempt)) { if (attempt.m_time >= purgeTime) { endReached = 0; break; } ++nofAttemptsToRemove; lastOld = state->m_current; } if (endReached) { state->m_usedSize = HEADER_SIZE; SET_NOFATTEMPTS(state, 0); } else { //lastOld should point to the first Attempt with a 'valid' time //first of all, do we need to move stuff? if (ATTEMPS_START(state) != lastOld) { size_t bytesToMove = state->m_usedSize - (size_t)((char*)lastOld - (char*)state->m_data); memmove(ATTEMPS_START(state), lastOld, bytesToMove); state->m_usedSize = bytesToMove + HEADER_SIZE; } if (nofAttemptsToRemove) INCREASE_NOFATTEMPTS(state, -1*nofAttemptsToRemove); } state->m_current = ATTEMPS_START(state); } pam-abl-code/test.c0000644000175000017500000000436012136474316013164 0ustar toortoor/* * pam_abl - a PAM module and program for automatic blacklisting of hosts and users * * Copyright (C) 2005-2012 * * 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, see . */ #include "test.h" #include #include #include #include #include #include #include #include "dbfun.h" void removeDir(const char *dirname) { DIR *dir; struct dirent *entry; char path[PATH_MAX]; dir = opendir(dirname); if (dir == NULL) return; while ((entry = readdir(dir)) != NULL) { if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) { snprintf(path, (size_t) PATH_MAX, "%s/%s", dirname, entry->d_name); if (entry->d_type == DT_DIR) { removeDir(path); } else { unlink(path); } } } closedir(dir); rmdir(dirname); return; } void makeDir(const char *dirname) { mkdir(dirname, S_IRWXU); } int main(int argc, const char *argv[]) { //for the test running a command we need to run an external command. Because we can't rely on a particular //executable being present, we provide our own external command if (argc >= 3) { if (strcmp(argv[1], "-e") == 0) { int exitCode = atoi(argv[2]); exit(exitCode); } } printf("Using db version \"%s\" %d.%d.%d\n", DB_VERSION_STRING, DB_VERSION_MAJOR, DB_VERSION_MINOR, DB_VERSION_PATCH); runTypeTests(); runDatabaseTests(); runtRuleTests(); testAbl(); testExternalCommand(argv[0]); testRunCommand(); testConfig(); return 0; } pam-abl-code/test_rule.c0000644000175000017500000001406312136474316014214 0ustar toortoor/* * pam_abl - a PAM module and program for automatic blacklisting of hosts and users * * Copyright (C) 2005-2012 * * 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, see . */ #include "test.h" #include "typefun.h" #include "rule.h" #include #include #include static void testRuleNoAttempts() { AuthState *state = NULL; if (createEmptyState(CLEAR, &state)) { printf(" Could not create an empty state.\n"); return; } if (rule_test(NULL, "*:10/1s", "user", "service", state, 10) == BLOCKED) printf(" No attempts should never match.\n"); } static void testEmptyRule() { AuthState *state = NULL; if (createEmptyState(CLEAR, &state)) { printf(" Could not create an empty state.\n"); return; } size_t i = 0; for (; i < 5; ++i) { if (addAttempt(state, USER_BLOCKED, i*2, "MyUser", "Service1", 0, 0)) printf(" Could not add an attempt.\n"); } if (rule_test(NULL, "", "user", "service", state, 5) == BLOCKED) printf(" The empty rule matched.\n"); if (rule_test(NULL, NULL, "user", "service", state, 5) == BLOCKED) printf(" The empty rule matched.\n"); } static void testNoMatch() { AuthState *state = NULL; if (createEmptyState(CLEAR, &state)) { printf(" Could not create an empty state.\n"); return; } size_t i = 0; for (; i < 5; ++i) { if (addAttempt(state, USER_BLOCKED, i*2, "MyUser", "Service1", 0, 0)) printf(" Could not add an attempt.\n"); } if (rule_test(NULL, "*:6/10s", "user", "service", state, 10) == BLOCKED) printf(" The rule matched.\n"); if (rule_test(NULL, "*:10/8s", "user", "service", state, 10) == BLOCKED) printf(" The rule matched.\n"); } static void testMatch() { AuthState *state = NULL; if (createEmptyState(CLEAR, &state)) { printf(" Could not create an empty state.\n"); return; } size_t i = 0; for (; i < 5; ++i) { if (addAttempt(state, USER_BLOCKED, i*2, "MyUser", "Service1", 0, 0)) printf(" Could not add an attempt.\n"); } if (rule_test(NULL, "*:5/10s", "user", "service", state, 10) == CLEAR) printf(" The rule did not match.\n"); if (rule_test(NULL, "*:3/10s", "user", "service", state, 10) == CLEAR) printf(" The rule did not match.\n"); } static void testMatchService() { AuthState *state = NULL; if (createEmptyState(CLEAR, &state)) { printf(" Could not create an empty state.\n"); return; } size_t i = 0; for (; i < 5; ++i) { if (addAttempt(state, USER_BLOCKED, i*2, "MyUser", "Service1", 0, 0)) printf(" Could not add an attempt.\n"); } if (rule_test(NULL, "*/Service1:5/10s *:10/1h", "MyUser", "Service1", state, 10) == CLEAR) printf(" The special service rule did not match.\n"); if (rule_test(NULL, "*/Service1:10/1h", "MyUser", "Service1", state, 10) == BLOCKED) printf(" The rule matched.\n"); } static void testNoService() { AuthState *state = NULL; if (createEmptyState(CLEAR, &state)) { printf(" Could not create an empty state.\n"); return; } size_t i = 0; for (; i < 5; ++i) { if (addAttempt(state, USER_BLOCKED, i*2, "MyUser", "Service1", 0, 0)) printf(" Could not add an attempt.\n"); } if (rule_test(NULL, "*/Service1:5/10s *:8/1h", "MyUser", NULL, state, 10) == BLOCKED) printf(" The rule matched.\n"); if (rule_test(NULL, "*:5/1h", "MyUser", NULL, state, 10) == CLEAR) printf(" The rule dit not match.\n"); } static void testMatchUser() { AuthState *state = NULL; if (createEmptyState(CLEAR, &state)) { printf(" Could not create an empty state.\n"); return; } size_t i = 0; for (; i < 5; ++i) { if (addAttempt(state, USER_BLOCKED, i*2, "MyUser", "Service1", 0, 0)) printf(" Could not add an attempt.\n"); } if (rule_test(NULL, "MyUser:5/10s *:10/1h", "MyUser", "Service1", state, 10) == CLEAR) printf(" The rule did not match.\n"); if (rule_test(NULL, "user2:1/1h *:10/1h", "MyUser", "Service1", state, 10) == BLOCKED) printf(" The rule did match.\n"); } static void testInvert() { AuthState *state = NULL; if (createEmptyState(CLEAR, &state)) { printf(" Could not create an empty state.\n"); return; } size_t i = 0; for (; i < 5; ++i) { if (addAttempt(state, USER_BLOCKED, i*2, "MyUser", "Service1", 0, 0)) printf(" Could not add an attempt.\n"); } if (rule_test(NULL, "!MyUser:1/10s *:10/1h", "MyUser", "Service1", state, 10) == BLOCKED) printf(" The rule did match.\n"); if (rule_test(NULL, "MyUser:1/10s !MyUser:10/1h", "MyUser", "Service1", state, 10) == CLEAR) printf(" The rule did not match.\n"); } void runtRuleTests() { printf("Rule test start.\n"); printf(" Starting testRuleNoAttempts.\n"); testRuleNoAttempts(); printf(" Starting testEmptyRule.\n"); testEmptyRule(); printf(" Starting testNoMatch.\n"); testNoMatch(); printf(" Starting testMatch.\n"); testMatch(); printf(" Starting testMatchService.\n"); testMatchService(); printf(" Starting testMatchUser.\n"); testMatchUser(); printf(" Starting testInvert.\n"); testInvert(); printf(" Starting testNoService.\n"); testNoService(); printf("Rule test end.\n"); } pam-abl-code/rule.h0000644000175000017500000000630412136474316013161 0ustar toortoor/* * pam_abl - a PAM module and program for automatic blacklisting of hosts and users * * Copyright (C) 2005-2012 * * 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, see . */ #ifndef RULE_H #define RULE_H #include "log.h" #include "typefun.h" /* Parses a long from the string pointed to by sp. On success sp will point to the char right after the long \param sp The string to parse a long with \param lp The found long \return zero on success, non zero otherwise */ int parse_long(const char **sp, long *lp); /* Parse a time specification in the form * [s|m|h|d] \param p The string that holds the time to parse \param t The parsed time in seconds \param min The minimum time that needs to be returned \return zero on success, non zero otherwise */ int rule_parse_time(const char *p, long *t, long min); /* Tries to match a comma seperated list of '/