moby/daemon/names.go
Yong Tang 86ae5d2895 Restrict checkpoint name to prevent directory traversal
This fix tries to address the issue raised in 28769 where
checkpoint name was not checked before passing to containerd.
As a result, it was possible to use a special checkpoint name
to get outside of the container's directory.

This fix add restriction `[a-zA-Z0-9][a-zA-Z0-9_.-]+` (`RestrictedNamePattern`).
This is the same as container name restriction.

This fix fixes 28769.

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
(cherry picked from commit c90ec05175)
Signed-off-by: Victor Vieux <victorvieux@gmail.com>
2016-11-28 11:17:55 -08:00

116 lines
2.8 KiB
Go

package daemon
import (
"fmt"
"strings"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/container"
"github.com/docker/docker/pkg/namesgenerator"
"github.com/docker/docker/pkg/registrar"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/utils"
)
var (
validContainerNameChars = utils.RestrictedNameChars
validContainerNamePattern = utils.RestrictedNamePattern
)
func (daemon *Daemon) registerName(container *container.Container) error {
if daemon.Exists(container.ID) {
return fmt.Errorf("Container is already loaded")
}
if err := validateID(container.ID); err != nil {
return err
}
if container.Name == "" {
name, err := daemon.generateNewName(container.ID)
if err != nil {
return err
}
container.Name = name
if err := container.ToDiskLocking(); err != nil {
logrus.Errorf("Error saving container name to disk: %v", err)
}
}
return daemon.nameIndex.Reserve(container.Name, container.ID)
}
func (daemon *Daemon) generateIDAndName(name string) (string, string, error) {
var (
err error
id = stringid.GenerateNonCryptoID()
)
if name == "" {
if name, err = daemon.generateNewName(id); err != nil {
return "", "", err
}
return id, name, nil
}
if name, err = daemon.reserveName(id, name); err != nil {
return "", "", err
}
return id, name, nil
}
func (daemon *Daemon) reserveName(id, name string) (string, error) {
if !validContainerNamePattern.MatchString(strings.TrimPrefix(name, "/")) {
return "", fmt.Errorf("Invalid container name (%s), only %s are allowed", name, validContainerNameChars)
}
if name[0] != '/' {
name = "/" + name
}
if err := daemon.nameIndex.Reserve(name, id); err != nil {
if err == registrar.ErrNameReserved {
id, err := daemon.nameIndex.Get(name)
if err != nil {
logrus.Errorf("got unexpected error while looking up reserved name: %v", err)
return "", err
}
return "", fmt.Errorf("Conflict. The container name %q is already in use by container %s. You have to remove (or rename) that container to be able to reuse that name.", name, id)
}
return "", fmt.Errorf("error reserving name: %s, error: %v", name, err)
}
return name, nil
}
func (daemon *Daemon) releaseName(name string) {
daemon.nameIndex.Release(name)
}
func (daemon *Daemon) generateNewName(id string) (string, error) {
var name string
for i := 0; i < 6; i++ {
name = namesgenerator.GetRandomName(i)
if name[0] != '/' {
name = "/" + name
}
if err := daemon.nameIndex.Reserve(name, id); err != nil {
if err == registrar.ErrNameReserved {
continue
}
return "", err
}
return name, nil
}
name = "/" + stringid.TruncateID(id)
if err := daemon.nameIndex.Reserve(name, id); err != nil {
return "", err
}
return name, nil
}
func validateID(id string) error {
if id == "" {
return fmt.Errorf("Invalid empty id")
}
return nil
}