C function (fgets) mitigation - c

I can't understand why taking the input using fgets always gives me "Wrong password" for my program.
However, when I use gets(), like gets(array); it works.
Expected outputs: when the password is wrong, prints "Wrong Passwor" and for correct one, let me see my "access is granted":
#include <stdio.h>
#include <string.h>
int main(void)
{
int n=15;
char array[n];
int pass = 0;
printf("\n Enter the password : \n");
fgets(array, n, stdin);
if(strncmp(array, "password",n))
{
printf ("\n Wrong Password \n");
}
else
{
printf ("\n Correct Password \n");
pass = 1;
}
if(pass)
{
/* Now Give root or admin rights to user*/
printf ("\n Root privileges given to the user \n");
}
return 0;
}

The point here is, fgets() reads and stores the trailing newline, check the man page for fgets().
fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the buffer. [...]
You need to remove that trailing newline before the comparison.
You can sanitize the input, using
array[strcspn(array, "\n")] = 0;
to remove the trailing newline from the input.

Related

Compiler ignoring subsequent commands

I have the following C code:
#include <stdio.h>
int main()
{
char* ptr;
printf("Enter the word: ");
gets(ptr);
printf("The input string is: ");
puts(ptr);
return 0;
}
It compiles and asks for the input, but after I enter the input, it takes a pause and exits. No further commands are executed or displayed. I am unable to understand the problem. Please help.
#include <stdio.h>
int main()
{
char str[100] = "";
printf("Enter the word: ");
fgets(str, sizeof str, stdin);
printf("The input string is: ");
printf("%s", str);
return 0;
}
Try to use fgets(). gets() is dangerous to use because gets() is inherently unsafe, because it copies all input from STDIN to the buffer without checking size. This allows the user to provide a string that is larger than the buffer size, resulting in an overflow condition.
puts is simpler than printf but be aware that the former automatically appends a newline. If that's not what you want, you can fputsyour string to stdout or use printf.

Issues with scanf() and accepting user input

I am trying to take in user input with spaces and store it in an array of characters.
After, I want to take in a single character value and store it as a char.
However, when I run my code, the prompt for the character gets ignored and a space is populated instead. How can I take in an array of chars and still be allowed to prompt for a single character after?
void main()
{
char userIn[30];
char findChar;
printf("Please enter a string: ");
scanf("%[^\n]s", userIn);
printf("Please enter a character to search for: ");
scanf("%c", &findChar);
//this was put here to see why my single char wasnt working in a function I had
printf("%c", findChar);
}
scanf("%c", &findChar); reads the next character pending in the input stream. This character will be the newline entered by the user that stopped the previous conversion, so findChar will be set to the value '\n', without waiting for any user input and printf will output this newline without any other visible effect.
Modify the call as scanf(" %c", &findChar) to ignore pending white space and get the next character from the user, or more reliably write a loop to read the read and ignore of the input line.
Note also that scanf("%[^\n]s", userIn); is incorrect:
scanf() may store bytes beyond the end of userIn if the user types more than 29 bytes of input.
the s after the ] is a bug, the conversion format for character classes is not a variation of the %s conversion.
Other problems:
void is not a proper type for the return value of the main() function.
the <stdio.h> header is required for this code.
Here is a modified version:
#include <stdio.h>
int main() {
char userIn[30];
int c;
char findChar;
int i, found;
printf("Please enter a string: ");
if (scanf("%29[^\n]", userIn) != 1) {
fprintf(stderr, "Input failure\n");
return 1;
}
/* read and ignore the rest of input line */
while ((c = getchar()) != EOF && c != '\n')
continue;
printf("Please enter a character to search for: ");
if (scanf("%c", &findChar) != 1) {
fprintf(stderr, "Input failure\n");
return 1;
}
printf("Searching for '%c'\n", findChar);
found = 0;
for (i = 0; userIn[i] != '\0'; i++) {
if (userIn[i] == findChar) {
found++;
printf("found '%c' at offset %d\n", c, i);
}
}
if (!found) {
printf("character '%c' not found\n", c);
}
return 0;
}
scanf("%[^\n]s", userIn); is a bit weird. The s is guaranteed not to match, since that character will always be \n. Also, you should use a width modifier to avoid a buffer overflow. Use scanf("%29[^\n]", userIn); That alone will not solve the problem, since the next scanf is going to consume the newline. There are a few options. You could consume the newline in the first scanf with:
scanf("%29[^\n]%*c", userIn);
or discard all whitespace in the next call with
scanf(" %c", &findChar);
The behavior will differ on lines of input that exceed 29 characters in length or when the user attempts to assign whitespace to findChar, so which solution you use will depend on how you want to handle those situations.

