Verwendung von getlogin() in Multithread-Anwendung

Beschreibung

Verwendung von getlogin() in Multithread-Anwendung ist eine Schwachstelle, bei der ein Programm die getlogin()-Funktion in einem Multithread-Kontext verwendet und potenziell falsche oder manipulierte Werte erhält aufgrund der nicht-reentranten Natur der Funktion. Die getlogin()-Funktion gibt einen Pointer auf eine Zeichenkette zurück, die den Benutzernamen enthält, der mit dem aufrufenden Prozess verknüpft ist, aber diese Funktion ist nicht thread-sicher. In Multithread-Anwendungen können konkurrierende Prozesse den zurückgegebenen Wert ohne Synchronisation ändern.

Risiko

Die Verwendung von getlogin() in Multithread-Anwendungen erzeugt ernsthafte Sicherheitsschwachstellen. Race-Conditions können dazu führen, dass die Funktion den falschen Benutzernamen zurückgibt, was zu Autorisierungsentscheidungen für den falschen Benutzer führt. Angreifer, die die Prozessumgebung beeinflussen können, können möglicherweise den zurückgegebenen Benutzernamen manipulieren, um andere Benutzer zu imitieren. Da getlogin() auf das kontrollierende Terminal angewiesen ist, können Prozesse ohne Terminal NULL oder unvorhersehbare Werte erhalten. Die Verwendung von Benutzernamen für Sicherheitsentscheidungen ist inhärent riskant.

Lösung

Ersetzen Sie getlogin() durch getlogin_r(), die reentrant Version, die einen vom Aufrufer bereitgestellten Puffer verwendet und Thread-Sicherheit bietet. Vermeiden Sie es noch besser, sich auf Benutzernamen für Sicherheitsentscheidungen zu verlassen, und verwenden Sie numerische Benutzer-IDs (UIDs) via getuid() oder geteuid(), die zuverlässiger und schwerer zu fälschen sind. Wenn Benutzernameinformationen erforderlich sind, verwenden Sie getpwuid() mit der UID. Verwenden Sie niemals getlogin() in sicherheitskritischen Kontexten wie Autorisierungsprüfungen.

Häufige Auswirkungen

AuswirkungDetails
IntegritätBereich: Integrität

Anwendungsdaten ändern - Race-Conditions können die Anwendung veranlassen, mit falschen Benutzerdaten zu arbeiten.
ZugriffskontrolleBereich: Zugriffskontrolle

Schutzmechanismus umgehen - Sicherheitsprüfungen basierend auf getlogin() können durch Race-Conditions oder Terminal-Manipulation umgangen werden.

Beispielcode

Verwundbarer Code

// Verwundbar: getlogin() in Multithread-Anwendung
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

void* check_user_access(void* arg) {
    char* resource = (char*)arg;

    // Verwundbar: getlogin() ist nicht thread-sicher
    char* username = getlogin();

    if (username == NULL) {
        printf("Benutzername könnte nicht ermittelt werden\n");
        return NULL;
    }

    // Verwundbar: Race-Condition zwischen getlogin() und Verwendung
    // Ein anderer Thread könnte den zurückgegebenen Wert ändern

    // Verwundbar: Benutzername für Sicherheitsentscheidung
    if (strcmp(username, "admin") == 0) {
        printf("Admin-Zugriff auf %s gewährt\n", resource);
        grant_admin_access(resource);
    } else {
        printf("Benutzer %s greift auf %s zu\n", username, resource);
        grant_user_access(resource);
    }

    return NULL;
}

int main() {
    pthread_t threads[10];

    // Mehrere Threads verwenden getlogin() gleichzeitig
    for (int i = 0; i < 10; i++) {
        pthread_create(&threads[i], NULL, check_user_access, "shared_resource");
    }

    for (int i = 0; i < 10; i++) {
        pthread_join(threads[i], NULL);
    }

    return 0;
}
// Verwundbar: Sicherheitsentscheidung basierend auf getlogin()
int is_trusted_user(void) {
    // Verwundbar: getlogin() unzuverlässig und nicht-reentrant
    char* login = getlogin();

    if (login == NULL) {
        return 0;
    }

    // Verwundbar: Passworteintrag basierend auf unzuverlässigem Namen
    struct passwd* pwd = getpwnam(login);

    if (pwd == NULL) {
        return 0;
    }

    // Verwundbar: Gruppenprüfung basierend auf potenziell falschem Benutzer
    if (is_trusted_group(pwd->pw_gid)) {
        return 1;  // Zugriff gewährt - aber für wen?
    }

    return 0;
}

Lösungscode

// Behoben: Verwendung von getlogin_r() - die reentrant Version
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>

#define LOGIN_NAME_MAX 256

