<commit_before>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// This contains code largely copy/pasted from the rust-lang/rust repo
// We will need to create processes using different windows API calls in
// order to be able to start habitat supervisor services as different users.

use std::ascii::AsciiExt;
use std::cmp;
use std::collections::HashMap;
use std::env;
use std::ffi::{OsStr, OsString};
use std::fmt;
use std::fs;
use std::io::{self, ErrorKind, Read};
use std::mem;
use std::ops::Deref;
use std::os::windows::ffi::OsStrExt;
use std::path::Path;
use std::ptr;
use std::slice::from_raw_parts_mut;
use std::sync::Mutex;

use kernel32;
use rand::{self, Rng};
use userenv;
use widestring::WideCString;
use winapi;
use winapi::winbase;

use error::{Error, Result};
use habitat_win_users::sid::{self, Sid};

use super::super::users::get_current_username;
use super::super::super::crypto::dpapi::decrypt;

lazy_static! {
    static ref CREATE_PROCESS_LOCK: Mutex<()> = Mutex::new(());
}

#[link(name = "user32")]
extern "system" {
    fn LogonUserW(
        lpszUsername: winapi::LPCWSTR,
        lpszDomain: winapi::LPCWSTR,
        lpszPassword: winapi::LPCWSTR,
        dwLogonType: winapi::DWORD,
        dwLogonProvider: winapi::DWORD,
        phToken: winapi::PHANDLE,
    ) -> winapi::BOOL;

    fn CreateProcessAsUserW(
        hToken: winapi::HANDLE,
        lpApplicationName: winapi::LPCWSTR,
        lpCommandLine: winapi::LPWSTR,
        lpProcessAttributes: winapi::LPSECURITY_ATTRIBUTES,
        lpThreadAttributes: winapi::LPSECURITY_ATTRIBUTES,
        bInheritHandles: winapi::BOOL,
        dwCreationFlags: winapi::DWORD,
        lpEnvironment: winapi::LPVOID,
        lpCurrentDirectory: winapi::LPCWSTR,
        lpStartupInfo: winapi::LPSTARTUPINFOW,
        lpProcessInformation: winapi::LPPROCESS_INFORMATION,
    ) -> winapi::BOOL;

    fn GetProcessWindowStation() -> winapi::HWINSTA;

    fn OpenDesktopW(
        lpszDesktop: winapi::LPWSTR,
        dwFlags: winapi::DWORD,
        fInherit: winapi::BOOL,
        dwDesiredAccess: winapi::ACCESS_MASK,
    ) -> winapi::HDESK;
}

const HANDLE_FLAG_INHERIT: winapi::DWORD = 0x00000001;

const LOGON32_LOGON_SERVICE: winapi::DWORD = 5;

enum ParsePart {
    Key,
    Value,
}

struct ServiceCredential {
    pub user: String,
    pub domain: String,
    pub password: String,
}

impl ServiceCredential {
    pub fn new(svc_user: &str, svc_encrypted_password: Option<&str>) -> Result<Self> {
        let mut full_user = svc_user.to_string();
        let (domain, user) = match full_user.find('\\') {
            Some(idx) => {
                let u = full_user.split_off(idx);
                (full_user, u.trim_matches('\\').to_string())
            }
            None => (".".to_string(), full_user),
        };
        let pass = match svc_encrypted_password {
            Some(password) => decrypt(password.to_string())?,
            None => String::new(),
        };
        Ok(Self {
            user: user,
            domain: domain,
            password: <PASSWORD>,
        })
    }

    pub fn is_current_user(&self) -> bool {
        self.user == get_current_username().unwrap_or(String::new())
    }

    pub fn user_wide(&self) -> WideCString {
        WideCString::from_str(self.user.as_str()).unwrap()
    }

    pub fn domain_wide(&self) -> WideCString {
        WideCString::from_str(self.domain.as_str()).unwrap()
    }

    pub fn password_wide(&self) -> WideCString {
        WideCString::from_str(self.password.as_str()).unwrap()
    }
}


pub struct Child {
    handle: Handle,
    pub stdout: Option<ChildStdout>,
    pub stderr: Option<ChildStderr>,
}

