readpass.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. #include <signal.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <termios.h>
  5. #include <unistd.h>
  6. #include "insecure_memzero.h"
  7. #include "warnp.h"
  8. #include "readpass.h"
  9. #define MAXPASSLEN 2048
  10. /* Signals we need to block. */
  11. static const int badsigs[] = {
  12. SIGALRM, SIGHUP, SIGINT,
  13. SIGPIPE, SIGQUIT, SIGTERM,
  14. SIGTSTP, SIGTTIN, SIGTTOU
  15. };
  16. #define NSIGS sizeof(badsigs)/sizeof(badsigs[0])
  17. /* Highest signal number we care about. */
  18. #define MAX2(a, b) ((a) > (b) ? (a) : (b))
  19. #define MAX4(a, b, c, d) MAX2(MAX2(a, b), MAX2(c, d))
  20. #define MAX8(a, b, c, d, e, f, g, h) MAX2(MAX4(a, b, c, d), MAX4(e, f, g, h))
  21. #define MAXBADSIG MAX2(SIGALRM, MAX8(SIGHUP, SIGINT, SIGPIPE, SIGQUIT, \
  22. SIGTERM, SIGTSTP, SIGTTIN, SIGTTOU))
  23. /* Has a signal of this type been received? */
  24. static volatile sig_atomic_t gotsig[MAXBADSIG + 1];
  25. /* Signal handler. */
  26. static void
  27. handle(int sig)
  28. {
  29. gotsig[sig] = 1;
  30. }
  31. /* Restore old signals and re-issue intercepted signals. */
  32. static void
  33. resetsigs(struct sigaction savedsa[NSIGS])
  34. {
  35. size_t i;
  36. /* Restore old signals. */
  37. for (i = 0; i < NSIGS; i++)
  38. sigaction(badsigs[i], &savedsa[i], NULL);
  39. /* If we intercepted a signal, re-issue it. */
  40. for (i = 0; i < NSIGS; i++) {
  41. if (gotsig[badsigs[i]])
  42. raise(badsigs[i]);
  43. }
  44. }
  45. /**
  46. * readpass(passwd, prompt, confirmprompt, devtty):
  47. * If ${devtty} is 0, read a password from stdin. If ${devtty} is 1, read a
  48. * password from /dev/tty if possible; if not, read from stdin. If ${devtty}
  49. * is 2, read a password from /dev/tty if possible; if not, exit with an error.
  50. * If reading from a tty (either /dev/tty or stdin), disable echo and prompt
  51. * the user by printing ${prompt} to stderr. If ${confirmprompt} is non-NULL,
  52. * read a second password (prompting if a terminal is being used) and repeat
  53. * until the user enters the same password twice. Return the password as a
  54. * malloced NUL-terminated string via ${passwd}.
  55. */
  56. int
  57. readpass(char ** passwd, const char * prompt,
  58. const char * confirmprompt, int devtty)
  59. {
  60. FILE * readfrom;
  61. char passbuf[MAXPASSLEN];
  62. char confpassbuf[MAXPASSLEN];
  63. struct sigaction sa, savedsa[NSIGS];
  64. struct termios term, term_old;
  65. size_t i;
  66. int usingtty;
  67. /* Where should we read the password from? */
  68. switch (devtty) {
  69. case 0:
  70. /* Read directly from stdin. */
  71. readfrom = stdin;
  72. break;
  73. case 1:
  74. /* Try to open /dev/tty; if that fails, read from stdin. */
  75. if ((readfrom = fopen("/dev/tty", "r")) == NULL)
  76. readfrom = stdin;
  77. break;
  78. case 2:
  79. /* Try to open /dev/tty; if that fails, bail. */
  80. if ((readfrom = fopen("/dev/tty", "r")) == NULL) {
  81. warnp("fopen(/dev/tty)");
  82. goto err1;
  83. }
  84. break;
  85. default:
  86. warn0("readpass does not support devtty=%d", devtty);
  87. goto err1;
  88. }
  89. /* We have not received any signals yet. */
  90. for (i = 0; i <= MAXBADSIG; i++)
  91. gotsig[i] = 0;
  92. /*
  93. * If we receive a signal while we're reading the password, we might
  94. * end up with echo disabled; to prevent this, we catch the signals
  95. * here, and we'll re-send them to ourselves later after we re-enable
  96. * terminal echo.
  97. */
  98. sa.sa_handler = handle;
  99. sa.sa_flags = 0;
  100. sigemptyset(&sa.sa_mask);
  101. for (i = 0; i < NSIGS; i++)
  102. sigaction(badsigs[i], &sa, &savedsa[i]);
  103. /* If we're reading from a terminal, try to disable echo. */
  104. if ((usingtty = isatty(fileno(readfrom))) != 0) {
  105. if (tcgetattr(fileno(readfrom), &term_old)) {
  106. warnp("Cannot read terminal settings");
  107. goto err2;
  108. }
  109. memcpy(&term, &term_old, sizeof(struct termios));
  110. term.c_lflag = (term.c_lflag & ~((tcflag_t)ECHO)) | ECHONL;
  111. if (tcsetattr(fileno(readfrom), TCSANOW, &term)) {
  112. warnp("Cannot set terminal settings");
  113. goto err2;
  114. }
  115. }
  116. retry:
  117. /* If we have a terminal, prompt the user to enter the password. */
  118. if (usingtty)
  119. fprintf(stderr, "%s: ", prompt);
  120. /* Read the password. */
  121. if (fgets(passbuf, MAXPASSLEN, readfrom) == NULL) {
  122. if (feof(readfrom))
  123. warn0("EOF reading password");
  124. else
  125. warnp("Cannot read password");
  126. goto err3;
  127. }
  128. /* Confirm the password if necessary. */
  129. if (confirmprompt != NULL) {
  130. if (usingtty)
  131. fprintf(stderr, "%s: ", confirmprompt);
  132. if (fgets(confpassbuf, MAXPASSLEN, readfrom) == NULL) {
  133. if (feof(readfrom))
  134. warn0("EOF reading password");
  135. else
  136. warnp("Cannot read password");
  137. goto err3;
  138. }
  139. if (strcmp(passbuf, confpassbuf)) {
  140. fprintf(stderr,
  141. "Passwords mismatch, please try again\n");
  142. goto retry;
  143. }
  144. }
  145. /* Terminate the string at the first "\r" or "\n" (if any). */
  146. passbuf[strcspn(passbuf, "\r\n")] = '\0';
  147. /* If we changed terminal settings, reset them. */
  148. if (usingtty)
  149. tcsetattr(fileno(readfrom), TCSANOW, &term_old);
  150. /* Restore old signals and re-issue intercepted signals. */
  151. resetsigs(savedsa);
  152. /* Close /dev/tty if we opened it. */
  153. if ((readfrom != stdin) && fclose(readfrom))
  154. warnp("fclose");
  155. /* Copy the password out. */
  156. if ((*passwd = strdup(passbuf)) == NULL) {
  157. warnp("Cannot allocate memory");
  158. goto err1;
  159. }
  160. /*
  161. * Zero any stored passwords. This is not guaranteed to work, since a
  162. * "sufficiently intelligent" compiler can optimize these out due to
  163. * the values not being accessed again; and even if we outwitted the
  164. * compiler, all we can do is ensure that *a* buffer is zeroed but
  165. * not that it is the only buffer containing the data in question.
  166. * Unfortunately the C standard does not provide any way to mark data
  167. * as "sensitive" in order to prevent extra copies being sprinkled
  168. * around the implementation address space.
  169. */
  170. insecure_memzero(passbuf, MAXPASSLEN);
  171. insecure_memzero(confpassbuf, MAXPASSLEN);
  172. /* Success! */
  173. return (0);
  174. err3:
  175. /* Reset terminal settings if necessary. */
  176. if (usingtty)
  177. tcsetattr(fileno(readfrom), TCSAFLUSH, &term_old);
  178. err2:
  179. /* Close /dev/tty if we opened it. */
  180. if ((readfrom != stdin) && fclose(readfrom))
  181. warnp("fclose");
  182. /* Restore old signals and re-issue intercepted signals. */
  183. resetsigs(savedsa);
  184. err1:
  185. /* Zero any stored passwords. */
  186. insecure_memzero(passbuf, MAXPASSLEN);
  187. insecure_memzero(confpassbuf, MAXPASSLEN);
  188. /* Failure! */
  189. return (-1);
  190. }