Прежде всего, пожалуйста, убедитесь, что вы понимаете последствия безопасности передачи данных, контролируемых внешним субъектом, функции, подобной popen
.Реализация, которую вы имеете сейчас, тривиальна для использования путем внедрения оболочки, добавляя специальные символы оболочки в строку запроса.Даже используя просто git со специально созданным именем хранилища, ваш текущий код позволяет выполнять произвольные команды на сервере.Попробуйте это, например:
git clone "$(echo -e 'http://localhost:9000/;echo\tunexpected\t>helloworld;cat\t/etc/passwd;exit;.git')"
Это создаст файл в рабочем каталоге сервера со строкой «непредвиденный» в нем и отправит обратно содержимое /etc/passwd
клиенту (используйте wiresharkчтобы увидеть это).
Чтобы избежать этого, вам нужно убедиться, что вы правильно экранировали входные данные, чтобы не могло произойти внедрение оболочки.В идеале вы должны использовать такие механизмы, как execve
, которые позволяют передавать переменные окружения и возможные аргументы командной строки в качестве буферов, а не генерировать потенциально небезопасные строки, которые затем анализируются оболочкой.Такое решение, конечно, более сложное, поскольку оно требует реструктуризации вашей программы.
Тогда вы используете небезопасный способ объединения строк.strcat
не может знать, насколько велик целевой буфер, поэтому он с радостью перезапишет стек после буфера при достаточном вводе.Это классическое переполнение стека, которое затем можно использовать.Используйте более безопасные альтернативы, такие как strlcat
или, что еще лучше, правильную библиотеку строк.
Теперь перейдем к исходной проблеме:
Вывод, полученный из git http-backend
, является необработанным двоичным выводом, включая нулевые байты,В ответном примере действительно будет нулевой байт после HEAD
, отделяющего список поддерживаемых функций.Вы можете увидеть это, выполнив вашу команду вручную и отправив ее в нечто вроде xxd
или выгрузив ее в файл и посмотрев на нее с помощью шестнадцатеричного редактора.
В цикле, где вы читаете из канала, а затемобъединяя выходные данные в буфер ответов, вы усекаете данные, потому что strcat
работает со строками C, которые заканчиваются нулевым байтом.Остальная часть строки HEAD
и сам нулевой байт никогда не попадают в ответ, нарушая протокол git.
Вы можете использовать fread
для чтения необработанных данных из канала непосредственно в буфер.Затем вам нужно будет скопировать этот буфер в буфер ответов с помощью функции, которая не останавливается на нулевом байте, например memcpy
.Чтобы это работало, вам также необходимо отслеживать уже прочитанные байты и сколько места остается в буфере ответа.
В качестве альтернативы, поскольку вы фактически не выполняете никакой обработки в конечном буфере ответа, вы можететакже напрямую отправляйте данные, прочитанные из канала, в клиентский сокет.Таким образом, вам не нужно беспокоиться о размере буфера ответов и отслеживании смещения и оставшегося пространства.Вот версия, которая работает для начального запроса git
:
char response[10000] = "HTTP/1.1 200 OK\r\n";
send(client_socket, response, strlen(response), 0);
while (!feof(g)) {
size_t bytes_read = fread(response, 1, sizeof(response), g);
if (bytes_read == 0)
break;
send(client_socket, response, bytes_read, 0);
}
Последующий запрос POST завершается неудачей.