segunda-feira, 11 de julho de 2011

Uma aplicação cliente servidor com autenticação - Debian

Plataforma: Debian (Linux debian 2.6.32-5-amd64)

Olá Pessoal,

hoje vamos trabalhar em uma aplicação cliente/servidor – client/server – onde nosso cliente irá enviar alguns comandos como (ls, mkdir, etc...) que serão executados no servidor e exibidos na tela do cliente. A topologia é bem simples, nosso servidor deverá ficar escutando – listening - em um determidada porta. Nosso servidor porvê autenticação, e a base de dados é a mesma do sistema. Assim, você irá se logar apenas se o usuário digitado, for um usuário cadastrado no sistema. Para verificar os usuários do sistema você pode verificar em: /etc/passwd.

Bom nosso servidor também loga em um arquivo quando o cliente se conecta e quando o mesmo é desconectado:

Para rodar nosso servidor executamos o seguinte comando: ./teste 4001

Onde 4001 é o número da porta que o servidor deve estar escutando. Você pode selecionar outra porta, sempre tendo o cuidado de não colocar números que estão sendo utilizados por outros serviços, como http – porta 80, hhtps: 443, etc...

Esse é o código do nosso servidor:
Cabeçários:

#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>
#include <crypt.h>
#include <shadow.h>


Funções Auxiliares, é sempre recomendado que você organize os seus códigos escritos na Linguagem C, em funções, assim o código ficará mais claro de ser entendido. Sempre que considerar necessários comentários são bem vindos:

Função responsável por gravar os dados dos cliente conectados. Veeja que a mesma recebe a estrutura de dados do cliente.

int gravaLog(struct sockaddr_in cliaddr, char * mensagem)
{
char buff[4096]; //armazenar informações do cliente
FILE * arqLog; //Arquivo de logs para IP e porta de conexão do cliente
int porta; //porta que o cliente usou para se conectar
time_t tem = time(NULL);
arqLog = fopen("logs","a+");
inet_ntop(AF_INET, &(cliaddr.sin_addr), buff, sizeof(buff));
porta = ntohs(cliaddr.sin_port);
fprintf(arqLog,"%s: IP %s, Porta %d as %s",mensagem,buff,porta, ctime(&tem));
fclose(arqLog);
return 1;
}

Função que faz a consulta no arquivo onde são armazenadas as senhas de nosso sistema.

int AutenPasswd(char * user, char * senha)
{
struct passwd * pw;
struct spwd * shad;
pw = getpwnam(user);
shad = getspnam(user);

if(pw == NULL)
return -1;
else
{
strcpy(pw->pw_passwd,shad->sp_pwdp);

if (strcmp(crypt(senha, pw->pw_passwd),pw->pw_passwd)==0)
return 1;
else
return 0;
}

}

Essa função recebe os dados fornecidos pelo programa cliente, ela faz uso da função AutenPasswd:

//Retorna 0 se a autenticacao falhar, 1 ser ocorrer ocm sucesso
int Autenticacao(int * connfd)
{
int err; //recebe valores de retorno das funções
char buff[256], user[256], password[256];

strcpy(buff, "1");
bzero(user,256);
bzero(password,256);
err = read(*connfd,user,sizeof(user));

err = read(*connfd,password,sizeof(password));

err = AutenPasswd(user,password);

if(err != 1) //Autenticacao falhou
{
strcpy(buff, "0");
write(*connfd,buff,sizeof(buff));
close(*connfd);
return 0;
}
else
write(*connfd,buff,sizeof(buff));

return 1;

}

Funções auxiliares para trabalhar com arquivos. O resultado do comando enviado pelo cliente é colocado em um arquivo, temos que ler este aquivo e colocá-lo em uma matriz para enviar. Aqui nós fixamos o tamanho do array, mas o mais interessante seria trabalhar com alocação de memória dinâmica, pegando o número de caracteres retornados pela função ContaCaracteres, assim o programa fica melhor estruturado, pois o tamanho do array pode passar de 4096 e comportamentos não desejados, ou até que comprometam a segurança do sistema podem ocorrer.