How do I properly utilize fgets() and strncmp() functions?

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;
}
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;
}
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;
}

Reversing String in c using loops....

I have created a code reverse a string but for some reason it is not working. But I think my logic is right. Then why is it not working??
#include <stdio.h>
#include <stdlib.h>
int main() {
char words[100];
int i=0;
printf("Enter a word/sentence: ");
scanf("%s", words);
while (words[i]!='\0') {
++i;
}
printf("\nThe Reverse is: ");
while (i<=0) {
printf("%s",words[i]);
i--;
}
return 0;
}
While you already have an answer, there are a few additional points you need to consider before you have a solution that doesn't have the potential to invoke Undefined behavior.
First, always, always validate all user input. For all you know a cat could have gone to sleep on the 'L' key (with millions being entered), or a more likely case, the user just decides to type a 100-char sentence (or more) which leaves 'words' as an array of chars that is NOT nul-terminated and thus not a valid string in C. Your loop to get the length now invokes Undefined Behavior by reading beyond the end of words off into the stack until the first random '0' is encounter or a SegFault occurs.
To prevent this behavior (you should really just use fgets) but with scanf you can provide a field-width modifier to prevent reading more than length - 1 chars. This insures space for the nul-terminating character.
Further, the "%s" conversion-specifier stops conversion on the first whitespace character encountered -- making your "Enter a .../sentence" an impossibility because scanf ("%s", words) will stop reading after the first word (at the first whitespace.
To correct this problem (you should really just use fgets) or with scanf you can use a character class (stuff between [...]) as the conversion specifier that will read until a '\n' is encountered., e.g. scanf ("%[^\n]", words). However, recall, that is still not good enough because more than 99-chars can be entered leaving the string un-terminated at 100 and invoking Undefined Behavior at character 101 (off the end of the array).
To prevent this problem (ditto on fgets), or include the field-width modifier, e.g. scanf ("%99[^\n]", words). Now no more than 99-chars will be read regardless of the cat sleeping on the 'L' key.
Putting that altogether, you could do something like:
#include <stdio.h>
#define MAXC 100 /* if you need a constant, define one */
int main(void) {
char words[MAXC] = "";
int i = 0, rtn = 0; /* rtn - capture the return of scanf */
printf ("Enter a word/sentence : ");
if ((rtn = scanf ("%99[^\n]", words)) != 1) { /* validate ! */
if (rtn == EOF) /* user cancel? [ctrl+d] or [ctrl+z]? */
fprintf (stderr, "user input canceled.\n");
else /* did an input failure occur ? */
fprintf (stderr, "error: invalid input - input failure.\n");
return 1; /* either way, bail */
}
for (; words[i]; i++) {} /* get the length */
printf ("Reversed word/sentence: ");
while (i--)
putchar (words[i]); /* no need for printf to output 1-char */
putchar ('\n');
return 0;
}
Example Use/Output
$ ./bin/strrevloops
Enter a word/sentence : My dog has fleas.
Reversed word/sentence: .saelf sah god yM
Look things over and let me know if you have any further questions.
There are few mistakes in your program.
After you have reached the end of the string.You should do i-- as your array index of i will be pointing to '\0'.
Your while loop checks for <= but it should be >=.
Use %c for printing chararcters. %s is used to print strings and not char.
#include <stdio.h>
#include <stdlib.h>
int main() {
char words[100];
int i=0;
printf("Enter a word/sentence: ");
scanf("%s", words);
while (words[i]!='\0') {
++i;
}
i--;
printf("\nThe Reverse is: ");
while (i>=0) {
printf("%c",words[i]);
i--;
}
return 0;
}

How to read a integer followed by a string in C? [duplicate]

This question already has an answer here:
How to read / parse input in C? The FAQ
(1 answer)
Closed 6 years ago.
I am trying to write a simple program which will read two input lines, an integer followed by a string. However, it doesn't seem to work for me.
int main()
{
int i;
char str[1024];
scanf("%d", &i);
scanf("%[^\n]", str);
printf("%d\n", i);
printf("%s\n", str);
return 0;
}
Immediately after entering the integer and pressing "Enter", the program prints the integer. It doesn't wait for me to enter the string. Whats wrong? Whats the correct way to program this?
What you need to know
The problem with %[^\n] is that it fails when the first character to be read is the newline character, and pushes it back into the stdin.
The Problem
After you enter a number for the first scanf, you press Enter. %d in the first scanf consumes the number, leaving the newline character ('\n'), generated by the Enter keypress, in the standard input stream (stdin). %[^\n] in the next scanf sees this \n and fails for the reason given in the first paragraph of this answer.
Fixes
Solutions include:
Changing scanf("%d", &i); to scanf("%d%*c", &i);. What %*c does is, it scans and discards a character.
I wouldn't recommend this way because an evil user could trick the scanf by inputting something like <number><a character>\n, ex: 2j\n and you'll face the same problem again.
Adding a space (any whitespace character will do) before %[^\n], i.e, changing scanf("%[^\n]", str); to scanf(" %[^\n]", str); as #Bathsheba mentioned in a comment.
What the whitespace character does is, it scans and discards any number of whitespace characters, including none, until the first non-whitespace character.
This means that any leading whitespace characters will be skipped when inputting for the second scanf.
This is my recommendation: Clear the stdin after every scanf. Create a function:
void flushstdin(void)
{
int c;
while((c = getchar()) != '\n' && c != EOF);
}
and call it after every scanf using flushstdin();.
Other issues:
Issues unrelated to your problem include:
You don't deal with the case if scanf fails. This can be due to a variety of reasons, say, malformed input, like inputting an alphabet for %d.
To do this, check the return value of scanf. It returns the number of items successfully scanned and assigned or -1 if EOF was encountered.
You don't check for buffer overflows. You need to prevent scanning in more than 1023 characters (+1 for the NUL-terminator) into str.
This can be acheived by using a length specifier in scanf.
The standards require main to be declared using either int main(void) or int main(int argc, char* argv[]), not int main().
You forgot to include stdio.h (for printf and scanf)
Fixed, Complete Program
#include <stdio.h>
void flushstdin(void)
{
int c;
while((c = getchar()) != '\n' && c != EOF);
}
int main(void)
{
int i;
char str[1024];
int retVal;
while((retVal = scanf("%d", &i)) != 1)
{
if(retVal == 0)
{
fputs("Invalid input; Try again", stderr);
flushstdin();
}
else
{
fputs("EOF detected; Bailing out!", stderr);
return -1;
}
}
flushstdin();
while((retVal = scanf("%1023[^\n]", str)) != 1)
{
if(retVal == 0)
{
fputs("Empty input; Try again", stderr);
flushstdin();
}
else
{
fputs("EOF detected; Bailing out!", stderr);
return -1;
}
}
flushstdin();
printf("%d\n", i);
printf("%s\n", str);
return 0;
}
This simply, will work:
scanf("%d %[^\n]s", &i, str);
Instaed of scanf() use fgets() followed by sscanf().
Check return values of almost all functions with a prototype in <stdio.h>.
#include <stdio.h>
int main(void) {
int i;
char test[1024]; // I try to avoid identifiers starting with "str"
char tmp[10000]; // input buffer
// first line
if (fgets(tmp, sizeof tmp, stdin)) {
if (sscanf(tmp, "%d", &i) != 1) {
/* conversion error */;
}
} else {
/* input error */;
}
// second line: read directly into test
if (fgets(test, sizeof test, stdin)) {
size_t len = strlen(test);
if (test[len - 1] == '\n') test[--len] = 0; // remove trailing ENTER
// use i and test
printf("i is %d\n", i);
printf("test is \"%s\" (len: %d)\n", test, (int)len);
} else {
/* input error */;
}
return 0;
}

Resources