You are not logged in.
Pages: 1
Currently I'm working on a script that will run in the initramfs environment where I want to ask the user to input a password, obviously I don't want what the user types to be echoed (I don't want * to be in place of what the user types) and I also want the only way to get out of the prompt to be to press enter, that is, I don't want control-c or similar to work while I'm asking for the password.
I have something that works (see below) but I believe it is considered obsolete and it will most probably break in the future (getpass is not even documented anymore in 'man unistd.h').
#include <stdio.h>
#include <unistd.h>
int main(){
char *passwd;
passwd = getpass("");
printf("%s",passwd);
}
I have been searching for a replacement and some opinions say to use curses (ncurses?), others show c code but even with my limited programming knowledge I can see the implementation can suffer from buffer overflows and there might be some other gotchas I am not aware of.
I am looking for suggestions of how to "modernise" my small program while keeping to the requirements, preferably something not too complicated that I can maintain in the future.
R00KIE
Tm90aGluZyB0byBzZWUgaGVyZSwgbW92ZSBhbG9uZy4K
Offline
This function is obsolete. Do not use it. If you want to read input without terminal echoing enabled, see the description of the ECHO flag in termios(3).
I didn't do too much reading but I think you need to use tcsetattr with one of the ECHO flags
Offline
Just ignore the signals and turn of echoing, then handle the input however you want:
#include <stdio.h>
#include <signal.h>
#include <termios.h>
int main() {
/* ignore signals */
signal(SIGINT, SIG_IGN);
signal(SIGTERM, SIG_IGN);
/* no echo */
struct termios term;
tcgetattr(1, &term);
term.c_lflag &= ~ECHO;
tcsetattr(1, TCSANOW, &term);
/* do whatever handling you want here.
* fgetc, appending c to a char array and
* realloc'ing as necessary may be safest. */
int c;
while ((c=fgetc(stdin)) != '\n');
/* reset the term */
term.c_lflag |= ECHO;
tcsetattr(1, TCSANOW, &term);
return 0;
}
Or for a full replacement function:
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <termios.h>
#define BASE_LENGTH 256
char *get_pass() {
static char *buf = NULL;
signal(SIGINT, SIG_IGN);
signal(SIGTERM, SIG_IGN);
struct termios term;
tcgetattr(1, &term);
term.c_lflag &= ~ECHO;
tcsetattr(1, TCSANOW, &term);
int c, len = BASE_LENGTH, pos = 0;
buf = realloc(buf, len);
buf[0] = '\0';
while ((c=fgetc(stdin)) != '\n') {
buf[pos++] = (char) c;
if (pos >= len)
buf = realloc(buf, (len += BASE_LENGTH));
}
buf[pos] = '\0';
term.c_lflag |= ECHO;
tcsetattr(1, TCSANOW, &term);
return buf;
}
This returns the string entered (not tested).
Last edited by Trilby (2015-10-27 03:01:34)
"UNIX is simple and coherent" - Dennis Ritchie; "GNU's Not Unix" - Richard Stallman
Offline
Thanks for the input. I'll give a try to your full replacement version and see if I can come up with with something very close to getpass().
Some of the modifications I might do is use sigaction instead of signal (I read sigaction may be better) and catch more signals (try to catch all except SIGKILL and SIGSTOP or at least control-c, control-d, control-z, control-s).
I guess I should somehow avoid the buffer getting cleared when pressing control-c. Your version might need only a few minor tweaks to get the job done (raw terminal input?).
Yesterday after a good dose of googling I had come up with this (not complete, just proof of concept, same problem with control-c):
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
int main(){
struct sigaction action;
memset(&action, 0, sizeof(struct sigaction));
action.sa_handler = SIG_IGN;
sigaction(SIGINT, &action, NULL);
char *line = NULL;
size_t len = 0;
size_t read;
read = getline(&line, &len, stdin);
printf("Text input: %.*s\nChars read: %lu\n",(int)read-1,line,read);
free(line);
return 0;
}
R00KIE
Tm90aGluZyB0byBzZWUgaGVyZSwgbW92ZSBhbG9uZy4K
Offline
I think I have managed to do what I wanted. Here's the code:
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
int main(){
struct termios termold, termnew;
tcgetattr(0, &termold);
termnew = termold;
termnew.c_lflag &= ~ECHO;
termnew.c_cc[VINTR] = '\0';
termnew.c_cc[VEOF] = '\0';
termnew.c_cc[VKILL] = '\0';
termnew.c_cc[VLNEXT] = '\0';
termnew.c_cc[VQUIT] = '\0';
termnew.c_cc[VSTART] = '\0';
termnew.c_cc[VSTOP] = '\0';
termnew.c_cc[VSUSP] = '\0';
termnew.c_cc[VWERASE] = '\0';
tcsetattr(0, TCSANOW, &termnew);
char *line = NULL;
size_t len = 0;
size_t read;
read = getline(&line, &len, stdin);
tcsetattr(0, TCSANOW, &termold);
printf("%.*s",(int)read-1,line);
free(line);
return 0;
}
What does the magic of catching control-c and friends as characters is setting the appropriate variables in termnew.c_cc. For tcsetattr it seems using stdin (0) or stdout (1) doesn't matter, it works exactly the same way (maybe I should replace the number by fileno(stdin) to be more explicit - for future maintenance). Will mark this as solved if no one comes forward with remarks or suggestions for improvements.
R00KIE
Tm90aGluZyB0byBzZWUgaGVyZSwgbW92ZSBhbG9uZy4K
Offline
Pages: 1