| package dockerfile | |
| import ( | |
| "bytes" | |
| "context" | |
| "os" | |
| "path/filepath" | |
| "strings" | |
| "github.com/containerd/platforms" | |
| "github.com/moby/moby/api/types/container" | |
| "github.com/moby/moby/api/types/jsonstream" | |
| "github.com/moby/moby/api/types/mount" | |
| "github.com/moby/moby/v2/errdefs" | |
| "github.com/moby/sys/user" | |
| "golang.org/x/sys/windows" | |
| ) | |
| // seTakeOwnershipPrivilege is "SE_TAKE_OWNERSHIP_NAME" in the win32 API. | |
| // | |
| // see https://learn.microsoft.com/en-us/windows/win32/secauthz/privilege-constants | |
| const seTakeOwnershipPrivilege = "SeTakeOwnershipPrivilege" | |
| // Constants for well-known SIDs in the Windows container. | |
| // These are currently undocumented. | |
| // See https://github.com/moby/buildkit/pull/5791#discussion_r1976652227 for more information. | |
| const ( | |
| containerAdministratorSidString = "S-1-5-93-2-1" // ContainerAdministrator | |
| containerUserSidString = "S-1-5-93-2-2" // ContainerUser | |
| ) | |
| func parseChownFlag(ctx context.Context, builder *Builder, state *dispatchState, chown, ctrRootPath string, identityMapping user.IdentityMapping) (identity, error) { | |
| if builder.options.Platform == "windows" { | |
| return getAccountIdentity(ctx, builder, chown, ctrRootPath, state) | |
| } | |
| uid, gid := identityMapping.RootPair() | |
| return identity{UID: uid, GID: gid}, nil | |
| } | |
| func getAccountIdentity(ctx context.Context, builder *Builder, accountName string, ctrRootPath string, state *dispatchState) (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 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 identity{SID: sid.String()}, nil | |
| } | |
| // Check if the account name is one unique to containers. | |
| if strings.EqualFold(accountName, "ContainerAdministrator") { | |
| return identity{SID: containerAdministratorSidString}, nil | |
| } else if strings.EqualFold(accountName, "ContainerUser") { | |
| return identity{SID: 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) (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 identity{}, errdefs.InvalidParameter(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 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 identity{}, &jsonstream.Error{ | |
| Message: stderr.String(), | |
| Code: err.StatusCode(), | |
| } | |
| } | |
| return identity{}, err | |
| } | |
| accountSid := stdout.String() | |
| return identity{SID: accountSid}, nil | |
| } |