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