From 4f42fe2e9e35a01181031ddca07c044647d7bf04 Mon Sep 17 00:00:00 2001
From: Giuseppe Scrivano <gscrivan@redhat.com>
Date: Fri, 21 Sep 2018 10:16:39 +0200
Subject: rootless: error out if there are not enough UIDs/GIDs available

Most container images assume there are at least 65536 UIDs/GIDs
available.  Raise an error if there are not enough IDs allocated to
the current user.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>

Closes: #1520
Approved by: rhatdan
---
 pkg/rootless/rootless_linux.go | 42 ++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 40 insertions(+), 2 deletions(-)

(limited to 'pkg/rootless')

diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go
index d0c6cfa16..5c45f2694 100644
--- a/pkg/rootless/rootless_linux.go
+++ b/pkg/rootless/rootless_linux.go
@@ -11,12 +11,14 @@ import (
 	"os/user"
 	"runtime"
 	"strconv"
+	"strings"
 	"syscall"
 	"unsafe"
 
 	"github.com/containers/storage/pkg/idtools"
 	"github.com/docker/docker/pkg/signal"
 	"github.com/pkg/errors"
+	"github.com/sirupsen/logrus"
 )
 
 /*
@@ -136,6 +138,22 @@ func JoinNSPath(path string) (bool, int, error) {
 	return true, int(ret), nil
 }
 
+const defaultMinimumMappings = 65536
+
+func getMinimumIDs(p string) int {
+	content, err := ioutil.ReadFile(p)
+	if err != nil {
+		logrus.Debugf("error reading data from %q, use a default value of %d", p, defaultMinimumMappings)
+		return defaultMinimumMappings
+	}
+	ret, err := strconv.Atoi(strings.TrimSuffix(string(content), "\n"))
+	if err != nil {
+		logrus.Debugf("error reading data from %q, use a default value of %d", p, defaultMinimumMappings)
+		return defaultMinimumMappings
+	}
+	return ret + 1
+}
+
 // BecomeRootInUserNS re-exec podman in a new userNS.  It returns whether podman was re-executed
 // into a new user namespace and the return code from the re-executed podman process.
 // If podman was re-executed the caller needs to propagate the error code returned by the child
@@ -176,8 +194,28 @@ func BecomeRootInUserNS() (bool, int, error) {
 		}
 	}
 	mappings, err := idtools.NewIDMappings(username, username)
-	if err != nil && os.Getenv("PODMAN_ALLOW_SINGLE_ID_MAPPING_IN_USERNS") == "" {
-		return false, -1, err
+	if os.Getenv("PODMAN_ALLOW_SINGLE_ID_MAPPING_IN_USERNS") == "" {
+		if err != nil {
+			return false, -1, err
+		}
+
+		availableGIDs, availableUIDs := 0, 0
+		for _, i := range mappings.UIDs() {
+			availableUIDs += i.Size
+		}
+
+		minUIDs := getMinimumIDs("/proc/sys/kernel/overflowuid")
+		if availableUIDs < minUIDs {
+			return false, 0, fmt.Errorf("not enough UIDs available for the user, at least %d are needed", minUIDs)
+		}
+
+		for _, i := range mappings.GIDs() {
+			availableGIDs += i.Size
+		}
+		minGIDs := getMinimumIDs("/proc/sys/kernel/overflowgid")
+		if availableGIDs < minGIDs {
+			return false, 0, fmt.Errorf("not enough GIDs available for the user, at least %d are needed", minGIDs)
+		}
 	}
 	if err == nil {
 		uids = mappings.UIDs()
-- 
cgit v1.2.3-54-g00ecf