18.10. Canonical ModeCanonical mode is simple: we issue a read, and the terminal driver returns when a line has been entered. Several conditions cause the read to return.
Examplegetpass FunctionWe now show the function getpass, which reads a password of some type from the user at a terminal. This function is called by the login(1) and crypt(1) programs. To read the password, the function must turn off echoing, but it can leave the terminal in canonical mode, as whatever we type as the password forms a complete line. Figure 18.17 shows a typical implementation on a UNIX system. There are several points to consider in this example.
The program in Figure 18.18 calls getpass and prints what we enter to let us verify that the ERASE and KILL characters work (as they should in canonical mode). Whenever a program that calls getpass is done with the cleartext password, the program should zero it out in memory, just to be safe. If the program were to generate a core file that others might be able to read or if some other process were somehow able to read our memory, they might be able to read the cleartext password. (By "cleartext," we mean the password that we type at the prompt that is printed by getpass. Most UNIX system programs then modify this cleartext password into an "encrypted" password. The field pw_passwd in the password file, for example, contains the encrypted password, not the cleartext password.) Figure 18.17. Implementation of getpass function
#include <signal.h>
#include <stdio.h>
#include <termios.h>
#define MAX_PASS_LEN 8 /* max #chars for user to enter */
char *
getpass(const char *prompt)
{
static char buf[MAX_PASS_LEN + 1]; /* null byte at end */
char *ptr;
sigset_t sig, osig;
struct termios ts, ots;
FILE *fp;
int c;
if ((fp = fopen(ctermid(NULL), "r+")) == NULL)
return(NULL);
setbuf(fp, NULL);
sigemptyset(&sig);
sigaddset(&sig, SIGINT); /* block SIGINT */
sigaddset(&sig, SIGTSTP); /* block SIGTSTP */
sigprocmask(SIG_BLOCK, &sig, &osig); /* and save mask */
tcgetattr(fileno(fp), &ts); /* save tty state */
ots = ts; /* structure copy */
ts.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
tcsetattr(fileno(fp), TCSAFLUSH, &ts);
fputs(prompt, fp);
ptr = buf;
while ((c = getc(fp)) != EOF && c != '\n')
if (ptr < &buf[MAX_PASS_LEN])
*ptr++ = c;
*ptr = 0; /* null terminate */
putc('\n', fp); /* we echo a newline */
tcsetattr(fileno(fp), TCSAFLUSH, &ots); /* restore TTY state */
sigprocmask(SIG_SETMASK, &osig, NULL); /* restore mask */
fclose(fp); /* done with /dev/tty */
return(buf);
}Figure 18.18. Call the getpass function
#include "apue.h"
char *getpass(const char *);
int
main(void)
{
char *ptr;
if ((ptr = getpass("Enter password:")) == NULL)
err_sys("getpass error");
printf("password: %s\n", ptr);
/* now use password (probably encrypt it) ... */
while (*ptr != 0)
*ptr++ = 0; /* zero it out when we're done with it */
exit(0);
}
|