Merge glibc-ports into ports/ directory.
[platform/upstream/glibc.git] / hurd / lookup-retry.c
1 /* hairy bits of Hurd file name lookup
2    Copyright (C) 1992-2012 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <http://www.gnu.org/licenses/>.  */
18
19 #include <hurd.h>
20 #include <hurd/lookup.h>
21 #include <hurd/term.h>
22 #include <hurd/paths.h>
23 #include <limits.h>
24 #include <fcntl.h>
25 #include <string.h>
26 #include <_itoa.h>
27
28 /* Translate the error from dir_lookup into the error the user sees.  */
29 static inline error_t
30 lookup_error (error_t error)
31 {
32   switch (error)
33     {
34     case EOPNOTSUPP:
35     case MIG_BAD_ID:
36       /* These indicate that the server does not understand dir_lookup
37          at all.  If it were a directory, it would, by definition.  */
38       return ENOTDIR;
39     default:
40       return error;
41     }
42 }
43
44 error_t
45 __hurd_file_name_lookup_retry (error_t (*use_init_port)
46                                  (int which, error_t (*operate) (file_t)),
47                                file_t (*get_dtable_port) (int fd),
48                                error_t (*lookup)
49                                  (file_t dir, char *name,
50                                   int flags, mode_t mode,
51                                   retry_type *do_retry, string_t retry_name,
52                                   mach_port_t *result),
53                                enum retry_type doretry,
54                                char retryname[1024],
55                                int flags, mode_t mode,
56                                file_t *result)
57 {
58   error_t err;
59   char *file_name;
60   int nloops;
61
62   error_t lookup_op (file_t startdir)
63     {
64       while (file_name[0] == '/')
65         file_name++;
66
67       return lookup_error ((*lookup) (startdir, file_name, flags, mode,
68                                       &doretry, retryname, result));
69     }
70   error_t reauthenticate (file_t unauth)
71     {
72       error_t err;
73       mach_port_t ref = __mach_reply_port ();
74       error_t reauth (auth_t auth)
75         {
76           return __auth_user_authenticate (auth, ref,
77                                            MACH_MSG_TYPE_MAKE_SEND,
78                                            result);
79         }
80       err = __io_reauthenticate (unauth, ref, MACH_MSG_TYPE_MAKE_SEND);
81       if (! err)
82         err = (*use_init_port) (INIT_PORT_AUTH, &reauth);
83       __mach_port_destroy (__mach_task_self (), ref);
84       __mach_port_deallocate (__mach_task_self (), unauth);
85       return err;
86     }
87
88   if (! lookup)
89     lookup = __dir_lookup;
90
91   nloops = 0;
92   err = 0;
93   do
94     {
95       file_t startdir = MACH_PORT_NULL;
96       int dirport = INIT_PORT_CWDIR;
97
98       switch (doretry)
99         {
100         case FS_RETRY_REAUTH:
101           if (err = reauthenticate (*result))
102             return err;
103           /* Fall through.  */
104
105         case FS_RETRY_NORMAL:
106           if (nloops++ >= SYMLOOP_MAX)
107             {
108               __mach_port_deallocate (__mach_task_self (), *result);
109               return ELOOP;
110             }
111
112           /* An empty RETRYNAME indicates we have the final port.  */
113           if (retryname[0] == '\0' &&
114               /* If reauth'd, we must do one more retry on "" to give the new
115                  translator a chance to make a new port for us.  */
116               doretry == FS_RETRY_NORMAL)
117             {
118               if (flags & O_NOFOLLOW)
119                 {
120                   /* In Linux, O_NOFOLLOW means to reject symlinks.  If we
121                      did an O_NOLINK lookup above and io_stat here to check
122                      for S_IFLNK, a translator like firmlink could easily
123                      spoof this check by not showing S_IFLNK, but in fact
124                      redirecting the lookup to some other name
125                      (i.e. opening the very same holes a symlink would).
126
127                      Instead we do an O_NOTRANS lookup above, and stat the
128                      underlying node: if it has a translator set, and its
129                      owner is not root (st_uid 0) then we reject it.
130                      Since the motivation for this feature is security, and
131                      that security presumes we trust the containing
132                      directory, this check approximates the security of
133                      refusing symlinks while accepting mount points.
134                      Note that we actually permit something Linux doesn't:
135                      we follow root-owned symlinks; if that is deemed
136                      undesireable, we can add a final check for that
137                      one exception to our general translator-based rule.  */
138                   struct stat64 st;
139                   err = __io_stat (*result, &st);
140                   if (!err
141                       && (st.st_mode & (S_IPTRANS|S_IATRANS)))
142                     {
143                       if (st.st_uid != 0)
144                         err = ENOENT;
145                       else if (st.st_mode & S_IPTRANS)
146                         {
147                           char buf[1024];
148                           char *trans = buf;
149                           size_t translen = sizeof buf;
150                           err = __file_get_translator (*result,
151                                                        &trans, &translen);
152                           if (!err
153                               && translen > sizeof _HURD_SYMLINK
154                               && !memcmp (trans,
155                                           _HURD_SYMLINK, sizeof _HURD_SYMLINK))
156                             err = ENOENT;
157                         }
158                     }
159                 }
160
161               /* We got a successful translation.  Now apply any open-time
162                  action flags we were passed.  */
163
164               if (!err && (flags & O_TRUNC)) /* Asked to truncate the file.  */
165                 err = __file_set_size (*result, 0);
166
167               if (err)
168                 __mach_port_deallocate (__mach_task_self (), *result);
169               return err;
170             }
171
172           startdir = *result;
173           file_name = retryname;
174           break;
175
176         case FS_RETRY_MAGICAL:
177           switch (retryname[0])
178             {
179             case '/':
180               dirport = INIT_PORT_CRDIR;
181               if (*result != MACH_PORT_NULL)
182                 __mach_port_deallocate (__mach_task_self (), *result);
183               if (nloops++ >= SYMLOOP_MAX)
184                 return ELOOP;
185               file_name = &retryname[1];
186               break;
187
188             case 'f':
189               if (retryname[1] == 'd' && retryname[2] == '/')
190                 {
191                   int fd;
192                   char *end;
193                   int save = errno;
194                   errno = 0;
195                   fd = (int) __strtoul_internal (&retryname[3], &end, 10, 0);
196                   if (end == NULL || errno || /* Malformed number.  */
197                       /* Check for excess text after the number.  A slash
198                          is valid; it ends the component.  Anything else
199                          does not name a numeric file descriptor.  */
200                       (*end != '/' && *end != '\0'))
201                     {
202                       errno = save;
203                       return ENOENT;
204                     }
205                   if (! get_dtable_port)
206                     err = EGRATUITOUS;
207                   else
208                     {
209                       *result = (*get_dtable_port) (fd);
210                       if (*result == MACH_PORT_NULL)
211                         {
212                           /* If the name was a proper number, but the file
213                              descriptor does not exist, we return EBADF instead
214                              of ENOENT.  */
215                           err = errno;
216                           errno = save;
217                         }
218                     }
219                   errno = save;
220                   if (err)
221                     return err;
222                   if (*end == '\0')
223                     return 0;
224                   else
225                     {
226                       /* Do a normal retry on the remaining components.  */
227                       startdir = *result;
228                       file_name = end + 1; /* Skip the slash.  */
229                       break;
230                     }
231                 }
232               else
233                 goto bad_magic;
234               break;
235
236             case 'm':
237               if (retryname[1] == 'a' && retryname[2] == 'c' &&
238                   retryname[3] == 'h' && retryname[4] == 't' &&
239                   retryname[5] == 'y' && retryname[6] == 'p' &&
240                   retryname[7] == 'e')
241                 {
242                   error_t err;
243                   struct host_basic_info hostinfo;
244                   mach_msg_type_number_t hostinfocnt = HOST_BASIC_INFO_COUNT;
245                   char *p;
246                   /* XXX want client's host */
247                   if (err = __host_info (__mach_host_self (), HOST_BASIC_INFO,
248                                          (integer_t *) &hostinfo,
249                                          &hostinfocnt))
250                     return err;
251                   if (hostinfocnt != HOST_BASIC_INFO_COUNT)
252                     return EGRATUITOUS;
253                   p = _itoa (hostinfo.cpu_subtype, &retryname[8], 10, 0);
254                   *--p = '/';
255                   p = _itoa (hostinfo.cpu_type, &retryname[8], 10, 0);
256                   if (p < retryname)
257                     abort ();   /* XXX write this right if this ever happens */
258                   if (p > retryname)
259                     strcpy (retryname, p);
260                   startdir = *result;
261                 }
262               else
263                 goto bad_magic;
264               break;
265
266             case 't':
267               if (retryname[1] == 't' && retryname[2] == 'y')
268                 switch (retryname[3])
269                   {
270                     error_t opentty (file_t *result)
271                       {
272                         error_t err;
273                         error_t ctty_open (file_t port)
274                           {
275                             if (port == MACH_PORT_NULL)
276                               return ENXIO; /* No controlling terminal.  */
277                             return __termctty_open_terminal (port,
278                                                              flags,
279                                                              result);
280                           }
281                         err = (*use_init_port) (INIT_PORT_CTTYID, &ctty_open);
282                         if (! err)
283                           err = reauthenticate (*result);
284                         return err;
285                       }
286
287                   case '\0':
288                     return opentty (result);
289                   case '/':
290                     if (err = opentty (&startdir))
291                       return err;
292                     strcpy (retryname, &retryname[4]);
293                     break;
294                   default:
295                     goto bad_magic;
296                   }
297               else
298                 goto bad_magic;
299               break;
300
301             default:
302             bad_magic:
303               return EGRATUITOUS;
304             }
305           break;
306
307         default:
308           return EGRATUITOUS;
309         }
310
311       if (startdir != MACH_PORT_NULL)
312         {
313           err = lookup_op (startdir);
314           __mach_port_deallocate (__mach_task_self (), startdir);
315           startdir = MACH_PORT_NULL;
316         }
317       else
318         err = (*use_init_port) (dirport, &lookup_op);
319     } while (! err);
320
321   return err;
322 }
323 weak_alias (__hurd_file_name_lookup_retry, hurd_file_name_lookup_retry)