1 /*
2    Utilities for VFS modules.
3 
4    Copyright (C) 1988-2021
5    Free Software Foundation, Inc.
6 
7    Copyright (C) 1995, 1996 Miguel de Icaza
8 
9    This file is part of the Midnight Commander.
10 
11    The Midnight Commander is free software: you can redistribute it
12    and/or modify it under the terms of the GNU General Public License as
13    published by the Free Software Foundation, either version 3 of the License,
14    or (at your option) any later version.
15 
16    The Midnight Commander is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20 
21    You should have received a copy of the GNU General Public License
22    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24 
25 /**
26  * \file
27  * \brief Source: Utilities for VFS modules
28  * \author Miguel de Icaza
29  * \date 1995, 1996
30  */
31 
32 #include <config.h>
33 
34 #include <ctype.h>
35 #include <sys/types.h>
36 #include <pwd.h>
37 #include <grp.h>
38 #include <stdlib.h>
39 #include <string.h>
40 
41 #include "lib/global.h"
42 #include "lib/unixcompat.h"
43 #include "lib/util.h"           /* mc_mkstemps() */
44 #include "lib/widget.h"         /* message() */
45 #include "lib/strutil.h"        /* INVALID_CONV */
46 
47 #include "vfs.h"
48 #include "utilvfs.h"
49 
50 /*** global variables ****************************************************************************/
51 
52 /*** file scope macro definitions ****************************************************************/
53 
54 #ifndef TUNMLEN
55 #define TUNMLEN 256
56 #endif
57 #ifndef TGNMLEN
58 #define TGNMLEN 256
59 #endif
60 
61 #define MC_HISTORY_VFS_PASSWORD       "mc.vfs.password"
62 
63 /*
64  * FIXME2, the "-993" is to reduce the chance of a hit on the first lookup.
65  */
66 #define GUID_DEFAULT_CONST -993
67 
68 /*** file scope type declarations ****************************************************************/
69 
70 /*** file scope variables ************************************************************************/
71 
72 /*** file scope functions ************************************************************************/
73 /* --------------------------------------------------------------------------------------------- */
74 
75 
76 /* --------------------------------------------------------------------------------------------- */
77 /*** public functions ****************************************************************************/
78 /* --------------------------------------------------------------------------------------------- */
79 /** Get current username
80  *
81  * @return g_malloc()ed string with the name of the currently logged in
82  *         user ("anonymous" if uid is not registered in the system)
83  */
84 
85 char *
vfs_get_local_username(void)86 vfs_get_local_username (void)
87 {
88     struct passwd *p_i;
89 
90     p_i = getpwuid (geteuid ());
91 
92     return (p_i && p_i->pw_name) ? g_strdup (p_i->pw_name) : g_strdup ("anonymous");    /* Unknown UID, strange */
93 }
94 
95 /* --------------------------------------------------------------------------------------------- */
96 /**
97  * Look up a user or group name from a uid/gid, maintaining a cache.
98  * FIXME, for now it's a one-entry cache.
99  * This file should be modified for non-unix systems to do something
100  * reasonable.
101  */
102 
103 /* --------------------------------------------------------------------------------------------- */
104 
105 int
vfs_finduid(const char * uname)106 vfs_finduid (const char *uname)
107 {
108     static int saveuid = GUID_DEFAULT_CONST;
109     static char saveuname[TUNMLEN] = "\0";
110 
111     size_t uname_len;
112 
113     uname_len = strlen (uname);
114 
115     if (uname[0] != saveuname[0]        /* Quick test w/o proc call */
116         || strncmp (uname, saveuname, MIN (uname_len, TUNMLEN - 1)) != 0)
117     {
118         struct passwd *pw;
119 
120         g_strlcpy (saveuname, uname, TUNMLEN);
121         pw = getpwnam (uname);
122         if (pw)
123         {
124             saveuid = pw->pw_uid;
125         }
126         else
127         {
128             static int my_uid = GUID_DEFAULT_CONST;
129 
130             if (my_uid < 0)
131                 my_uid = getuid ();
132 
133             saveuid = my_uid;
134         }
135     }
136     return saveuid;
137 }
138 
139 /* --------------------------------------------------------------------------------------------- */
140 
141 int
vfs_findgid(const char * gname)142 vfs_findgid (const char *gname)
143 {
144     static int savegid = GUID_DEFAULT_CONST;
145     static char savegname[TGNMLEN] = "\0";
146 
147     size_t gname_len;
148 
149     gname_len = strlen (gname);
150 
151     if (gname[0] != savegname[0]        /* Quick test w/o proc call */
152         || strncmp (gname, savegname, MIN (gname_len, TGNMLEN - 1)) != 0)
153     {
154         struct group *gr;
155 
156         g_strlcpy (savegname, gname, TGNMLEN);
157         gr = getgrnam (gname);
158         if (gr)
159         {
160             savegid = gr->gr_gid;
161         }
162         else
163         {
164             static int my_gid = GUID_DEFAULT_CONST;
165 
166             if (my_gid < 0)
167                 my_gid = getgid ();
168 
169             savegid = my_gid;
170         }
171     }
172     return savegid;
173 }
174 
175 /* --------------------------------------------------------------------------------------------- */
176 /**
177  * Create a temporary file with a name resembling the original.
178  * This is needed e.g. for local copies requested by extfs.
179  * Some extfs scripts may look at the extension.
180  * We also protect stupid scripts agains dangerous names.
181  */
182 
183 int
vfs_mkstemps(vfs_path_t ** pname_vpath,const char * prefix,const char * param_basename)184 vfs_mkstemps (vfs_path_t ** pname_vpath, const char *prefix, const char *param_basename)
185 {
186     const char *p;
187     GString *suffix;
188     int shift;
189     int fd;
190 
191     /* Strip directories */
192     p = strrchr (param_basename, PATH_SEP);
193     if (p == NULL)
194         p = param_basename;
195     else
196         p++;
197 
198     /* Protection against very long names */
199     shift = strlen (p) - (MC_MAXPATHLEN - 16);
200     if (shift > 0)
201         p += shift;
202 
203     suffix = g_string_sized_new (32);
204 
205     /* Protection against unusual characters */
206     for (; *p != '\0' && *p != '#'; p++)
207         if (strchr (".-_@", *p) != NULL || g_ascii_isalnum (*p))
208             g_string_append_c (suffix, *p);
209 
210     fd = mc_mkstemps (pname_vpath, prefix, suffix->str);
211     g_string_free (suffix, TRUE);
212 
213     return fd;
214 }
215 
216 /* --------------------------------------------------------------------------------------------- */
217 /**  Extract the hostname and username from the path
218  *
219  * Format of the path is [user@]hostname:port/remote-dir, e.g.:
220  *
221  * ftp://sunsite.unc.edu/pub/linux
222  * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
223  * ftp://tsx-11.mit.edu:8192/
224  * ftp://joe@foo.edu:11321/private
225  * ftp://joe:password@foo.se
226  *
227  * @param path is an input string to be parsed
228  * @param default_port is an input default port
229  * @param flags are parsing modifier flags (@see vfs_url_flags_t)
230  *
231  * @return g_malloc()ed url info.
232  *         If the user is empty, e.g. ftp://@roxanne/private, and URL_USE_ANONYMOUS
233  *         is not set, then the current login name is supplied.
234  *         Return value is a g_malloc()ed structure with the pathname relative to the
235  *         host.
236  */
237 
238 vfs_path_element_t *
vfs_url_split(const char * path,int default_port,vfs_url_flags_t flags)239 vfs_url_split (const char *path, int default_port, vfs_url_flags_t flags)
240 {
241     vfs_path_element_t *path_element;
242 
243     char *pcopy;
244     size_t pcopy_len;
245     const char *pend;
246     char *colon, *at, *rest;
247 
248     path_element = g_new0 (vfs_path_element_t, 1);
249     path_element->port = default_port;
250 
251     pcopy_len = strlen (path);
252     pcopy = g_strndup (path, pcopy_len);
253     pend = pcopy + pcopy_len;
254 
255     if ((flags & URL_NOSLASH) == 0)
256     {
257         char *dir = pcopy;
258 
259         /* locate path component */
260         while (!IS_PATH_SEP (*dir) && *dir != '\0')
261             dir++;
262         if (*dir == '\0')
263             path_element->path = g_strdup (PATH_SEP_STR);
264         else
265         {
266             path_element->path = g_strndup (dir, pcopy_len - (size_t) (dir - pcopy));
267             *dir = '\0';
268         }
269     }
270 
271     /* search for any possible user */
272     at = strrchr (pcopy, '@');
273 
274     /* We have a username */
275     if (at == NULL)
276         rest = pcopy;
277     else
278     {
279         char *inner_colon;
280 
281         *at = '\0';
282         inner_colon = strchr (pcopy, ':');
283         if (inner_colon != NULL)
284         {
285             *inner_colon = '\0';
286             inner_colon++;
287             path_element->password = g_strdup (inner_colon);
288         }
289 
290         if (*pcopy != '\0')
291             path_element->user = g_strdup (pcopy);
292 
293         if (pend == at + 1)
294             rest = at;
295         else
296             rest = at + 1;
297     }
298 
299     if ((flags & URL_USE_ANONYMOUS) == 0)
300     {
301         g_free (path_element->user);
302         path_element->user = vfs_get_local_username ();
303     }
304     /* Check if the host comes with a port spec, if so, chop it */
305     if (*rest != '[')
306         colon = strchr (rest, ':');
307     else
308     {
309         colon = strchr (++rest, ']');
310         if (colon != NULL)
311         {
312             colon[0] = '\0';
313             colon[1] = '\0';
314             colon++;
315         }
316         else
317         {
318             vfs_path_element_free (path_element);
319             g_free (pcopy);
320             return NULL;
321         }
322     }
323 
324     if (colon != NULL)
325     {
326         *colon = '\0';
327         /* cppcheck-suppress invalidscanf */
328         if (sscanf (colon + 1, "%d", &path_element->port) == 1)
329         {
330             if (path_element->port <= 0 || path_element->port >= 65536)
331                 path_element->port = default_port;
332         }
333         else
334             while (*(++colon) != '\0')
335             {
336                 switch (*colon)
337                 {
338                 case 'C':
339                     path_element->port = 1;
340                     break;
341                 case 'r':
342                     path_element->port = 2;
343                     break;
344                 default:
345                     break;
346                 }
347             }
348     }
349 
350     path_element->host = g_strdup (rest);
351     g_free (pcopy);
352 #ifdef HAVE_CHARSET
353     path_element->dir.converter = INVALID_CONV;
354 #endif
355 
356     return path_element;
357 }
358 
359 /* --------------------------------------------------------------------------------------------- */
360 
vfs_die(const char * m)361 void __attribute__ ((noreturn)) vfs_die (const char *m)
362 {
363     message (D_ERROR, _("Internal error:"), "%s", m);
364     exit (EXIT_FAILURE);
365 }
366 
367 /* --------------------------------------------------------------------------------------------- */
368 
369 char *
vfs_get_password(const char * msg)370 vfs_get_password (const char *msg)
371 {
372     return input_dialog (msg, _("Password:"), MC_HISTORY_VFS_PASSWORD, INPUT_PASSWORD,
373                          INPUT_COMPLETE_NONE);
374 }
375 
376 /* --------------------------------------------------------------------------------------------- */
377