private static string GetDefaultStore()
        {
            if (PlatformUtils.IsWindows())
            {
                return(StoreNames.WindowsCredentialManager);
            }

            if (PlatformUtils.IsMacOS())
            {
                return(StoreNames.MacOSKeychain);
            }

            // Other platforms have no default store
            return(null);
        }
        private void ValidateGpgPass(out string storeRoot, out string execPath)
        {
            if (!PlatformUtils.IsPosix())
            {
                throw new Exception(
                          $"Can only use the '{StoreNames.Gpg}' credential store on POSIX systems." +
                          Environment.NewLine +
                          $"See {Constants.HelpUrls.GcmCredentialStores} for more information."
                          );
            }

            execPath = GetGpgPath();

            // If we are in a headless environment, and don't have the GPG_TTY or SSH_TTY
            // variables set, then error - we need a TTY device path for pin-entry to work headless.
            if (!_context.SessionManager.IsDesktopSession &&
                !_context.Environment.Variables.ContainsKey("GPG_TTY") &&
                !_context.Environment.Variables.ContainsKey("SSH_TTY"))
            {
                throw new Exception(
                          "GPG_TTY is not set; add `export GPG_TTY=$(tty)` to your profile." +
                          Environment.NewLine +
                          $"See {Constants.HelpUrls.GcmCredentialStores} for more information."
                          );
            }

            // Check for a redirected pass store location
            if (!_context.Settings.TryGetSetting(
                    GpgPassCredentialStore.PasswordStoreDirEnvar,
                    null, null,
                    out storeRoot))
            {
                // Use default store root at ~/.password-store
                storeRoot = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".password-store");
            }

            // Check we have a GPG ID to sign credential files with
            string gpgIdFile = Path.Combine(storeRoot, ".gpg-id");

            if (!_context.FileSystem.FileExists(gpgIdFile))
            {
                throw new Exception(
                          $"Password store has not been initialized at '{storeRoot}'; run `pass init <gpg-id>` to initialize the store." +
                          Environment.NewLine +
                          $"See {Constants.HelpUrls.GcmCredentialStores} for more information."
                          );
            }
        }
Exemple #3
0
        private string GetGitConfigAppPath()
        {
            string path = Context.ApplicationPath;

            // On Windows we must use UNIX style path separators
            if (PlatformUtils.IsWindows())
            {
                path = path.Replace('\\', '/');
            }

            // We must escape escape characters like ' ', '(', and ')'
            return(path
                   .Replace(" ", "\\ ")
                   .Replace("(", "\\(")
                   .Replace(")", "\\)"));;
        }
Exemple #4
0
        protected override async Task <int> RunInternalAsync(string[] args)
        {
            var rootCommand     = new RootCommand();
            var diagnoseCommand = new DiagnoseCommand(Context);

            // Add standard commands
            rootCommand.AddCommand(new GetCommand(Context, _providerRegistry));
            rootCommand.AddCommand(new StoreCommand(Context, _providerRegistry));
            rootCommand.AddCommand(new EraseCommand(Context, _providerRegistry));
            rootCommand.AddCommand(new ConfigureCommand(Context, _configurationService));
            rootCommand.AddCommand(new UnconfigureCommand(Context, _configurationService));
            rootCommand.AddCommand(diagnoseCommand);

            // Add any custom provider commands
            foreach (ProviderCommand providerCommand in _providerCommands)
            {
                rootCommand.AddCommand(providerCommand);
            }

            // Add any custom provider diagnostic tests
            foreach (IDiagnostic providerDiagnostic in _diagnostics)
            {
                diagnoseCommand.AddDiagnostic(providerDiagnostic);
            }

            // Trace the current version, OS, runtime, and program arguments
            PlatformInformation info = PlatformUtils.GetPlatformInformation();

            Context.Trace.WriteLine($"Version: {Constants.GcmVersion}");
            Context.Trace.WriteLine($"Runtime: {info.ClrVersion}");
            Context.Trace.WriteLine($"Platform: {info.OperatingSystemType} ({info.CpuArchitecture})");
            Context.Trace.WriteLine($"OSVersion: {info.OperatingSystemVersion}");
            Context.Trace.WriteLine($"AppPath: {Context.ApplicationPath}");
            Context.Trace.WriteLine($"Arguments: {string.Join(" ", args)}");

            var parser = new CommandLineBuilder(rootCommand)
                         .UseDefaults()
                         .UseExceptionHandler(OnException)
                         .Build();

            return(await parser.InvokeAsync(args));
        }
        private void ValidateSecretService()
        {
            if (!PlatformUtils.IsLinux())
            {
                throw new Exception(
                          $"Can only use the '{StoreNames.SecretService}' credential store on Linux." +
                          Environment.NewLine +
                          $"See {Constants.HelpUrls.GcmCredentialStores} for more information."
                          );
            }

            if (!_context.SessionManager.IsDesktopSession)
            {
                throw new Exception(
                          $"Cannot use the '{StoreNames.SecretService}' credential backing store without a graphical interface present." +
                          Environment.NewLine +
                          $"See {Constants.HelpUrls.GcmCredentialStores} for more information."
                          );
            }
        }
        private void ValidateWindowsCredentialManager()
        {
            if (!PlatformUtils.IsWindows())
            {
                throw new Exception(
                          $"Can only use the '{StoreNames.WindowsCredentialManager}' credential store on Windows." +
                          Environment.NewLine +
                          $"See {Constants.HelpUrls.GcmCredentialStores} for more information."
                          );
            }

            if (!WindowsCredentialManager.CanPersist())
            {
                throw new Exception(
                          $"Unable to persist credentials with the '{StoreNames.WindowsCredentialManager}' credential store." +
                          Environment.NewLine +
                          $"See {Constants.HelpUrls.GcmCredentialStores} for more information."
                          );
            }
        }
