1 /* This file is part of GNU Pies
2 Copyright (C) 2015-2020 Sergey Poznyakoff
3
4 GNU Pies is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Pies is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Pies. If not, see <http://www.gnu.org/licenses/>. */
16
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 #include "libpies.h"
21 #include <unistd.h>
22 #include <pwd.h>
23 #include <grecs.h>
24 #include <wordsplit.h>
25 #include <errno.h>
26 #include <netdb.h>
27
28 /* Compare two hostnames. Return 0 if they have the same address type,
29 address length *and* at least one of the addresses of A matches
30 B */
31 static int
hostcmp(const char * a,const char * b)32 hostcmp (const char *a, const char *b)
33 {
34 struct hostent *hp = gethostbyname (a);
35 char **addrlist;
36 char *dptr;
37 char **addr;
38 size_t i, count;
39 size_t entry_length;
40 int entry_type;
41
42 if (!hp)
43 return 1;
44
45 for (count = 1, addr = hp->h_addr_list; *addr; addr++)
46 count++;
47 addrlist = grecs_malloc (count * (sizeof *addrlist + hp->h_length)
48 - hp->h_length);
49 dptr = (char *) (addrlist + count);
50 for (i = 0; i < count - 1; i++)
51 {
52 memcpy (dptr, hp->h_addr_list[i], hp->h_length);
53 addrlist[i] = dptr;
54 dptr += hp->h_length;
55 }
56 addrlist[i] = NULL;
57 entry_length = hp->h_length;
58 entry_type = hp->h_addrtype;
59
60 hp = gethostbyname (b);
61 if (!hp || entry_length != hp->h_length || entry_type != hp->h_addrtype)
62 {
63 grecs_free (addrlist);
64 return 1;
65 }
66
67 for (addr = addrlist; *addr; addr++)
68 {
69 char **p;
70
71 for (p = hp->h_addr_list; *p; p++)
72 {
73 if (memcmp (*addr, *p, entry_length) == 0)
74 {
75 grecs_free (addrlist);
76 return 0;
77 }
78 }
79 }
80 grecs_free (addrlist);
81 return 1;
82 }
83
84 static int
match_url(size_t argc,char ** argv,struct pies_url * url)85 match_url (size_t argc, char **argv, struct pies_url *url)
86 {
87 if (hostcmp (argv[1], url->host ? url->host : "localhost") == 0)
88 {
89 if (argc >= 4 && strcmp (argv[2], "port") == 0)
90 {
91 unsigned long n = strtoul (argv[3], NULL, 10);
92 if (n == url->port)
93 return 1;
94 }
95 else
96 return 1;
97 }
98 return 0;
99 }
100
101 static void
parse_args(struct grecs_locus * loc,char ** argv,char ** username,char ** password)102 parse_args (struct grecs_locus *loc, char **argv,
103 char **username, char **password)
104 {
105 if (*username)
106 {
107 grecs_free (*username);
108 *username = NULL;
109 }
110 if (*password)
111 {
112 grecs_free (*password);
113 *password = NULL;
114 }
115
116 while (*argv)
117 {
118 if (!argv[1])
119 {
120 grecs_error (loc, 0, _("incomplete sentence"));
121 break;
122 }
123 if (strcmp (*argv, "login") == 0)
124 *username = grecs_strdup (argv[1]);
125 else if (strcmp (*argv, "password") == 0)
126 *password = grecs_strdup (argv[1]);
127 argv += 2;
128 }
129 }
130
131 /* Parse traditional .netrc file. Set up auth_args fields in accordance with
132 it. */
133 void
netrc_scan_file(FILE * fp,struct grecs_locus * loc,struct pies_url * url)134 netrc_scan_file (FILE *fp, struct grecs_locus *loc, struct pies_url *url)
135 {
136 char *buf = NULL;
137 size_t n = 0;
138 struct wordsplit ws;
139 int wsflags = WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_QUOTE | WRDSF_SQUEEZE_DELIMS;
140 char *username = NULL;
141 char *password = NULL;
142
143 while (grecs_getline (&buf, &n, fp) > 0)
144 {
145 loc->beg.line++;
146
147 if (wordsplit (buf, &ws, wsflags))
148 {
149 grecs_error (loc, 0, "wordsplit: %s", wordsplit_strerror (&ws));
150 continue;
151 }
152 wsflags |= WRDSF_REUSE;
153
154 if (ws.ws_wordc == 0)
155 continue;
156
157 if (strcmp (ws.ws_wordv[0], "machine") == 0)
158 {
159 if (match_url (ws.ws_wordc, ws.ws_wordv, url))
160 {
161 parse_args (loc, ws.ws_wordv + 2, &username, &password);
162 break;
163 }
164 }
165 else if (strcmp (ws.ws_wordv[0], "default") == 0)
166 parse_args (loc, ws.ws_wordv + 1, &username, &password);
167 else
168 grecs_error (loc, 0, _("ignoring unrecognized line"));
169 }
170 grecs_free (buf);
171
172 if (wsflags & WRDSF_REUSE)
173 wordsplit_free (&ws);
174
175 url->user = username;
176 url->passwd = password;
177 }
178
179 void
netrc_scan(struct pies_url * url)180 netrc_scan (struct pies_url *url)
181 {
182 FILE *fp;
183 struct grecs_locus loc;
184 char *filename;
185 char *homedir;
186
187 if (url->user)
188 return;
189
190 homedir = getenv ("HOME");
191 if (!homedir)
192 {
193 struct passwd *pwd = getpwuid (getuid ());
194 if (!pwd)
195 return;
196 homedir = pwd->pw_dir;
197 }
198
199 filename = mkfilename (homedir, ".netrc", NULL);
200 fp = fopen (filename, "r");
201 if (!fp)
202 {
203 if (errno != ENOENT)
204 grecs_error (NULL, 0,
205 _("cannot open configuration file %s: %s"),
206 filename, strerror (errno));
207 free (filename);
208 return;
209 }
210
211 loc.beg.file = loc.end.file = (char*) filename;
212 loc.beg.col = loc.end.col = 0;
213 loc.beg.line = 0;
214 netrc_scan_file (fp, &loc, url);
215 fclose (fp);
216 free (filename);
217 }
218