1 1.30 christos /* $NetBSD: getpass.c,v 1.30 2016/01/31 23:41:38 christos Exp $ */ 2 1.5 cgd 3 1.17 christos /*- 4 1.17 christos * Copyright (c) 2012 The NetBSD Foundation, Inc. 5 1.17 christos * All rights reserved. 6 1.17 christos * 7 1.17 christos * This code is derived from software contributed to The NetBSD Foundation 8 1.17 christos * by Christos Zoulas. 9 1.1 cgd * 10 1.1 cgd * Redistribution and use in source and binary forms, with or without 11 1.1 cgd * modification, are permitted provided that the following conditions 12 1.1 cgd * are met: 13 1.1 cgd * 1. Redistributions of source code must retain the above copyright 14 1.1 cgd * notice, this list of conditions and the following disclaimer. 15 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 cgd * notice, this list of conditions and the following disclaimer in the 17 1.1 cgd * documentation and/or other materials provided with the distribution. 18 1.1 cgd * 19 1.17 christos * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.17 christos * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.17 christos * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.17 christos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.17 christos * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.17 christos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.17 christos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.17 christos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.17 christos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.17 christos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.17 christos * POSSIBILITY OF SUCH DAMAGE. 30 1.1 cgd */ 31 1.10 christos #include <sys/cdefs.h> 32 1.1 cgd #if defined(LIBC_SCCS) && !defined(lint) 33 1.30 christos __RCSID("$NetBSD: getpass.c,v 1.30 2016/01/31 23:41:38 christos Exp $"); 34 1.1 cgd #endif /* LIBC_SCCS and not lint */ 35 1.1 cgd 36 1.11 jtc #include "namespace.h" 37 1.5 cgd 38 1.13 lukem #include <assert.h> 39 1.17 christos #ifdef TEST 40 1.17 christos #include <stdio.h> 41 1.17 christos #endif 42 1.17 christos #include <errno.h> 43 1.22 christos #include <ctype.h> 44 1.18 christos #include <signal.h> 45 1.17 christos #include <string.h> 46 1.5 cgd #include <paths.h> 47 1.17 christos #include <stdbool.h> 48 1.17 christos #include <stdlib.h> 49 1.13 lukem #include <termios.h> 50 1.1 cgd #include <unistd.h> 51 1.17 christos #include <fcntl.h> 52 1.23 christos #include <poll.h> 53 1.11 jtc 54 1.11 jtc #ifdef __weak_alias 55 1.19 christos __weak_alias(getpassfd,_getpassfd) 56 1.17 christos __weak_alias(getpass_r,_getpass_r) 57 1.14 mycroft __weak_alias(getpass,_getpass) 58 1.11 jtc #endif 59 1.1 cgd 60 1.17 christos /* 61 1.17 christos * Notes: 62 1.17 christos * - There is no getpass_r in POSIX 63 1.17 christos * - Historically EOF is documented to be treated as EOL, we provide a 64 1.19 christos * tunable for that GETPASS_FAIL_EOF to disable this. 65 1.17 christos * - Historically getpass ate extra characters silently, we provide 66 1.19 christos * a tunable for that GETPASS_BUF_LIMIT to disable this. 67 1.17 christos * - Historically getpass "worked" by echoing characters when turning 68 1.19 christos * off echo failed, we provide a tunable GETPASS_NEED_TTY to 69 1.17 christos * disable this. 70 1.17 christos * - Some implementations say that on interrupt the program shall 71 1.18 christos * receive an interrupt signal before the function returns. We 72 1.18 christos * send all the tty signals before we return, but we don't expect 73 1.18 christos * suspend to do something useful unless the caller calls us again. 74 1.19 christos * We also provide a tunable to disable signal delivery 75 1.19 christos * GETPASS_NO_SIGNAL. 76 1.19 christos * - GETPASS_NO_BEEP disables beeping. 77 1.20 christos * - GETPASS_ECHO_STAR will echo '*' for each character of the password 78 1.19 christos * - GETPASS_ECHO will echo the password (as pam likes it) 79 1.25 christos * - GETPASS_7BIT strips the 8th bit 80 1.25 christos * - GETPASS_FORCE_UPPER forces to uppercase 81 1.25 christos * - GETPASS_FORCE_LOWER forces to uppercase 82 1.25 christos * - GETPASS_ECHO_NL echo's a new line on success if echo was off. 83 1.17 christos */ 84 1.1 cgd char * 85 1.19 christos /*ARGSUSED*/ 86 1.25 christos getpassfd(const char *prompt, char *buf, size_t len, int *fd, int flags, 87 1.23 christos int tout) 88 1.1 cgd { 89 1.17 christos struct termios gt; 90 1.17 christos char c; 91 1.19 christos int sig; 92 1.25 christos bool lnext, havetty, allocated, opentty, good; 93 1.25 christos int fdc[3]; 94 1.1 cgd 95 1.13 lukem _DIAGASSERT(prompt != NULL); 96 1.13 lukem 97 1.25 christos if (buf != NULL && len == 0) { 98 1.25 christos errno = EINVAL; 99 1.25 christos return NULL; 100 1.25 christos } 101 1.25 christos 102 1.25 christos good = false; 103 1.25 christos opentty = false; 104 1.25 christos if (fd == NULL) { 105 1.25 christos /* 106 1.25 christos * Try to use /dev/tty if possible; otherwise read from stdin 107 1.25 christos * and write to stderr. 108 1.25 christos */ 109 1.25 christos fd = fdc; 110 1.29 christos if ((fd[0] = fd[1] = fd[2] = open(_PATH_TTY, 111 1.29 christos O_RDWR | O_CLOEXEC)) == -1) { 112 1.25 christos fd[0] = STDIN_FILENO; 113 1.25 christos fd[1] = fd[2] = STDERR_FILENO; 114 1.25 christos } else 115 1.25 christos opentty = true; 116 1.25 christos } 117 1.25 christos 118 1.18 christos sig = 0; 119 1.19 christos allocated = buf == NULL; 120 1.19 christos if (tcgetattr(fd[0], >) == -1) { 121 1.17 christos havetty = false; 122 1.19 christos if (flags & GETPASS_NEED_TTY) 123 1.19 christos goto out; 124 1.17 christos memset(>, -1, sizeof(gt)); 125 1.17 christos } else 126 1.17 christos havetty = true; 127 1.17 christos 128 1.17 christos if (havetty) { 129 1.17 christos struct termios st = gt; 130 1.17 christos 131 1.17 christos st.c_lflag &= ~(ECHO|ECHOK|ECHOE|ECHOKE|ECHOCTL|ISIG|ICANON); 132 1.17 christos st.c_cc[VMIN] = 1; 133 1.17 christos st.c_cc[VTIME] = 0; 134 1.19 christos if (tcsetattr(fd[0], TCSAFLUSH|TCSASOFT, &st) == -1) 135 1.17 christos goto out; 136 1.17 christos } 137 1.8 christos 138 1.17 christos if (prompt != NULL) { 139 1.17 christos size_t plen = strlen(prompt); 140 1.19 christos (void)write(fd[1], prompt, plen); 141 1.19 christos } 142 1.19 christos 143 1.19 christos if (allocated) { 144 1.19 christos len = 1024; 145 1.19 christos if ((buf = malloc(len)) == NULL) 146 1.19 christos goto restore; 147 1.16 christos } 148 1.17 christos 149 1.17 christos c = '\1'; 150 1.17 christos lnext = false; 151 1.17 christos for (size_t l = 0; c != '\0'; ) { 152 1.23 christos if (tout) { 153 1.23 christos struct pollfd pfd; 154 1.23 christos pfd.fd = fd[0]; 155 1.23 christos pfd.events = POLLIN|POLLRDNORM; 156 1.23 christos pfd.revents = 0; 157 1.23 christos switch (poll(&pfd, 1, tout * 1000)) { 158 1.23 christos case 0: 159 1.24 christos errno = ETIMEDOUT; 160 1.23 christos /*FALLTHROUGH*/ 161 1.23 christos case -1: 162 1.23 christos goto restore; 163 1.23 christos default: 164 1.23 christos break; 165 1.23 christos } 166 1.23 christos } 167 1.19 christos if (read(fd[0], &c, 1) != 1) 168 1.17 christos goto restore; 169 1.17 christos 170 1.23 christos #define beep() \ 171 1.23 christos do \ 172 1.23 christos if (flags & GETPASS_NO_BEEP) \ 173 1.23 christos (void)write(fd[2], "\a", 1); \ 174 1.19 christos while (/*CONSTCOND*/ 0) 175 1.19 christos #define erase() (void)write(fd[1], "\b \b", 3) 176 1.26 christos /* 177 1.26 christos * We test for both _POSIX_VDISABLE and NUL here because _POSIX_VDISABLE 178 1.26 christos * propagation does not seem to be very consistent on multiple daemon hops 179 1.26 christos * between different OS's. Perhaps we should not even bother with 180 1.26 christos * _POSIX_VDISABLE and use ~0 and 0 directly. 181 1.26 christos */ 182 1.26 christos #define C(a, b) ((gt.c_cc[(a)] == _POSIX_VDISABLE || gt.c_cc[(a)] == '\0') ? \ 183 1.26 christos (b) : gt.c_cc[(a)]) 184 1.17 christos if (lnext) { 185 1.17 christos lnext = false; 186 1.17 christos goto add; 187 1.17 christos } 188 1.17 christos 189 1.17 christos /* Ignored */ 190 1.22 christos if (c == C(VREPRINT, CTRL('r')) || c == C(VSTART, CTRL('q')) || 191 1.17 christos c == C(VSTOP, CTRL('s')) || c == C(VSTATUS, CTRL('t')) || 192 1.17 christos c == C(VDISCARD, CTRL('o'))) 193 1.17 christos continue; 194 1.17 christos 195 1.17 christos /* Literal next */ 196 1.17 christos if (c == C(VLNEXT, CTRL('v'))) { 197 1.17 christos lnext = true; 198 1.17 christos continue; 199 1.17 christos } 200 1.17 christos 201 1.17 christos /* Line or word kill, treat as reset */ 202 1.17 christos if (c == C(VKILL, CTRL('u')) || c == C(VWERASE, CTRL('w'))) { 203 1.20 christos if (flags & (GETPASS_ECHO | GETPASS_ECHO_STAR)) { 204 1.19 christos while (l--) 205 1.19 christos erase(); 206 1.19 christos } 207 1.17 christos l = 0; 208 1.17 christos continue; 209 1.17 christos } 210 1.17 christos 211 1.17 christos /* Character erase */ 212 1.17 christos if (c == C(VERASE, CTRL('h'))) { 213 1.17 christos if (l == 0) 214 1.17 christos beep(); 215 1.19 christos else { 216 1.17 christos l--; 217 1.20 christos if (flags & (GETPASS_ECHO | GETPASS_ECHO_STAR)) 218 1.19 christos erase(); 219 1.19 christos } 220 1.17 christos continue; 221 1.17 christos } 222 1.17 christos 223 1.18 christos /* tty signal characters */ 224 1.18 christos if (c == C(VINTR, CTRL('c'))) { 225 1.18 christos sig = SIGINT; 226 1.18 christos goto out; 227 1.18 christos } 228 1.18 christos if (c == C(VQUIT, CTRL('\\'))) { 229 1.18 christos sig = SIGQUIT; 230 1.18 christos goto out; 231 1.18 christos } 232 1.18 christos if (c == C(VSUSP, CTRL('z')) || c == C(VDSUSP, CTRL('y'))) { 233 1.18 christos sig = SIGTSTP; 234 1.18 christos goto out; 235 1.18 christos } 236 1.18 christos 237 1.18 christos /* EOF */ 238 1.18 christos if (c == C(VEOF, CTRL('d'))) { 239 1.19 christos if (flags & GETPASS_FAIL_EOF) { 240 1.19 christos errno = ENODATA; 241 1.19 christos goto out; 242 1.19 christos } else { 243 1.19 christos c = '\0'; 244 1.19 christos goto add; 245 1.19 christos } 246 1.17 christos } 247 1.17 christos 248 1.17 christos /* End of line */ 249 1.28 christos if (c == C(VEOL, CTRL('j')) || c == C(VEOL2, CTRL('m'))) 250 1.17 christos c = '\0'; 251 1.17 christos add: 252 1.17 christos if (l >= len) { 253 1.19 christos if (allocated) { 254 1.22 christos size_t nlen = len + 1024; 255 1.22 christos char *nbuf = realloc(buf, nlen); 256 1.22 christos if (nbuf == NULL) 257 1.19 christos goto restore; 258 1.22 christos buf = nbuf; 259 1.22 christos len = nlen; 260 1.19 christos } else { 261 1.19 christos if (flags & GETPASS_BUF_LIMIT) { 262 1.19 christos beep(); 263 1.19 christos continue; 264 1.19 christos } 265 1.19 christos if (c == '\0' && l > 0) 266 1.19 christos l--; 267 1.19 christos else 268 1.19 christos continue; 269 1.19 christos } 270 1.17 christos } 271 1.25 christos 272 1.25 christos if (flags & GETPASS_7BIT) 273 1.25 christos c &= 0x7f; 274 1.25 christos if ((flags & GETPASS_FORCE_LOWER) && isupper((unsigned char)c)) 275 1.25 christos c = tolower((unsigned char)c); 276 1.25 christos if ((flags & GETPASS_FORCE_UPPER) && islower((unsigned char)c)) 277 1.25 christos c = toupper((unsigned char)c); 278 1.25 christos 279 1.19 christos buf[l++] = c; 280 1.20 christos if (c) { 281 1.20 christos if (flags & GETPASS_ECHO_STAR) 282 1.20 christos (void)write(fd[1], "*", 1); 283 1.20 christos else if (flags & GETPASS_ECHO) 284 1.22 christos (void)write(fd[1], isprint((unsigned char)c) ? 285 1.22 christos &c : "?", 1); 286 1.20 christos } 287 1.1 cgd } 288 1.25 christos good = true; 289 1.17 christos 290 1.17 christos restore: 291 1.30 christos out: 292 1.19 christos if (havetty) { 293 1.19 christos c = errno; 294 1.19 christos (void)tcsetattr(fd[0], TCSAFLUSH|TCSASOFT, >); 295 1.19 christos errno = c; 296 1.19 christos } 297 1.25 christos if (good && (flags & GETPASS_ECHO_NL)) 298 1.25 christos (void)write(fd[1], "\n", 1); 299 1.25 christos 300 1.25 christos if (opentty) { 301 1.25 christos c = errno; 302 1.25 christos (void)close(fd[0]); 303 1.25 christos errno = c; 304 1.25 christos } 305 1.25 christos 306 1.25 christos if (good) 307 1.25 christos return buf; 308 1.25 christos 309 1.18 christos if (sig) { 310 1.19 christos if ((flags & GETPASS_NO_SIGNAL) == 0) 311 1.19 christos (void)raise(sig); 312 1.18 christos errno = EINTR; 313 1.18 christos } 314 1.19 christos memset(buf, 0, len); 315 1.19 christos if (allocated) 316 1.19 christos free(buf); 317 1.17 christos return NULL; 318 1.17 christos } 319 1.17 christos 320 1.17 christos char * 321 1.19 christos getpass_r(const char *prompt, char *buf, size_t len) 322 1.19 christos { 323 1.27 christos return getpassfd(prompt, buf, len, NULL, GETPASS_ECHO_NL, 0); 324 1.19 christos } 325 1.19 christos 326 1.19 christos char * 327 1.17 christos getpass(const char *prompt) 328 1.17 christos { 329 1.17 christos static char e[] = ""; 330 1.17 christos static char *buf; 331 1.17 christos static long bufsiz; 332 1.17 christos char *rv; 333 1.17 christos 334 1.19 christos /* 335 1.19 christos * Strictly speaking we could double allocate here, if we get 336 1.19 christos * called at the same time, but this function is not re-entrant 337 1.19 christos * anyway and it is not supposed to work if called concurrently. 338 1.19 christos */ 339 1.17 christos if (buf == NULL) { 340 1.17 christos if ((bufsiz = sysconf(_SC_PASS_MAX)) == -1) 341 1.17 christos return e; 342 1.17 christos if ((buf = malloc((size_t)bufsiz)) == NULL) 343 1.17 christos return e; 344 1.1 cgd } 345 1.17 christos 346 1.17 christos if ((rv = getpass_r(prompt, buf, (size_t)bufsiz)) == NULL) 347 1.17 christos return e; 348 1.17 christos 349 1.17 christos return rv; 350 1.1 cgd } 351 1.17 christos 352 1.17 christos #ifdef TEST 353 1.17 christos int 354 1.17 christos main(int argc, char *argv[]) 355 1.17 christos { 356 1.17 christos char buf[28]; 357 1.25 christos printf("[%s]\n", getpassfd("foo>", buf, sizeof(buf), NULL, 358 1.25 christos GETPASS_ECHO_STAR|GETPASS_ECHO_NL, 2)); 359 1.17 christos return 0; 360 1.17 christos } 361 1.17 christos #endif 362