summaryrefslogtreecommitdiff
path: root/builder/dockerfile/internals_windows.go
blob: 53c302c7146fce229ba173ddba719efe597a0a40 (plain)
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
package dockerfile // import "github.com/docker/docker/builder/dockerfile"

import (
	"bytes"
	"context"
	"os"
	"path/filepath"
	"strings"

	"github.com/containerd/containerd/platforms"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/mount"
	"github.com/docker/docker/pkg/idtools"
	"github.com/docker/docker/pkg/jsonmessage"
	"golang.org/x/sys/windows"
)

func parseChownFlag(ctx context.Context, builder *Builder, state *dispatchState, chown, ctrRootPath string, identityMapping idtools.IdentityMapping) (idtools.Identity, error) {
	if builder.options.Platform == "windows" {
		return getAccountIdentity(ctx, builder, chown, ctrRootPath, state)
	}

	return identityMapping.RootPair(), nil
}

func getAccountIdentity(ctx context.Context, builder *Builder, accountName string, ctrRootPath string, state *dispatchState) (idtools.Identity, error) {
	// If this is potentially a string SID then attempt to convert it to verify
	// this, otherwise continue looking for the account.
	if strings.HasPrefix(accountName, "S-") || strings.HasPrefix(accountName, "s-") {
		sid, err := windows.StringToSid(accountName)

		if err == nil {
			return idtools.Identity{SID: sid.String()}, nil
		}
	}

	// Attempt to obtain the SID using the name.
	sid, _, accType, err := windows.LookupSID("", accountName)

	// If this is a SID that is built-in and hence the same across all systems then use that.
	if err == nil && (accType == windows.SidTypeAlias || accType == windows.SidTypeWellKnownGroup) {
		return idtools.Identity{SID: sid.String()}, nil
	}

	// Check if the account name is one unique to containers.
	if strings.EqualFold(accountName, "ContainerAdministrator") {
		return idtools.Identity{SID: idtools.ContainerAdministratorSidString}, nil

	} else if strings.EqualFold(accountName, "ContainerUser") {
		return idtools.Identity{SID: idtools.ContainerUserSidString}, nil
	}

	// All other lookups failed, so therefore determine if the account in
	// question exists in the container and if so, obtain its SID.
	return lookupNTAccount(ctx, builder, accountName, state)
}

func lookupNTAccount(ctx context.Context, builder *Builder, accountName string, state *dispatchState) (idtools.Identity, error) {

	source, _ := filepath.Split(os.Args[0])

	target := "C:\\Docker"
	targetExecutable := target + "\\containerutility.exe"

	optionsPlatform, err := platforms.Parse(builder.options.Platform)
	if err != nil {
		return idtools.Identity{}, err
	}

	runConfig := copyRunConfig(state.runConfig,
		withCmdCommentString("internal run to obtain NT account information.", optionsPlatform.OS))

	runConfig.Cmd = []string{targetExecutable, "getaccountsid", accountName}

	hostConfig := &container.HostConfig{Mounts: []mount.Mount{
		{
			Type:     mount.TypeBind,
			Source:   source,
			Target:   target,
			ReadOnly: true,
		},
	},
	}

	container, err := builder.containerManager.Create(ctx, runConfig, hostConfig)
	if err != nil {
		return idtools.Identity{}, err
	}

	stdout := new(bytes.Buffer)
	stderr := new(bytes.Buffer)

	if err := builder.containerManager.Run(ctx, container.ID, stdout, stderr); err != nil {
		if err, ok := err.(*statusCodeError); ok {
			return idtools.Identity{}, &jsonmessage.JSONError{
				Message: stderr.String(),
				Code:    err.StatusCode(),
			}
		}
		return idtools.Identity{}, err
	}

	accountSid := stdout.String()

	return idtools.Identity{SID: accountSid}, nil
}