int ContaCaracteres()
{
int contador = 1;
char letra;
FILE *arq = fopen("arqTemp","r");
letra = fgetc(arq);
while (letra != EOF)
{
contador++;
letra = fgetc(arq);
}
fclose(arq);
return contador;

}
void EnviaResultado(int * connfd)
{
FILE * arquivo;
char buffer[4096], letra;
int contador, x=0;


contador = ContaCaracteres();
arquivo = fopen("arqTemp","r");

letra = fgetc(arquivo);
while(x<contador)
{
buffer[x] = letra;
letra = fgetc(arquivo);
x++;
}
buffer[--x]='\0';


fclose(arquivo);


write(*connfd,buffer,sizeof(buffer));

}

Enfim, nosso programa main:

int main(int argc, char ** argv)
{
int listenfd, connfd, porta; //descitores de conexao e varialvel porta que ira armazenar a porta do servidor
struct sockaddr_in servaddr, cliaddr; //estruturas sockadd_in para armazenar os dados do server e do cliente
socklen_t len; //variavel para armazenar o tamanho da estrutura do cliente
char buff[4096];//vai armazenar informacoes sobre o cliente

int n,err;

if (strcmp("-h",argv[1]) ==0)
{
printf("\n\tChamada do programa:\n\t./serv numPort\n\tOnde numPort e o numero da porta TCP\n");
return 1;
}

else
if(argc != 2)
{
printf("\n\tErro parametros invalidos\n");
return 0;
}

porta = atoi(argv[1]);
listenfd = socket(AF_INET, SOCK_STREAM,0);


memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(porta);



bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
listen(listenfd,1);



len = sizeof(cliaddr);
connfd = accept(listenfd, (struct sockaddr *)&cliaddr,&len);//vamos aceitar o cliente

gravaLog(cliaddr, "Cliente conectou");
err = Autenticacao(&connfd);
if(!err)
return 0;



do
{



read(connfd,buff,sizeof(buff));
if(strcmp(buff,"exit")!=0)
strcat(buff, " > arqTemp");

);
system(buff);
EnviaResultado(&connfd);

}while(strcmp(buff,"exit"));

gravaLog(cliaddr, "Cliente desconectou");
close(connfd);


return 1;
}

Agora nosso programa cliente. O mesmo pode ser executado com o comando ./teste

O IP do servidor e a porta, já estão fixos no código. Seria interessante modificar, para que os mesmos sejam passados pela linha de comando.

#include <sys/socket.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>

int RequisitaAutenticacao(int * sockfd)
{
int err;
char buff[256];
char user[256], password[256];
char *ptr;


printf("\nInforme o usuário: ");

scanf("%s",user);
ptr = getpass("\nInforme sua senha:");
strcpy(password,ptr);

err = write(*sockfd, user, sizeof(user)); //envia usuario
err = write(*sockfd,password,sizeof(password)); //escreve a senha




err = read(*sockfd,buff,sizeof(buff));

if(!strcmp(buff,"1"))
return 1; //autenticacao OK
else
return -1; //autenticacao falhou


}

int main()
{
char buff[4096];
int sockfd, ret;
struct sockaddr_in servaddr;
sockfd = socket(AF_INET,SOCK_STREAM,0);
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(4001);
inet_pton(AF_INET,"10.0.2.2", &servaddr.sin_addr);
connect(sockfd, (struct sockaddr *)&servaddr,sizeof(servaddr));
ret = RequisitaAutenticacao(&sockfd);
if(ret != 1)
{
printf("\n\tAutenticacao Falhou");
close(sockfd);
return 0;
}

else
printf("\n\tUsuario Autenticado com SUCESSO!!!");

do
{
printf("\n>");


fgets(buff,4095,stdin);
buff[strlen(buff)-1]='\0';
write(sockfd,buff,sizeof(buff));

if(strcmp(buff,"exit")!=0)
{

read(sockfd,buff,sizeof(buff));
printf("\n%s",buff);
}

}while(strcmp(buff,"exit"));
close(sockfd);



return 0;
}

Nenhum comentário:

Postar um comentário