2

Can someone explain me why strcmp returns the same value even if passwords are correct/incorrect? I define valid password just below include section and checking it with entered one at the end of my program.

Here's my code:

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

#define TIME 10
#define MAXPASSWORD 12
#define PASSWORD "pass123"

void sigalrm_handler() {
    printf("\nERR: Time is up...\n");
}

int getch() {
    struct termios oldtc, newtc;
    int ch;
    tcgetattr(STDIN_FILENO, &oldtc);
    newtc = oldtc;
    newtc.c_lflag &= ~(ICANON | ECHO);
    tcsetattr(STDIN_FILENO, TCSANOW, &newtc);
    ch=getchar();
    tcsetattr(STDIN_FILENO, TCSANOW, &oldtc);
    return ch;
}

int main(int argc, char * argv[]) {
    char password[MAXPASSWORD] = {0};
    printf("Enter correct password. You have %d seconds: ", TIME);
    signal(SIGALRM, sigalrm_handler);
    alarm(TIME);
    fflush(stdout);

    for(int i=0; i<MAXPASSWORD; i++)
    {
      password[i] = getch();
      if (password[i] == '\n')
        break;
      printf("*");
    }

    if (strcmp(password, PASSWORD) == 0) {
        printf("\nValid password\n");
    } else {
        printf("\nInvalid password\n");
    }
}
Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
Raven
  • 57
  • 5
  • 1
    What is that same value? – Sourav Ghosh Feb 14 '19 at 12:53
  • 1
    Add `printf("<%s> <%s>\n", password, PASSWORD);` at the end of the code, examine the output and you'll probably find out. – Jabberwocky Feb 14 '19 at 12:54
  • 2
    Isn't it like you're always storing an extra newline into the user input buffer (just like the `fgets()` thingy)? – Sourav Ghosh Feb 14 '19 at 12:54
  • Why are you defining `getch()` in your code when `getchar()` is already available? ( _[getch() is a nonstandard function . It is not part of the C standard library or ISO C...](https://www.geeksforgeeks.org/difference-getchar-getch-getc-getche/)_.) – ryyker Feb 14 '19 at 13:10
  • This is not working because '\n' is part of the string input always and not part of macro. if you add password[i] = '\0'; inside the if check and before the break; this program will work fine. – Deepak Feb 14 '19 at 13:30
  • don't switch to raw mode, it's enough to take off echo, if you change to raw mode no ctrl-c or interrupt will be available. and of course, dont use getch() then, as stdio continues to do buffered input. – Luis Colorado Feb 15 '19 at 15:19
  • @ryyker, i'm afraid that precisely the reason is that... as `getch()` is not a standard function, he feels free to implement his own version.... don't blame him for that... I'd do probably the same :) It is better to indicate the correct way to implement a private version of `getch()` (as the nonstandard library ncurses could have implemented) than to blame. – Luis Colorado Feb 15 '19 at 15:23

3 Answers3

3

You forgot to replace the \n by NUL

...
for(int i=0; i<MAXPASSWORD; i++)
{
  password[i] = getch();
  if (password[i] == '\n')
  {
    password[i] = 0;   // <<<< add this line
    break;
  }
  printf("*");
}
...

There is another problem: consider what happens if the user enters more than 11 characters before hitting Enter. I let you find out yourself as an exercise.

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
  • 1
    I was gonna say this too but they do zero initialize the buffer. So there will accidentally be null termination unless all 12 characters are read, in which case it will be missing. – Lundin Feb 14 '19 at 12:56
  • 1
    @Lundin actually the `\n` needs to be replaced by NUL. – Jabberwocky Feb 14 '19 at 12:57
3

Problem is you have \n stored into your input buffer so that pass123 and pass123\n will not match.

Thus null terminate the input if you find \n as below.

  if (password[i] == '\n')
  {
     password[i] = '\0';
     break;
  }
kiran Biradar
  • 12,700
  • 3
  • 19
  • 44
  • 1
    Thanks mate, it's worked! I didn't know that I have to add '\0' to the array. You explained it simply and cleanly! – Raven Feb 14 '19 at 13:51
1

You have stated that strcmp() indicates two strings are equal, when you know they are not...

The problems with the \n mentioned in the other answers not withstanding, if you are indeed seeing strcmp() return a wrong indication, the question becomes why?

In C, a string is defined as a null terminated character array. So if for example if you have the following:

char password[MAXPASSWORD] = {0};//where MAXPASSWORD == 12

|p|a|s|s|1|2|3|\0|\0|\0|\0|\0| // `PASSWORD ` - legal string
|s|o|m|e|p|a|s|s|1|2|3|4|\n|  // 'password' 

even after replacing the \n character, this array is too long by one character:

|s|o|m|e|p|a|s|s|1|2|3|4|\0|  // 'password' - too long by 1 character
                        ^     // end of legal definition of `password`

If the password array has too many characters, even in this case after replacing the last char \n with NULL in a location beyond the legal definition of the string, the code becomes subject to undefined behavior.

String functions are designed to work exclusively with strings. When a non-nul terminated character array is presented, the function, in this case strcmp(), because it is looking for the nul terminator to know where the end of a string is, cannot be expected to behave with predictability. (In this case, the location of the nul character would be the cause of undefined behavior.)

To prevent this from happening, even if user were able to enter too many characters in password, always terminate with a statement such as:

password[MAXPASSWORD-1] = 0;  //for a properly initialized array, (as your code indicates)
                              //this guarantees termination occurs 
                              //within legal memory area of defined variable. 

With this, there will be no undefined behavior, and if the strings are different, strcmp() will indicate so.

ryyker
  • 22,849
  • 3
  • 43
  • 87
  • @Jabberwocky - The title of the question asserts OP is seeing `strcmp` indicate equality, when it is known that the _strings_ are indeed not equal. I am addressing that. Your answer seems to address how to make the strings equal. No one (that I have noticed yet) has asked how long is the array that was entered my user. If it was equal to the `sizeof` the char array definition, then you have undefined behavior. – ryyker Feb 14 '19 at 13:26
  • I think This code is not UB. this is not working because break point is after storing the '\n' character in the array and that is compared. so '\n' is not present in the macro defined. so this is the expected behavior always. if he just assign password[i] = '\0' before the break, this should work. Please correct me if this i understood UB you mentioned improperly – Deepak Feb 14 '19 at 13:27
  • @ryyker I suppose the phrasing of the question is wrong (not the first time this happens). There _is_ definitively an issue with the `\n`. If the user enters this: ABC and then presses the Enter key, the buffer definitely contains `"ABC\n" ` (NUL terminated) because the buffer is zero initialized (`char password[MAXPASSWORD] = {0};`) – Jabberwocky Feb 14 '19 at 13:29
  • 1
    @ryyker: I just want to learn more and wanted to know if i misout something, this comment was not to say anything against. sorry if this felt so. – Deepak Feb 14 '19 at 14:35