(linenum→info "unix/slp.c:2238")

glibc/2.7/hurd/lookup-retry.c

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