/// <summary>
        /// Initializes a new instance of the <see cref="Storage"/> class.
        /// The actual cache reading and writing is OS specific:
        /// <list type="bullet">
        /// <item>
        ///     <term>Windows</term>
        ///     <description>DPAPI encrypted file on behalf of the user. </description>
        /// </item>
        /// <item>
        ///     <term>Mac</term>
        ///     <description>Cache is stored in KeyChain.  </description>
        /// </item>
        /// <item>
        ///     <term>Linux</term>
        ///     <description>Cache is stored in Gnome KeyRing - https://developer.gnome.org/libsecret/0.18/  </description>
        /// </item>
        /// </list>
        /// </summary>
        /// <param name="creationProperties">Properties for creating the cache storage on disk</param>
        /// <param name="logger">logger</param>
        /// <returns></returns>
        public static Storage Create(StorageCreationProperties creationProperties, TraceSource logger = null)
        {
            TraceSourceLogger actualLogger = logger == null ? s_staticLogger.Value : new TraceSourceLogger(logger);

            ICacheAccessor cacheAccessor;

            if (creationProperties.UseUnencryptedFallback)
            {
                cacheAccessor = new FileAccessor(creationProperties.CacheFilePath, setOwnerOnlyPermissions: true, logger: actualLogger);
            }
            else
            {
                if (SharedUtilities.IsWindowsPlatform())
                {
                    cacheAccessor = new DpApiEncryptedFileAccessor(creationProperties.CacheFilePath, logger: actualLogger);
                }
                else if (SharedUtilities.IsMacPlatform())
                {
                    cacheAccessor = new MacKeychainAccessor(
                        creationProperties.CacheFilePath,
                        creationProperties.MacKeyChainServiceName,
                        creationProperties.MacKeyChainAccountName,
                        actualLogger);
                }
                else if (SharedUtilities.IsLinuxPlatform())
                {
                    if (creationProperties.UseLinuxUnencryptedFallback)
                    {
                        cacheAccessor = new FileAccessor(creationProperties.CacheFilePath, setOwnerOnlyPermissions: true, actualLogger);
                    }
                    else
                    {
                        cacheAccessor = new LinuxKeyringAccessor(
                            creationProperties.CacheFilePath,
                            creationProperties.KeyringCollection,
                            creationProperties.KeyringSchemaName,
                            creationProperties.KeyringSecretLabel,
                            creationProperties.KeyringAttribute1.Key,
                            creationProperties.KeyringAttribute1.Value,
                            creationProperties.KeyringAttribute2.Key,
                            creationProperties.KeyringAttribute2.Value,
                            actualLogger);
                    }
                }
                else
                {
                    throw new PlatformNotSupportedException();
                }
            }

            return(new Storage(creationProperties, cacheAccessor, actualLogger));
        }
        private static string GetUserHomeDirOnUnix()
        {
            if (SharedUtilities.IsWindowsPlatform())
            {
                throw new NotSupportedException();
            }

            if (!string.IsNullOrEmpty(SharedUtilities.s_homeEnvVar))
            {
                return(SharedUtilities.s_homeEnvVar);
            }

            string username = null;

            if (!string.IsNullOrEmpty(SharedUtilities.s_lognameEnvVar))
            {
                username = s_lognameEnvVar;
            }
            else if (!string.IsNullOrEmpty(SharedUtilities.s_userEnvVar))
            {
                username = s_userEnvVar;
            }
            else if (!string.IsNullOrEmpty(SharedUtilities.s_lNameEnvVar))
            {
                username = s_lNameEnvVar;
            }
            else if (!string.IsNullOrEmpty(SharedUtilities.s_usernameEnvVar))
            {
                username = s_usernameEnvVar;
            }

            if (SharedUtilities.IsMacPlatform())
            {
                return(!string.IsNullOrEmpty(username) ? Path.Combine("/Users", username) : null);
            }
            else if (SharedUtilities.IsLinuxPlatform())
            {
                if (LinuxNativeMethods.getuid() == LinuxNativeMethods.RootUserId)
                {
                    return("/root");
                }
                else
                {
                    return(!string.IsNullOrEmpty(username) ? Path.Combine("/home", username) : null);
                }
            }
            else
            {
                throw new NotSupportedException();
            }
        }
