Beispiel #1
0
// isNameFile determines if the path points to a gocryptfs.longname.*.name
// file
func (rfs *ReverseFS) isNameFile(relPath string) bool {
	if rfs.args.PlaintextNames {
		return false
	}
	fileType := nametransform.NameType(filepath.Base(relPath))
	return fileType == nametransform.LongNameFilename
}
Beispiel #2
0
func (rfs *ReverseFS) decryptPath(relPath string) (string, error) {
	if rfs.args.PlaintextNames || relPath == "" {
		return relPath, nil
	}
	var err error
	var transformedParts []string
	parts := strings.Split(relPath, "/")
	for i, part := range parts {
		// Start at the top and recurse
		currentCipherDir := filepath.Join(parts[:i]...)
		nameType := nametransform.NameType(part)
		dirIV := derivePathIV(currentCipherDir, ivPurposeDirIV)
		var transformedPart string
		if nameType == nametransform.LongNameNone {
			transformedPart, err = rfs.nameTransform.DecryptName(part, dirIV)
			if err != nil {
				// We get lots of decrypt requests for names like ".Trash" that
				// are invalid base64. Convert them to ENOENT so the correct
				// error gets returned to the user.
				if _, ok := err.(base64.CorruptInputError); ok {
					return "", syscall.ENOENT
				}
				// Stat attempts on the link target of encrypted symlinks.
				// These are always valid base64 but the length is not a
				// multiple of 16.
				if err == syscall.EINVAL {
					return "", syscall.ENOENT
				}
				return "", err
			}
		} else if nameType == nametransform.LongNameContent {
			currentPlainDir := filepath.Join(transformedParts[:i]...)
			transformedPart, err = rfs.findLongnameParent(currentPlainDir, dirIV, part)
			if err != nil {
				return "", err
			}
		} else {
			// It makes no sense to decrypt a ".name" file
			tlog.Warn.Printf("decryptPath: tried to decrypt %q!? Returning EINVAL.", part)
			return "", syscall.EINVAL
		}
		transformedParts = append(transformedParts, transformedPart)
	}
	return filepath.Join(transformedParts...), nil
}
Beispiel #3
0
// OpenDir implements pathfs.FileSystem
func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) {
	tlog.Debug.Printf("OpenDir(%s)", dirName)
	cDirName, err := fs.encryptPath(dirName)
	if err != nil {
		return nil, fuse.ToStatus(err)
	}
	// Read ciphertext directory
	cipherEntries, status := fs.FileSystem.OpenDir(cDirName, context)
	if cipherEntries == nil {
		return nil, status
	}
	// Get DirIV (stays nil if PlaintextNames is used)
	var cachedIV []byte
	var cDirAbsPath string
	if !fs.args.PlaintextNames {
		// Read the DirIV once and use it for all later name decryptions
		cDirAbsPath = filepath.Join(fs.args.Cipherdir, cDirName)
		cachedIV, err = nametransform.ReadDirIV(cDirAbsPath)
		if err != nil {
			// This can happen during normal operation when the directory has
			// been deleted concurrently. But it can also mean that the
			// gocryptfs.diriv is missing due to an error, so log the event
			// at "info" level.
			tlog.Info.Printf("OpenDir: %v", err)
			return nil, fuse.ToStatus(err)
		}
	}

	// Decrypted directory entries
	var plain []fuse.DirEntry
	var errorCount int
	// Filter and decrypt filenames
	for i := range cipherEntries {
		cName := cipherEntries[i].Name
		if dirName == "" && cName == configfile.ConfDefaultName {
			// silently ignore "gocryptfs.conf" in the top level dir
			continue
		}
		if !fs.args.PlaintextNames && cName == nametransform.DirIVFilename {
			// silently ignore "gocryptfs.diriv" everywhere if dirIV is enabled
			continue
		}

		if fs.args.PlaintextNames {
			plain = append(plain, cipherEntries[i])
			continue
		}

		// Handle long file name
		isLong := nametransform.LongNameNone
		if fs.args.LongNames {
			isLong = nametransform.NameType(cName)
		}
		if isLong == nametransform.LongNameContent {
			cNameLong, err := nametransform.ReadLongName(filepath.Join(cDirAbsPath, cName))
			if err != nil {
				tlog.Warn.Printf("Skipping entry %q in dir %q: Could not read .name: %v",
					cName, cDirName, err)
				errorCount++
				continue
			}
			cName = cNameLong
		} else if isLong == nametransform.LongNameFilename {
			// ignore "gocryptfs.longname.*.name"
			continue
		}

		name, err := fs.nameTransform.DecryptName(cName, cachedIV)
		if err != nil {
			tlog.Warn.Printf("Skipping entry %q in dir %q: %s",
				cName, cDirName, err)
			errorCount++
			continue
		}

		cipherEntries[i].Name = name
		plain = append(plain, cipherEntries[i])
	}

	if errorCount > 0 && len(plain) == 0 {
		// Don't let the user stare on an empty directory. Report that things went
		// wrong.
		tlog.Warn.Printf("All %d entries in directory %q were invalid, returning EIO",
			errorCount, cDirName)
		status = fuse.EIO
	}

	return plain, status
}