realpath.c
上传用户:zibowangxu
上传日期:2007-01-04
资源大小:331k
文件大小:9k
源码类别:

Ftp客户端

开发平台:

Unix_Linux

  1. /****************************************************************************    
  2.   Copyright (c) 1999 WU-FTPD Development Group.  
  3.   All rights reserved.
  4.    
  5.   Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994  
  6.     The Regents of the University of California. 
  7.   Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.  
  8.   Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.  
  9.   Portions Copyright (c) 1989 Massachusetts Institute of Technology.  
  10.   Portions Copyright (c) 1998 Sendmail, Inc.  
  11.   Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P.  Allman.  
  12.   Portions Copyright (c) 1997 by Stan Barber.  
  13.   Portions Copyright (c) 1997 by Kent Landfield.  
  14.   Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997  
  15.     Free Software Foundation, Inc.    
  16.    
  17.   Use and distribution of this software and its source code are governed   
  18.   by the terms and conditions of the WU-FTPD Software License ("LICENSE").  
  19.    
  20.   If you did not receive a copy of the license, it may be obtained online  
  21.   at http://www.wu-ftpd.org/license.html.  
  22.    
  23.   $Id: realpath.c,v 1.10 1999/10/01 02:34:59 wuftpd Exp $  
  24.    
  25. ****************************************************************************/
  26. /* Originally taken from FreeBSD 3.0's libc; adapted to handle chroot
  27.  * directories in BeroFTPD by Bernhard Rosenkraenzer
  28.  * <bero@beroftpd.unix.eu.org>
  29.  *
  30.  * Added super-user permissions so we can determine the real pathname even
  31.  * if the user cannot access the file. <lundberg+wuftpd@vr.net>
  32.  */
  33. #include "config.h"
  34. #include <sys/param.h>
  35. #include <sys/stat.h>
  36. #include <errno.h>
  37. #if defined(HAVE_FCNTL_H)
  38. #include <fcntl.h>
  39. #endif
  40. #include <stdlib.h>
  41. #include <string.h>
  42. #include <unistd.h>
  43. #include "proto.h"
  44. #ifndef MAXSYMLINKS /* Workaround for Linux libc 4.x/5.x */
  45. #define MAXSYMLINKS 5
  46. #endif
  47. #ifndef HAVE_LSTAT
  48. #define lstat stat
  49. #endif
  50. char *wu_realpath(const char *path, char resolved_path[MAXPATHLEN], char *chroot_path)
  51. {
  52.     char *ptr;
  53.     char q[MAXPATHLEN];
  54.     fb_realpath(path, q);
  55.     if (chroot_path == NULL)
  56. strcpy(resolved_path, q);
  57.     else {
  58. strcpy(resolved_path, chroot_path);
  59. if (q[0] != '/') {
  60.     if (strlen(resolved_path) + strlen(q) < MAXPATHLEN)
  61. strcat(resolved_path, q);
  62.     else /* Avoid buffer overruns... */
  63. return NULL;
  64. }
  65. else if (q[1] != '') {
  66.     for (ptr = q; *ptr != ''; ptr++);
  67.     if (ptr == resolved_path || *--ptr != '/') {
  68. if (strlen(resolved_path) + strlen(q) < MAXPATHLEN)
  69.     strcat(resolved_path, q);
  70. else /* Avoid buffer overruns... */
  71.     return NULL;
  72.     }
  73.     else {
  74. if (strlen(resolved_path) + strlen(q) - 1 < MAXPATHLEN)
  75.     strcat(resolved_path, &q[1]);
  76. else /* Avoid buffer overruns... */
  77.     return NULL;
  78.     }
  79. }
  80.     }
  81.     return resolved_path;
  82. }
  83. /*
  84.  * char *fb_realpath(const char *path, char resolved_path[MAXPATHLEN]);
  85.  *
  86.  * Find the real name of path, by removing all ".", ".." and symlink
  87.  * components.  Returns (resolved) on success, or (NULL) on failure,
  88.  * in which case the path which caused trouble is left in (resolved).
  89.  */
  90. char *fb_realpath(const char *path, char *resolved)
  91. {
  92.     struct stat sb;
  93.     int fd, n, rootd, serrno;
  94.     char *p, *q, wbuf[MAXPATHLEN];
  95.     int symlinks = 0;
  96.     int resultcode;
  97. #ifdef HAS_NO_FCHDIR
  98. /* AIX Has no fchdir() so we hope the getcwd() call doesn't overrun the buffer! */
  99.     char cwd[MAXPATHLEN + 1];
  100.     char *pcwd;
  101. #endif
  102.     /* Save the starting point. */
  103.     errno = 0;
  104. #ifdef HAS_NO_FCHDIR
  105. #ifdef HAVE_GETCWD
  106.     pcwd = getcwd(cwd, sizeof(cwd));
  107. #else
  108.     pcwd = getwd(cwd);
  109. #endif
  110. #else
  111.     fd = open(".", O_RDONLY);
  112. #endif
  113.     if (EACCES == errno) {
  114. uid_t userid = geteuid();
  115. delay_signaling(); /* we can't allow any signals while euid==0: kinch */
  116. seteuid(0);
  117. #ifdef HAS_NO_FCHDIR
  118. #ifdef HAVE_GETCWD
  119. pcwd = getcwd(cwd, sizeof(cwd));
  120. #else
  121. pcwd = getwd(cwd);
  122. #endif
  123. #else
  124. fd = open(".", O_RDONLY);
  125. #endif
  126. seteuid(userid);
  127. enable_signaling(); /* we can allow signals once again: kinch */
  128.     }
  129. #ifdef HAS_NO_FCHDIR
  130.     if (pcwd == NULL)
  131. #else
  132.     if (fd < 0)
  133. #endif
  134.     {
  135. (void) strcpy(resolved, ".");
  136. return (NULL);
  137.     }
  138.     /*
  139.      * Find the dirname and basename from the path to be resolved.
  140.      * Change directory to the dirname component.
  141.      * lstat the basename part.
  142.      *     if it is a symlink, read in the value and loop.
  143.      *     if it is a directory, then change to that directory.
  144.      * get the current directory name and append the basename.
  145.      */
  146.     (void) strncpy(resolved, path, MAXPATHLEN - 1);
  147.     resolved[MAXPATHLEN - 1] = '';
  148.   loop:
  149.     q = strrchr(resolved, '/');
  150.     if (q != NULL) {
  151. p = q + 1;
  152. if (q == resolved)
  153.     q = "/";
  154. else {
  155.     do {
  156. --q;
  157.     } while (q > resolved && *q == '/');
  158.     q[1] = '';
  159.     q = resolved;
  160. }
  161. errno = 0;
  162. resultcode = chdir(q);
  163. if (EACCES == errno) {
  164.     uid_t userid = geteuid();
  165.     delay_signaling(); /* we can't allow any signals while euid==0: kinch */
  166.     seteuid(0);
  167.     errno = 0;
  168.     resultcode = chdir(q);
  169.     seteuid(userid);
  170.     enable_signaling(); /* we can allow signals once again: kinch */
  171. }
  172. if (resultcode < 0)
  173.     goto err1;
  174.     }
  175.     else
  176. p = resolved;
  177.     /* Deal with the last component. */
  178.     if (*p != '') {
  179. errno = 0;
  180. resultcode = lstat(p, &sb);
  181. if (EACCES == errno) {
  182.     uid_t userid = geteuid();
  183.     delay_signaling(); /* we can't allow any signals while euid==0: kinch */
  184.     seteuid(0);
  185.     errno = 0;
  186.     resultcode = lstat(p, &sb);
  187.     seteuid(userid);
  188.     enable_signaling(); /* we can allow signals once again: kinch */
  189. }
  190. if (resultcode == 0) {
  191. #ifdef HAVE_LSTAT
  192.     if (S_ISLNK(sb.st_mode)) {
  193. if (++symlinks > MAXSYMLINKS) {
  194.     errno = ELOOP;
  195.     goto err1;
  196. }
  197. errno = 0;
  198. {
  199.     size_t len = strlen(p);
  200.     char *tmp = calloc(len + 1, sizeof(char));
  201.     if (tmp == 0) {
  202. serrno = errno;
  203. goto err1;
  204.     }
  205.     strcpy(tmp, p);
  206.     p = tmp;
  207. }
  208. n = readlink(p, resolved, MAXPATHLEN);
  209. if (EACCES == errno) {
  210.     uid_t userid = geteuid();
  211.     delay_signaling(); /* we can't allow any signals while euid==0: kinch */
  212.     seteuid(0);
  213.     errno = 0;
  214.     n = readlink(p, resolved, MAXPATHLEN);
  215.     seteuid(userid);
  216.     enable_signaling(); /* we can allow signals once again: kinch */
  217. }
  218. if (n < 0) {
  219.     free(p);
  220.     goto err1;
  221. }
  222. free(p);
  223. resolved[n] = '';
  224. goto loop;
  225.     }
  226. #endif /* HAVE_LSTAT */
  227.     if (S_ISDIR(sb.st_mode)) {
  228. errno = 0;
  229. resultcode = chdir(p);
  230. if (EACCES == errno) {
  231.     uid_t userid = geteuid();
  232.     delay_signaling(); /* we can't allow any signals while euid==0: kinch */
  233.     seteuid(0);
  234.     errno = 0;
  235.     resultcode = chdir(p);
  236.     seteuid(userid);
  237.     enable_signaling(); /* we can allow signals once again: kinch */
  238. }
  239. if (resultcode < 0)
  240.     goto err1;
  241. p = "";
  242.     }
  243. }
  244.     }
  245.     /*
  246.      * Save the last component name and get the full pathname of
  247.      * the current directory.
  248.      */
  249.     (void) strcpy(wbuf, p);
  250.     errno = 0;
  251. #ifdef HAVE_GETCWD
  252.     resultcode = getcwd(resolved, MAXPATHLEN) == NULL ? 0 : 1;
  253. #else
  254.     resultcode = getwd(resolved) == NULL ? 0 : 1;
  255.     if (resolved[MAXPATHLEN - 1] != '') {
  256. resultcode = 0;
  257. errno = ERANGE;
  258.     }
  259. #endif
  260.     if (EACCES == errno) {
  261. uid_t userid = geteuid();
  262. delay_signaling(); /* we can't allow any signals while euid==0: kinch */
  263. seteuid(0);
  264. errno = 0;
  265. #ifdef HAVE_GETCWD
  266. resultcode = getcwd(resolved, MAXPATHLEN) == NULL ? 0 : 1;
  267. #else
  268. resultcode = getwd(resolved) == NULL ? 0 : 1;
  269. if (resolved[MAXPATHLEN - 1] != '') {
  270.     resultcode = 0;
  271.     errno = ERANGE;
  272. }
  273. #endif
  274. seteuid(userid);
  275. enable_signaling(); /* we can allow signals once again: kinch */
  276.     }
  277.     if (resultcode == 0)
  278. goto err1;
  279.     /*
  280.      * Join the two strings together, ensuring that the right thing
  281.      * happens if the last component is empty, or the dirname is root.
  282.      */
  283.     if (resolved[0] == '/' && resolved[1] == '')
  284. rootd = 1;
  285.     else
  286. rootd = 0;
  287.     if (*wbuf) {
  288. if (strlen(resolved) + strlen(wbuf) + rootd + 1 > MAXPATHLEN) {
  289.     errno = ENAMETOOLONG;
  290.     goto err1;
  291. }
  292. if (rootd == 0)
  293.     (void) strcat(resolved, "/");
  294. (void) strcat(resolved, wbuf);
  295.     }
  296.     /* Go back to where we came from. */
  297.     errno = 0;
  298. #ifdef HAS_NO_FCHDIR
  299.     resultcode = chdir(cwd);
  300. #else
  301.     resultcode = fchdir(fd);
  302. #endif
  303.     if (EACCES == errno) {
  304. uid_t userid = geteuid();
  305. delay_signaling(); /* we can't allow any signals while euid==0: kinch */
  306. seteuid(0);
  307. errno = 0;
  308. #ifdef HAS_NO_FCHDIR
  309. resultcode = chdir(cwd);
  310. #else
  311. resultcode = fchdir(fd);
  312. #endif
  313. seteuid(userid);
  314. enable_signaling(); /* we can allow signals once again: kinch */
  315.     }
  316.     if (resultcode < 0) {
  317. serrno = errno;
  318. goto err2;
  319.     }
  320. #ifndef HAS_NO_FCHDIR
  321.     /* It's okay if the close fails, what's an fd more or less? */
  322.     (void) close(fd);
  323. #endif
  324.     return (resolved);
  325.   err1:serrno = errno;
  326. #ifdef HAS_NO_FCHDIR
  327.     (void) chdir(cwd);
  328. #else
  329.     (void) fchdir(fd);
  330. #endif
  331.     if (EACCES == errno) {
  332. uid_t userid = geteuid();
  333. delay_signaling(); /* we can't allow any signals while euid==0: kinch */
  334. seteuid(0);
  335. #ifdef HAS_NO_FCHDIR
  336. (void) chdir(cwd);
  337. #else
  338. (void) fchdir(fd);
  339. #endif
  340. seteuid(userid);
  341. enable_signaling(); /* we can allow signals once again: kinch */
  342.     }
  343. #ifdef HAS_NO_FCHDIR
  344.   err2:errno = serrno;
  345. #else
  346.   err2:(void) close(fd);
  347.     errno = serrno;
  348. #endif
  349.     return (NULL);
  350. }