void* check_user_access_safe(void* arg) {
    char* resource = (char*)arg;
    char username[LOGIN_NAME_MAX];

    // Behoben: Reentrant getlogin_r() mit lokalem Puffer verwenden
    if (getlogin_r(username, sizeof(username)) != 0) {
        fprintf(stderr, "Benutzername könnte nicht ermittelt werden: %s\n", strerror(errno));
        return NULL;
    }

    // Behoben: Benutzername ist jetzt im thread-lokalen Puffer
    // Keine Race-Condition mit anderen Threads

    return NULL;
}

// Behoben: UID statt Benutzername für Sicherheit verwenden
void* check_user_access_secure(void* arg) {
    char* resource = (char*)arg;

    // Behoben: Numerische UID verwenden - zuverlässiger als Benutzername
    uid_t uid = getuid();
    uid_t euid = geteuid();

    // Behoben: Gegen bekannte Admin-UID prüfen
    // UID 0 ist root auf Unix-Systemen
    if (euid == 0) {
        printf("Admin-Zugriff auf %s\n", resource);
        grant_admin_access(resource);
    } else {
        printf("Benutzer %d greift auf %s zu\n", uid, resource);
        grant_user_access(resource);
    }

    return NULL;
}
// Behoben: Ordnungsgemäße Benutzeridentifikation
#include <pwd.h>
#include <grp.h>
#include <sys/types.h>

// Behoben: UID für Sicherheitsentscheidungen verwenden
int is_trusted_user_secure(void) {
    // Behoben: Echte und effektive UIDs abrufen
    uid_t real_uid = getuid();
    uid_t effective_uid = geteuid();

    // Behoben: getpwuid_r (reentrant) für Benutzerinfo verwenden
    struct passwd pwd;
    struct passwd* result;
    char buffer[1024];

    if (getpwuid_r(effective_uid, &pwd, buffer, sizeof(buffer), &result) != 0) {
        return 0;  // Fehler beim Abrufen der Benutzerinfo
    }

    if (result == NULL) {
        return 0;  // Benutzer nicht gefunden
    }

    // Behoben: Gruppenmitgliedschaft über GID prüfen
    if (is_trusted_group(pwd.pw_gid)) {
        return 1;
    }

    // Behoben: Zusätzliche Gruppen prüfen
    gid_t groups[NGROUPS_MAX];
    int ngroups = getgroups(NGROUPS_MAX, groups);

    for (int i = 0; i < ngroups; i++) {
        if (is_trusted_group(groups[i])) {
            return 1;
        }
    }

    return 0;
}

// Behoben: Thread-sichere Autorisierung
void authorize_action_secure(const char* action) {
    uid_t uid = getuid();
    gid_t gid = getgid();

    // Behoben: Mit zuverlässigen Identifikatoren protokollieren
    syslog(LOG_INFO, "UID %d GID %d fordert an: %s", uid, gid, action);

    // Behoben: Autorisierung basierend auf UID/GID, nicht Benutzername
    if (uid == 0 || is_authorized_uid(uid, action)) {
        perform_privileged_action(action);
    } else {
        deny_action(action);
    }
}
// Behoben: Vollständiger sicherer Benutzerkontext
typedef struct {
    uid_t uid;
    uid_t euid;
    gid_t gid;
    gid_t egid;
    char username[256];
    gid_t groups[NGROUPS_MAX];
    int ngroups;
} UserContext;

int get_user_context(UserContext* ctx) {
    if (ctx == NULL) return -1;

    ctx->uid = getuid();
    ctx->euid = geteuid();
    ctx->gid = getgid();
    ctx->egid = getegid();
    ctx->ngroups = getgroups(NGROUPS_MAX, ctx->groups);

    // Behoben: Thread-sichere Benutzernamen-Abfrage
    struct passwd pwd;
    struct passwd* result;
    char buffer[1024];

    if (getpwuid_r(ctx->uid, &pwd, buffer, sizeof(buffer), &result) == 0 && result) {
        strncpy(ctx->username, pwd.pw_name, sizeof(ctx->username) - 1);
        ctx->username[sizeof(ctx->username) - 1] = '\0';
    } else {
        snprintf(ctx->username, sizeof(ctx->username), "uid:%d", ctx->uid);
    }

    return 0;
}

CVE-Beispiele

Keine spezifischen CVEs für diese CWE, aber das Schwachstellenmuster bezieht sich auf Race-Conditions bei der Authentifizierung, die in verschiedenen Unix-Anwendungen beobachtet wurden.


Referenzen

  1. MITRE Corporation. "CWE-558: Use of getlogin() in Multithreaded Application." https://cwe.mitre.org/data/definitions/558.html
  2. CERT. "POS30-C. Use the readdir_r() function rather than readdir()."
  3. IEEE Std 1003.1 (POSIX). "getlogin, getlogin_r - get login name."