From 75d53c469ea6115db0386155262565a8aa15556d Mon Sep 17 00:00:00 2001
From: jingrui <jingrui@huawei.com>
Date: Thu, 3 Jan 2019 16:04:37 +0800
Subject: [PATCH 044/111] plugin:  Fix plugin security bug caused by
 unchecked plugin name

reason: cherry-pick commits to docker-18.09

cherry-pick from 48c9622f82 | * plugin,bugfix: Fix plugin security bug
caused by unchecked plugin name

Docker may activate plugins outside plugin directory if plugin name
contains string like "../../". This patch fix this bug by checking the
combined plugin path before use it.

fix issue docker/docker#268

Change-Id: Icff8b24e50fc92721149267bc8c29a8652046d8a
Signed-off-by: majiuyue <majiuyue@huawei.com>
Signed-off-by: jingrui <jingrui@huawei.com>
---
 components/engine/pkg/plugins/discovery.go | 27 ++++++++++++++++------
 components/engine/pkg/plugins/plugins.go   |  2 +-
 2 files changed, 21 insertions(+), 8 deletions(-)

diff --git a/components/engine/pkg/plugins/discovery.go b/components/engine/pkg/plugins/discovery.go
index 4b79bd29ad..51f1d8ebea 100644
--- a/components/engine/pkg/plugins/discovery.go
+++ b/components/engine/pkg/plugins/discovery.go
@@ -14,6 +14,8 @@ import (
 )
 
 var (
+	// ErrForbidden plugin sock/spec outside allowed location
+	ErrForbidden = errors.New("plugin outside allowed location")
 	// ErrNotFound plugin not found
 	ErrNotFound = errors.New("plugin not found")
 	socketsPath = "/run/docker/plugins"
@@ -82,7 +84,10 @@ func Scan() ([]string, error) {
 
 // Plugin returns the plugin registered with the given name (or returns an error).
 func (l *localRegistry) Plugin(name string) (*Plugin, error) {
-	socketpaths := pluginPaths(socketsPath, name, ".sock")
+	socketpaths, err := pluginPaths(socketsPath, name, ".sock")
+	if err != nil {
+		return nil, ErrForbidden
+	}
 
 	for _, p := range socketpaths {
 		if fi, err := os.Stat(p); err == nil && fi.Mode()&os.ModeSocket != 0 {
@@ -92,8 +97,10 @@ func (l *localRegistry) Plugin(name string) (*Plugin, error) {
 
 	var txtspecpaths []string
 	for _, p := range specsPaths {
-		txtspecpaths = append(txtspecpaths, pluginPaths(p, name, ".spec")...)
-		txtspecpaths = append(txtspecpaths, pluginPaths(p, name, ".json")...)
+		for _, ext := range []string{".spec", ".json"} {
+			paths, _ := pluginPaths(p, name, ext)
+			txtspecpaths = append(txtspecpaths, paths...)
+		}
 	}
 
 	for _, p := range txtspecpaths {
@@ -146,9 +153,15 @@ func readPluginJSONInfo(name, path string) (*Plugin, error) {
 	return &p, nil
 }
 
-func pluginPaths(base, name, ext string) []string {
-	return []string{
-		filepath.Join(base, name+ext),
-		filepath.Join(base, name, name+ext),
+func pluginPaths(base, name, ext string) ([]string, error) {
+	paths := []string{
+		filepath.Clean(filepath.Join(base, name+ext)),
+		filepath.Clean(filepath.Join(base, name, name+ext)),
+	}
+	for _, p := range paths {
+		if !strings.HasPrefix(p, base) {
+			return nil, ErrForbidden
+		}
 	}
+	return paths, nil
 }
diff --git a/components/engine/pkg/plugins/plugins.go b/components/engine/pkg/plugins/plugins.go
index 6962079df9..8a6fbeda29 100644
--- a/components/engine/pkg/plugins/plugins.go
+++ b/components/engine/pkg/plugins/plugins.go
@@ -208,7 +208,7 @@ func loadWithRetry(name string, retry bool) (*Plugin, error) {
 	for {
 		pl, err := registry.Plugin(name)
 		if err != nil {
-			if !retry {
+			if !retry || err == ErrForbidden {
 				return nil, err
 			}
 
-- 
2.17.1