Exemple #7
0
        public override async Task <ICredential> GenerateCredentialAsync(InputArguments input)
        {
            ThrowIfDisposed();

            Uri uri = input.GetRemoteUri();

            // Determine the if the host supports Windows Integration Authentication (WIA)
            if (IsWindowsAuthAllowed)
            {
                if (PlatformUtils.IsWindows())
                {
                    Context.Trace.WriteLine($"Checking host '{uri.AbsoluteUri}' for Windows Integrated Authentication...");
                    bool isWiaSupported = await _winAuth.GetIsSupportedAsync(uri);

                    if (!isWiaSupported)
                    {
                        Context.Trace.WriteLine("Host does not support WIA.");
                    }
                    else
                    {
                        Context.Trace.WriteLine("Host supports WIA - generating empty credential...");

                        // WIA is signaled to Git using an empty username/password
                        return(new GitCredential(string.Empty, string.Empty));
                    }
                }
                else
                {
                    string osType = PlatformUtils.GetPlatformInformation().OperatingSystemType;
                    Context.Trace.WriteLine($"Skipping check for Windows Integrated Authentication on {osType}.");
                }
            }
            else
            {
                Context.Trace.WriteLine("Windows Integrated Authentication detection has been disabled.");
            }

            Context.Trace.WriteLine("Prompting for basic credentials...");
            return(_basicAuth.GetCredentials(uri.AbsoluteUri, input.UserName));
        }
        private void ValidateDpapi(out string storeRoot)
        {
            if (!PlatformUtils.IsWindows())
            {
                throw new Exception(
                          $"Can only use the '{StoreNames.Dpapi}' credential store on Windows." +
                          Environment.NewLine +
                          $"See {Constants.HelpUrls.GcmCredentialStores} for more information."
                          );
            }

            // Check for a redirected credential store location
            if (!_context.Settings.TryGetSetting(
                    Constants.EnvironmentVariables.GcmDpapiStorePath,
                    Constants.GitConfiguration.Credential.SectionName,
                    Constants.GitConfiguration.Credential.DpapiStorePath,
                    out storeRoot))
            {
                // Use default store root at ~/.gcm/dpapi_store
                storeRoot = Path.Combine(_context.FileSystem.UserDataDirectoryPath, "dpapi_store");
            }
        }
        /// <summary>
        /// Ensure the store root directory exists. If it does not, create a new directory with
        /// permissions that only permit the owner to read/write/execute. Permissions on an existing
        /// directory are not modified.
        /// </summary>
        private void EnsureStoreRoot()
        {
            if (FileSystem.DirectoryExists(StoreRoot))
            {
                // Don't touch the permissions on the existing directory
                return;
            }

            FileSystem.CreateDirectory(StoreRoot);

            // We only set file system permissions on POSIX platforms
            if (!PlatformUtils.IsPosix())
            {
                return;
            }

            // Set store root permissions such that only the owner can read/write/execute
            var mode = Interop.Posix.Native.NativeFileMode.S_IRUSR |
                       Interop.Posix.Native.NativeFileMode.S_IWUSR |
                       Interop.Posix.Native.NativeFileMode.S_IXUSR;

            // Ignore the return code.. this is a best effort only
            Interop.Posix.Native.Stat.chmod(StoreRoot, mode);
        }
        public CommandContext(string appPath)
        {
            EnsureArgument.NotNullOrWhiteSpace(appPath, nameof(appPath));

            ApplicationPath = appPath;
            Streams         = new StandardStreams();
            Trace           = new Trace();

            if (PlatformUtils.IsWindows())
            {
                FileSystem     = new WindowsFileSystem();
                SessionManager = new WindowsSessionManager();
                SystemPrompts  = new WindowsSystemPrompts();
                Environment    = new WindowsEnvironment(FileSystem);
                Terminal       = new WindowsTerminal(Trace);
                string gitPath = GetGitPath(Environment, FileSystem, Trace);
                Git = new GitProcess(
                    Trace,
                    Environment,
                    gitPath,
                    FileSystem.GetCurrentDirectory()
                    );
                Settings = new WindowsSettings(Environment, Git, Trace);
            }
            else if (PlatformUtils.IsMacOS())
            {
                FileSystem     = new MacOSFileSystem();
                SessionManager = new MacOSSessionManager();
                SystemPrompts  = new MacOSSystemPrompts();
                Environment    = new PosixEnvironment(FileSystem);
                Terminal       = new MacOSTerminal(Trace);
                string gitPath = GetGitPath(Environment, FileSystem, Trace);
                Git = new GitProcess(
                    Trace,
                    Environment,
                    gitPath,
                    FileSystem.GetCurrentDirectory()
                    );
                Settings = new Settings(Environment, Git);
            }
            else if (PlatformUtils.IsLinux())
            {
                FileSystem = new LinuxFileSystem();
                // TODO: support more than just 'Posix' or X11
                SessionManager = new PosixSessionManager();
                SystemPrompts  = new LinuxSystemPrompts();
                Environment    = new PosixEnvironment(FileSystem);
                Terminal       = new LinuxTerminal(Trace);
                string gitPath = GetGitPath(Environment, FileSystem, Trace);
                Git = new GitProcess(
                    Trace,
                    Environment,
                    gitPath,
                    FileSystem.GetCurrentDirectory()
                    );
                Settings = new Settings(Environment, Git);
            }
            else
            {
                throw new PlatformNotSupportedException();
            }

            HttpClientFactory = new HttpClientFactory(FileSystem, Trace, Settings, Streams);
            CredentialStore   = new CredentialStore(this);

            // Set the parent window handle/ID
            SystemPrompts.ParentWindowId = Settings.ParentWindowId;
        }
