1 /*
2 * password.c
3 *
4 * This file is part of msmtp, an SMTP client, and of mpop, a POP3 client.
5 *
6 * Copyright (C) 2019, 2020, 2021 Martin Lambers <marlam@marlam.de>
7 * Jay Soffian <jaysoffian@gmail.com> (Mac OS X keychain support)
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #ifdef HAVE_LIBSECRET
32 # include <libsecret/secret.h>
33 #endif
34 #ifdef HAVE_MACOSXKEYRING
35 # include <Security/Security.h>
36 #endif
37
38 #include "gettext.h"
39 #define _(string) gettext(string)
40
41 #include "netrc.h"
42 #include "tools.h"
43 #include "xalloc.h"
44 #include "password.h"
45
46 #ifdef W32_NATIVE
47 #define SYSNETRCFILE "netrc.txt"
48 #define USERNETRCFILE "netrc.txt"
49 #else /* UNIX */
50 #define SYSNETRCFILE "netrc"
51 #define USERNETRCFILE ".netrc"
52 #endif
53
54
55 /*
56 * password_get()
57 *
58 * see password.h
59 */
60
61 #ifdef HAVE_LIBSECRET
get_schema(void)62 static const SecretSchema *get_schema(void)
63 {
64 static const SecretSchema schema = {
65 "de.marlam." PACKAGE_NAME ".password", SECRET_SCHEMA_DONT_MATCH_NAME,
66 {
67 { "host", SECRET_SCHEMA_ATTRIBUTE_STRING },
68 { "service", SECRET_SCHEMA_ATTRIBUTE_STRING },
69 { "user", SECRET_SCHEMA_ATTRIBUTE_STRING },
70 { "NULL", 0 },
71 }
72 };
73 return &schema;
74 }
service_string(password_service_t service)75 static const char *service_string(password_service_t service)
76 {
77 switch (service)
78 {
79 case password_service_smtp:
80 return "smtp";
81 case password_service_pop3:
82 return "pop3";
83 }
84 return NULL;
85 }
86 #endif
87
password_get(const char * hostname,const char * user,password_service_t service,int consult_netrc,int getpass_only_via_tty)88 char *password_get(const char *hostname, const char *user,
89 password_service_t service,
90 int consult_netrc,
91 int getpass_only_via_tty)
92 {
93 char *password = NULL;
94
95 #ifdef HAVE_LIBSECRET
96 if (!password)
97 {
98 gchar* libsecret_pw = secret_password_lookup_sync(
99 get_schema(),
100 NULL, NULL,
101 "host", hostname,
102 "service", service_string(service),
103 "user", user,
104 NULL);
105 if (!libsecret_pw)
106 {
107 /* for compatibility with passwords stored by the older
108 * libgnome-keyring */
109 libsecret_pw = secret_password_lookup_sync(
110 SECRET_SCHEMA_COMPAT_NETWORK,
111 NULL, NULL,
112 "user", user,
113 "protocol", service_string(service),
114 "server", hostname,
115 NULL);
116 }
117 if (libsecret_pw)
118 {
119 password = xstrdup(libsecret_pw);
120 secret_password_free(libsecret_pw);
121 }
122 }
123 #endif /* HAVE_LIBSECRET */
124
125 #ifdef HAVE_MACOSXKEYRING
126 if (!password)
127 {
128 void *password_data;
129 UInt32 password_length;
130 if (SecKeychainFindInternetPassword(
131 NULL,
132 strlen(hostname), hostname,
133 0, NULL,
134 strlen(user), user,
135 0, (char *)NULL,
136 0,
137 service == password_service_smtp ? kSecProtocolTypeSMTP : kSecProtocolTypePOP3,
138 kSecAuthenticationTypeDefault,
139 &password_length, &password_data,
140 NULL) == noErr)
141 {
142 password = xmalloc((password_length + 1) * sizeof(char));
143 strncpy(password, password_data, (size_t)password_length);
144 password[password_length] = '\0';
145 SecKeychainItemFreeContent(NULL, password_data);
146 }
147 }
148 #endif /* HAVE_MACOSXKEYRING */
149
150 if (!password && consult_netrc)
151 {
152 char *netrc_directory;
153 char *netrc_filename;
154 netrc_entry *netrc_hostlist;
155 netrc_entry *netrc_host;
156
157 netrc_directory = get_homedir();
158 netrc_filename = get_filename(netrc_directory, USERNETRCFILE);
159 free(netrc_directory);
160 if ((netrc_hostlist = parse_netrc(netrc_filename)))
161 {
162 if ((netrc_host = search_netrc(netrc_hostlist, hostname, user)))
163 {
164 password = xstrdup(netrc_host->password);
165 }
166 free_netrc(netrc_hostlist);
167 }
168 free(netrc_filename);
169 if (!password)
170 {
171 netrc_directory = get_sysconfdir();
172 netrc_filename = get_filename(netrc_directory, SYSNETRCFILE);
173 free(netrc_directory);
174 if ((netrc_hostlist = parse_netrc(netrc_filename)))
175 {
176 if ((netrc_host = search_netrc(netrc_hostlist, hostname, user)))
177 {
178 password = xstrdup(netrc_host->password);
179 }
180 free_netrc(netrc_hostlist);
181 }
182 free(netrc_filename);
183 }
184 }
185
186 if (!password)
187 {
188 int getpass_is_allowed = 1;
189 if (getpass_only_via_tty)
190 {
191 /* Do not let getpass() read from stdin, because we read the mail from
192 * there. Our W32 getpass() uses _getch(), which always reads from the
193 * 'console' and not stdin. On other systems, we test if /dev/tty can be
194 * opened before calling getpass(). */
195 int getpass_uses_tty;
196 FILE *tty;
197 #if defined W32_NATIVE || defined __CYGWIN__
198 getpass_uses_tty = 1;
199 #else
200 getpass_uses_tty = 0;
201 if ((tty = fopen("/dev/tty", "w+")))
202 {
203 getpass_uses_tty = 1;
204 fclose(tty);
205 }
206 #endif
207 if (!getpass_uses_tty)
208 {
209 getpass_is_allowed = 0;
210 }
211 }
212 if (getpass_is_allowed)
213 {
214 char *prompt = xasprintf(_("password for %s at %s: "), user, hostname);
215 char *gpw = getpass(prompt);
216 free(prompt);
217 if (gpw)
218 {
219 password = xstrdup(gpw);
220 }
221 }
222 }
223
224 return password;
225 }
226
227
228 /*
229 * password_eval()
230 *
231 * see password.h
232 */
233
234 #define LINEBUFSIZE 501
password_eval(const char * arg,char ** buf,char ** errstr)235 int password_eval(const char *arg, char **buf, char **errstr)
236 {
237 FILE *eval;
238 size_t bufsize;
239 size_t len;
240
241 *buf = NULL;
242 *errstr = NULL;
243 errno = 0;
244 bufsize = 1; /* Account for the null character. */
245
246 if (!(eval = popen(arg, "r")))
247 {
248 if (errno == 0)
249 {
250 errno = ENOMEM;
251 }
252 *errstr = xasprintf(_("cannot evaluate '%s': %s"), arg, strerror(errno));
253 return 1;
254 }
255
256 do
257 {
258 bufsize += LINEBUFSIZE;
259 *buf = xrealloc(*buf, bufsize);
260 if (!fgets(&(*buf)[bufsize - LINEBUFSIZE - 1], LINEBUFSIZE + 1, eval))
261 {
262 *errstr = xasprintf(_("cannot read output of '%s'"), arg);
263 pclose(eval);
264 free(*buf);
265 *buf = NULL;
266 return 1;
267 }
268 len = strlen(*buf);
269 if (len > 0 && (*buf)[len - 1] == '\n')
270 {
271 /* Read only the first line. */
272 break;
273 }
274 }
275 while (!feof(eval));
276 pclose(eval);
277
278 if (len > 0 && (*buf)[len - 1] == '\n')
279 {
280 (*buf)[len - 1] = '\0';
281 if (len - 1 > 0 && (*buf)[len - 2] == '\r')
282 {
283 (*buf)[len - 2] = '\0';
284 }
285 }
286
287 return 0;
288 }
289