Question

I've been working with a friend to write some code, and it's mostly been tested on archlinux. When we came to recompile and test on ubuntu it gets a way through the program, and then returns a stack smashing error. I've added some print statements to work out where the crash is happening, but neither of us can see the cause.

The code is as below, the error seems to be happening when the code returns from the function "authenticate":

/** Includes **/
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>

#include <pthread.h>
#include <mysql.h>


/* Definitions */
#define     LIC_SERVER_PORTNO   2325
//#define       LIC_SERVER_ADDR     "69.68.67.66"
#define     SERVER_PORTNO       4959
#define     UPLEN           45
#define     MAX_USER_NUM        100
//#define       MAX_KA_TIME     2000
#define     MAX_KA_TIME     20

/** Structure to hold info on logged in users **/
typedef struct {
    char *ip;
    time_t login_time;
    time_t last_check;
} user;

/** GLOBALS **/
user *users;
int user_num;

/** Version string function **/
const char* version()
{
     return "0.1r3";
}

void exit_signal(int sig)
{
  pthread_exit(NULL);
   printf("\nTerminating server. Goodbye!\n\n"); 
  (void) signal(SIGINT, SIG_DFL); 
  exit(0);
}

/** Function for error reporting **/
void error(char *msg)
{
    perror(msg);
    exit(1);
}

int authenticate(char* uname,char* pwd)
{
   MYSQL *conn;
   MYSQL_RES *res;
   MYSQL_ROW row;

   int r=0;

   const char *server = "localhost";
   const char *user = "root";
   const char *password = "12345"; /* set me first */
   const char *database = "test";
    printf("inside authenticate %s\n","one");
   conn = mysql_init(NULL);

   /* Connect to database */
   if (!mysql_real_connect(conn, server,user, password, database, 0, NULL, 0)) {
      fprintf(stderr, "%s\n", mysql_error(conn));
      exit(1);
   }
    printf("inside authenticate %s\n","two");
   /* send SQL query */
   char query [] = "select * from users where username='";
   char q2 [] = "';";
   //char *tmp,*query;

   strcat(query,uname);
   strcat(query,q2);
   printf("inside authenticate %s\n","three");
   if (mysql_query(conn,query)) {
      fprintf(stderr, "%s\n", mysql_error(conn));
      exit(1);
   }

   res = mysql_use_result(conn);
   if (res==NULL) return 0;
   row = mysql_fetch_row(res);
   if (row==NULL) return 0;
   printf("inside authenticate %s\n","four");
   if ( (strcmp(row[2],uname)==0) && (strcmp(row[3],pwd)==0) )
    r = 1;

       /* close connection */
       mysql_free_result(res);
       mysql_close(conn);
       printf("inside authenticate %s\n","five");
       return r;
    }

    /** Keep alive guard **/
    void *keep_alive_guard(void *thread_data)
    {
        int i,k,c;
        time_t ts;


        while (1) {
    ts = time(NULL);
        for (i=0;i<user_num;i++) {

            char the_ip[50],the_ip2[50];
    char scall1[1024],scall2[1024];

        if (ts-users[i].last_check>MAX_KA_TIME) {           
              strncpy(the_ip,users[i].ip,50);      
              strncpy(the_ip2,users[i].ip,50);

              // system call to delete redirection for all incoming packets to licence server for port 1055
              strncpy(scall1,"iptables -t nat -D PREROUTING -s ",1024);   

              strcat(scall1,the_ip);
              strcat(scall1," -j REDIRECT -p tcp --to-port 1055 --dport 4957");
              //printf("%s\n",scall1);
              FILE *phony = popen(scall1,"r");
              pclose(phony);

              // system call to delete redirection for all incoming packets to licence server for port 2325
              strncpy(scall2,"iptables -t nat -D PREROUTING -s ",1024);

              strcat(scall2,the_ip2);             
              strcat(scall2," -j REDIRECT -p tcp --to-port 2325 --dport 4958");           
              //printf("%s\n",scall2);
              FILE *phony2 = popen(scall2,"r");
              pclose(phony2);

              for (k=i;k<user_num;k++) {
            if (k!=MAX_USER_NUM-1) {
                memcpy(&users[k],&users[k+1],sizeof(user));
            }
              }
              user_num--;

        }
    }
    }
}