Beispiel #3
0
        private void ClearCore()
        {
            _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, "Clearing cache");
            bool cacheFileExists = File.Exists(CacheFilePath);

            _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"ReadDataCore Cache file exists '{cacheFileExists}'");

            TryProcessFile(() =>
            {
                _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, "Before deleting the cache file");
                try
                {
                    File.Delete(CacheFilePath);
                }
                catch (Exception e)
                {
                    _logger.TraceEvent(TraceEventType.Error, /*id*/ 0, $"Problem deleting the cache file '{e}'");
                }

                _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"After deleting the cache file.");
            });

            if (SharedUtilities.IsMacPlatform())
            {
                _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, "Before delete mac keychain");
                MacKeyChain.DeleteKey(
                    _creationProperties.MacKeyChainServiceName,
                    _creationProperties.MacKeyChainAccountName);
                _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, "After delete mac keychain");
            }
            else if (SharedUtilities.IsLinuxPlatform())
            {
                _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"Before deletring secret from linux keyring");

                IntPtr error = IntPtr.Zero;

                Libsecret.secret_password_clear_sync(
                    schema: GetLibsecretSchema(),
                    cancellable: IntPtr.Zero,
                    error: out error,
                    attribute1Type: _creationProperties.KeyringAttribute1.Key,
                    attribute1Value: _creationProperties.KeyringAttribute1.Value,
                    attribute2Type: _creationProperties.KeyringAttribute2.Key,
                    attribute2Value: _creationProperties.KeyringAttribute2.Value,
                    end: IntPtr.Zero);

                if (error != IntPtr.Zero)
                {
                    try
                    {
                        GError err = (GError)Marshal.PtrToStructure(error, typeof(GError));
                        _logger.TraceEvent(TraceEventType.Error, /*id*/ 0, $"An error was encountered while clearing secret from keyring in the {nameof(MsalCacheStorage)} domain:'{err.Domain}' code:'{err.Code}' message:'{err.Message}'");
                    }
                    catch (Exception e)
                    {
                        _logger.TraceEvent(TraceEventType.Error, /*id*/ 0, $"An exception was encountered while processing libsecret error information during clearing secret in the {nameof(MsalCacheStorage)} ex:'{e}'");
                    }
                }

                _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, "After deleting secret from linux keyring");
            }
            else if (!SharedUtilities.IsWindowsPlatform())
            {
                _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, "Not supported platform");
                throw new PlatformNotSupportedException();
            }
        }
Beispiel #4
0
        private void WriteDataCore(byte[] data)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"Write Data core, going to write '{data.Length}' to the storage");

            if (SharedUtilities.IsMacPlatform() || SharedUtilities.IsLinuxPlatform())
            {
                if (SharedUtilities.IsMacPlatform())
                {
                    _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, "Before write to mac keychain");
                    MacKeyChain.WriteKey(
                        _creationProperties.MacKeyChainServiceName,
                        _creationProperties.MacKeyChainAccountName,
                        data);

                    _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, "After write to mac keychain");
                }
                else if (SharedUtilities.IsLinuxPlatform())
                {
                    _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, "Before saving to linux keyring");

                    IntPtr error = IntPtr.Zero;

                    Libsecret.secret_password_store_sync(
                        schema: GetLibsecretSchema(),
                        collection: _creationProperties.KeyringCollection,
                        label: _creationProperties.KeyringSecretLabel,
                        password: Convert.ToBase64String(data),
                        cancellable: IntPtr.Zero,
                        error: out error,
                        attribute1Type: _creationProperties.KeyringAttribute1.Key,
                        attribute1Value: _creationProperties.KeyringAttribute1.Value,
                        attribute2Type: _creationProperties.KeyringAttribute2.Key,
                        attribute2Value: _creationProperties.KeyringAttribute2.Value,
                        end: IntPtr.Zero);

                    if (error != IntPtr.Zero)
                    {
                        try
                        {
                            GError err = (GError)Marshal.PtrToStructure(error, typeof(GError));
                            _logger.TraceEvent(TraceEventType.Error, /*id*/ 0, $"An error was encountered while saving secret to keyring in the {nameof(MsalCacheStorage)} domain:'{err.Domain}' code:'{err.Code}' message:'{err.Message}'");
                        }
                        catch (Exception e)
                        {
                            _logger.TraceEvent(TraceEventType.Error, /*id*/ 0, $"An exception was encountered while processing libsecret error information during saving in the {nameof(MsalCacheStorage)} ex:'{e}'");
                        }
                    }

                    _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, "After saving to linux keyring");
                }

                // Change data to 1 byte so we can write it to the cache file to update the last write time using the same write code used for windows.
                data = new byte[] { 1 };
            }

            string directoryForCacheFile = Path.GetDirectoryName(CacheFilePath);

            if (!Directory.Exists(directoryForCacheFile))
            {
                string directory = Path.GetDirectoryName(CacheFilePath);
                _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"Creating directory '{directory}'");
                Directory.CreateDirectory(directory);
            }

            _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"Cache file directory exists. '{Directory.Exists(directoryForCacheFile)}' now writing cache file");

            TryProcessFile(() =>
            {
                File.WriteAllBytes(CacheFilePath, data);
            });
        }
