51 broke -E logic completely, rewise it
[tfcrypt.git] / getpasswd.c
1 /*
2  * tfcrypt -- high security Threefish encryption tool.
3  *
4  * tfcrypt is copyrighted:
5  * Copyright (C) 2012-2019 Andrey Rys. All rights reserved.
6  *
7  * tfcrypt is licensed to you under the terms of std. MIT/X11 license:
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining
10  * a copy of this software and associated documentation files (the
11  * "Software"), to deal in the Software without restriction, including
12  * without limitation the rights to use, copy, modify, merge, publish,
13  * distribute, sublicense, and/or sell copies of the Software, and to
14  * permit persons to whom the Software is furnished to do so, subject to
15  * the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be
18  * included in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
24  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
25  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
26  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27  */
28
29 #include <stdio.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <termios.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #include "getpasswd.h"
38
39 size_t xgetpasswd(struct getpasswd_state *getps)
40 {
41         char c;
42         int tty_opened = 0, x;
43         int clear;
44         struct termios s, t;
45         size_t l, echolen = 0;
46
47         if (!getps) return ((size_t)-1);
48
49         /*
50          * Both stdin and stderr point to same fd. This cannot happen.
51          * This only means that getps was memzero'd.
52          * Do not blame user for that, just fix it.
53          */
54         if ((getps->fd == 0 && getps->efd == 0) || getps->efd == -1) getps->efd = 2;
55
56         if (getps->fd == -1) {
57                 if ((getps->fd = open("/dev/tty", O_RDONLY|O_NOCTTY)) == -1) getps->fd = 0;
58                 else tty_opened = 1;
59         }
60
61         memset(&t, 0, sizeof(struct termios));
62         memset(&s, 0, sizeof(struct termios));
63         if (tcgetattr(getps->fd, &t) == -1) {
64                 getps->error = errno;
65                 return ((size_t)-1);
66         }
67         s = t;
68         if (getps->sanetty) memcpy(getps->sanetty, &s, sizeof(struct termios));
69         cfmakeraw(&t);
70         t.c_iflag |= ICRNL;
71         if (tcsetattr(getps->fd, TCSANOW, &t) == -1) {
72                 getps->error = errno;
73                 return ((size_t)-1);
74         }
75
76         if (getps->echo) {
77                 echolen = strlen(getps->echo);
78                 if (write(getps->efd, getps->echo, echolen) == -1) {
79                         getps->error = errno;
80                         l = ((size_t)-1);
81                         goto _xerr;
82                 }
83         }
84
85         l = 0; x = 0;
86         memset(getps->passwd, 0, getps->pwlen);
87         while (1) {
88                 clear = 1;
89                 if (read(getps->fd, &c, sizeof(char)) == -1) {
90                         getps->error = errno;
91                         l = ((size_t)-1);
92                         goto _xerr;
93                 }
94                 if (getps->charfilter) {
95                         x = getps->charfilter(getps, c, l);
96                         if (x == 0) {
97                                 clear = 0;
98                                 goto _newl;
99                         }
100                         else if (x == 2) continue;
101                         else if (x == 3) goto _erase;
102                         else if (x == 4) goto _delete;
103                         else if (x == 5) break;
104                         else if (x == 6) {
105                                 clear = 0;
106                                 l = getps->retn;
107                                 memset(getps->passwd, 0, getps->pwlen);
108                                 goto _err;
109                         }
110                 }
111                 if (l >= getps->pwlen && (getps->flags & GETP_WAITFILL)) clear = 0;
112
113                 if (c == '\x7f'
114                 || (c == '\x08' && !(getps->flags & GETP_NOINTERP))) { /* Backspace / ^H */
115 _erase:                 if (l == 0) continue;
116                         clear = 0;
117                         l--;
118                         if (!(getps->flags & GETP_NOECHO)) {
119                                 if (write(getps->efd, "\x08\033[1X", sizeof("\x08\033[1X")-1) == -1) {
120                                         getps->error = errno;
121                                         l = ((size_t)-1);
122                                         goto _xerr;
123                                 }
124                         }
125                 }
126                 else if (!(getps->flags & GETP_NOINTERP)
127                 && (c == '\x15' || c == '\x17')) { /* ^U / ^W */
128 _delete:                clear = 0;
129                         l = 0;
130                         memset(getps->passwd, 0, getps->pwlen);
131                         if (write(getps->efd, "\033[2K\033[0G", sizeof("\033[2K\033[0G")-1) == -1) {
132                                 getps->error = errno;
133                                 l = ((size_t)-1);
134                                 goto _xerr;
135                         }
136                         if (getps->echo) {
137                                 if (write(getps->efd, getps->echo, echolen) == -1) {
138                                         getps->error = errno;
139                                         l = ((size_t)-1);
140                                         goto _xerr;
141                                 }
142                         }
143                 }
144 _newl:          if (c == '\n'
145                 || c == '\r'
146                 || (!(getps->flags & GETP_NOINTERP) && c == '\x04')) break;
147                 if (clear) {
148                         *(getps->passwd+l) = c;
149                         l++;
150                         if (!(getps->flags & GETP_NOECHO)) {
151                                 if (getps->maskchar &&
152                                         write(getps->efd, &getps->maskchar,
153                                         sizeof(char)) == -1) {
154                                                 getps->error = errno;
155                                                 l = ((size_t)-1);
156                                                 goto _xerr;
157                                 }
158                         }
159                 }
160                 if (l >= getps->pwlen && !(getps->flags & GETP_WAITFILL)) break;
161         };
162
163 _err:   if (write(getps->efd, "\r\n", sizeof("\r\n")-1) == -1) {
164                 getps->error = errno;
165                 l = ((size_t)-1);
166         }
167         if (x != 6) *(getps->passwd+l) = 0;
168
169 _xerr:  if (tcsetattr(getps->fd, TCSANOW, &s) == -1) {
170                 if (getps->error == 0) {
171                         getps->error = errno;
172                         l = ((size_t)-1);
173                 }
174         }
175
176         if (tty_opened) close(getps->fd);
177
178         return l;
179 }