Ошибка HTTP Git 'фатальная: ошибка протокола: неверный символ длины строки:' - PullRequest
0 голосов
/ 22 декабря 2018

В настоящее время я пытаюсь создать простой Git HTTP-сервер в C без уже существующего веб-сервера.В настоящее время единственное, что я делаю, - это создание сокета сервера и выполнение CGI-скрипта git-http-backend с переменными среды из запроса клиента.Запрос на извлечение уже работает, но только для пустых репозиториев.Когда я пытаюсь клонировать репозиторий с контентом, я получаю эту ошибку на стороне клиента:

fatal: protocol error: bad line length character: 

Вот журнал связи между клиентом и сервером:

C: GET /test.git/info/refs?service=git-upload-pack HTTP/1.1
C: Host: localhost:9000
C: User-Agent: git/2.20.1
C: Accept: */*
C: Accept-Encoding: deflate, gzip
C: Accept-Language: en-US, *;q=0.9
C: Pragma: no-cache
C:

S: HTTP/1.1 200 OK
S: Expires: Fri, 01 Jan 1980 00:00:00 GMT
S: Pragma: no-cache
S: Cache-Control: no-cache, max-age=0, must-revalidate
S: Content-Type: application/x-git-upload-pack-advertisement
S: 
S: 001e# service=git-upload-pack
S: 000000fadd3fba560f4afe000e70464ac3a7a9991ad13eb0
S: HEAD003fdd3fba560f4afe000e70464ac3a7a9991ad13eb0 refs/heads/master
S: 0000

Небольшое примечание: HTTP / 1.1 200 OK добавляется вручную, остальное - из скрипта CGI.Также вы можете найти мой код здесь .Сначала у меня была теория, что содержимое ответа сервера ложно размещает новые строки (например, заголовок должен быть на строку выше), но оказывается, что это не совсем так.Итак, мой вопрос: могу ли я что-нибудь сделать?Редактирование этого ответа в хорошем формате довольно сложно в C, особенно с более длинными ответами.

1 Ответ

0 голосов
/ 26 декабря 2018

Прежде всего, пожалуйста, убедитесь, что вы понимаете последствия безопасности передачи данных, контролируемых внешним субъектом, функции, подобной 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 завершается неудачей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...