Back to index

glibc  2.9
lookup-retry.c
Go to the documentation of this file.
00001 /* hairy bits of Hurd file name lookup
00002    Copyright (C) 1992,1993,1994,1995,1996,1997,1999,2001,2002,2003,2005
00003        Free Software Foundation, Inc.
00004    This file is part of the GNU C Library.
00005 
00006    The GNU C Library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Lesser General Public
00008    License as published by the Free Software Foundation; either
00009    version 2.1 of the License, or (at your option) any later version.
00010 
00011    The GNU C Library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Lesser General Public License for more details.
00015 
00016    You should have received a copy of the GNU Lesser General Public
00017    License along with the GNU C Library; if not, write to the Free
00018    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00019    02111-1307 USA.  */
00020 
00021 #include <hurd.h>
00022 #include <hurd/lookup.h>
00023 #include <hurd/term.h>
00024 #include <hurd/paths.h>
00025 #include <limits.h>
00026 #include <fcntl.h>
00027 #include <string.h>
00028 #include "stdio-common/_itoa.h"
00029 
00030 /* Translate the error from dir_lookup into the error the user sees.  */
00031 static inline error_t
00032 lookup_error (error_t error)
00033 {
00034   switch (error)
00035     {
00036     case EOPNOTSUPP:
00037     case MIG_BAD_ID:
00038       /* These indicate that the server does not understand dir_lookup
00039         at all.  If it were a directory, it would, by definition.  */
00040       return ENOTDIR;
00041     default:
00042       return error;
00043     }
00044 }
00045 
00046 error_t
00047 __hurd_file_name_lookup_retry (error_t (*use_init_port)
00048                               (int which, error_t (*operate) (file_t)),
00049                             file_t (*get_dtable_port) (int fd),
00050                             error_t (*lookup)
00051                               (file_t dir, char *name,
00052                               int flags, mode_t mode,
00053                               retry_type *do_retry, string_t retry_name,
00054                               mach_port_t *result),
00055                             enum retry_type doretry,
00056                             char retryname[1024],
00057                             int flags, mode_t mode,
00058                             file_t *result)
00059 {
00060   error_t err;
00061   char *file_name;
00062   int nloops;
00063 
00064   error_t lookup_op (file_t startdir)
00065     {
00066       while (file_name[0] == '/')
00067        file_name++;
00068 
00069       return lookup_error ((*lookup) (startdir, file_name, flags, mode,
00070                                   &doretry, retryname, result));
00071     }
00072   error_t reauthenticate (file_t unauth)
00073     {
00074       error_t err;
00075       mach_port_t ref = __mach_reply_port ();
00076       error_t reauth (auth_t auth)
00077        {
00078          return __auth_user_authenticate (auth, ref,
00079                                       MACH_MSG_TYPE_MAKE_SEND,
00080                                       result);
00081        }
00082       err = __io_reauthenticate (unauth, ref, MACH_MSG_TYPE_MAKE_SEND);
00083       if (! err)
00084        err = (*use_init_port) (INIT_PORT_AUTH, &reauth);
00085       __mach_port_destroy (__mach_task_self (), ref);
00086       __mach_port_deallocate (__mach_task_self (), unauth);
00087       return err;
00088     }
00089 
00090   if (! lookup)
00091     lookup = __dir_lookup;
00092 
00093   nloops = 0;
00094   err = 0;
00095   do
00096     {
00097       file_t startdir = MACH_PORT_NULL;
00098       int dirport = INIT_PORT_CWDIR;
00099 
00100       switch (doretry)
00101        {
00102        case FS_RETRY_REAUTH:
00103          if (err = reauthenticate (*result))
00104            return err;
00105          /* Fall through.  */
00106 
00107        case FS_RETRY_NORMAL:
00108          if (nloops++ >= SYMLOOP_MAX)
00109            {
00110              __mach_port_deallocate (__mach_task_self (), *result);
00111              return ELOOP;
00112            }
00113 
00114          /* An empty RETRYNAME indicates we have the final port.  */
00115          if (retryname[0] == '\0' &&
00116              /* If reauth'd, we must do one more retry on "" to give the new
00117                translator a chance to make a new port for us.  */
00118              doretry == FS_RETRY_NORMAL)
00119            {
00120              if (flags & O_NOFOLLOW)
00121               {
00122                 /* In Linux, O_NOFOLLOW means to reject symlinks.  If we
00123                    did an O_NOLINK lookup above and io_stat here to check
00124                    for S_IFLNK, a translator like firmlink could easily
00125                    spoof this check by not showing S_IFLNK, but in fact
00126                    redirecting the lookup to some other name
00127                    (i.e. opening the very same holes a symlink would).
00128 
00129                    Instead we do an O_NOTRANS lookup above, and stat the
00130                    underlying node: if it has a translator set, and its
00131                    owner is not root (st_uid 0) then we reject it.
00132                    Since the motivation for this feature is security, and
00133                    that security presumes we trust the containing
00134                    directory, this check approximates the security of
00135                    refusing symlinks while accepting mount points.
00136                    Note that we actually permit something Linux doesn't:
00137                    we follow root-owned symlinks; if that is deemed
00138                    undesireable, we can add a final check for that
00139                    one exception to our general translator-based rule.  */
00140                 struct stat64 st;
00141                 err = __io_stat (*result, &st);
00142                 if (!err
00143                     && (st.st_mode & (S_IPTRANS|S_IATRANS)))
00144                   {
00145                     if (st.st_uid != 0)
00146                      err = ENOENT;
00147                     else if (st.st_mode & S_IPTRANS)
00148                      {
00149                        char buf[1024];
00150                        char *trans = buf;
00151                        size_t translen = sizeof buf;
00152                        err = __file_get_translator (*result,
00153                                                  &trans, &translen);
00154                        if (!err
00155                            && translen > sizeof _HURD_SYMLINK
00156                            && !memcmp (trans,
00157                                      _HURD_SYMLINK, sizeof _HURD_SYMLINK))
00158                          err = ENOENT;
00159                      }
00160                   }
00161               }
00162 
00163              /* We got a successful translation.  Now apply any open-time
00164                action flags we were passed.  */
00165 
00166              if (!err && (flags & O_TRUNC)) /* Asked to truncate the file.  */
00167               err = __file_set_size (*result, 0);
00168 
00169              if (err)
00170               __mach_port_deallocate (__mach_task_self (), *result);
00171              return err;
00172            }
00173 
00174          startdir = *result;
00175          file_name = retryname;
00176          break;
00177 
00178        case FS_RETRY_MAGICAL:
00179          switch (retryname[0])
00180            {
00181            case '/':
00182              dirport = INIT_PORT_CRDIR;
00183              if (*result != MACH_PORT_NULL)
00184               __mach_port_deallocate (__mach_task_self (), *result);
00185              if (nloops++ >= SYMLOOP_MAX)
00186               return ELOOP;
00187              file_name = &retryname[1];
00188              break;
00189 
00190            case 'f':
00191              if (retryname[1] == 'd' && retryname[2] == '/')
00192               {
00193                 int fd;
00194                 char *end;
00195                 int save = errno;
00196                 errno = 0;
00197                 fd = (int) strtoul (&retryname[3], &end, 10);
00198                 if (end == NULL || errno || /* Malformed number.  */
00199                     /* Check for excess text after the number.  A slash
00200                       is valid; it ends the component.  Anything else
00201                       does not name a numeric file descriptor.  */
00202                     (*end != '/' && *end != '\0'))
00203                   {
00204                     errno = save;
00205                     return ENOENT;
00206                   }
00207                 if (! get_dtable_port)
00208                   err = EGRATUITOUS;
00209                 else
00210                   {
00211                     *result = (*get_dtable_port) (fd);
00212                     if (*result == MACH_PORT_NULL)
00213                      {
00214                        /* If the name was a proper number, but the file
00215                           descriptor does not exist, we return EBADF instead
00216                           of ENOENT.  */
00217                        err = errno;
00218                        errno = save;
00219                      }
00220                   }
00221                 errno = save;
00222                 if (err)
00223                   return err;
00224                 if (*end == '\0')
00225                   return 0;
00226                 else
00227                   {
00228                     /* Do a normal retry on the remaining components.  */
00229                     startdir = *result;
00230                     file_name = end + 1; /* Skip the slash.  */
00231                     break;
00232                   }
00233               }
00234              else
00235               goto bad_magic;
00236              break;
00237 
00238            case 'm':
00239              if (retryname[1] == 'a' && retryname[2] == 'c' &&
00240                 retryname[3] == 'h' && retryname[4] == 't' &&
00241                 retryname[5] == 'y' && retryname[6] == 'p' &&
00242                 retryname[7] == 'e')
00243               {
00244                 error_t err;
00245                 struct host_basic_info hostinfo;
00246                 mach_msg_type_number_t hostinfocnt = HOST_BASIC_INFO_COUNT;
00247                 char *p;
00248                 /* XXX want client's host */
00249                 if (err = __host_info (__mach_host_self (), HOST_BASIC_INFO,
00250                                     (integer_t *) &hostinfo,
00251                                     &hostinfocnt))
00252                   return err;
00253                 if (hostinfocnt != HOST_BASIC_INFO_COUNT)
00254                   return EGRATUITOUS;
00255                 p = _itoa (hostinfo.cpu_subtype, &retryname[8], 10, 0);
00256                 *--p = '/';
00257                 p = _itoa (hostinfo.cpu_type, &retryname[8], 10, 0);
00258                 if (p < retryname)
00259                   abort (); /* XXX write this right if this ever happens */
00260                 if (p > retryname)
00261                   strcpy (retryname, p);
00262                 startdir = *result;
00263               }
00264              else
00265               goto bad_magic;
00266              break;
00267 
00268            case 't':
00269              if (retryname[1] == 't' && retryname[2] == 'y')
00270               switch (retryname[3])
00271                 {
00272                   error_t opentty (file_t *result)
00273                     {
00274                      error_t err;
00275                      error_t ctty_open (file_t port)
00276                        {
00277                          if (port == MACH_PORT_NULL)
00278                            return ENXIO; /* No controlling terminal.  */
00279                          return __termctty_open_terminal (port,
00280                                                       flags,
00281                                                       result);
00282                        }
00283                      err = (*use_init_port) (INIT_PORT_CTTYID, &ctty_open);
00284                      if (! err)
00285                        err = reauthenticate (*result);
00286                      return err;
00287                     }
00288 
00289                 case '\0':
00290                   return opentty (result);
00291                 case '/':
00292                   if (err = opentty (&startdir))
00293                     return err;
00294                   strcpy (retryname, &retryname[4]);
00295                   break;
00296                 default:
00297                   goto bad_magic;
00298                 }
00299              else
00300               goto bad_magic;
00301              break;
00302 
00303            default:
00304            bad_magic:
00305              return EGRATUITOUS;
00306            }
00307          break;
00308 
00309        default:
00310          return EGRATUITOUS;
00311        }
00312 
00313       if (startdir != MACH_PORT_NULL)
00314        {
00315          err = lookup_op (startdir);
00316          __mach_port_deallocate (__mach_task_self (), startdir);
00317          startdir = MACH_PORT_NULL;
00318        }
00319       else
00320        err = (*use_init_port) (dirport, &lookup_op);
00321     } while (! err);
00322 
00323   return err;
00324 }
00325 weak_alias (__hurd_file_name_lookup_retry, hurd_file_name_lookup_retry)