Академический Документы
Профессиональный Документы
Культура Документы
Universidade Estadual de Campinas
EA872K - Laboratório de Programação
de Software Básico
Relatório Final
Grupo 1: RA:
Guilherme Augusto Sakai Yoshike 138446
Dener Stassun Christinele 155151
Campinas, 03 de Dezembro de 2017
Descrição
O projeto final da disciplina consiste em um servidor que é acessado localmente. Para tal,
fizemos uso de conceitos como análise léxica e sintática das requisições feitas pelo cliente;
arquitetura WWW e protocolo HTTP ao receber e interpretar as requisições feitas pelos
navegadores comerciais; sistemas de arquivos, onde é feita a leitura, escrita e
gerenciamento de permissões dos arquivos oferecidos pelo servidor; socket para realizar a
comunicação entre cliente e servidor; gerenciamento de processos para controlar os
processos utilizados na comunicação entre cliente-servidor e no processamento das
requisições; e por fim Secure Socket Layer (SSL) para estabelecimento de uma conexão
segura.
O servidor implementa com sucesso as seguintes funcionalidades:
● Execução do servidor com passagem pela linha de comando dos parâmetros:
○ Diretório do webspace;
○ Arquivo de log;
○ Porta;
○ Tempo de timeout para uma conexão;
○ Certificado SSL;
○ Chave SSL;
● Obtenção das requisições do navegador via sockets;
● Passagem direta dos dados recebidos pelo socket para os analisadores léxicos e
sintáticos
● Análise léxica e sintática utilizando as ferramentas lex/yacc;
● Processamento adequado das requisições:
○ GET;
○ HEAD;
○ OPTIONS;
○ TRACE;
● Apresentação de páginas com mensagem de erro adequadas para cada tipo de erro:
○ 400 - Bad Request;
○ 403 - Forbidden;
○ 404 - Not Found;
○ 405 - Method Not Allowed;
○ 500 - Internal Server Error;
○ 501 - Not Implemented;
○ 503 - Service Unavailable;
● Uso de processos filhos para atender as requisições enquanto o processo principal
se mantém aberto a novas conexões;
● Uso da biblioteca openSSL para realizar comunicação encriptada segura com o
cliente.
Assim o servidor já está aguardando as requisições do cliente. É possível abrir o arquivo log
passado como parâmetro (caso não exista, será criado um novo) e assim verificar as
requisições e respostas.
Fluxogramas
O fluxograma da Figura 1 representa a comunicação do cliente com o servidor, mostrando
o handshake para inicializar a conexão segura SSL.
1. Hello do cliente: cliente conecta-se ao servidor
2. Hello do servidor: é estabelecido qual algoritmo de criptografia será utilizado na
conexão e servidor envia o certificado com a chave pública para o cliente. Além
disso, servidor pode pedir o certificado do cliente (opcional).
3. Cliente verifica se o certificado do servidor é válido, conferindo os CA’s que
assinaram o certificado. Há uma lista de CA’s configurados no browser. Caso o CA
que assinou o certificado do servidor não esteja nesta lista, o browser exibe uma
mensagem que a conexão não é segura, e fica a critério do usuário continuar ou não
com a conexão.
4. Cliente envia uma chave secreta compartilhada que será usada pelo cliente e pelo
servidor para criptografar e descriptografar as mensagens trocadas. Para essa
chave não ser interceptada nessa comunicação, o cliente criptografa essa chave
com a chave pública do servidor. Assim, apenas o servidor com a chave privada que
só ele possui pode descriptografar e descobrir qual é a chave compartilhada.
5. Caso o cliente envie um certificado, o servidor analisa o mesmo (opcional).
6. Servidor envia um Finish para finalizar o handshake.
7. Servidor envia um Finish para finalizar o handshake.
8. Agora com a conexão segura estabelecida, as mensagens podem ser criptografadas
e trocadas utilizando a chave secreta compartilhada que só os dois possuem.
A Figura 2 mostra o fluxo geral do servidor. Na primeira etapa, o servidor:
● Estabelece o tratamento de sinais, feito para que tratemos o sinal que é recebido
quando um processo filho é terminado;
● Inicializa o openSSL, carregando os algoritmos criptográficos, e criando uma variável
do tipo “contexto SSL” onde é armazenado qual método de criptografia será usado,
além de qual certificado e qual chave serão utilizados;
● Realiza a abertura do socket que estará na escuta para novas conexões.
Em seguida, fica no aguardo de uma nova conexão. Ao recebê-la, o servidor faz um fork. O
processo pai volta a aguardar novas conexões, enquanto o processo filho irá tratar da
conexão recebida. Assim, o processo filho irá:
● Criar uma nova variável do tipo “SSL”, que é carregada com o “contexto SSL”
configurado na primeira etapa.
● Associar o descriptor do socket de comunicação com esta nova variável “SSL”
utilizando a chamada SSL_set_fd.
Após isto, será verificado se o cliente requisitou uma conexão segura. Isto é feito utilizando
a chamada SSL_accept. Em ambos os casos, o servidor em seguida irá fazer a leitura do
socket de comunicação para receber a requisição. A diferença reside no fato de, quando
utilizamos a conexão segura, a chamada SSL_read é utilizada para realizar a leitura ao
invés da chamada read padrão.
Com a requisição recebida na memória, o servidor segue para a etapa de processamento
desta. Esta etapa consiste principalmente em:
● Enviar a requisição para o analisador léxico/sintático para gerar uma lista que
representa as informações da requisição;
● Verificar se a requisição é implementada e permitida no servidor;
● Responder a requisição, das formas:
○ GET e HEAD: O recurso pedido será procurado no Webspace. Caso não
exista ou não haja permissão de leitura, os devidos erros são enviados para
o cliente. Com o recurso em mãos, o servidor irá coletar dados sobre o
recurso (última modificação, tamanho, formato). Estes dados, juntos com os
headers padrões da resposta, são enviados ao cliente. Caso a requisição
seja de HEAD, o processamento acaba aqui. Caso seja de GET, o servidor
por fim escreve no socket o recurso pedido.
○ OPTIONS: Envia, junto com os headers padrões de resposta, às requisições
permitidas no servidor, especificadas na array de strings constante
REQUEST_ALLOWED.
○ TRACE: Envia, junto com os headers padrões da resposta, o tamanho da
requisição recebida e uma cópia da requisição recebida.
Ao fim do processamento, o processo filho volta a esperar novas escritas no socket por um
tempo determinado pelo parâmetro de timeout passado pela linha de comando quando o
servidor é executado. Caso até lá não haja novos dados no socket, o processo filho realiza
as devidas limpezas e termina.
Figura 2 - Fluxograma do sistema implementado
Webspace
● Diretório dir1 existente e com permissão de execução na raiz do webspace;
● Diretório dir11 existente e com permissão de execução dentro do diretório dir1 (dir11
deve estar vazio);
● Diretório dir2 existente e sem permissão de execução nem de leitura na raiz do
webspace;
● Diretório dir3 existente e com permissão de execução na raiz do webspace;
● Diretório dir4 existente e sem permissão de execução, mas apenas de leitura na raiz
do webspace;
● Arquivo index.html existente e com permissão de leitura na raiz do webspace;
● Arquivo texto1.html existente e com permissão de leitura no dir1 ;
● Arquivo texto2.html existente e sem permissão de leitura em dir1;
● Arquivo arquivo.txt existente e sem permissão de leitura em dir1;
● Arquivo relatorio_final.pdf existente e sem permissão de leitura em dir1;
● Arquivo gatos.jpg existente e sem permissão de leitura em dir1;
● Arquivo giphy.gif existente e sem permissão de leitura em dir1;
● Arquivo index.html existente e com permissão de leitura em dir2;
● Arquivo welcome.html existente e com permissão de leitura em dir3;
● Arquivo index.html existente e com permissão de leitura em dir4;
● https://localhost:9876
Ao requisitar apenas o endereço do servidor e a porta, abre o index.html, arquivo da pasta
raiz do Webspace.
● https://localhost:9876/dir1
Ao requisitar o diretório dir1, não encontra nem o arquivo index.html nem welcome.html.
Figura 4 - Não há index.html nem welcome.html no diretório dir1
● https://localhost:9876/dir1/texto1.html
Ao requisitar o arquivo texto1.html no diretório dir1, exibe o arquivo pois possui permissão
de leitura.
Figura 5 - Arquivo texto1.html no diretório dir1
● https://localhost:9876/dir1/texto2.html
Ao requisitar o arquivo texto2.html no diretório dir1, não consegue abrir pois não há
permissão de leitura.
Figura 6 - Arquivo texto2.html no diretório dir1 não possui permissão de leitura
● https://localhost:9876/dir1/arquivo.txt
Não há problema ao requisitar o arquivo.txt no diretório dir1 pois este possui permissão de
leitura.
● https://localhost:9876/dir1/giphy.gif
Também possível requisitar um arquivo gif giphy.gif que está no dir1. Este também possui
permissão de leitura.
Figura 9 - Arquivo giphy.gif no diretório dir1
● https://localhost:9876/dir1/dir11
Ao requisitar o diretório dir11, não encontra nem arquivo index.html nem welcome.html.
Figura 10 - Não há index.html nem welcome.html no diretório dir11
● https://localhost:9876/dir2
Como não há permissão de leitura no diretório dir2, retorna uma mensagem 403.
Figura 11 - Diretório dir2 não possui permissão de leitura
● https://localhost:9876/dir3
Ao requisitar o diretório dir3, não encontra o arquivo index.html, porém encontra o arquivo
welcome.html.
● https://localhost:9876/dir4
Como não há permissão de leitura no diretório dir4, retorna uma mensagem 403.
Figura 13 - Diretório dir4 não possui permissão de execução
● Número máximo de filhos ultrapassado
Caso ultrapasse o número de filhos, retorna uma mensagem para tentar novamente mais
tarde e o erro 503 associado.
Figura 14 - Servidor está com o número máximo de processos filhos para atender
requisições.
Figura 15 - Analisando com Wireshark os pacotes passados de forma insegura
Figura 16 - Analisando com Wireshark os pacotes passados de forma segura
● Bad Request
Requisição:
_____GET ALGO MUITO ERRADO
Host: localhost:8080
Resposta:
HTTP/1.1 400 Bad Request
Date: Sun Dec 3 22:54:08 2017
Server: Servidor HTTP ver. 0.1 de Guilherme Yoshike e Dener Stassun
Content-type: HTML document
Content-Length: 148
<!DOCTYPE html>
<html>
<head>
<title>400 Bad Request</title>
</head>
<body>
<font size="10">400 Bad Request</font>
</body>
</html>
Resposta:
HTTP/1.1 405 Method Not Allowed
Date: Sun Dec 3 23:10:48 2017
Server: Servidor HTTP ver. 0.1 de Guilherme Yoshike e Dener Stassun
Connection: close
Content-type: HTML document
Content-Length: 162
<!DOCTYPE html>
<html>
<head>
<title>405 Method Not Allowed</title>
</head>
<body>
<font size="10">405 Method Not Allowed</font>
</body>
</html>
● Not Implemented
Requisição:
____EUNAOEXISTO / HTTP/1.1
Host: localhost:8080
Connection: close
Resposta:
HTTP/1.1 501 Not Implemented
Date: Sun Dec 3 23:07:08 2017
Server: Servidor HTTP ver. 0.1 de Guilherme Yoshike e Dener Stassun
Connection: close
Content-type: HTML document
Content-Length: 156
<!DOCTYPE html>
<html>
<head>
<title>501 Not Implemented</title>
</head>
<body>
<font size="10">501 Not Implemented</font>
</body>
</html>
● HEAD
Requisição:
_____HEAD /dir1/gatos.jpg HTTP/1.1
Host: localhost:8080
Connection: close
Resposta:
HTTP/1.1 200 OK
Date: Sun Dec 3 23:12:45 2017
Server: Servidor HTTP ver. 0.1 de Guilherme Yoshike e Dener Stassun
Connection: close
Last-Modified: Mon Nov 20 02:36:08 2017
Content-Length: 330381
Content-Type: JPEG image data, JFIF standard 1.01, resolution (DPI), density 300x300,
segment length 16, baseline, precision 8, 1254x705, frames 3
● OPTIONS
Requisição:
_____OPTIONS / HTTP/1.1
Host: localhost:8080
Connection: close
Resposta:
HTTP/1.1 200 OK
Date: Sun Dec 3 23:15:21 2017
Server: Servidor HTTP ver. 0.1 de Guilherme Yoshike e Dener Stassun
Connection: close
Allow: GET, HEAD, TRACE, OPTIONS
Content-Length: 0
● TRACE
Requisição:
TRACE / HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:55.0) Gecko/20100101
Firefox/55.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: pt-BR,pt;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Resposta:
HTTP/1.1 200 OK
Date: Sun Dec 3 23:16:04 2017
Server: Servidor HTTP ver. 0.1 de Guilherme Yoshike e Dener Stassun
Connection: keep-alive
Content-Length: 336
Content-Type: ASCII text
Limitações
A principal limitação do servidor ocorreu ao implementar a conexão segura utilizando
SSL/TLS. Quando um cliente tenta se conectar ao servidor de maneira não segura (HTTP
comum) a requisição recebida é incompleta (em geral faltando os primeiros cinco bytes),
como ilustrado na Figura 18. Isto resulta em um erro de bad request ou de request not
implemented. Acreditamos que isto ocorre pois o servidor consome parte do buffer de
entrada ao verificar se o cliente requisitou ou não uma conexão segura.
Figura 17 - Resposta do servidor a uma requisição incompleta.
Trabalhos Futuros
É necessário que se encontre e resolva o problema que faz o buffer de entrada ser
consumido em conexões não-seguras. Além disto, caso este servidor fosse utilizado na
prática, o principal refinamento que precisaria ser feito seria uma verificação mais extensa
de possíveis erros das syscalls e seus devidos tratamentos. Também existem alguns
trechos de código que estão sujeitos a buffer overflow, tornando-os inseguros. Por fim, seria
interessante utilizar threads ao invés de processos para realizar o processamento das
requisições, visto que a troca de contexto entre threads tem muito menos overhead que a
troca de contexto entre processos.
Comentários
Acreditamos que a disciplina trouxe muito aprendizado e o projeto do servidor conseguiu
englobar e forçar a prática de muitos conceitos de programação de sistemas. No entanto,
gostaríamos ter aprendido e posto em prática mais profundamente conceitos de sistemas
operacionais, compreender melhor como funciona o linux internamente, programar
aplicações de baixo nível para linux. Além disso, no projeto do servidor muito tempo é
consumido com as formalidades especificadas pelas CRFs, tornando o tempo de dedicação
necessário para completar a disciplina um pouco maior do que devia.