Socket race conditions
Материал из ALT Linux Wiki
(Новая страница: «Простая задача: создать сокет, поправить у него права (например, для группового доступа) и ...») |
(source же) |
||
Строка 48: | Строка 48: | ||
=== Защита с помощью явного указания umask === | === Защита с помощью явного указания umask === | ||
Если по каким-то причинам перемещать сокет нельзя, необходимо ''сначала'' выставить <code>umask</code> построже (например, <code>0777</code>), затем делать <code>chown()</code>, и только затем — <code>chmod()</code>. | Если по каким-то причинам перемещать сокет нельзя, необходимо ''сначала'' выставить <code>umask</code> построже (например, <code>0777</code>), затем делать <code>chown()</code>, и только затем — <code>chmod()</code>. | ||
- | < | + | <source lang="C"> |
#include <stdio.h> | #include <stdio.h> | ||
#include <sys/socket.h> | #include <sys/socket.h> | ||
Строка 79: | Строка 79: | ||
unlink(TESTSOCKET); | unlink(TESTSOCKET); | ||
} | } | ||
- | </ | + | </source> |
Этот код работает так: | Этот код работает так: |
Версия 11:43, 22 мая 2014
Простая задача: создать сокет, поправить у него права (например, для группового доступа) и передать соответствующей группе (например №10).
Рассмотрим код, который ровно это реализует. Для того, чтобы убедиться в наличии небезопасных race conditions, добавим в него просмотр текущих прав на сокет.
#include <stdio.h> #include <sys/socket.h> #include <sys/un.h> #include <sys/stat.h> #define TESTSOCKET "/tmp/testsocket" void pstat(const char *path) { static int iter = 1; struct stat info; stat(path, &info); printf("%d) %4d:%-4d %o\n",iter++, info.st_uid, info.st_gid, info.st_mode); } void main(void) { struct sockaddr_un addr = { AF_UNIX, TESTSOCKET }; struct stat info; int sock = socket(AF_UNIX, SOCK_STREAM, 0); bind(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)); pstat(TESTSOCKET); chmod(TESTSOCKET, 0660); pstat(TESTSOCKET); chown(TESTSOCKET, -1, 10); pstat(TESTSOCKET); shutdown(sock, SHUT_RDWR); unlink(TESTSOCKET); }
компилируем, запускаем, наблюдаем следующее:
1) 500:500 140755 2) 500:500 140660 3) 500:10 140660
Это значит буквально вот что:
- После создания сокет получает права в соответствие с
umask
. В нашем случаеumask
не соответствует результату; хорошо ещё, что не 0 , а ведь бывает и так! В это время сокет доступен на чтение всем. - После
chmod()
сокет доступен на запись кому не надо: членам группы500
. В нашем случае это не страшно, но если бы запускающий процесс имел какую-нибудь более популярную группу в качестве основной, на это время сокет стал бы доступен на чтение-запись всем её членам. - После
chown()
наконец-то всё приходит в порядок.
Защита с помощью directory traversal
Есть два способа избежать небезопасных гонок. Самый простой — оставить в покое сам сокет и ограничивать права на каталог, в котором он заводится. В отличие от сокета, каталог можно завести заранее, выдать ему права, допустим "500:10 0750
". Тогда сокет, заведённый в этом каталоге, не будет доступен кому не надо в любом случае. Что, конечно, не отменяет chmod()
(да хоть 0666
).
Защита с помощью явного указания umask
Если по каким-то причинам перемещать сокет нельзя, необходимо сначала выставить umask
построже (например, 0777
), затем делать chown()
, и только затем — chmod()
.
#include <stdio.h> #include <sys/socket.h> #include <sys/un.h> #include <sys/stat.h> #define TESTSOCKET "/tmp/testsocket" void pstat(const char *path) { static int iter=1; struct stat info; stat(path, &info); printf("%d) %4d:%-4d %o\n",iter++, info.st_uid, info.st_gid, info.st_mode); } void main(void) { struct sockaddr_un addr = { AF_UNIX, TESTSOCKET }; struct stat info; int sock = socket(AF_UNIX, SOCK_STREAM, 0); mode_t oldumask = umask(0777); bind(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)); umask(oldumask); pstat(TESTSOCKET); chown(TESTSOCKET, -1, 10); pstat(TESTSOCKET); chmod(TESTSOCKET, 0660); pstat(TESTSOCKET); shutdown(sock, SHUT_RDWR); unlink(TESTSOCKET); }
Этот код работает так:
1) 500:500 140000 2) 500:10 140000 3) 500:10 140660
Соответственно, сокет становится доступен только после последней операции.