Exemple #11
0
        public static void OpenDefaultBrowser(IEnvironment environment, Uri uri)
        {
            if (!uri.Scheme.Equals(Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) &&
                !uri.Scheme.Equals(Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase))
            {
                throw new ArgumentException("Can only open HTTP/HTTPS URIs", nameof(uri));
            }

            string url = uri.ToString();

            ProcessStartInfo psi = null;

            if (PlatformUtils.IsLinux())
            {
                // On Linux, 'shell execute' utilities like xdg-open launch a process without
                // detaching from the standard in/out descriptors. Some applications (like
                // Chromium) write messages to stdout, which is currently hooked up and being
                // consumed by Git, and cause errors.
                //
                // Sadly, the Framework does not allow us to redirect standard streams if we
                // set ProcessStartInfo::UseShellExecute = true, so we must manually launch
                // these utilities and redirect the standard streams manually.
                //
                // We try and use the same 'shell execute' utilities as the Framework does,
                // searching for them in the same order until we find one.
                //
                // One additional 'shell execute' utility we also attempt to use is `wslview`
                // that is commonly found on WSL (Windows Subsystem for Linux) distributions that
                // opens the browser on the Windows host.
                foreach (string shellExec in new[] { "xdg-open", "gnome-open", "kfmclient", "wslview" })
                {
                    if (environment.TryLocateExecutable(shellExec, out string shellExecPath))
                    {
                        psi = new ProcessStartInfo(shellExecPath, url)
                        {
                            RedirectStandardOutput = true,
                            RedirectStandardError  = true
                        };

                        // We found a way to open the URI; stop searching!
                        break;
                    }
                }

                if (psi is null)
                {
                    throw new Exception("Failed to locate a utility to launch the default web browser.");
                }
            }
            else
            {
                // On Windows and macOS, `ShellExecute` and `/usr/bin/open` disconnect the child process
                // from our standard in/out streams, so we can just use the Framework to do this.
                psi = new ProcessStartInfo(url)
                {
                    UseShellExecute = true
                };
            }

            Process.Start(psi);
        }