Beispiel #5
0
        private byte[] ReadDataCore()
        {
            _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, "ReadDataCore");

            byte[] fileData = null;

            bool cacheFileExists = File.Exists(CacheFilePath);

            _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"ReadDataCore Cache file exists '{cacheFileExists}'");

            if (SharedUtilities.IsWindowsPlatform())
            {
                if (cacheFileExists)
                {
                    TryProcessFile(() =>
                    {
                        fileData = File.ReadAllBytes(CacheFilePath);
                        _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"ReadDataCore, read '{fileData.Length}' bytes from the file");
                    });
                }
            }
            else if (SharedUtilities.IsMacPlatform())
            {
                _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"ReadDataCore, Before reading from mac keychain");
                fileData = MacKeyChain.RetrieveKey(_creationProperties.MacKeyChainServiceName, _creationProperties.MacKeyChainAccountName, _logger);

                _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"ReadDataCore, read '{fileData?.Length}' bytes from the keychain");
            }
            else if (SharedUtilities.IsLinuxPlatform())
            {
                _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"ReadDataCore, Before reading from linux keyring");

                IntPtr error = IntPtr.Zero;

                string secret = Libsecret.secret_password_lookup_sync(
                    schema: GetLibsecretSchema(),
                    cancellable: IntPtr.Zero,
                    error: out error,
                    attribute1Type: _creationProperties.KeyringAttribute1.Key,
                    attribute1Value: _creationProperties.KeyringAttribute1.Value,
                    attribute2Type: _creationProperties.KeyringAttribute2.Key,
                    attribute2Value: _creationProperties.KeyringAttribute2.Value,
                    end: IntPtr.Zero);

                if (error != IntPtr.Zero)
                {
                    try
                    {
                        GError err = (GError)Marshal.PtrToStructure(error, typeof(GError));
                        _logger.TraceEvent(TraceEventType.Error, /*id*/ 0, $"An error was encountered while reading secret from keyring in the {nameof(MsalCacheStorage)} domain:'{err.Domain}' code:'{err.Code}' message:'{err.Message}'");
                    }
                    catch (Exception e)
                    {
                        _logger.TraceEvent(TraceEventType.Error, /*id*/ 0, $"An exception was encountered while processing libsecret error information during reading in the {nameof(MsalCacheStorage)} ex:'{e}'");
                    }
                }
                else if (string.IsNullOrEmpty(secret))
                {
                    _logger.TraceEvent(TraceEventType.Error, /*id*/ 0, "No matching secret found in the keyring");
                }
                else
                {
                    _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, "Base64 decoding the secret string");
                    fileData = Convert.FromBase64String(secret);
                    _logger.TraceEvent(TraceEventType.Information, /*id*/ 0, $"ReadDataCore, read '{fileData?.Length}' bytes from the keyring");
                }
            }
            else
            {
                _logger.TraceEvent(TraceEventType.Error, /*id*/ 0, "Platform not supported");
                throw new PlatformNotSupportedException();
            }

            return(fileData);
        }