Link to home
Start Free TrialLog in
Avatar of roccogalati
roccogalati

asked on

[C server] Login and blank spaces

Hi to all!

I was wondering about the possibility of managing usernames which contain blank spaces.

I use read function to read the commands which the client sends to the server, something like what i posted below.

Each line ends with a "\r\n" sequence, so i try to delete it and to consider just what the client really writes.
The problem is that my code works fine if the username doesn't contain blank spaces, but if it is something like: "user name" my code doesn't work and it crashes.

It's like when you use scanf() and fgets()...

What can I do in order to consider also the blank spaces and continue also to not consider \r\n?
what's the better way in order to avoid security problems, too?

the server works in this way:

server: WHO?
client: HELOusername\r\n
server: PASS
client:password\r\n

but it has also to work fine in this case:


server: WHO?
client: HELOuser name\r\n
server: PASS
client:password\r\n


thanks a lot!

while( (n = read(fd, buffer, MAX_LINE)) > 0){
   buffer[n] = '\0';
   char *comando = buffer;
   
   lenght = strlen(comando);
    if(lenght == 0){
            exit(0);
            }
   while (comando) {
  char *next = strstr(comando, "\r\n");
  if (next) {
    *next = '\0';
    next += 2;
  }
      
   if ( strncmp(comando, "HELO", 4) == 0){  
    
   username = strtok((comando+4), "\r\n");
   if (strlen(username) > 16){
   Writeline(fd, alert, strlen(alert));
   }
   Writeline(fd, passw, strlen(passw));
   read(fd, password, MAX_LINE);  
   passwd = strtok(password, "\r\n");
 
   if (verify_login(username, passwd)){
   auth=1;
   Writeline(fd, good, strlen(good));
   } 
   else {
   auth=0;
   Writeline(fd, fail, strlen(fail));
   }
   
   }

Open in new window

Avatar of sunnycoder
sunnycoder
Flag of India image

I am not sure what Writeline function does. Also why do you need to strtok on "\r\n"? Would there be newlines in the middle of your string?
Check the following code ... It should do what you are seeking.
char buffer[32];
chat * temp;
char * username;
char password[17];
...
while (fgets (buffer, 32, file)) //if you are reading input from user, use stdin in place of file
//Also would you be processing these commands at a stretch?? seems unlikely - in which case you do not need a while loop here
{
   temp = strstr(buffer,"\r\n");
   *temp = '\0';
 
   if (strncmp(buffer, "HELO", 4) == 0) 
   {
        username = buffer+4;
        if (strlen(username)>16)
            //handle error
        //prompt for password
        fgets(password,17,stdin);
        temp = strstr(password, "\r\n"); //add error checking if (temp) etc.
        *temp = '\0';
        verify(username,password);
        //handle failure and success of verification
   }
}

Open in new window

usual security issues - you would be accepting passwords in clear text being echoed on screen ... you need to either switch off the echo to the terminal or use a function like getpass(now obsolete)
Avatar of roccogalati
roccogalati

ASKER

the code i posted is on my server, i use Writeline to send messages to the client, and read to read from the client.

The client sends HELOusername and the server has to store the username in a string, then it asks to the client for a password and then it compares the data...

The actual problem is that the username can't contains blank spaces, because if i write HELOuser name it doesn't work fine... if i use HELOusername it is OK...
this is my writeline:
 
ssize_t Writeline(int sockd, const void *vptr, size_t n) {
    size_t      nleft;
    ssize_t     nwritten;
    const char *buffer;
 
    buffer = vptr;
    nleft  = n;
 
    while ( nleft > 0 ) {
	if ( (nwritten = write(sockd, buffer, nleft)) <= 0 ) {
	    if ( errno == EINTR )
		nwritten = 0;
	    else
		return -1;
	}
	nleft  -= nwritten;
	buffer += nwritten;
    }
 
    return n;
}

Open in new window

It would indeed help tremendously if you could provide some more details about the issue you are facing. Which line is causing what problem? Any error messages - error numbers ...



The code really looks fine except for the strtok which is probably not required

   
   username = strtok((comando+4), "\r\n");      --->> why this strtok ... You already replaced the first \r of "\r\n" ... Note that string of delimiters means any of these can act as a delimiter ... it does not mean this string as delimiter. In all probability you dont want this statement.


>while( (n = read(fd, buffer, MAX_LINE)) > 0){
Is this loop meant to read multiple commands? In case you intend this function to authenticate only then a loop is unneccessary.

  if (next) {
    *next = '\0';
    next += 2;
  }
     
Again this piece of code serves no purpose for you.
>> because if i write HELOuser name it doesn't work fine...

define "doesn't work fine". Where and how does it fail ?
if (next) {
    *next = '\0';
    next += 2;
  }
     
Again this piece of code serves no purpose for you. --->> referring to next+=2 bit ... you dont use value of next again
I try to explain better what i'd like to do..

i need to use a loop because i have to handle multiple commands...


At the moment it seems to work OK, it verifies correctly usernames with and without blank spaces...

but i'd like to ask you, if you think my code is correct for what i want to do... because it seems to work now, but i'd like to avoid possible mistakes...

What i need is to be able to read username and password and to store it in a string and passing them to the verify function.
But i have to delete the \r\n from each line, because i need just the correct username and password...

However, it seems to work OK now...

if ( strncmp(comando, "HELO", 4) == 0){
// if the string sended by client begin with HELO
    
username = strtok((comando+4), "\r\n");
//I read the username written 4 byte after HELO in the string and i don't need \r\n, //i have to delete them because i need just the username
 
   if (strlen(username) > 16){
   Writeline(fd, alert, strlen(alert));
   }
   Writeline(fd, passw, strlen(passw));
   read(fd, password, MAX_LINE);  
   passwd = strtok(password, "\r\n");
   //I try to read the password sended by the client and i store it in passwd, but i   //don't need extra chars, i need just the pass string, so i have to delete \r\n
 
   if (verify_login(username, passwd)){
   auth=1;
   Writeline(fd, good, strlen(good));
   } 
   else {
   auth=0;
   Writeline(fd, fail, strlen(fail));
   }

Open in new window

>, but i'd like to avoid possible mistakes...
Use defensive programming techniques ...
initialize all variables
check all return values
check for NULL before dereferencing
check all parameters for sanity
use safe functions which allow you to limit the memory you would be accessing e.g. strncpy instead of strcpy
http://en.wikipedia.org/wiki/Defensive_programming

>But i have to delete the \r\n from each line, because i need just the correct username and password...
Thats right .... whatever is in the input stream has to be read ... there are various ways of doing it but probably the most robust is the one you are using - fread()/fgets()/read().
Just one drawback ... what if input is malformed and there is no \r\n in say first 200 bytes ...

>passwd = strtok(password, "\r\n");
This would tokenize your string at either \r or \n ... this does NOT mean tokenize at sequence "\r\n" ... This statement is equivalent to
passwd = strtok(password, "\n\r");
Probably what you want is strstr as you were using before ... that would return NULL if the string is malformed (too long, incorrectly delimited)

char * temp = strstr (buffer, "\r\n");
if (!temp)
       //no delimiter ... handle malformed string
else
       *temp  = '\0'; // okay now effectively \r\n has been removed form the "string"

Cheers!
>>char * temp = strstr (buffer, "\r\n");
>>if (!temp)
    >>   //no delimiter ... handle malformed string
>>else
    >>   *temp  = '\0'; // okay now effectively \r\n has been removed form the "string"

Thanks for your advices, sunnycoder!

I'll try and let you know in a while!

thanks a lot! u are great! ;)
but if i'm using this at the begin of my code:

while( (n = read(fd, buffer, MAX_LINE)) > 0){
   buffer[n] = '\0';
   char *comando = buffer;
   
   lenght = strlen(comando);
    if(lenght ==0){
            exit(0);
            }
   while (comando) {
  char *next = strstr(comando, "\r\n");
  if (next) {
    *next = '\0';
    next += 2;
  }
   if ( lenght <= 500 ){  // strings long at max 500
 
   
   if ( strncmp(comando, "HELO", 4) == 0){  
   
   username = strtok((comando+4), "\r\n");
   if (strlen(username) > 16){
   Writeline(fd, alert, strlen(alert));
   }
   Writeline(fd, passw, strlen(passw));
   read(fd, password, MAX_LINE);  
   passwd = strtok(password, "\r\n");
 
   if (verify_login(username, passwd)){
   auth=1;
   Writeline(fd, good, strlen(good));
   }
   else {
   auth=0;
   Writeline(fd, fail, strlen(fail));
   }
   
   }


does it means that the string has already been purged by "\r\n" ?


because i'm having problems when i try to login, after having sending the password, the server close the connection, it seems that it crashes...

i try to understand what you wrote previously but i'm a little confused...  
i debugged it by adding a printf...
it never calls verify_login, it crashes before...

Indent your code properly ... that would make it more readable and also prevent brace mismatches.
while( (n = read(fd, buffer, MAX_LINE)) > 0)
{
   buffer[n] = '\0';
   char *comando = buffer;
   
   lenght = strlen(comando);
   if(lenght ==0)
   {
       exit(0);
   }
 
   while (comando) 
   {
        char *next = strstr(comando, "\r\n");
        if (next)
        {
            *next = '\0'; //Dont move next yet.
        }
        if ( lenght <= 500 )
        {   
             if ( strncmp(comando, "HELO", 4) == 0)
             {     
                 //username = strtok((comando+4), "\r\n"); If I remember correctly, username is a 16 char string right after HELO. Right now, comando contains HELOusername\0\nnextcommand\r\n ... So you do not want a strtok on \r\n
                 username = commando+4;
                 if (strlen(username) > 16)
                 {
                      Writeline(fd, alert, strlen(alert));
                 }
                 Writeline(fd, passw, strlen(passw));
                 read(fd, password, MAX_LINE);  
                 passwd = strtok(password, "\r\n");
 
                 if (verify_login(username, passwd))
                 {
                      auth=1;
                      Writeline(fd, good, strlen(good));
                 }
                 else 
                 {
                      auth=0;
                      Writeline(fd, fail, strlen(fail));
                 }  
           }     
     }
     if (next)
         commando = next+2; //move to next command here
}

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of sunnycoder
sunnycoder
Flag of India image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks a lot, i indented better my code and used your inputs, now it looks like better...

thanks, thanks a lot!
thanks!
Great Input!