func (s *Contester) Clear(request *contester_proto.ClearSandboxRequest, response *contester_proto.EmptyMessage) error { ec := tools.ErrorContext("Clear") sandbox, err := getSandboxById(s.Sandboxes, request.GetSandbox()) if err != nil { return ec.NewError(err, "getSandboxById") } sandbox.Mutex.Lock() defer sandbox.Mutex.Unlock() repeat := true for retries := 10; retries > 0 && repeat; retries-- { repeat, err = tryClearPath(sandbox.Path) if repeat && err != nil { log.Error(err) time.Sleep(time.Second / 2) } } if err != nil { return ec.NewError(err, "tryClearPath") } return nil }
// Login and load user profile. Also, set finalizer on s to logout() above. func (s *LoginInfo) Prepare() error { var err error if s.Username == "" { return nil } ec := tools.ErrorContext("LoginInfo.Prepare") s.HUser, err = win32.LogonUser( syscall.StringToUTF16Ptr(s.Username), syscall.StringToUTF16Ptr("."), syscall.StringToUTF16Ptr(s.Password), win32.LOGON32_LOGON_INTERACTIVE, win32.LOGON32_PROVIDER_DEFAULT) if err != nil { return ec.NewError(err) } s.HProfile, err = loadProfile(s.HUser, s.Username) if err != nil { syscall.CloseHandle(s.HUser) s.HUser = syscall.InvalidHandle return ec.NewError(err) } runtime.SetFinalizer(s, logout) return nil }
func (sub *Subprocess) CreateFrozen() (*SubprocessData, error) { ec := tools.ErrorContext("CreateFrozen") if sub.Cmd.ApplicationName == nil { return nil, ec.NewError(fmt.Errorf("Application name must be present"), "init") } d := &SubprocessData{} var stdh linux.StdHandles err := d.wAllRedirects(sub, &stdh) defer stdh.Close() if err != nil { return nil, ec.NewError(err, "Redirects") } var uid int if sub.Login != nil { uid = sub.Login.Uid } d.platformData.params, err = linux.CreateCloneParams( *sub.Cmd.ApplicationName, sub.Cmd.Parameters, sub.Environment, sub.CurrentDirectory, uid, stdh) if err != nil { return nil, ec.NewError(err, "CreateCloneParams") } syscall.ForkLock.Lock() d.platformData.Pid, err = d.platformData.params.CloneFrozen() closeDescriptors(d.closeAfterStart) syscall.ForkLock.Unlock() if err != nil { return nil, ec.NewError(err, "CloneFrozen") } err = SetupControlGroup(sub, d) if err != nil { return nil, ec.NewError(err, "SetupControlGroup") } return d, nil }
func Interconnect(s1, s2 *Subprocess, d1, d2 *os.File) error { ec := tools.ErrorContext("Interconnect") read1, write1, err := RecordingPipe(d1) if err != nil { return ec.NewError(err, "RecordingPipe") } read2, write2, err := RecordingPipe(d2) if err != nil { read1.Close() write1.Close() return ec.NewError(err, "RecordingPipe") } s1.StdIn = &Redirect{ Mode: REDIRECT_PIPE, Pipe: read1, } s2.StdOut = &Redirect{ Mode: REDIRECT_PIPE, Pipe: write1, } s1.StdOut = &Redirect{ Mode: REDIRECT_PIPE, Pipe: write2, } s2.StdIn = &Redirect{ Mode: REDIRECT_PIPE, Pipe: read2, } return nil }
func CreateJob(s *Subprocess, d *SubprocessData) error { var e error ec := tools.ErrorContext("CreateJob") d.platformData.hJob, e = win32.CreateJobObject(nil, nil) if e != nil { return ec.NewError(e, "CreateJobObject") } if s.RestrictUi { var info win32.JobObjectBasicUiRestrictions info.UIRestrictionClass = (win32.JOB_OBJECT_UILIMIT_DESKTOP | win32.JOB_OBJECT_UILIMIT_DISPLAYSETTINGS | win32.JOB_OBJECT_UILIMIT_EXITWINDOWS | win32.JOB_OBJECT_UILIMIT_GLOBALATOMS | win32.JOB_OBJECT_UILIMIT_HANDLES | win32.JOB_OBJECT_UILIMIT_READCLIPBOARD | win32.JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS | win32.JOB_OBJECT_UILIMIT_WRITECLIPBOARD) e = win32.SetJobObjectBasicUiRestrictions(d.platformData.hJob, &info) if e != nil { return ec.NewError(e, "SetJobObjectBasicUiRestrictions") } } var einfo win32.JobObjectExtendedLimitInformation einfo.BasicLimitInformation.LimitFlags = win32.JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION | win32.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE if s.HardTimeLimit > 0 { einfo.BasicLimitInformation.PerJobUserTimeLimit = uint64(s.HardTimeLimit.Nanoseconds() / 100) einfo.BasicLimitInformation.PerProcessUserTimeLimit = uint64(s.HardTimeLimit.Nanoseconds() / 100) einfo.BasicLimitInformation.LimitFlags |= win32.JOB_OBJECT_LIMIT_PROCESS_TIME | win32.JOB_OBJECT_LIMIT_JOB_TIME } if s.ProcessLimit > 0 { einfo.BasicLimitInformation.ActiveProcessLimit = s.ProcessLimit einfo.BasicLimitInformation.LimitFlags |= win32.JOB_OBJECT_LIMIT_ACTIVE_PROCESS } if s.HardMemoryLimit > 0 { einfo.ProcessMemoryLimit = uintptr(s.HardMemoryLimit) einfo.JobMemoryLimit = uintptr(s.HardMemoryLimit) einfo.BasicLimitInformation.MaximumWorkingSetSize = uintptr(s.HardMemoryLimit) einfo.BasicLimitInformation.LimitFlags |= win32.JOB_OBJECT_LIMIT_JOB_MEMORY | win32.JOB_OBJECT_LIMIT_PROCESS_MEMORY | win32.JOB_OBJECT_LIMIT_WORKINGSET } // If we don't create job then we need to set process affinity on the process handle after its creation. // if s.ProcessAffinityMask != 0 { // einfo.BasicLimitInformation.Affinity = uintptr(s.ProcessAffinityMask) // einfo.BasicLimitInformation.LimitFlags |= win32.JOB_OBJECT_LIMIT_AFFINITY //} e = win32.SetJobObjectExtendedLimitInformation(d.platformData.hJob, &einfo) if e != nil { return ec.NewError(e, "SetJobObjectExtendedLimitInformation") } return nil }
func NewLoginInfo(username, password string) (*LoginInfo, error) { ec := tools.ErrorContext("NewLoginInfo") u, err := user.Lookup(username) if err != nil { return nil, ec.NewError(err, "user.Lookup") } uid, err := strconv.Atoi(u.Uid) if err != nil { return nil, ec.NewError(err, "strconv.Atoi") } return &LoginInfo{ Uid: uid, }, nil }
func filerUpload(localName, remoteName, checksum, moduleType, authToken string) (stat *contester_proto.FileStat, err error) { ec := tools.ErrorContext("upload") if stat, err = tools.StatFile(localName, true); err != nil || stat == nil { return stat, err } if checksum != "" && stat.GetChecksum() != checksum { return nil, fmt.Errorf("Checksum mismatch, local %s != %s", stat.GetChecksum(), checksum) } checksum = stat.GetChecksum() local, err := os.Open(localName) if err != nil { return nil, ec.NewError(err, "local.Open") } defer local.Close() req, err := http.NewRequest("PUT", remoteName, local) if err != nil { return nil, err } if moduleType != "" { req.Header.Add("X-FS-Module-Type", moduleType) } if authToken != "" { req.Header.Add("Authorization", "bearer "+authToken) } req.Header.Add("X-FS-Content-Length", strconv.FormatUint(stat.GetSize_(), 10)) var base64sha1 string if checksum != "" && strings.HasPrefix(checksum, "sha1:") { if data, err := hex.DecodeString(strings.TrimPrefix(checksum, "sha1:")); err == nil { base64sha1 = base64.StdEncoding.EncodeToString(data) req.Header.Add("Digest", "SHA="+base64sha1) } } resp, err := http.DefaultClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() var st uploadStatus err = json.NewDecoder(resp.Body).Decode(&st) if err != nil { return nil, err } if st.Size != int64(stat.GetSize_()) || (base64sha1 != "" && base64sha1 != st.Digests["SHA"]) { return nil, fmt.Errorf("upload integrity verification failed") } return stat, nil }
func (s *Contester) Put(request *contester_proto.FileBlob, response *contester_proto.FileStat) error { ec := tools.ErrorContext("Put") resolved, sandbox, err := resolvePath(s.Sandboxes, *request.Name, true) if err != nil { return ec.NewError(err, "resolvePath") } if sandbox != nil { sandbox.Mutex.Lock() defer sandbox.Mutex.Unlock() } var destination *os.File for { destination, err = os.Create(resolved) loop, err := OnOsCreateError(err) if err != nil { return ec.NewError(err, "os.Create") } if !loop { break } } data, err := request.Data.Bytes() if err != nil { return ec.NewError(err, "request.Data.Bytes") } _, err = destination.Write(data) if err != nil { return ec.NewError(err, "destination.Write") } destination.Close() if sandbox != nil { return sandbox.Own(resolved) } stat, err := tools.StatFile(resolved, true) if err != nil { return ec.NewError(err, "statFile") } *response = *stat return nil }
// Loads user profile, using handle and username. func loadProfile(user syscall.Handle, username string) (syscall.Handle, error) { ec := tools.ErrorContext("loadProfile") var pinfo win32.ProfileInfo var err error pinfo.Size = uint32(unsafe.Sizeof(pinfo)) pinfo.Flags = win32.PI_NOUI pinfo.Username, err = syscall.UTF16PtrFromString(username) if err != nil { return syscall.InvalidHandle, ec.NewError(err, ERR_USER, "UTF16PtrFromString") } err = win32.LoadUserProfile(user, &pinfo) if err != nil { log.Debug("Error loading profile for %d/%s", user, username) return syscall.InvalidHandle, ec.NewError(err, "LoadUserProfile") } return pinfo.Profile, nil }
func (s *Contester) Stat(request *contester_proto.StatRequest, response *contester_proto.FileStats) error { ec := tools.ErrorContext("Stat") if request.SandboxId != nil { sandbox, err := getSandboxById(s.Sandboxes, *request.SandboxId) if err != nil { return ec.NewError(err, "getSandboxById") } sandbox.Mutex.RLock() defer sandbox.Mutex.RUnlock() } response.Entries = make([]*contester_proto.FileStat, 0, len(request.Name)) for _, name := range request.Name { resolved, _, err := resolvePath(s.Sandboxes, name, false) if err != nil { return ec.NewError(err, "resolvePath") } var expanded []string if request.GetExpand() { expanded, err = filepath.Glob(resolved) if err != nil { return ec.NewError(err, "filepath.Glob") } } else { expanded = []string{resolved} } for _, name := range expanded { stat, err := tools.StatFile(name, request.GetCalculateChecksum()) if err != nil { return ec.NewError(err, "statFile") } if stat != nil { response.Entries = append(response.Entries, stat) } } } return nil }
func (s *Contester) Get(request *contester_proto.GetRequest, response *contester_proto.FileBlob) error { ec := tools.ErrorContext("Get") resolved, sandbox, err := resolvePath(s.Sandboxes, *request.Name, false) if err != nil { return ec.NewError(err, "resolvePath") } if sandbox != nil { sandbox.Mutex.RLock() defer sandbox.Mutex.RUnlock() } source, err := os.Open(resolved) if err != nil { return ec.NewError(err, "os.Open") } defer source.Close() response.Name = &resolved response.Data, err = contester_proto.BlobFromStream(source) return err }
func InjectDll(d *SubprocessData, loadLibraryW uintptr, dll string) error { if int(loadLibraryW) == 0 { return nil } ec := tools.ErrorContext("InjectDll") log.Debug("InjectDll: Injecting library %s with call to %d", dll, loadLibraryW) name, err := syscall.UTF16FromString(dll) if err != nil { return ec.NewError(err, ERR_USER, "UTF16FromString") } nameLen := uint32((len(name) + 1) * 2) remoteName, err := win32.VirtualAllocEx(d.platformData.hProcess, 0, nameLen, win32.MEM_COMMIT, win32.PAGE_READWRITE) if err != nil { return ec.NewError(err) } defer win32.VirtualFreeEx(d.platformData.hProcess, remoteName, 0, win32.MEM_RELEASE) _, err = win32.WriteProcessMemory(d.platformData.hProcess, remoteName, unsafe.Pointer(&name[0]), nameLen) if err != nil { return ec.NewError(err) } thread, _, err := win32.CreateRemoteThread(d.platformData.hProcess, win32.MakeInheritSa(), 0, loadLibraryW, remoteName, 0) if err != nil { return ec.NewError(err) } defer syscall.CloseHandle(thread) wr, err := syscall.WaitForSingleObject(thread, syscall.INFINITE) if err != nil { return ec.NewError(os.NewSyscallError("WaitForSingleObject", err)) } if wr != syscall.WAIT_OBJECT_0 { return ec.NewError(fmt.Errorf("Unexpected wait result %s", wr)) } return nil }
func tryClearPath(path string) (bool, error) { ec := tools.ErrorContext("tryClearPath") files, err := ioutil.ReadDir(path) if err != nil { return false, ec.NewError(err, "ioutil.ReadDir") } if len(files) == 0 { return false, nil } for _, info := range files { if info.Name() == "." || info.Name() == ".." { continue } fullpath := filepath.Join(path, info.Name()) err = os.RemoveAll(fullpath) if err != nil { return true, ec.NewError(err, "os.RemoveAll") } } return true, nil }
func (sub *Subprocess) CreateFrozen() (*SubprocessData, error) { d := &SubprocessData{} si := &syscall.StartupInfo{} si.Cb = uint32(unsafe.Sizeof(*si)) si.Flags = win32.STARTF_FORCEOFFFEEDBACK | syscall.STARTF_USESHOWWINDOW si.ShowWindow = syscall.SW_SHOWMINNOACTIVE useCreateProcessWithLogonW := sub.NoJob || win32.IsWindows8OrGreater() if !useCreateProcessWithLogonW && sub.Options != nil && sub.Options.Desktop != "" { si.Desktop = syscall.StringToUTF16Ptr(sub.Options.Desktop) } ec := tools.ErrorContext("CreateFrozen") e := d.wAllRedirects(sub, si) if e != nil { return nil, e } pi := &syscall.ProcessInformation{} applicationName := win32.StringPtrToUTF16Ptr(sub.Cmd.ApplicationName) commandLine := win32.StringPtrToUTF16Ptr(sub.Cmd.CommandLine) environment := win32.ListToEnvironmentBlock(sub.Environment) currentDirectory := win32.StringPtrToUTF16Ptr(sub.CurrentDirectory) var syscallName string syscall.ForkLock.Lock() wSetInherit(si) if sub.Login != nil { if useCreateProcessWithLogonW { syscallName = "CreateProcessWithLogonW" e = win32.CreateProcessWithLogonW( syscall.StringToUTF16Ptr(sub.Login.Username), syscall.StringToUTF16Ptr("."), syscall.StringToUTF16Ptr(sub.Login.Password), win32.LOGON_WITH_PROFILE, applicationName, commandLine, win32.CREATE_SUSPENDED|syscall.CREATE_UNICODE_ENVIRONMENT, environment, currentDirectory, si, pi) } else { syscallName = "CreateProcessAsUser" e = win32.CreateProcessAsUser( sub.Login.HUser, applicationName, commandLine, nil, nil, true, win32.CREATE_NEW_PROCESS_GROUP|win32.CREATE_NEW_CONSOLE|win32.CREATE_SUSPENDED| syscall.CREATE_UNICODE_ENVIRONMENT|win32.CREATE_BREAKAWAY_FROM_JOB, environment, currentDirectory, si, pi) } } else { syscallName = "CreateProcess" e = syscall.CreateProcess( applicationName, commandLine, nil, nil, true, win32.CREATE_NEW_PROCESS_GROUP|win32.CREATE_NEW_CONSOLE|win32.CREATE_SUSPENDED| syscall.CREATE_UNICODE_ENVIRONMENT|win32.CREATE_BREAKAWAY_FROM_JOB, environment, currentDirectory, si, pi) } closeDescriptors(d.closeAfterStart) syscall.ForkLock.Unlock() if e != nil { if errno, ok := e.(syscall.Errno); ok && errno == syscall.Errno(136) { e = tools.NewError(e, ERR_USER) } return nil, ec.NewError(e, syscallName) } d.platformData.hProcess = pi.Process d.platformData.hThread = pi.Thread d.platformData.hJob = syscall.InvalidHandle for _, dll := range sub.Options.InjectDLL { if e = InjectDll(d, sub.Options.LoadLibraryW, dll); e != nil { break } } if e != nil { // Terminate process/thread here. d.platformData.terminateAndClose() return nil, ec.NewError(e, "InjectDll") } if sub.ProcessAffinityMask != 0 { e = win32.SetProcessAffinityMask(d.platformData.hProcess, sub.ProcessAffinityMask) if e != nil { d.platformData.terminateAndClose() return nil, ec.NewError(e, "SetProcessAffinityMask") } } if !sub.NoJob { e = CreateJob(sub, d) if e != nil { if sub.FailOnJobCreationFailure { d.platformData.terminateAndClose() return nil, ec.NewError(e, "CreateJob") } log.Error("CreateFrozen/CreateJob: %s", e) } else { e = win32.AssignProcessToJobObject(d.platformData.hJob, d.platformData.hProcess) if e != nil { syscall.CloseHandle(d.platformData.hJob) d.platformData.hJob = syscall.InvalidHandle if sub.FailOnJobCreationFailure { d.platformData.terminateAndClose() return nil, ec.NewError(e, "AssignProcessToJobObject") } log.Error("CreateFrozen/AssignProcessToJobObject: %s", e) } } } return d, nil }
func (s *mongodbStorage) Copy(localName, remoteName string, toRemote bool, checksum, moduleType string) (stat *contester_proto.FileStat, err error) { ec := tools.ErrorContext("mongodb.Copy") if toRemote { stat, err = tools.StatFile(localName, true) if err != nil { err = ec.NewError(err, "local.CalculateChecksum") } // If file doesn't exist then stat == nil. if err != nil || stat == nil { return } if checksum != "" && *stat.Checksum != checksum { return nil, ec.NewError(fmt.Errorf("Checksum mismatch, local %s != %s", stat.Checksum, checksum)) } checksum = *stat.Checksum } var local *os.File if toRemote { local, err = os.Open(localName) } else { local, err = os.Create(localName) } if err != nil { return nil, ec.NewError(err, "local.Open") } defer local.Close() var remote *mgo.GridFile if toRemote { // Remove all files with the same remoteName. if err = s.GridFS.Remove(remoteName); err != nil { return nil, ec.NewError(err, "remote.Remove") } remote, err = s.GridFS.Create(remoteName) } else { remote, err = s.GridFS.Open(remoteName) } if err != nil { return nil, ec.NewError(err, "remote.Open") } defer remote.Close() var source io.ReadCloser if toRemote { source = local } else { source = remote var meta fileMetadata if err = remote.GetMeta(&meta); err != nil { return nil, ec.NewError(err, "remote.GetMeta") } if meta.CompressionType == "ZLIB" { source, err = zlib.NewReader(source) if err != nil { return nil, ec.NewError(err, "zlib.NewReader") } } } var destination io.WriteCloser if toRemote { destination = zlib.NewWriter(remote) } else { destination = local } size, err := io.Copy(destination, source) if err != nil { return nil, ec.NewError(err, "io.Copy") } if toRemote { var meta fileMetadata meta.OriginalSize = uint64(size) meta.CompressionType = "ZLIB" meta.Checksum = *stat.Checksum meta.ModuleType = moduleType remote.SetMeta(meta) } if err = destination.Close(); err != nil { return nil, ec.NewError(err, "destination.Close") } if err = source.Close(); err != nil { return nil, ec.NewError(err, "source.Close") } if !toRemote { stat, err = tools.StatFile(localName, true) if err != nil { return nil, ec.NewError(err, "StatFile") } } return stat, nil }