/* $Id: fhtrans.C,v 1.14 2001/01/13 19:46:13 dm Exp $ */ /* * * Copyright (C) 1998 David Mazieres (dm@uun.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * */ /* * NFS <-> SFS filehandle translation * * An NFS file handle has around 8 32-bit words which always include: * * - A filesystem ID (which contains the device number) * - The inode number of the file * - The generation number of the file * * In addition, implementations often include some of the following: * * - A second filesystem ID word (for a 64-bit fsid) * - The length of the filehandle data * - The inode number of the export point (to prevent lookup ("..") there) * - The generation number of the export point * - Another copy of the second filesystem ID word (for the export point?) * - one or more unused 0 words * * The key thing to note here is that all but two of the words (namely * the inode number and generation number of the file) are constant * for the entire entire filesystem if we just export the root. We * therefore pull the inode and generation number out of the file * handle, but store the rest in a lookup table to shorten file * handles. * * The final thing to note is that because of NFS's abysmal security, * one inode, generation number pair of any directory may be all it * takes to access all files on a filesystem one does not have mount * permissions on. For that reason, the SFS filehandles are encrypted * using blowfish in CBC mode before being shipped to the remote * system. This should also prevent malicious clients from guessing * the filehandles of files they have read permission on in * directories they do not have execute permission on. */ #include "sfsrwsd.h" #include "ihash.h" #include "blowfish.h" #ifdef __linux__ # define FHINO_POS1 0 # define FHINO_POS2 1 #else /* default */ # define FHINO_POS1 3 # define FHINO_POS2 4 #endif /* default */ #ifndef FHINO_POS3 # define FHINO_POS3 FHINO_POS1 # define FHINO_POS4 FHINO_POS2 #elif !defined (FHINO_POS4) # define FHINO_POS4 FHINO_POS1 #endif /* FHINO_POS3 && !FHINO_POS4 */ static inline u_int32_t extract32 (const char *base, size_t bytes, size_t pos) { if (pos + 4 <= bytes) return *reinterpret_cast (base); base += 4 * pos; switch (bytes - 4 * pos) { case 3: return base[0] << 24 | base[1] << 16 | base[2] << 8; case 2: return base[0] << 24 | base[1] << 16; case 1: return base[0] << 24; default: return 0; } } struct fhccmp { fhccmp () {} hash_t operator() (const nfs_fh3 &fh) const { u_int res = 0; for (int n = fh.data.size () + 3 / 4; n-- > 0;) if (n != FHINO_POS1 && n != FHINO_POS2 && n != FHINO_POS3 && n != FHINO_POS4) res ^= extract32 (fh.data.base (), fh.data.size (), n); return res; } bool operator() (const nfs_fh3 &a, const nfs_fh3 &b) const { size_t size = a.data.size (); if (b.data.size () != size) return false; for (int n = size + 3 / 4; n-- > 0;) if (n != FHINO_POS1 && n != FHINO_POS2 && n != FHINO_POS3 && n != FHINO_POS4 && (extract32 (a.data.base (), a.data.size (), n) != extract32 (b.data.base (), b.data.size (), n))) return false; return true; } }; struct fhclass { nfs_fh3 fhproto; ihash_entry link; size_t pos () const; }; static vec fhcvec; static ihash fhctab; inline size_t fhclass::pos () const { return this - fhcvec.base (); } int fh3_compress (fh3trans &fht, nfs_fh3 *fhp) { nfs_fh3 res; fhclass *fhc; u_int32_t *dp, *rp; switch (fht.mode) { case fh3trans::ENCODE: if (fhp->data.size () < 4 * (FHINO_POS3 + 1) || fhp->data.size () > NFS3_FHSIZE - 4) { warn << "fh3_compress: cannot compress " << fhp->data.size () << "-byte file handle\n"; return NFS3ERR_BADHANDLE; } fhc = fhctab[*fhp]; if (!fhc) { fhc = &fhcvec.push_back (); fhc->fhproto.data.setsize (fhp->data.size ()); memcpy (fhc->fhproto.data.base (), fhp->data.base (), fhp->data.size ()); fhctab.insert (fhc); } res.data.setsize (20); dp = reinterpret_cast (res.data.base ()); dp[0] = extract32 (fhp->data.base (), fhp->data.size (), FHINO_POS1); dp[1] = extract32 (fhp->data.base (), fhp->data.size (), FHINO_POS2); dp[2] = extract32 (fhp->data.base (), fhp->data.size (), FHINO_POS3); dp[3] = extract32 (fhp->data.base (), fhp->data.size (), FHINO_POS4); dp[4] = fhc->pos (); *fhp = res; break; case fh3trans::DECODE: if (fhp->data.size () != 16) { warn << "fh3_compress: cannot uncompress " << fhp->data.size () << "-byte file handle\n"; return NFS3ERR_BADHANDLE; } dp = reinterpret_cast (fhp->data.base ()); if (dp[4] >= fhcvec.size ()) { warn << "fh3_compress: bad file handle\n"; return NFS3ERR_BADHANDLE; } res = fhcvec[dp[4]].fhproto; rp = reinterpret_cast (res.data.base ()); rp[FHINO_POS1] = dp[0]; rp[FHINO_POS2] = dp[1]; rp[FHINO_POS3] = dp[2]; rp[FHINO_POS4] = dp[3]; if (rp[FHINO_POS1] != dp[0] || rp[FHINO_POS2] != dp[1] || rp[FHINO_POS3] != dp[2]) { warn << "fh3_compress: bad file handle\n"; return NFS3ERR_BADHANDLE; } *fhp = res; break; } return 0; }