PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : race conditions & secure programming



f0rtex
21-09-2002, 18:56
Hallo Leute

Bin hier gerade einen Artikel über race conditions am drucharbeiten.
Folgendes Beispielprogramm (ex_01.c) soll das entstehen einer race-condition verdeutlichen



#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>

int main(int argc, char *argv[])
{
struct stat st;
FILE *fp;

if(argc != 3) {
fprintf(stderr, "usage : %s file message\n", argv[0]);
exit(EXIT_FAILURE);
}
if(stat(argv[1], & st) < 0){
fprintf(stderr, "can't find %s\n", argv[1]);
exit(EXIT_FAILURE);
}
if(st.st_uid != getuid()){
fprintf(stderr, "not the owner of %s\n", argv[1]);
exit(EXIT_FAILURE);
}
if(!S_ISREG(st.st_mode)){
fprintf(stderr, "%s is not a normal file\n", argv[1]);
exit(EXIT_FAILURE);
}
if((fp = fopen(argv[1], "w")) == NULL){
fprintf(stderr, "Can't open\n");
exit(EXIT_FAILURE);
}
/* Damit man genug Zeit hat. Geht ja nur ums Prinzip :-) */
sleep(20);
fprintf(fp, "%s\n", argv[2]);
fclose(fp);
fprintf(stderr, "Write Ok\n");
exit(EXIT_SUCCESS);
}


Nun wird folgendes gemacht:


# cp /etc/shadow /etc/shadow.bak
# chown root.root ex_01
# chmod +s ex_01
$ ls -l ex_01
-rwsrwsr-x 1 root root ....
$ touch fic
$ ./ex_01 fic "root::1:99999:::::" &
$ rm -f fic
$ ln -s /etc/shadow ./fic
$ fg
./ex_01 fic "root::99999:::::"
Write Ok
$


Nun sagt der Artikel, dass im /etc/shadow der von mir vorgenommene Eintrag stehen sollte.
Das ist bei mir nicht der Fall.
Müsste der "normale" user nicht zur gruppe root gehören, damit das funktioniert?
Wo liegt mein Fehler? (der Fehler des Autors :confused: ?)

MfG
f0rtex

anda_skoa
21-09-2002, 19:22
Original geschrieben von f0rtex
Nun sagt der Artikel, dass im /etc/shadow der von mir vorgenommene Eintrag stehen sollte.
Das ist bei mir nicht der Fall.
Müsste der "normale" user nicht zur gruppe root gehören, damit das funktioniert?
Wo liegt mein Fehler? (der Fehler des Autors :confused: ?)


Der User muß nciht in der Gruppe root sein, weil das Executable ja mit root Rechten läuft (glaub ich halt).
Ich glaube das sleep müßte zwischen den letzten beiden if's stehen.
Wenn er das File einemal geöffnet hat, hilft löschen nichts mehr, denn es wird nur aus dem Verzeichnis eintfernt, es belibt offen und benutzbar, bis es der Prozess geschlossen hat.

Aber wenn zwischen den letzten beiden if's das FIle durch den symbolischen Link ersetzt wird, öffnet er /etc/shadow.
Die Checks macht er noch auf "fic", d.h. er sieht ein reguläres File, aber öffnen tut er dann den Link, bzw die verlinkte Datei.

Probier mal sleep vor das letzte if zu setzen.

Ciao,
_

f0rtex
21-09-2002, 19:35
Der User muß nciht in der Gruppe root sein, weil das Executable ja mit root Rechten läuft (glaub ich halt).

Wenn es -rwsrws--- root root wäre, könnte ich es ja nicht ausführen da ich ja nicht der user root bin und auch nicht zu dieser Gruppe gehöre.



Aber wenn zwischen den letzten beiden if's das FIle durch den symbolischen Link ersetzt wird, öffnet er /etc/shadow.
Die Checks macht er noch auf "fic", d.h. er sieht ein reguläres File, aber öffnen tut er dann den Link, bzw die verlinkte Datei.

Das kling logisch :D Danke!



Probier mal sleep vor das letzte if zu setzen.

Hat funktioniert :D

Nochmals Danke!
f0rtex

p.s.: schon noch tricky ein [B]sicheres[B] Programm zu schreiben.

f0rtex
22-09-2002, 11:48
Wenn ich nun den oberen Code wie folgt Umwandle:



#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>