impl Child {
    pub fn spawn(
        program: &str,
        args: Vec<&str>,
        env: &HashMap<String, String>,
        svc_user: &str,
        svc_encrypted_password: Option<&str>,
    ) -> Result<Child> {
        let mut os_env: HashMap<OsString, OsString> = env::vars_os()
            .map(|(key, val)| (mk_key(key.to_str().unwrap()), val))
            .collect();
        for (k, v) in env {
            os_env.insert(mk_key(k.as_str()), OsString::from(v));
        }

        let program_path = {
            let mut res = None;
            for (key, v) in &os_env {
                if &OsString::from("PATH") != key {
                    continue;
                }
                // Split the value and test each path to see if the
                // program exists.
                for path in env::split_paths(&v) {
                    let path = path.join(program).with_extension(
                        env::consts::EXE_EXTENSION,
                    );
                    if fs::metadata(&path).is_ok() {
                        res = Some(path.into_os_string());
                    }
                }
                break;
            }
            res
        };

        let mut si = zeroed_startupinfo();
        si.cb = mem::size_of::<winapi::STARTUPINFOW>() as winapi::DWORD;
        si.dwFlags = winbase::STARTF_USESTDHANDLES;<commit_msg>fix expansion of program to spawn on windows so it chooses the first path with a match and not the last<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// This contains code largely copy/pasted from the rust-lang/rust repo
// We will need to create processes using different windows API calls in
// order to be able to start habitat supervisor services as different users.

use std::ascii::AsciiExt;
use std::cmp;
use std::collections::HashMap;
use std::env;
use std::ffi::{OsStr, OsString};
use std::fmt;
use std::fs;
use std::io::{self, ErrorKind, Read};
use std::mem;
use std::ops::Deref;
use std::os::windows::ffi::OsStrExt;
use std::path::Path;
use std::ptr;
use std::slice::from_raw_parts_mut;
use std::sync::Mutex;

use kernel32;
use rand::{self, Rng};
use userenv;
use widestring::WideCString;
use winapi;
use winapi::winbase;

use error::{Error, Result};
use habitat_win_users::sid::{self, Sid};

use super::super::users::get_current_username;
use super::super::super::crypto::dpapi::decrypt;

lazy_static! {
    static ref CREATE_PROCESS_LOCK: Mutex<()> = Mutex::new(());
}

#[link(name = "user32")]
extern "system" {
    fn LogonUserW(
        lpszUsername: winapi::LPCWSTR,
        lpszDomain: winapi::LPCWSTR,
        lpszPassword: winapi::LPCWSTR,
        dwLogonType: winapi::DWORD,
        dwLogonProvider: winapi::DWORD,
        phToken: winapi::PHANDLE,
    ) -> winapi::BOOL;

    fn CreateProcessAsUserW(
        hToken: winapi::HANDLE,
        lpApplicationName: winapi::LPCWSTR,
        lpCommandLine: winapi::LPWSTR,
        lpProcessAttributes: winapi::LPSECURITY_ATTRIBUTES,
        lpThreadAttributes: winapi::LPSECURITY_ATTRIBUTES,
        bInheritHandles: winapi::BOOL,
        dwCreationFlags: winapi::DWORD,
        lpEnvironment: winapi::LPVOID,
        lpCurrentDirectory: winapi::LPCWSTR,
        lpStartupInfo: winapi::LPSTARTUPINFOW,
        lpProcessInformation: winapi::LPPROCESS_INFORMATION,
    ) -> winapi::BOOL;

    fn GetProcessWindowStation() -> winapi::HWINSTA;

    fn OpenDesktopW(
        lpszDesktop: winapi::LPWSTR,
        dwFlags: winapi::DWORD,
        fInherit: winapi::BOOL,
        dwDesiredAccess: winapi::ACCESS_MASK,
    ) -> winapi::HDESK;
}

const HANDLE_FLAG_INHERIT: winapi::DWORD = 0x00000001;

const LOGON32_LOGON_SERVICE: winapi::DWORD = 5;

enum ParsePart {
    Key,
    Value,
}

struct ServiceCredential {
    pub user: String,
    pub domain: String,
    pub password: String,
}

impl ServiceCredential {
    pub fn new(svc_user: &str, svc_encrypted_password: Option<&str>) -> Result<Self> {
        let mut full_user = svc_user.to_string();
        let (domain, user) = match full_user.find('\\') {
            Some(idx) => {
                let u = full_user.split_off(idx);
                (full_user, u.trim_matches('\\').to_string())
            }
            None => (".".to_string(), full_user),
        };
        let pass = match svc_encrypted_password {
            Some(password) => decrypt(password.to_string())?,
            None => String::new(),
        };
        Ok(Self {
            user: user,
            domain: domain,
            password: <PASSWORD>,
        })
    }

    pub fn is_current_user(&self) -> bool {
        self.user == get_current_username().unwrap_or(String::new())
    }

    pub fn user_wide(&self) -> WideCString {
        WideCString::from_str(self.user.as_str()).unwrap()
    }

    pub fn domain_wide(&self) -> WideCString {
        WideCString::from_str(self.domain.as_str()).unwrap()
    }

    pub fn password_wide(&self) -> WideCString {
        WideCString::from_str(self.password.as_str()).unwrap()
    }
}


pub struct Child {
    handle: Handle,
    pub stdout: Option<ChildStdout>,
    pub stderr: Option<ChildStderr>,
}

impl Child {
    pub fn spawn(
        program: &str,
        args: Vec<&str>,
        env: &HashMap<String, String>,
        svc_user: &str,
        svc_encrypted_password: Option<&str>,
    ) -> Result<Child> {
        let mut os_env: HashMap<OsString, OsString> = env::vars_os()
            .map(|(key, val)| (mk_key(key.to_str().unwrap()), val))
            .collect();
        for (k, v) in env {
            os_env.insert(mk_key(k.as_str()), OsString::from(v));
        }

        let program_path = {
            let mut res = None;
            for (key, v) in &os_env {
                if &OsString::from("PATH") != key {
                    continue;
                }
                // Split the value and test each path to see if the
                // program exists.
                for path in env::split_paths(&v) {
                    let path = path.join(program).with_extension(
                        env::consts::EXE_EXTENSION,
                    );
                    if fs::metadata(&path).is_ok() {
                        res = Some(path.into_os_string());
                        break;
                    }
                }
                break;
            }
            res
        };

        let mut si = zeroed_startupinfo();
        si.cb = mem::size_of::<winapi::STARTUPINFOW>() as winapi::DWORD;
        si.dwFlags = winbase::STARTF_USESTDHANDLES;
{'fraction_non_alphanumeric': 0.1258698141460407, 'fraction_numerical': 0.004932616929445961, 'mean_word_length': 2.508652657601978, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '340575', 'n_tokens_mistral': 3611, 'n_tokens_neox': 3527, 'n_words': 870}