0

I am trying to secure some C code by utilizing the fgets() and strncmp() functions. The program runs fine, however, if I enter the correct password ("password") more than once it still indicates a correct password. Also, even when using fgets() the results (if longer than the 9 indicated in the buffer) still appear in the output. Any help would be greatly appreciated.

#include <stdio.h>
#include <string.h>

int main(void)
{
    char buffer[9];
    int pass = 0;
    char password[] = "password";

    printf("\n Enter your password : \n");
    fgets(buffer, 9, stdin);

    if(strcmp(buffer, password))
    {
        printf ("\n Incorrect Password \n");
    }
    else
    {
        printf ("\n Correct Password \n");
        pass = 1;
    }

    if(pass)
    {
        printf ("\n Root privileges authorized \n");
    }

    return 0;
}
lost_in_the_source
  • 10,998
  • 9
  • 46
  • 75
TEX
  • 37
  • 2

2 Answers2

1

The problem with your code is that fgets is taking the first 8 characters off the input and ignoring the rest. Obviously, if you are inviting a password you don't want to ignore any input! You might want to do something a little more fancy to ensure that you capture the full input.

My first two tries at answering this were wrong. Thanks to wildplasser for holding my feet to the fire.

So, the hack answer is: use a really big buffer. fgets is probably your easier solution there.

Alternatively, you could allocate memory dynamically as your input string exceeds your buffer.

But, just for fun, here is an implementation that breaks us out of the "line buffer" trap that I wasn't aware getchar was in.

For this, I leveraged a very beautiful comment here: getchar() and stdin

PS: Ok, Ok, I tested it this time. It works. On my Mac.

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

int main(void)
{
    int c, i;
    char buffer[9];
    struct termios tty_opts_default, tty_opts_raw;

    if (!isatty(STDIN_FILENO)) {
      printf("Error: stdin is not a TTY\n");
      return 1;
    }

    /* save tty settings for later. */
    tcgetattr(STDIN_FILENO, &tty_opts_default);

    /* put tty settings into raw mode. */
    cfmakeraw(&tty_opts_raw);
    tcsetattr(STDIN_FILENO, TCSANOW, &tty_opts_raw);

    /* NOW we can grab the input one character at a time. */
    c = getchar();
    while (i < 8 && c != EOF && c != '\n' && c != '\r') {
      /* Since we are collecting a pwd, we might want to enforce other
         password logic here, such as no control characters! */
      putchar('*');
      buffer[i++] = c;
      c = getchar();
    }
    buffer[i] = '\0';

    /* Restore default TTY settings */
    tcsetattr(STDIN_FILENO, TCSANOW, &tty_opts_default);

    /* Report results to user. */
    printf("\nPassword received.\n");
    printf("(It was '%s' -- don't tell anyone! Quick! Hide the terminal!)\n", buffer);

    return 0;
}
Blunt Jackson
  • 616
  • 4
  • 17
  • 1
    `getchar()` returns an `int`. (and: your loop repeats the first character 8 times, followed by a NUL.) – wildplasser Jun 16 '19 at 14:56
  • Dang. Obviously, I hadn't. Sure looked good, though! Sometimes I learn more by answering questions poorly than I do by asking them. Thanks, wildplasser. – Blunt Jackson Jun 16 '19 at 19:51
  • 1
    You can do it without character-at-a-time input. This is now `POSIX-C90`, which was probably not what the OP intended. – Neil Jun 16 '19 at 20:25
  • I know; I think I referenced those ways in the text. Given that I colossally screwed up my first attempts, I thought I would just have fun and figure out how to do it the way I had originally imagined it working. – Blunt Jackson Jun 16 '19 at 20:36
  • it is still plain wrong.(except for the no-echo thing, which was not part of the original question) Did you test it, using "passwordXXXX" as input? – wildplasser Jun 16 '19 at 21:29
  • Yes, wildplasser... I did test it this time and while I admit to the asterix elaboration, it works as intended for me. At this point I will defer to your better experience, so if you would like to point out my error this time, I will happily receive it. – Blunt Jackson Jun 16 '19 at 21:53
  • your reading stops at 8 characters+NUL. so: `passwordX` will be read as `password`, and pass (falsely) when comparing to `password` – wildplasser Jun 16 '19 at 22:10
  • No: you can't type that X, because the allowed input stops after the 8th character and flow proceeds. This may be a stupid way to accept input, but it *was* my reading of the intention of the original question. Maybe my interpretation of the question was wrong. – Blunt Jackson Jun 16 '19 at 22:20
  • Yes, you can type that X, **but it wont be compared**. Leading to exactly the same behaviour as in the OQ. – wildplasser Jun 17 '19 at 00:13
  • That’s Iike saying you can keep typing in a window after it loses focus. You can hit the keyboard all you want... – Blunt Jackson Jun 17 '19 at 00:20
  • There is no concept of focus in C .Please go back to JavaScript class. – wildplasser Jun 18 '19 at 23:00
0
  • fgets()reads the (CR+)LF too, and stores it into the buffer.

  • But only if there is sufficient place!

  • Otherwise, your buffer will contain the first n-1 characters, plus a NUL character.

    So: allocate a large-enough buffer, and strip the CR/LF:


#include <stdio.h>
#include <string.h>

int main(void)
{
    int pass = 0;
    char password[] = "password";
    char buffer[3+ sizeof password];

    printf("\n Enter your password : \n");
    fgets(buffer, sizeof buffer, stdin);
    buffer[strcspn(buffer,"\r\n")]=0;

    if(strcmp(buffer, password))
    {
        printf ("\n Incorrect Password \n");
    }
   
    else
    {
        printf ("\n Correct Password \n");
        pass = 1;
    }

    if(pass)
    {
        printf ("\n Root privileges authorized \n");
    }

    return 0;
}
Community
  • 1
  • 1
wildplasser
  • 43,142
  • 8
  • 66
  • 109
  • Thank you for your help. Using the 'sizeof' option will alleviate having a set number in the buffer in case passwords are longer. I will be sure to use this in the future. Thanks again. – TEX Jun 16 '19 at 14:58
  • This implies you're running on Windows. Better to use just `\n` and let the compiler adjust for the operating system. – lost_in_the_source Jun 16 '19 at 15:04
  • 1
    No.If the`\r` is not encountered, it won't be removed. Also: input *could* come from a file or terminal. (This is the *oblivious* way) – wildplasser Jun 16 '19 at 15:11
  • 1
    +1 this is exactly the problem. Ensuring that the additional characters typed do not get truncated and return a false positive by adding to the buffer space is a good idea. – Neil Jun 16 '19 at 20:07
  • Of course, this one only works if you know the password size in advance... – Blunt Jackson Jun 16 '19 at 20:12