You are not logged in.

#1 2015-10-26 17:33:57

R00KIE
Forum Fellow
From: Between a computer and a chair
Registered: 2008-09-14
Posts: 4,734

Replacement for getpass()

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

#2 2015-10-27 02:00:38

crondog
Member
Registered: 2011-04-21
Posts: 130

Re: Replacement for getpass()

getpass(3) - Linux man page wrote:

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

#3 2015-10-27 02:50:02

Trilby
Inspector Parrot
Registered: 2011-11-29
Posts: 29,522
Website

Re: Replacement for getpass()

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

#4 2015-10-27 08:47:26

R00KIE
Forum Fellow
From: Between a computer and a chair
Registered: 2008-09-14
Posts: 4,734

Re: Replacement for getpass()

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

#5 2015-10-27 18:27:42

R00KIE
Forum Fellow
From: Between a computer and a chair
Registered: 2008-09-14
Posts: 4,734

Re: Replacement for getpass()

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

Board footer

Powered by FluxBB