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
| Auswirkung | Details |
|---|---|
| Integrität | Bereich: Integrität Anwendungsdaten ändern - Race-Conditions können die Anwendung veranlassen, mit falschen Benutzerdaten zu arbeiten. |
| Zugriffskontrolle | Bereich: 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
- MITRE Corporation. "CWE-558: Use of getlogin() in Multithreaded Application." https://cwe.mitre.org/data/definitions/558.html
- CERT. "POS30-C. Use the readdir_r() function rather than readdir()."
- IEEE Std 1003.1 (POSIX). "getlogin, getlogin_r - get login name."