/* Main function */
int main(int argc,char* argv[])
{
     // Initializing
     printf("Licence proxy (server) v%s\n",version());
     printf("Initializing...");

     user_num = 0;
     users = (user*)malloc(MAX_USER_NUM*sizeof(user));

     (void) signal(SIGINT, exit_signal);

     FILE *notha = popen("iptables -A INPUT ! -s 127.0.0.1 -p tcp --dport 1055 -j DROP","r");
     pclose(notha);
     FILE *notha2 = popen("iptables -A INPUT ! -s 127.0.0.1 -p tcp --dport 2325 -j DROP","r");
     pclose(notha2);

     // Basic variables for socket handling
     int sockfd, newsockfd, portno, clilen;
     struct sockaddr_in serv_addr, cli_addr; //addresses
     int n,i;

     // Create socket
     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     if (sockfd < 0) 
        error((char*)"ERROR opening socket");

     bzero((char *) &serv_addr, sizeof(serv_addr));
     portno = SERVER_PORTNO;  // Set port number to listen

     // Set options for socket and bind to port
     serv_addr.sin_family = AF_INET;
     serv_addr.sin_addr.s_addr = INADDR_ANY;
     serv_addr.sin_port = htons(portno);
     if (bind(sockfd, (struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) 
              error((char*)"ERROR on binding");

     printf("ok!\n\nRunning...\n\n");

     // Start listening
    listen(sockfd,5);

    // Create thread for keep alive guard
    pthread_t kag;
    int rc;
    rc = pthread_create(&kag,NULL,keep_alive_guard,(void *)user_num);

    char datauser [UPLEN],*user;
    char datapass [UPLEN],*pass;
    char datareq_id [UPLEN],*req_id;
    char respOK [] = "OK";
    char respNK [] = "NK";

    while (1) { 
    // Accept connection from peer
    clilen = sizeof(cli_addr);
    newsockfd = accept(sockfd,(struct sockaddr *) &cli_addr,(socklen_t*)&clilen);
    if (newsockfd < 0) 
          error((char*)"ERROR on accept");

    n = recv(newsockfd,datareq_id,UPLEN,0);
    if (n < 0) error((char*)"ERROR reading username from client");
    req_id = (char*)malloc(strlen(datareq_id));
    strncpy(req_id,datareq_id,strlen(datareq_id));

    if (req_id[0]=='K') {
        for (i=0;i<user_num;i++) {
        if (strcmp(users[i].ip,inet_ntoa(cli_addr.sin_addr))==0) {
            users[i].last_check = time(NULL);
        }
        }           
    }
    else if (req_id[0]=='L') {

        printf("Transaction requested from %s\n",inet_ntoa(cli_addr.sin_addr)); 

        // Read and wrap username from client   
        n = recv(newsockfd,datauser,UPLEN,0);
            if (n < 0) error((char*)"ERROR reading username from client");
    user = (char*)malloc(strlen(datauser));
        strncpy(user,datauser,strlen(datauser));
        printf("here %s\n","one");
        // Read and wrap password from client   
        n = recv(newsockfd,datapass,UPLEN,0);
        if (n < 0) error((char*)"ERROR reading password from client");      
        pass = (char*)malloc(strlen(datapass));
        strncpy(pass,datapass,strlen(datapass));
        printf("here %s\n","two");
        char scall[1024],scall2[1024];
        printf("here %s\n","two + one line");
        //Cross-check authorization data against MYSQL database and take action
        if (authenticate(user,pass)==1) {
              printf("outside authenticate (success) %s\n","one");
          n = send(newsockfd,respOK,UPLEN,0);
              printf("outside authenticate (success) %s\n","two");
          if (n < 0) error((char*)"ERROR writing to socket");
          printf("here %s\n","two and a lot");
          // system call to redirect all incoming packets to licence server for port 1055
          strncpy(scall,"iptables -t nat -A PREROUTING -s ",1024);   
          printf("here %s\n","three");
          strcat(scall,inet_ntoa(cli_addr.sin_addr));         
          strcat(scall," -j REDIRECT -p tcp --to-port 1055 --dport 4957");
          //printf("%s\n",scall);
          FILE *phony = popen(scall,"r");
          pclose(phony);
            printf("here %s\n","four");
          // system call to redirect all incoming packets to licence server for port 2325
          strncpy(scall2,"iptables -t nat -A PREROUTING -s ",1024);   

          strcat(scall2,inet_ntoa(cli_addr.sin_addr));        
          strcat(scall2," -j REDIRECT -p tcp --to-port 2325 --dport 4958");
          //printf("%s\n",scall2);
          FILE *phony2 = popen(scall2,"r");
          pclose(phony2);                 
          printf("here %s\n","five");
          printf("%s successfully logged in.\n",inet_ntoa(cli_addr.sin_addr));
          users[user_num].ip = (char*)malloc(strlen(inet_ntoa(cli_addr.sin_addr)));
          strncpy(users[user_num].ip,inet_ntoa(cli_addr.sin_addr),strlen(inet_ntoa(cli_addr.sin_addr)));

          printf("here %s\n","six");          
          users[user_num].login_time = time(NULL);
          users[user_num].last_check = time(NULL);
          user_num++;

        }
        else 
        {         
          printf("outside authenticate (fail) %s","one");
          n = send(newsockfd,respNK,UPLEN,0);
          if (n < 0) error((char*)"ERROR writing to socket");
          printf("Access denied to %s.\n",inet_ntoa(cli_addr.sin_addr));
        }

        free(user);
        free(pass);
    }
    close(newsockfd);       
     }
     shutdown(sockfd,SHUT_RDWR);
     close(sockfd);
     pthread_exit(NULL);

     return 0; 
}

The error location is given away from the console output below:

    Transaction requested from 192.168.1.10
    here one
    here two
    here two + one line
    inside authenticate one
    inside authenticate two
    inside authenticate three
    inside authenticate four
    inside authenticate five
    *** stack smashing detected ***: ./lproxy-server terminated
    ======= Backtrace: =========
    /lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x50)[0xb757f390]
    /lib/tls/i686/cmov/libc.so.6(+0xe233a)[0xb757f33a]
    ./lproxy-server(authenticate+0x28c)[0x8049299]
    ./lproxy-server(main+0x49d)[0x804998e]
    /lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0xb74b3bd6]
    ./lproxy-server[0x8048f41]
    ======= Memory map: ========
    08048000-0804b000 r-xp 00000000 08:01 128583     /home/martin/Downloads/licproxy-server2/lproxy-server
    0804b000-0804c000 r--p 00002000 08:01 128583     /home/martin/Downloads/licproxy-server2/lproxy-server
    0804c000-0804d000 rw-p 00003000 08:01 128583     /home/martin/Downloads/licproxy-server2/lproxy-server
    0915b000-0917c000 rw-p 00000000 00:00 0          [heap]
    b6bce000-b6beb000 r-xp 00000000 08:01 2648       /lib/libgcc_s.so.1
    b6beb000-b6bec000 r--p 0001c000 08:01 2648       /lib/libgcc_s.so.1
    b6bec000-b6bed000 rw-p 0001d000 08:01 2648       /lib/libgcc_s.so.1
    b6bfc000-b6bfd000 ---p 00000000 00:00 0 
    b6bfd000-b73ff000 rw-p 00000000 00:00 0 
    b73ff000-b7412000 r-xp 00000000 08:01 2763       /lib/libz.so.1.2.3.3
    b7412000-b7413000 r--p 00012000 08:01 2763       /lib/libz.so.1.2.3.3
    b7413000-b7414000 rw-p 00013000 08:01 2763       /lib/libz.so.1.2.3.3
    b7414000-b7438000 r-xp 00000000 08:01 8012       /lib/tls/i686/cmov/libm-2.11.1.so
    b7438000-b7439000 r--p 00023000 08:01 8012       /lib/tls/i686/cmov/libm-2.11.1.so
    b7439000-b743a000 rw-p 00024000 08:01 8012       /lib/tls/i686/cmov/libm-2.11.1.so
    b743a000-b744d000 r-xp 00000000 08:01 8048       /lib/tls/i686/cmov/libnsl-2.11.1.so
    b744d000-b744e000 r--p 00012000 08:01 8048       /lib/tls/i686/cmov/libnsl-2.11.1.so
    b744e000-b744f000 rw-p 00013000 08:01 8048       /lib/tls/i686/cmov/libnsl-2.11.1.so
    b744f000-b7451000 rw-p 00000000 00:00 0 
    b7451000-b745a000 r-xp 00000000 08:01 7807       /lib/tls/i686/cmov/libcrypt-2.11.1.so
    b745a000-b745b000 r--p 00008000 08:01 7807       /lib/tls/i686/cmov/libcrypt-2.11.1.so
    b745b000-b745c000 rw-p 00009000 08:01 7807       /lib/tls/i686/cmov/libcrypt-2.11.1.so
    b745c000-b7483000 rw-p 00000000 00:00 0 
    b7483000-b7498000 r-xp 00000000 08:01 8259       /lib/tls/i686/cmov/libpthread-2.11.1.so
    b7498000-b7499000 r--p 00014000 08:01 8259       /lib/tls/i686/cmov/libpthread-2.11.1.so
    b7499000-b749a000 rw-p 00015000 08:01 8259       /lib/tls/i686/cmov/libpthread-2.11.1.so
    b749a000-b749d000 rw-p 00000000 00:00 0 
    b749d000-b75f0000 r-xp 00000000 08:01 7789       /lib/tls/i686/cmov/libc-2.11.1.so
    b75f0000-b75f1000 ---p 00153000 08:01 7789       /lib/tls/i686/cmov/libc-2.11.1.so
    b75f1000-b75f3000 r--p 00153000 08:01 7789       /lib/tls/i686/cmov/libc-2.11.1.so
    b75f3000-b75f4000 rw-p 00155000 08:01 7789       /lib/tls/i686/cmov/libc-2.11.1.so
    b75f4000-b75f7000 rw-p 00000000 00:00 0 
    b75f7000-b77a1000 r-xp 00000000 08:01 5403       /usr/lib/libmysqlclient.so.16.0.0
    b77a1000-b77a2000 ---p 001aa000 08:01 5403       /usr/lib/libmysqlclient.so.16.0.0
    b77a2000-b77a5000 r--p 001aa000 08:01 5403       /usr/lib/libmysqlclient.so.16.0.0
    b77a5000-b77ea000 rw-p 001ad000 08:01 5403       /usr/lib/libmysqlclient.so.16.0.0
    b77ea000-b77eb000 rw-p 00000000 00:00 0 
    b77ed000-b77f7000 r-xp 00000000 08:01 8119       /lib/tls/i686/cmov/libnss_files-2.11.1.so
    b77f7000-b77f8000 r--p 00009000 08:01 8119       /lib/tls/i686/cmov/libnss_files-2.11.1.so
    b77f8000-b77f9000 rw-p 0000a000 08:01 8119       /lib/tls/i686/cmov/libnss_files-2.11.1.so
    b77f9000-b77fc000 rw-p 00000000 00:00 0 
    b77fc000-b77fd000 r-xp 00000000 00:00 0          [vdso]
    b77fd000-b7818000 r-xp 00000000 08:01 2813       /lib/ld-2.11.1.so
    b7818000-b7819000 r--p 0001a000 08:01 2813       /lib/ld-2.11.1.so
    b7819000-b781a000 rw-p 0001b000 08:01 2813       /lib/ld-2.11.1.so
    bfd4c000-bfd61000 rw-p 00000000 00:00 0          [stack]
    Aborted

Can anyone shed any light on what we've done wrong, how we could have worked that ourselves, and how we could avoid similar mistakes in the future.

Also if anyone has any insight onto why it works fine in archlinux, but not ubuntu I'd be very interested to hear it.

Was it helpful?

Solution

You use strcat to append to an array (query) that lives on the stack and only has the exact size required for the initial value. This way, you overflow a buffer on the stack, which can lead to all kinds of curious and interesting behaviour.

That it works on Arch Linux is coincidence; gcc on Ubuntu has stack checking enabled by default and finds the bug.

OTHER TIPS

This line looks suspicious to me:

strncpy(users[user_num].ip,inet_ntoa(cli_addr.sin_addr),strlen(inet_ntoa(cli_addr.sin_addr)));

BTW, strncpy does not add any trailing \0. When the area returned by malloc is not cleared, it could lead you to have subsequent string operations to overflow.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top