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