1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
package nametransform
import (
"io"
"os"
"path/filepath"
"strings"
"syscall"
"github.com/rfjakob/gocryptfs/internal/cryptocore"
"github.com/rfjakob/gocryptfs/internal/syscallcompat"
"github.com/rfjakob/gocryptfs/internal/tlog"
)
const (
// DirIVLen is identical to AES block size
DirIVLen = 16
// DirIVFilename is the filename used to store directory IV.
// Exported because we have to ignore this name in directory listing.
DirIVFilename = "gocryptfs.diriv"
)
// ReadDirIV - read the "gocryptfs.diriv" file from "dir" (absolute ciphertext path)
// This function is exported because it allows for an efficient readdir implementation.
func ReadDirIV(dir string) (iv []byte, err error) {
fd, err := os.Open(filepath.Join(dir, DirIVFilename))
if err != nil {
// Note: getting errors here is normal because of concurrent deletes.
return nil, err
}
defer fd.Close()
return fdReadDirIV(fd)
}
// ReadDirIVAt reads "gocryptfs.diriv" from the directory that is opened as "dirfd".
// Using the dirfd makes it immune to concurrent renames of the directory.
func ReadDirIVAt(dirfd *os.File) (iv []byte, err error) {
fdRaw, err := syscallcompat.Openat(int(dirfd.Fd()), DirIVFilename, syscall.O_RDONLY, 0)
if err != nil {
tlog.Warn.Printf("ReadDirIVAt: opening %q in dir %q failed: %v",
DirIVFilename, dirfd.Name(), err)
return nil, err
}
fd := os.NewFile(uintptr(fdRaw), DirIVFilename)
defer fd.Close()
return fdReadDirIV(fd)
}
// fdReadDirIV reads and verifies the DirIV from an opened gocryptfs.diriv file.
func fdReadDirIV(fd *os.File) (iv []byte, err error) {
// We want to detect if the file is bigger than DirIVLen, so
// make the buffer 1 byte bigger than necessary.
iv = make([]byte, DirIVLen+1)
n, err := fd.Read(iv)
if err != nil && err != io.EOF {
tlog.Warn.Printf("ReadDirIVAt: Read failed: %v", err)
return nil, err
}
iv = iv[0:n]
if len(iv) != DirIVLen {
tlog.Warn.Printf("ReadDirIVAt: wanted %d bytes, got %d. Returning EINVAL.", DirIVLen, len(iv))
return nil, syscall.EINVAL
}
return iv, nil
}
// WriteDirIV - create diriv file inside "dir" (absolute ciphertext path)
// This function is exported because it is used from pathfs_frontend, main,
// and also the automated tests.
func WriteDirIV(dir string) error {
iv := cryptocore.RandBytes(DirIVLen)
file := filepath.Join(dir, DirIVFilename)
// 0400 permissions: gocryptfs.diriv should never be modified after creation.
// Don't use "ioutil.WriteFile", it causes trouble on NFS: https://github.com/rfjakob/gocryptfs/issues/105
fd, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0400)
if err != nil {
tlog.Warn.Printf("WriteDirIV: OpenFile: %v", err)
return err
}
_, err = fd.Write(iv)
if err != nil {
tlog.Warn.Printf("WriteDirIV: Write: %v", err)
return err
}
err = fd.Close()
if err != nil {
tlog.Warn.Printf("WriteDirIV: Close: %v", err)
return err
}
return nil
}
// EncryptPathDirIV - encrypt relative plaintext path using EME with DirIV.
// Components that are longer than 255 bytes are hashed if be.longnames == true.
func (be *NameTransform) EncryptPathDirIV(plainPath string, rootDir string) (cipherPath string, err error) {
// Empty string means root directory
if plainPath == "" {
return plainPath, nil
}
// Reject names longer than 255 bytes already here. This relieves everybody
// who uses hashed long names from checking for that later.
baseName := filepath.Base(plainPath)
if len(baseName) > syscall.NAME_MAX {
return "", syscall.ENAMETOOLONG
}
// Check if the DirIV is cached
parentDir := filepath.Dir(plainPath)
iv, cParentDir := be.DirIVCache.lookup(parentDir)
if iv != nil {
cBaseName := be.EncryptName(baseName, iv)
if be.longNames && len(cBaseName) > syscall.NAME_MAX {
cBaseName = be.HashLongName(cBaseName)
}
cipherPath = filepath.Join(cParentDir, cBaseName)
return cipherPath, nil
}
// Not cached - walk the directory tree
var wd = rootDir
var encryptedNames []string
plainNames := strings.Split(plainPath, "/")
for _, plainName := range plainNames {
iv, err = ReadDirIV(wd)
if err != nil {
return "", err
}
encryptedName := be.EncryptName(plainName, iv)
if be.longNames && len(encryptedName) > syscall.NAME_MAX {
encryptedName = be.HashLongName(encryptedName)
}
encryptedNames = append(encryptedNames, encryptedName)
wd = filepath.Join(wd, encryptedName)
}
cipherPath = filepath.Join(encryptedNames...)
// Cache the final DirIV
cParentDir = filepath.Dir(cipherPath)
be.DirIVCache.store(parentDir, iv, cParentDir)
return cipherPath, nil
}
|