int main(int argc, char *argv[])
{
struct stat st;
int fd;
FILE *fp;

if(argc != 3) {
fprintf(stderr, "usage : %s file message\n", argv[0]);
exit(EXIT_FAILURE);
}
if((fd = open(argv[1], O_WRONLY, 0)) < 0) {
printf(stderr, "Can't open %s\n", argv[1]);
exit(EXIT_FAILURE);
}
fstat(fd, &st);
if(st.st_uid != getuid()){
fprintf(stderr, "not the owner of %s\n", argv[1]);
exit(EXIT_FAILURE);
}
if(!S_ISREG(st.st_mode)){
fprintf(stderr, "%s is not a normal file\n", argv[1]);
exit(EXIT_FAILURE);
}
sleep(20);
if((fp = fdopen(fd, "w")) == NULL){
fprintf(stderr, "Can't open\n");
exit(EXIT_FAILURE);
}
fprintf(fp, "%s\n", argv[2]);
fclose(fp);
fprintf(stderr, "Write Ok\n");
exit(EXIT_SUCCESS);
}


Durch die Benutzung von int fd, open und fdopen umgehe ich ja das Problem der Namensprüfung. Ich benutze den Dateideskriptor.

Wie sind Dateideskripotren zu interpretieren? Sind die Gleich den Inodes?
Wo wird meine "message" geschrieben, wenn ich in den 20 Sekunden mein Feil fic lösche?

MfG
f0rtex

anda_skoa
22-09-2002, 12:07
Original geschrieben von f0rtex
Durch die Benutzung von int fd, open und fdopen umgehe ich ja das Problem der Namensprüfung. Ich benutze den Dateideskriptor.


Nein, du umgehst das Problem, weil du schonfrüher das open machst.
Sobald du die Datei einmal geöffnet hast, behälts du sie, auch wenn sie gelsöcht wird (siehe meine erste Antwort)



Wie sind Dateideskripotren zu interpretieren? Sind die Gleich den Inodes?


Nein, Dateideskriptoren haben nichts mit inodes zu tun. Inodes gibt es ja nur in Dateisystemen.
Ein Dateideskriptor ist nur eine ID für die geöffnete Datei. Datei hier im Unixsinn, also auch ein Socket oder ein Chardevice.
Die Deskriptoren 0,1,2 sind im Normalfall stdin, stdout, und stderr.



Wo wird meine "message" geschrieben, wenn ich in den 20 Sekunden mein Feil fic lösche?


In die Datei fic, weil du die ja bei open geöffnet hast.
Das fdopen erzeugt nur einen FILE Handle/Stream auf dem schon geöffneten Deskriptor.

Ciao,
_

f0rtex
22-09-2002, 12:37
In die Datei fic, weil du die ja bei open geöffnet hast.




user$ touch fic
user$ ./ex_02 fic hallo &
user$ rm fic
user$ fg
./ex_02 fic hallo
Write Ok
user$ ls
ex_01 ex_02

Wo ist die "fic" nun hin? :confused:
(sorry:rolleyes: )

Danke
f0rtex

anda_skoa
22-09-2002, 12:50
Wenn du "rm fic" schreibst, wo wird sie da sein?

Richtig!
Gelöscht.

Ciao,
_

f0rtex
22-09-2002, 12:54
/* Hier ist die Datei geöffnet, oder? */
sleep(20);
/* und hier lösche ich die Datei */
if((fp = fdopen(fd, "w")) == NULL){
fprintf(stderr, "Can't open\n");
exit(EXIT_FAILURE);
}
/* und wo schreibe ich hier wenn die Datei gelöscht ist?!? */
fprintf(fp, "%s\n", argv[2]);
fclose(fp);
fprintf(stderr, "Write Ok\n");
exit(EXIT_SUCCESS);


Sorry das ich dich stresse, aber das will mir nicht in den Kopf... :(

anda_skoa
22-09-2002, 13:11
Original geschrieben von f0rtex

Sorry das ich dich stresse, aber das will mir nicht in den Kopf... :(

Kein Problem :)

Also:
Der Prozess öffnet die Datei.
if((fd = open(argv[1], O_WRONLY, 0)) < 0) {

Dann wird in der Shell mit rm fic die Datei gelöscht.
Da aber zu diesem Zeitpunkt die Datei von einem Prozess geöffnet ist, wird nur der Eintrag aus dem Verzeichnis genommen un die Datei markiert.
Die Datei existiert für alle Prozesse, die sie schon geöffnet haben, weiter und wird erst gelöscht, wenn der letzte Prozess sie geschlossen hat.

Leicht zu überprüfen:
Eine Textdatei mit less anschaun:
#> less test.txt &
#> rm test.txt
#> fg

less kann weiter die Datei anzeigen, als ob sie noch da wäre (sie ist es ja auch), abe sie ist nicht mehr sichtbar und kann daher nicht aufgelistet oder erneut geöffnet werden.

Alles klar?

Ciao,
_

f0rtex
22-09-2002, 13:35
Merci!
Jetzt ist der Groschen entlich gefallen :D

Danke
f0rtex

p.s.: Das Bsp. mit less hat bei mir nicht funktioniert. Der Less-Prozess wird sofort gestoppt.
Trotzdem hab ich's begriffen, was du mir erklärt hast :)