A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://github.com/opencontainers/runc/commit/f0b652ea61ff6750a8fcc69865d45a7abf37accf below:

try to scope MkdirAll to stay inside the rootfs · opencontainers/runc@f0b652e · GitHub

File tree Expand file treeCollapse file tree 3 files changed

+176

-10

lines changed

Filter options

Expand file treeCollapse file tree 3 files changed

+176

-10

lines changed Original file line number Diff line number Diff line change

@@ -253,7 +253,7 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error {

253 253

if c.cgroupns {

254 254

subsystemPath := filepath.Join(c.root, b.Destination)

255 255

subsystemName := filepath.Base(b.Destination)

256 -

if err := os.MkdirAll(subsystemPath, 0o755); err != nil {

256 +

if err := utils.MkdirAllInRoot(c.root, subsystemPath, 0o755); err != nil {

257 257

return err

258 258

}

259 259

if err := utils.WithProcfd(c.root, b.Destination, func(procfd string) error {

@@ -406,15 +406,26 @@ func createMountpoint(rootfs string, m *configs.Mount, mountFd *int, source stri

406 406

return "", fmt.Errorf("%w: file bind mount over rootfs", errRootfsToFile)

407 407

}

408 408

// Make the parent directory.

409 -

if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil {

409 +

destDir, destBase := filepath.Split(dest)

410 +

destDirFd, err := utils.MkdirAllInRootOpen(rootfs, destDir, 0o755)

411 +

if err != nil {

410 412

return "", fmt.Errorf("make parent dir of file bind-mount: %w", err)

411 413

}

412 -

// Make the target file.

413 -

f, err := os.OpenFile(dest, os.O_CREATE, 0o755)

414 -

if err != nil {

415 -

return "", fmt.Errorf("create target of file bind-mount: %w", err)

414 +

defer destDirFd.Close()

415 +

// Make the target file. We want to avoid opening any file that is

416 +

// already there because it could be a "bad" file like an invalid

417 +

// device or hung tty that might cause a DoS, so we use mknodat.

418 +

// destBase does not contain any "/" components, and mknodat does

419 +

// not follow trailing symlinks, so we can safely just call mknodat

420 +

// here.

421 +

if err := unix.Mknodat(int(destDirFd.Fd()), destBase, unix.S_IFREG|0o644, 0); err != nil {

422 +

// If we get EEXIST, there was already an inode there and

423 +

// we can consider that a success.

424 +

if !errors.Is(err, unix.EEXIST) {

425 +

err = &os.PathError{Op: "mknod regular file", Path: dest, Err: err}

426 +

return "", fmt.Errorf("create target of file bind-mount: %w", err)

427 +

}

416 428

}

417 -

_ = f.Close()

418 429

// Nothing left to do.

419 430

return dest, nil

420 431

}

@@ -433,7 +444,7 @@ func createMountpoint(rootfs string, m *configs.Mount, mountFd *int, source stri

433 444

}

434 445

}

435 446 436 -

if err := os.MkdirAll(dest, 0o755); err != nil {

447 +

if err := utils.MkdirAllInRoot(rootfs, dest, 0o755); err != nil {

437 448

return "", err

438 449

}

439 450

return dest, nil

@@ -463,7 +474,7 @@ func mountToRootfs(m *configs.Mount, c *mountConfig) error {

463 474

} else if !fi.IsDir() {

464 475

return fmt.Errorf("filesystem %q must be mounted on ordinary directory", m.Device)

465 476

}

466 -

if err := os.MkdirAll(dest, 0o755); err != nil {

477 +

if err := utils.MkdirAllInRoot(rootfs, dest, 0o755); err != nil {

467 478

return err

468 479

}

469 480

// Selinux kernels do not support labeling of /proc or /sys.

@@ -751,7 +762,7 @@ func createDeviceNode(rootfs string, node *devices.Device, bind bool) error {

751 762

if dest == rootfs {

752 763

return fmt.Errorf("%w: mknod over rootfs", errRootfsToFile)

753 764

}

754 -

if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil {

765 +

if err := utils.MkdirAllInRoot(rootfs, filepath.Dir(dest), 0o755); err != nil {

755 766

return err

756 767

}

757 768

if bind {

Original file line number Diff line number Diff line change

@@ -6,6 +6,8 @@ package system

6 6

import (

7 7

"os"

8 8

"os/exec"

9 +

"runtime"

10 +

"strings"

9 11

"unsafe"

10 12 11 13

"golang.org/x/sys/unix"

@@ -102,3 +104,42 @@ func GetSubreaper() (int, error) {

102 104 103 105

return int(i), nil

104 106

}

107 + 108 +

func prepareAt(dir *os.File, path string) (int, string) {

109 +

if dir == nil {

110 +

return unix.AT_FDCWD, path

111 +

}

112 + 113 +

// Rather than just filepath.Join-ing path here, do it manually so the

114 +

// error and handle correctly indicate cases like path=".." as being

115 +

// relative to the correct directory. The handle.Name() might end up being

116 +

// wrong but because this is (currently) only used in MkdirAllInRoot, that

117 +

// isn't a problem.

118 +

dirName := dir.Name()

119 +

if !strings.HasSuffix(dirName, "/") {

120 +

dirName += "/"

121 +

}

122 +

fullPath := dirName + path

123 + 124 +

return int(dir.Fd()), fullPath

125 +

}

126 + 127 +

func Openat(dir *os.File, path string, flags int, mode uint32) (*os.File, error) {

128 +

dirFd, fullPath := prepareAt(dir, path)

129 +

fd, err := unix.Openat(dirFd, path, flags, mode)

130 +

if err != nil {

131 +

return nil, &os.PathError{Op: "openat", Path: fullPath, Err: err}

132 +

}

133 +

runtime.KeepAlive(dir)

134 +

return os.NewFile(uintptr(fd), fullPath), nil

135 +

}

136 + 137 +

func Mkdirat(dir *os.File, path string, mode uint32) error {

138 +

dirFd, fullPath := prepareAt(dir, path)

139 +

err := unix.Mkdirat(dirFd, path, mode)

140 +

if err != nil {

141 +

err = &os.PathError{Op: "mkdirat", Path: fullPath, Err: err}

142 +

}

143 +

runtime.KeepAlive(dir)

144 +

return err

145 +

}

Original file line number Diff line number Diff line change

@@ -4,12 +4,17 @@

4 4

package utils

5 5 6 6

import (

7 +

"errors"

7 8

"fmt"

8 9

"os"

10 +

"path/filepath"

9 11

"strconv"

10 12

"strings"

11 13

_ "unsafe" // for go:linkname

12 14 15 +

"github.com/opencontainers/runc/libcontainer/system"

16 + 17 +

securejoin "github.com/cyphar/filepath-securejoin"

13 18

"golang.org/x/sys/unix"

14 19

)

15 20

@@ -130,3 +135,112 @@ func IsLexicallyInRoot(root, path string) bool {

130 135

}

131 136

return strings.HasPrefix(path, root)

132 137

}

138 + 139 +

// MkdirAllInRootOpen attempts to make

140 +

//

141 +

// path, _ := securejoin.SecureJoin(root, unsafePath)

142 +

// os.MkdirAll(path, mode)

143 +

// os.Open(path)

144 +

//

145 +

// safer against attacks where components in the path are changed between

146 +

// SecureJoin returning and MkdirAll (or Open) being called. In particular, we

147 +

// try to detect any symlink components in the path while we are doing the

148 +

// MkdirAll.

149 +

//

150 +

// NOTE: Unlike os.MkdirAll, mode is not Go's os.FileMode, it is the unix mode

151 +

// (the suid/sgid/sticky bits are not the same as for os.FileMode).

152 +

//

153 +

// NOTE: If unsafePath is a subpath of root, we assume that you have already

154 +

// called SecureJoin and so we use the provided path verbatim without resolving

155 +

// any symlinks (this is done in a way that avoids symlink-exchange races).

156 +

// This means that the path also must not contain ".." elements, otherwise an

157 +

// error will occur.

158 +

//

159 +

// This is a somewhat less safe alternative to

160 +

// <https://github.com/cyphar/filepath-securejoin/pull/13>, but it should

161 +

// detect attempts to trick us into creating directories outside of the root.

162 +

// We should migrate to securejoin.MkdirAll once it is merged.

163 +

func MkdirAllInRootOpen(root, unsafePath string, mode uint32) (_ *os.File, Err error) {

164 +

// If the path is already "within" the root, use it verbatim.

165 +

fullPath := unsafePath

166 +

if !IsLexicallyInRoot(root, unsafePath) {

167 +

var err error

168 +

fullPath, err = securejoin.SecureJoin(root, unsafePath)

169 +

if err != nil {

170 +

return nil, err

171 +

}

172 +

}

173 +

subPath, err := filepath.Rel(root, fullPath)

174 +

if err != nil {

175 +

return nil, err

176 +

}

177 + 178 +

// Check for any silly mode bits.

179 +

if mode&^0o7777 != 0 {

180 +

return nil, fmt.Errorf("tried to include non-mode bits in MkdirAll mode: 0o%.3o", mode)

181 +

}

182 + 183 +

currentDir, err := os.OpenFile(root, unix.O_DIRECTORY|unix.O_CLOEXEC, 0)

184 +

if err != nil {

185 +

return nil, fmt.Errorf("open root handle: %w", err)

186 +

}

187 +

defer func() {

188 +

if Err != nil {

189 +

currentDir.Close()

190 +

}

191 +

}()

192 + 193 +

for _, part := range strings.Split(subPath, string(filepath.Separator)) {

194 +

switch part {

195 +

case "", ".":

196 +

// Skip over no-op components.

197 +

continue

198 +

case "..":

199 +

return nil, fmt.Errorf("possible breakout detected: found %q component in SecureJoin subpath %s", part, subPath)

200 +

}

201 + 202 +

nextDir, err := system.Openat(currentDir, part, unix.O_DIRECTORY|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0)

203 +

switch {

204 +

case err == nil:

205 +

// Update the currentDir.

206 +

_ = currentDir.Close()

207 +

currentDir = nextDir

208 + 209 +

case errors.Is(err, unix.ENOTDIR):

210 +

// This might be a symlink or some other random file. Either way,

211 +

// error out.

212 +

return nil, fmt.Errorf("cannot mkdir in %s/%s: %w", currentDir.Name(), part, unix.ENOTDIR)

213 + 214 +

case errors.Is(err, os.ErrNotExist):

215 +

// Luckily, mkdirat will not follow trailing symlinks, so this is

216 +

// safe to do as-is.

217 +

if err := system.Mkdirat(currentDir, part, mode); err != nil {

218 +

return nil, err

219 +

}

220 +

// Open the new directory. There is a race here where an attacker

221 +

// could swap the directory with a different directory, but

222 +

// MkdirAll's fuzzy semantics mean we don't care about that.

223 +

nextDir, err := system.Openat(currentDir, part, unix.O_DIRECTORY|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0)

224 +

if err != nil {

225 +

return nil, fmt.Errorf("open newly created directory: %w", err)

226 +

}

227 +

// Update the currentDir.

228 +

_ = currentDir.Close()

229 +

currentDir = nextDir

230 + 231 +

default:

232 +

return nil, err

233 +

}

234 +

}

235 +

return currentDir, nil

236 +

}

237 + 238 +

// MkdirAllInRoot is a wrapper around MkdirAllInRootOpen which closes the

239 +

// returned handle, for callers that don't need to use it.

240 +

func MkdirAllInRoot(root, unsafePath string, mode uint32) error {

241 +

f, err := MkdirAllInRootOpen(root, unsafePath, mode)

242 +

if err == nil {

243 +

_ = f.Close()

244 +

}

245 +

return err

246 +

}

You can’t perform that action at this time.


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4