06
なし 時代遅れの 18
に依存し、回避する 、推奨されるアプローチは、25
を介してターミナル ECHO を無効にすることです。 使用する。定型の柔軟なパスワードルーチンを見つけるために数回検索した後、C でスタンドアロンで使用するルーチンがほとんどないことに驚きました。 termios 43
で オプション、もう少し一般化されたアプローチでは、いくつかの追加が必要です。 50
の置き換えを超えて ルーチンは、オーバーフローを防ぐために指定された最大長を強制し、ユーザーが最大長を超えて入力しようとした場合は切り捨て、何らかの方法で切り捨てが発生した場合は警告する必要があります。
以下、追加により任意の 65
からの読み取りが可能になります 入力ストリームの長さを指定された長さに制限し、入力を受け取るときに最小限の編集 (バックスペース) 機能を提供し、文字マスクを指定または完全に無効にできるようにし、最後に入力されたパスワードの長さを返します。入力されたパスワードが最大長または指定された長さに切り捨てられた場合の警告が追加されました。
同様の解決策を探しているこの質問を持つ他の人に役立つことを願っています:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#define MAXPW 32
/* read a string from fp into pw masking keypress with mask char.
getpasswd will read upto sz - 1 chars into pw, null-terminating
the resulting string. On success, the number of characters in
pw are returned, -1 otherwise.
*/
ssize_t getpasswd (char **pw, size_t sz, int mask, FILE *fp)
{
if (!pw || !sz || !fp) return -1; /* validate input */
#ifdef MAXPW
if (sz > MAXPW) sz = MAXPW;
#endif
if (*pw == NULL) { /* reallocate if no address */
void *tmp = realloc (*pw, sz * sizeof **pw);
if (!tmp)
return -1;
memset (tmp, 0, sz); /* initialize memory to 0 */
*pw = (char*) tmp;
}
size_t idx = 0; /* index, number of chars in read */
int c = 0;
struct termios old_kbd_mode; /* orig keyboard settings */
struct termios new_kbd_mode;
if (tcgetattr (0, &old_kbd_mode)) { /* save orig settings */
fprintf (stderr, "%s() error: tcgetattr failed.\n", __func__);
return -1;
} /* copy old to new */
memcpy (&new_kbd_mode, &old_kbd_mode, sizeof(struct termios));
new_kbd_mode.c_lflag &= ~(ICANON | ECHO); /* new kbd flags */
new_kbd_mode.c_cc[VTIME] = 0;
new_kbd_mode.c_cc[VMIN] = 1;
if (tcsetattr (0, TCSANOW, &new_kbd_mode)) {
fprintf (stderr, "%s() error: tcsetattr failed.\n", __func__);
return -1;
}
/* read chars from fp, mask if valid char specified */
while (((c = fgetc (fp)) != '\n' && c != EOF && idx < sz - 1) ||
(idx == sz - 1 && c == 127))
{
if (c != 127) {
if (31 < mask && mask < 127) /* valid ascii char */
fputc (mask, stdout);
(*pw)[idx++] = c;
}
else if (idx > 0) { /* handle backspace (del) */
if (31 < mask && mask < 127) {
fputc (0x8, stdout);
fputc (' ', stdout);
fputc (0x8, stdout);
}
(*pw)[--idx] = 0;
}
}
(*pw)[idx] = 0; /* null-terminate */
/* reset original keyboard */
if (tcsetattr (0, TCSANOW, &old_kbd_mode)) {
fprintf (stderr, "%s() error: tcsetattr failed.\n", __func__);
return -1;
}
if (idx == sz - 1 && c != '\n') /* warn if pw truncated */
fprintf (stderr, " (%s() warning: truncated at %zu chars.)\n",
__func__, sz - 1);
return idx; /* number of chars in passwd */
}
使い方を示す簡単なプログラムは次のようになります。パスワードを保持するために文字の静的配列を使用する場合は、ポインターが関数に渡されることを確認してください。
int main (void ) {
char pw[MAXPW] = {0};
char *p = pw;
FILE *fp = stdin;
ssize_t nchr = 0;
printf ( "\n Enter password: ");
nchr = getpasswd (&p, MAXPW, '*', fp);
printf ("\n you entered : %s (%zu chars)\n", p, nchr);
printf ( "\n Enter password: ");
nchr = getpasswd (&p, MAXPW, 0, fp);
printf ("\n you entered : %s (%zu chars)\n\n", p, nchr);
return 0;
}
出力例
$ ./bin/getpasswd2
Enter password: ******
you entered : 123456 (6 chars)
Enter password:
you entered : abcdef (6 chars)
Linux の世界では、通常、マスキングはアスタリスクで行われません。通常、エコーはオフにされ、端末には空白が表示されます。 79
を使用する場合 または仮想端末などにログインします。
パスワードの取得を処理するライブラリ関数があり、パスワードをアスタリスクでマスクしませんが、端末へのパスワードのエコーを無効にします。私が持っているLinuxの本からこれを引き出しました。 posix標準の一部だと思います
#include <unistd.h> char *getpass(const char *prompt); /*Returns pointer to statically allocated input password string on success, or NULL on error*/
getpass() 関数は、最初にエコーと端末特殊文字 (割り込み文字、通常は Control-C) のすべての処理を無効にします。
次に、prompt が指す文字列を出力し、入力行を読み取り、関数の結果として末尾の改行を取り除いたヌル終了入力文字列を返します。
getpass() の Google 検索には、GNU 実装への参照 (ほとんどの Linux ディストリビューションにあるはずです) と、必要に応じて独自に実装するためのいくつかのサンプル コードがあります
http://www.gnu.org/s/hello/manual/libc/getpass.html
独自のロールの例:
#include <termios.h>
#include <stdio.h>
ssize_t
my_getpass (char **lineptr, size_t *n, FILE *stream)
{
struct termios old, new;
int nread;
/* Turn echoing off and fail if we can't. */
if (tcgetattr (fileno (stream), &old) != 0)
return -1;
new = old;
new.c_lflag &= ~ECHO;
if (tcsetattr (fileno (stream), TCSAFLUSH, &new) != 0)
return -1;
/* Read the password. */
nread = getline (lineptr, n, stream);
/* Restore terminal. */
(void) tcsetattr (fileno (stream), TCSAFLUSH, &old);
return nread;
}
必要に応じて、これをベースとして使用し、アスタリスクを表示するように変更できます。