パスワードの入力

C 言語でパスワードのような標準入力から入力された文字を表示しないプログラムのサンプルを作成してみました。ioctl() で、termio 構造体の c_lflag(ローカル・モード・フラグ)を変更することによって、容易に端末の ECHO 属性を変更できるようです。ioctl$B$r;H$C$?%W%m%0%i%`(Bで丁寧に解説されているので非常に分かり易いです。

#include <stdio.h>                                      
#include <stdlib.h>                                     
#include <sys/ioctl.h>                                  
#include <asm/termbits.h>                               

int main(void) {

    char buff[BUFSIZ];
    struct termio tty, tty_save;

    ioctl(0, TCGETA, &tty);
    tty_save = tty;        
                           
    /* invert ECHO bit */
    tty.c_lflag &= ~ECHO;
    /* enable ECHO New Line */
    tty.c_lflag |= ECHONL;

    /* set terminal attribute */
    ioctl(0, TCSETAF, &tty);

    printf("passwd: ");
    fgets(buff, BUFSIZ, stdin);
    printf("passwd is %s", buff);

    /* back to normal */
    ioctl(0, TCSETAF, &tty_save);

    return EXIT_SUCCESS;
}

実行結果。

$ ./a.out
passwd:
passwd is secret

python だと、getpass ライブラリを用いると簡単に作成できます。

>>> from getpass import *
>>> passwd = getpass("passwd: ")
passwd:
>>> passwd
'secret'

せっかくなので python のライブラリ実装を覗いてみます。C 言語のそれと同様に端末の属性をコピーした後に termios のローカルフラグから ECHO 属性を反転させていることが分かります。C 言語の実装と併せてみると、ライブラリの便利さを実感できますねb(^ ^)

def unix_getpass(prompt='Password: ', stream=None):
    """Prompt for a password, with echo turned off.
    The prompt is written on stream, by default stdout.

    Restore terminal settings at end.
    """
    if stream is None:
        stream = sys.stdout

    try:
        fd = sys.stdin.fileno()
    except:
        return default_getpass(prompt)

    old = termios.tcgetattr(fd)     # a copy to save
    new = old[:]

    new[3] = new[3] & ~termios.ECHO # 3 == 'lflags'
    try:
        termios.tcsetattr(fd, termios.TCSADRAIN, new)
        passwd = _raw_input(prompt, stream)
    finally:
        termios.tcsetattr(fd, termios.TCSADRAIN, old)

    stream.write('\n')
    return passwd

リファレンス:
標準入力の基本 : 2