Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions src/Security/AuthorizationEngine.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#if __MACOS__
#nullable enable

using System;
using System.Runtime.Versioning;
using CoreFoundation;
using ObjCRuntime;

namespace Security {

/// <summary>Represents an opaque reference to an authorization engine used in authorization plugin views.</summary>
public class AuthorizationEngine : NativeObject {
[Preserve (Conditional = true)]
internal AuthorizationEngine (NativeHandle handle, bool owns)
: base (handle, owns)
{
}
#if !COREBUILD
/// <summary>Creates an <see cref="AuthorizationEngine" /> from a raw handle without taking ownership.</summary>
/// <param name="handle">The native <c>AuthorizationEngineRef</c> handle.</param>
/// <returns>A managed wrapper, or <see langword="null" /> if the handle is zero.</returns>
public static AuthorizationEngine? Create (NativeHandle handle)
{
if (handle == IntPtr.Zero)
return null;
return new AuthorizationEngine (handle, owns: false);
}
#endif // !COREBUILD
}
}
#endif // __MACOS__
100 changes: 100 additions & 0 deletions src/Security/SecKeychain.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#if __MACOS__
#nullable enable

using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using ObjCRuntime;
using CoreFoundation;

namespace Security {

/// <summary>Represents a keychain on macOS.</summary>
public class SecKeychain : NativeObject {
[Preserve (Conditional = true)]
internal SecKeychain (NativeHandle handle, bool owns)
: base (handle, owns)
{
}
#if !COREBUILD
[SupportedOSPlatform ("macos")]
[ObsoletedOSPlatform ("macos10.10")]
[DllImport (Constants.SecurityLibrary)]
extern static unsafe int /* OSStatus */ SecKeychainGetTypeID ();

/// <summary>Returns the Core Foundation type identifier for SecKeychain.</summary>
/// <returns>The Core Foundation type identifier.</returns>
public static nint GetTypeID ()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public static nint GetTypeID ()
public static nint GetTypeId ()

{
return SecKeychainGetTypeID ();
}

[SupportedOSPlatform ("macos")]
[ObsoletedOSPlatform ("macos10.10")]
[DllImport (Constants.SecurityLibrary)]
extern static unsafe int /* OSStatus */ SecKeychainCopyDefault (IntPtr* keychain);

/// <summary>Gets the default keychain.</summary>
/// <returns>The default <see cref="SecKeychain" />, or <see langword="null" /> on failure.</returns>
public static SecKeychain? GetDefault ()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should provide the status code as an out argument?

Suggested change
public static SecKeychain? GetDefault ()
public static SecKeychain? GetDefault (out OSStatus status)

{
IntPtr handle;
int status;
unsafe {
status = SecKeychainCopyDefault (&handle);
}
if (status != 0 || handle == IntPtr.Zero)
return null;
return new SecKeychain (handle, owns: true);
}

[SupportedOSPlatform ("macos")]
[ObsoletedOSPlatform ("macos10.10")]
[DllImport (Constants.SecurityLibrary)]
extern static unsafe int /* OSStatus */ SecKeychainOpen (IntPtr pathName, IntPtr* keychain);

/// <summary>Opens the keychain at the specified file path.</summary>
/// <param name="path">The file system path of the keychain to open.</param>
/// <returns>A <see cref="SecKeychain" /> for the specified path, or <see langword="null" /> on failure.</returns>
public static SecKeychain? Open (string path)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nullability wise, the Try* pattern ends up producing nice consuming C# code, so I like it:

Suggested change
public static SecKeychain? Open (string path)
public static bool TryOpen (string path, out OSStatus status, [NotNullWhen (true)] out SecKeychain? keychain)

{
if (path is null)
ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (path));
using var pathStr = new TransientString (path);
IntPtr handle;
int status;
unsafe {
status = SecKeychainOpen (pathStr, &handle);
}
if (status != 0 || handle == IntPtr.Zero)
return null;
return new SecKeychain (handle, owns: true);
}

[SupportedOSPlatform ("macos")]
[ObsoletedOSPlatform ("macos10.10")]
[DllImport (Constants.SecurityLibrary)]
extern static unsafe int /* OSStatus */ SecKeychainGetPath (IntPtr keychainRef, int* ioPathLength, IntPtr pathName);

/// <summary>Gets the file system path of this keychain.</summary>
/// <returns>The POSIX path of the keychain, or <see langword="null" /> on failure.</returns>
public string? GetPath ()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Provide the status code as an out argument.

{
int pathLength = 1024;
IntPtr buffer = Marshal.AllocHGlobal (pathLength);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a byte array (of length pathLength) to avoid allocating/freeing memory manually.

try {
int status;
unsafe {
status = SecKeychainGetPath (Handle, &pathLength, buffer);
}
if (status != 0)
return null;
return Marshal.PtrToStringUTF8 (buffer, pathLength);
} finally {
Marshal.FreeHGlobal (buffer);
}
}
#endif // !COREBUILD
}
}
#endif // __MACOS__
50 changes: 50 additions & 0 deletions src/Security/SecKeychainSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#if __MACOS__
#nullable enable

using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;

namespace Security {

/// <summary>Represents keychain settings for lock behavior on macOS.</summary>
[StructLayout (LayoutKind.Sequential)]
public struct SecKeychainSettings {
int /* UInt32 */ version;
byte /* Boolean */ lockOnSleep;
byte /* Boolean */ useLockInterval;
int /* UInt32 */ lockInterval;

/// <summary>Gets or sets the version of this settings structure.</summary>
public int Version {
get => version;
set => version = value;
}

/// <summary>Gets or sets whether the keychain locks when the system sleeps.</summary>
public bool LockOnSleep {
get => lockOnSleep != 0;
set => lockOnSleep = value ? (byte) 1 : (byte) 0;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's an AsByte () extension method:

Suggested change
set => lockOnSleep = value ? (byte) 1 : (byte) 0;
set => lockOnSleep = value.AsByte ();

}

/// <summary>Gets or sets whether the lock interval is used.</summary>
public bool UseLockInterval {
get => useLockInterval != 0;
set => useLockInterval = value ? (byte) 1 : (byte) 0;
}

/// <summary>Gets or sets the number of seconds before the keychain auto-locks.</summary>
public int LockInterval {
get => lockInterval;
set => lockInterval = value;
}

/// <summary>Creates a new <see cref="SecKeychainSettings" /> with the current version.</summary>
/// <returns>A new <see cref="SecKeychainSettings" /> initialized with version 1.</returns>
public static SecKeychainSettings Create ()
{
return new SecKeychainSettings { version = 1 };
}
}
}
#endif // __MACOS__
Loading
Loading