1 /*
2 * Unix SMB/CIFS implementation.
3 *
4 * getpass.c - platform independent getpass function.
5 *
6 * Copyright (c) 2010-2012 Andreas Schneider <asn@samba.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23
24 #include "system/filesys.h"
25 #include "system/terminal.h"
26
27 #if !defined(SMB_MALLOC)
28 #undef malloc
29 #define SMB_MALLOC(s) malloc((s))
30 #endif
31
32 /**
33 * @internal
34 *
35 * @brief Get the password from the console.
36 *
37 * @param[in] prompt The prompt to display.
38 *
39 * @param[in] buf The buffer to fill.
40 *
41 * @param[in] len The length of the buffer.
42 *
43 * @param[in] verify Should the password be verified?
44 *
45 * @return 1 on success, 0 on error.
46 */
samba_gets(const char * prompt,char * buf,size_t len,bool verify)47 static int samba_gets(const char *prompt, char *buf, size_t len, bool verify)
48 {
49 char *tmp;
50 char *ptr = NULL;
51 int ok = 0;
52
53 tmp = SMB_MALLOC(len);
54 if (tmp == NULL) {
55 return 0;
56 }
57 memset(tmp,'\0',len);
58
59 /* read the password */
60 while (!ok) {
61 if (buf[0] != '\0') {
62 fprintf(stdout, "%s[%s] ", prompt, buf);
63 } else {
64 fprintf(stdout, "%s", prompt);
65 }
66 fflush(stdout);
67 if (fgets(tmp, len, stdin) == NULL) {
68 free(tmp);
69 return 0;
70 }
71
72 if ((ptr = strchr(tmp, '\n'))) {
73 *ptr = '\0';
74 }
75 fprintf(stdout, "\n");
76
77 if (*tmp) {
78 strncpy(buf, tmp, len);
79 }
80
81 if (verify) {
82 char *key_string;
83
84 key_string = SMB_MALLOC(len);
85 if (key_string == NULL) {
86 break;
87 }
88 memset(key_string, '\0', len);
89
90 fprintf(stdout, "\nVerifying, please re-enter. %s", prompt);
91 fflush(stdout);
92 if (! fgets(key_string, len, stdin)) {
93 memset(key_string, '\0', len);
94 SAFE_FREE(key_string);
95 clearerr(stdin);
96 continue;
97 }
98 if ((ptr = strchr(key_string, '\n'))) {
99 *ptr = '\0';
100 }
101 fprintf(stdout, "\n");
102 if (strcmp(buf, key_string)) {
103 printf("\n\07\07Mismatch - try again\n");
104 memset(key_string, '\0', len);
105 SAFE_FREE(key_string);
106 fflush(stdout);
107 continue;
108 }
109 memset(key_string, '\0', len);
110 SAFE_FREE(key_string);
111 }
112 ok = 1;
113 }
114 memset(tmp, '\0', len);
115 free(tmp);
116
117 return ok;
118 }
119
120 /**
121 * @brief Get a password from the console.
122 *
123 * You should make sure that the buffer is an empty string!
124 *
125 * You can also use this function to ask for a username. Then you can fill the
126 * buffer with the username and it is shows to the users. If the users just
127 * presses enter the buffer will be untouched.
128 *
129 * @code
130 * char username[128];
131 *
132 * snprintf(username, sizeof(username), "john");
133 *
134 * samba_getpass("Username:", username, sizeof(username), 1, 0);
135 * @endcode
136 *
137 * The prompt will look like this:
138 *
139 * Username: [john]
140 *
141 * If you press enter then john is used as the username, or you can type it in
142 * to change it.
143 *
144 * @param[in] prompt The prompt to show to ask for the password.
145 *
146 * @param[out] buf The buffer the password should be stored. It NEEDS to be
147 * empty or filled out.
148 *
149 * @param[in] len The length of the buffer.
150 *
151 * @param[in] echo Should we echo what you type.
152 *
153 * @param[in] verify Should we ask for the password twice.
154 *
155 * @return 0 on success, -1 on error.
156 */
samba_getpass(const char * prompt,char * buf,size_t len,bool echo,bool verify)157 int samba_getpass(const char *prompt,
158 char *buf,
159 size_t len,
160 bool echo,
161 bool verify)
162 {
163 struct termios attr;
164 struct termios old_attr;
165 int ok = 0;
166 int fd = -1;
167
168 /* fgets needs at least len - 1 */
169 if (prompt == NULL || buf == NULL || len < 2) {
170 return -1;
171 }
172
173 if (isatty (STDIN_FILENO)) {
174
175 ZERO_STRUCT(attr);
176 ZERO_STRUCT(old_attr);
177
178 /* get local terminal attributes */
179 if (tcgetattr(STDIN_FILENO, &attr) < 0) {
180 perror("tcgetattr");
181 return -1;
182 }
183
184 /* save terminal attributes */
185 memcpy(&old_attr, &attr, sizeof(attr));
186 if((fd = fcntl(0, F_GETFL, 0)) < 0) {
187 perror("fcntl");
188 return -1;
189 }
190
191 /* disable echo */
192 if (!echo) {
193 attr.c_lflag &= ~(ECHO);
194 }
195
196 /* write attributes to terminal */
197 if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr) < 0) {
198 perror("tcsetattr");
199 return -1;
200 }
201 }
202
203 /* disable nonblocking I/O */
204 if (fd & O_NDELAY) {
205 fcntl(0, F_SETFL, fd & ~O_NDELAY);
206 }
207
208 ok = samba_gets(prompt, buf, len, verify);
209
210 if (isatty (STDIN_FILENO)) {
211
212 /* reset terminal */
213 tcsetattr(STDIN_FILENO, TCSANOW, &old_attr);
214 }
215
216 /* close fd */
217 if (fd & O_NDELAY) {
218 fcntl(0, F_SETFL, fd);
219 }
220
221 if (!ok) {
222 memset (buf, '\0', len);
223 return -1;
224 }
225
226 /* force termination */
227 buf[len - 1] = '\0';
228
229 return 0;
230 }
231