Ich habe einen Daemon, der als Root gestartet wird (damit er an niedrige Ports binden kann). Nach der Initialisierung möchte ich aus Sicherheitsgründen gerne die Root-Rechte verlieren.
Kann mir jemand einen Hinweis geben richtig bekannt Stück Code in C, der dies tun wird?
Ich habe die Manpages gelesen, ich habe mir verschiedene Implementierungen davon in verschiedenen Anwendungen angesehen, und sie sind alle unterschiedlich und einige von ihnen sind wirklich komplex. Dies ist sicherheitsrelevanter Code, und ich möchte wirklich nicht die gleichen Fehler neu erfinden, die andere Leute machen. Was ich suche, ist eine bewährte, bekannte gute, portable Bibliotheksfunktion, die ich in dem Wissen verwenden kann, dass sie es richtig machen wird. Gibt es so etwas?
Als Referenz: Ich fange als root an; Ich muss ändern, um unter einer anderen uid und gid zu laufen; Ich muss die Zusatzgruppen korrekt einrichten lassen; Ich muss danach nicht wieder zu Root-Rechten wechseln.
Dies variiert ziemlich stark zwischen den Unixen – gibt es bestimmte? Wenn Sie eine “portable” Lösung brauchen, wird es chaotisch, und Sie greifen am besten zB auf die permanent_set_uid()-Funktion von OpenSSH – in der uidswap.c-Datei
– Nr
28. Juli 2010 um 22:05 Uhr
Vielleicht eine bessere Strategie, erfordert kein Root, damit es nicht gelöscht werden muss. Siehe auch Nicht-Root-Prozess erlauben, sich an Port 80 und 443 zu binden? Ein verwandtes Thema ist Setuid entmystifiziert Tschüss Chen, Dean und Wagner. Verschiedene Varianten von Unix haben leicht unterschiedliche Verhaltensweisen. Dies ist ein weiterer Grund, Ihren nicht privilegierten Prozess zu punten und zuzulassen, dass er sich an den Port bindet.
Um alle Privilegien (Benutzer und Gruppe) zu löschen, müssen Sie die Gruppe vor dem Benutzer löschen. Angesichts dessen userid und groupid enthält die IDs des Benutzers und der Gruppe, zu der Sie wechseln möchten, und unter der Annahme, dass die effektiven IDs auch root sind, wird dies durch Aufrufen erreicht setuid() und setgid():
if (getuid() == 0) {
/* process is running as root, drop privileges */
if (setgid(groupid) != 0)
fatal("setgid: Unable to drop group privileges: %s", strerror(errno));
if (setuid(userid) != 0)
fatal("setuid: Unable to drop user privileges: %S", strerror(errno));
}
Wenn Sie paranoid sind, können Sie versuchen, Ihre Root-Rechte zurückzubekommen, was fehlschlagen sollte. Wenn es nicht fehlschlägt, retten Sie:
if (setuid(0) != -1)
fatal("ERROR: Managed to regain root privileges?");
Auch wenn Sie immer noch paranoid sind, möchten Sie vielleicht seteuid() und setegid() auch, aber es sollte nicht notwendig sein, da setuid() und setgid() bereits alle IDs setzen, wenn der Prozess root gehört.
Die Ergänzungsgruppenliste ist ein Problem, weil es keine POSIX-Funktion gibt, um Ergänzungsgruppen zu setzen (es gibt getgroups(), aber keine setgroups()). Es gibt eine BSD- und eine Linux-Erweiterung setgroups() die Sie verwenden können, wenn es Sie betrifft.
Du solltest auch chdir("https://stackoverflow.com/") oder in ein beliebiges anderes Verzeichnis, sodass der Prozess nicht in einem root-eigenen Verzeichnis verbleibt.
Da sich Ihre Frage allgemein auf Unix bezieht, ist dies der sehr allgemeine Ansatz. Beachten Sie, dass dies unter Linux nicht mehr der bevorzugte Ansatz ist. In aktuellen Linux-Versionen sollten Sie die CAP_NET_BIND_SERVICE Fähigkeit auf der ausführbaren Datei und führen Sie sie als normaler Benutzer aus. Es ist kein Root-Zugriff erforderlich.
Sie möchten auch die gid festlegen – und dies kann je nach Unix leicht variieren, wenn es darum geht, ob setuid / setgid tatsächlich auch mit den echten und gespeicherten IDs durcheinander kommt
– Nr
28. Juli 2010 um 22:06 Uhr
Dies muss in der Tat eine portable Lösung sein, also ist keine Linux-Fähigkeit erlaubt, fürchte ich. Und ich habe tatsächlich den einfachen Ansatz mit setuid() und setgid(); es setzt die Gruppen nicht richtig (und wenn Sie setgroups() nicht aufrufen, können Sie anscheinend immer noch Mitglied einiger Gruppen von root sein!).
– David gegeben
28. Juli 2010 um 22:14 Uhr
@nos Danke. Erweitert um Gruppen abzudecken. Wenn der Prozess root gehört (wie im OP erwähnt) oder wenn er setuid-root ist, dann setzen setuid() und setgid() bereits alle IDs (real, effektiv und gespeichert). Das steht in der Spezifikation. Andernfalls wäre die Implementierung nicht POSIX-konform.
– Juliano
28. Juli 2010 um 22:59 Uhr
Sie können verwenden initgroups(3) Um die zusätzlichen Gruppen zurückzusetzen, ist es portabel genug (zumindest BSD und GNU libc haben es).
– Daniel Röthlisberger
18. März 2012 um 13:57 Uhr
Löschen Sie auch immer die Zusatzgruppen. initgroups existiert auf AIX, Solaris, HP-UX, FreeBSD, Darwin, Linux und wahrscheinlich anderen. Schließen Sie es ein, damit der Build fehlschlägt, wenn es nicht gefunden werden kann. Wenn nicht alle Gruppen gelöscht werden, kann dies zu schwerwiegenden Systemkompromittierungen und CVEs führen. Ich glaube nicht, dass chdir irgendwelche Auswirkungen auf die Sicherheit hat, oder?
#define _GNU_SOURCE // for secure_getenv()
int drop_root_privileges(void) { // returns 0 on success and -1 on failure
gid_t gid;
uid_t uid;
// no need to "drop" the privileges that you don't have in the first place!
if (getuid() != 0) {
return 0;
}
// when your program is invoked with sudo, getuid() will return 0 and you
// won't be able to drop your privileges
if ((uid = getuid()) == 0) {
const char *sudo_uid = secure_getenv("SUDO_UID");
if (sudo_uid == NULL) {
printf("environment variable `SUDO_UID` not found\n");
return -1;
}
errno = 0;
uid = (uid_t) strtoll(sudo_uid, NULL, 10);
if (errno != 0) {
perror("under-/over-flow in converting `SUDO_UID` to integer");
return -1;
}
}
// again, in case your program is invoked using sudo
if ((gid = getgid()) == 0) {
const char *sudo_gid = secure_getenv("SUDO_GID");
if (sudo_gid == NULL) {
printf("environment variable `SUDO_GID` not found\n");
return -1;
}
errno = 0;
gid = (gid_t) strtoll(sudo_gid, NULL, 10);
if (errno != 0) {
perror("under-/over-flow in converting `SUDO_GID` to integer");
return -1;
}
}
if (setgid(gid) != 0) {
perror("setgid");
return -1;
}
if (setuid(uid) != 0) {
perror("setgid");
return -1;
}
// change your directory to somewhere else, just in case if you are in a
// root-owned one (e.g. /root)
if (chdir("https://stackoverflow.com/") != 0) {
perror("chdir");
return -1;
}
// check if we successfully dropped the root privileges
if (setuid(0) == 0 || seteuid(0) == 0) {
printf("could not drop root privileges!\n");
return -1;
}
return 0;
}
Ich denke, der erste Check sollte kommen geteuid() seit getuid() gibt die echte UID zurück.
Dies variiert ziemlich stark zwischen den Unixen – gibt es bestimmte? Wenn Sie eine “portable” Lösung brauchen, wird es chaotisch, und Sie greifen am besten zB auf die permanent_set_uid()-Funktion von OpenSSH – in der uidswap.c-Datei
– Nr
28. Juli 2010 um 22:05 Uhr
Vielleicht eine bessere Strategie, erfordert kein Root, damit es nicht gelöscht werden muss. Siehe auch Nicht-Root-Prozess erlauben, sich an Port 80 und 443 zu binden? Ein verwandtes Thema ist Setuid entmystifiziert Tschüss Chen, Dean und Wagner. Verschiedene Varianten von Unix haben leicht unterschiedliche Verhaltensweisen. Dies ist ein weiterer Grund, Ihren nicht privilegierten Prozess zu punten und zuzulassen, dass er sich an den Port bindet.
– jww
2. Januar 2019 um 9:30 Uhr
Siehe auch Setuid entmystifiziert auf Usenix.
– jww
26. Juni 2019 um 14:41 Uhr