authfile.c C 140 lines View on github.com → Search inside
1/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */2#include <stdio.h>3#include <stdlib.h>4#include <stdbool.h>5#include <string.h>6#include <sys/types.h>7#include <sys/stat.h>8#include <unistd.h>9#include <inttypes.h>10#include <pthread.h>1112#include "authfile.h"13#include "util.h"1415// TODO: frontend needs a refactor so this can avoid global objects.1617#define MAX_ENTRY_LEN 25618// Not supposed to be a huge database!19#define MAX_ENTRIES 82021typedef struct auth_entry {22    char *user;23    size_t ulen;24    char *pass;25    size_t plen;26} auth_t;2728auth_t main_auth_entries[MAX_ENTRIES];29int entry_cnt = 0;30char *main_auth_data = NULL;31pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;3233enum authfile_ret authfile_load(const char *file) {34    struct stat sb;35    char *auth_data = NULL;36    auth_t auth_entries[MAX_ENTRIES];3738    if (!file || strlen(file) == 0) {39        return AUTHFILE_OPENFAIL;40    }4142    FILE *pwfile = fopen(file, "r");43    if (pwfile == NULL) {44        return AUTHFILE_OPENFAIL;45    } else if (fstat(fileno(pwfile), &sb)) {46        fclose(pwfile);47        return AUTHFILE_STATFAIL;48    }4950    auth_data = calloc(1, sb.st_size + 2);5152    char *auth_cur = auth_data;53    // fgets will stop at EOF or a newline, reading at most one bytes less54    // than the size limit. If a user supplies a file without an ending55    // newline we will end up chopping the last character of the password.56    char *auth_end = auth_data + sb.st_size + 1;57    auth_t *entry_cur = auth_entries;58    int used = 0;5960    while ((fgets(auth_cur, auth_end - auth_cur < MAX_ENTRY_LEN ? auth_end - auth_cur : MAX_ENTRY_LEN, pwfile)) != NULL) {61        int x;62        int found = 0;6364        for (x = 0; x < MAX_ENTRY_LEN; x++) {65            if (!found) {66                if (auth_cur[x] == '\0') {67                    // The username is malformed - this is either the end of the file or a null byte.68                    break;69                } else if (auth_cur[x] == ':') {70                    entry_cur->user = auth_cur;71                    entry_cur->ulen = x;72                    entry_cur->pass = &auth_cur[x+1];73                    found = 1;74                }75            } else {76                // Find end of password.77                if (auth_cur[x] == '\n' ||78                    auth_cur[x] == '\r' ||79                    auth_cur[x] == '\0') {80                    entry_cur->plen = x - (entry_cur->ulen + 1);81                    break;82                }83            }84        }8586        // malformed line.87        if (!found) {88            (void)fclose(pwfile);89            free(auth_data);90            return AUTHFILE_MALFORMED;91        }9293        // FIXME: no silent truncation.94        if (++used == MAX_ENTRIES) {95            break;96        }97        // EOF98        if (auth_cur[x] == '\0')99            break;100101        auth_cur += x;102        entry_cur++;103    }104105    pthread_mutex_lock(&lock);106    // swap the main pointer out now, so if there's an error reloading we107    // don't break the existing authentication.108    if (main_auth_data != NULL) {109        free(main_auth_data);110    }111112    entry_cnt = used;113    main_auth_data = auth_data;114    memcpy(main_auth_entries, auth_entries, sizeof(auth_entries));115    pthread_mutex_unlock(&lock);116117    (void)fclose(pwfile);118119    return AUTHFILE_OK;120}121122// if only loading the file could be this short...123int authfile_check(const char *user, unsigned int ulen, const char *pass, unsigned int plen) {124    int ret = 0;125126    pthread_mutex_lock(&lock);127    for (int x = 0; x < entry_cnt; x++) {128        auth_t *e = &main_auth_entries[x];129        if (ulen == e->ulen && plen == e->plen &&130            safe_memcmp(user, e->user, e->ulen) &&131            safe_memcmp(pass, e->pass, e->plen)) {132            ret = 1;133            break;134        }135    }136    pthread_mutex_unlock(&lock);137138    return ret;139}

Code quality findings 3

Warning: Allocation result must be checked for NULL before use to prevent null pointer dereference.
warning correctness malloc-unchecked
auth_data = calloc(1, sb.st_size + 2);
Warning: Variable declared without initialization. Uninitialized local variables contain indeterminate values, which can lead to undefined behavior.
warning correctness uninitialized-variable
int x;
Warning: Function definition without explicit return type (C89 implicit int). Always specify the return type explicitly for clarity and C99+ compliance.
warning maintainability implicit-int-return
safe_memcmp(pass, e->pass, e->plen)) {

Security findings 3

Critical: gets() is inherently unsafe and can lead to buffer overflows. Use fgets() instead.
security gets-usage
while ((fgets(auth_cur, auth_end - auth_cur < MAX_ENTRY_LEN ? auth_end - auth_cur : MAX_ENTRY_LEN, pwfile)) != NULL) {
Warning: Potential use-after-free. Pointer should be set to NULL after free() and not used again without reassignment.
security use-after-free
free(main_auth_data);
Info: memcpy() does not handle overlapping memory regions. If source and destination may overlap, use memmove() as a safer alternative.
security memcpy-overlap
memcpy(main_auth_entries, auth_entries, sizeof(auth_entries));

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.