Skip to content

Commit

Permalink
Merge pull request #8 from Daniel15/pinentry
Browse files Browse the repository at this point in the history
Fix passphrase callback
  • Loading branch information
wget committed Feb 11, 2019
2 parents 219c5a5 + ccb582c commit 31a7350
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 10 deletions.
1 change: 1 addition & 0 deletions Examples/PgpEncryptDecrypt/Program.cs
Expand Up @@ -12,6 +12,7 @@ class Program
static void Main()
{
Context ctx = new Context();
ctx.PinentryMode = PinentryMode.Loopback;

if (ctx.Protocol != Protocol.OpenPGP)
ctx.SetEngineInfo(Protocol.OpenPGP, null, null);
Expand Down
31 changes: 31 additions & 0 deletions GPGME.Native.Shared/Enums.cs
Expand Up @@ -378,4 +378,35 @@ public enum PgpSubkeyAlgorithm
[AlgorithmCapability(AlgorithmCapability.Unknown)]
RSAUseCapabilities = 7
}

public enum PinentryMode
{
/// <summary>
/// Use the default of the agent, which is ask.
/// </summary>
Default = gpgme_pinentry_mode_t.GPGME_PINENTRY_MODE_DEFAULT,

/// <summary>
/// Force the use of the Pinentry.
/// </summary>
Ask = gpgme_pinentry_mode_t.GPGME_PINENTRY_MODE_ASK,

/// <summary>
/// Emulate use of Pinentry's cancel button.
/// </summary>
Cancel = gpgme_pinentry_mode_t.GPGME_PINENTRY_MODE_CANCEL,

/// <summary>
/// Return a Pinentry error "No Pinentry".
/// </summary>
Error = gpgme_pinentry_mode_t.GPGME_PINENTRY_MODE_ERROR,

/// <summary>
/// Redirect Pinentry queries to the caller. This enables the use of
/// gpgme_set_passphrase_cb because pinentry queries are redirected to gpgme
/// Note: For 2.1.0 - 2.1.12 this mode requires allow-loopback-pinentry
/// to be enabled in the gpg-agent.conf or an agent started with that option.
/// </summary>
Loopback = gpgme_pinentry_mode_t.GPGME_PINENTRY_MODE_LOOPBACK,
}
}
3 changes: 3 additions & 0 deletions GPGME.Native.Shared/NativeMethodsWrapper.cs
Expand Up @@ -33,11 +33,13 @@ public class NativeMethodsWrapper
public Func<IntPtr, int> gpgme_get_include_certs { get; set; }
public GPGGetKey gpgme_get_key { get; set; }
public Func<IntPtr, gpgme_keylist_mode_t> gpgme_get_keylist_mode { get; set; }
public Func<IntPtr, gpgme_pinentry_mode_t> gpgme_get_pinentry_mode { get; set; }
public Func<gpgme_protocol_t, IntPtr> gpgme_get_protocol_name { get; set; }
public Func<IntPtr, int> gpgme_get_textmode { get; set; }
public Func<IntPtr, gpgme_protocol_t> gpgme_get_protocol { get; set; }
public Func<gpgme_hash_algo_t, IntPtr> gpgme_hash_algo_name { get; set; }
public Func<int, byte[], UIntPtr, IntPtr> gpgme_io_write { get; set; }
public Func<int, byte[], UIntPtr, int> gpgme_io_writen { get; set; }
public Action<IntPtr> gpgme_key_release { get; set; }
public GPGNew gpgme_new { get; set; }
public Func<IntPtr, IntPtr, IntPtr, int> gpgme_op_decrypt { get; set; }
Expand Down Expand Up @@ -71,6 +73,7 @@ public class NativeMethodsWrapper
public Action<IntPtr, int> gpgme_set_include_certs { get; set; }
public Func<IntPtr, gpgme_keylist_mode_t, int> gpgme_set_keylist_mode { get; set; }
public Action<IntPtr, gpgme_passphrase_cb_t, IntPtr> gpgme_set_passphrase_cb { get; set; }
public Func<IntPtr, gpgme_pinentry_mode_t, int> gpgme_set_pinentry_mode { get; set; }
public Action<IntPtr, int> gpgme_set_textmode { get; set; }
public Func<IntPtr, gpgme_protocol_t, int> gpgme_set_protocol { get; set; }
public Func<IntPtr, IntPtr, IntPtr, gpgme_sig_notation_flags_t, int> gpgme_sig_notation_add { get; set; }
Expand Down
17 changes: 9 additions & 8 deletions GPGME.Native.Shared/libgpgme_Delegates.cs
@@ -1,9 +1,10 @@
using System;
using System.Runtime.InteropServices;

namespace Libgpgme.Interop
{
/* Request a passphrase from the user. */

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int gpgme_passphrase_cb_t(
IntPtr hook,
IntPtr uid_hint,
Expand All @@ -12,7 +13,7 @@ namespace Libgpgme.Interop
int fd);

/* Inform the user about progress made. */

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void gpgme_progress_cb_t(
IntPtr opaque,
IntPtr what,
Expand All @@ -23,7 +24,7 @@ namespace Libgpgme.Interop
/* Read up to SIZE bytes into buffer BUFFER from the data object with
the handle HANDLE. Return the number of characters read, 0 on EOF
and -1 on error. If an error occurs, errno is set. */

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate IntPtr gpgme_data_read_cb_t(
IntPtr handle,
IntPtr buffer,
Expand All @@ -32,7 +33,7 @@ namespace Libgpgme.Interop
/* Write up to SIZE bytes from buffer BUFFER to the data object with
the handle HANDLE. Return the number of characters written, or -1
on error. If an error occurs, errno is set. */

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate IntPtr gpgme_data_write_cb_t(
IntPtr handle,
IntPtr buffer,
Expand All @@ -41,7 +42,7 @@ namespace Libgpgme.Interop
/* Set the current position from where the next read or write starts
in the data object with the handle HANDLE to OFFSET, relativ to
WHENCE. */

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate IntPtr gpgme_data_seek_cb_t(
IntPtr handle,
IntPtr offset,
Expand All @@ -50,19 +51,19 @@ namespace Libgpgme.Interop
/* Set the current position from where the next read or write starts
in the data object with the handle HANDLE to OFFSET, relativ to
WHENCE. */

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate long gpgme_data_seek_cb_t_lfs(
IntPtr handle,
long offset,
int whence);

/* Close the data object with the handle DL. */

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void gpgme_data_release_cb_t(
IntPtr handle);

/* Interact with the user about an edit operation. */

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int gpgme_edit_cb_t(
IntPtr opaque, // void *
int status, //gpgme_status_code_t
Expand Down
31 changes: 31 additions & 0 deletions GPGME.Native.Shared/libgpgme_Enums.cs
Expand Up @@ -322,4 +322,35 @@ public enum gpgme_sigsum_t
GPGME_SIGSUM_BAD_POLICY = 0x0400, /* A policy was not met. */
GPGME_SIGSUM_SYS_ERROR = 0x0800 /* A system error occured. */
}

public enum gpgme_pinentry_mode_t
{
/// <summary>
/// Use the default of the agent, which is ask.
/// </summary>
GPGME_PINENTRY_MODE_DEFAULT,

/// <summary>
/// Force the use of the Pinentry.
/// </summary>
GPGME_PINENTRY_MODE_ASK,

/// <summary>
/// Emulate use of Pinentry's cancel button.
/// </summary>
GPGME_PINENTRY_MODE_CANCEL,

/// <summary>
/// Return a Pinentry error "No Pinentry".
/// </summary>
GPGME_PINENTRY_MODE_ERROR,

/// <summary>
/// Redirect Pinentry queries to the caller. This enables the use of
/// gpgme_set_passphrase_cb because pinentry queries are redirected to gpgme
/// Note: For 2.1.0 - 2.1.12 this mode requires allow-loopback-pinentry
/// to be enabled in the gpg-agent.conf or an agent started with that option.
/// </summary>
GPGME_PINENTRY_MODE_LOOPBACK
}
}
28 changes: 28 additions & 0 deletions GPGME.Native.Win32/NativeMethods.cs
Expand Up @@ -182,6 +182,25 @@ error ERR.
internal static extern gpgme_protocol_t gpgme_get_protocol(
[In] IntPtr ctx);

/* Specifies the pinentry mode to be used.
For GnuPG >= 2.1 this option is required to be set to GPGME_PINENTRY_MODE_LOOPBACK
to enable the passphrase callback mechanism in GPGME through gpgme_set_passphrase_cb.
gpgme_error_t gpgme_set_pinentry_mode (gpgme_ctx_t ctx, gpgme_pinentry_mode_t mode)
*/

[DllImport(LIBRARY_NAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
internal static extern int gpgme_set_pinentry_mode(
[In] IntPtr ctx,
gpgme_pinentry_mode_t mode);

/* Returns the mode set for the context.
gpgme_pinentry_mode_t gpgme_get_pinentry_mode(gpgme_ctx_t ctx); */

[DllImport(LIBRARY_NAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
internal static extern gpgme_pinentry_mode_t gpgme_get_pinentry_mode(
[In] IntPtr ctx);

/* Get the information about the configured engines. A pointer to the
first engine in the statically allocated linked list is returned.
The returned data is valid until the next gpgme_ctx_set_engine_info.
Expand Down Expand Up @@ -870,6 +889,12 @@ error ERR.
[In] byte[] buffer,
[In] UIntPtr count);

[DllImport(LIBRARY_NAME, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
internal static extern int gpgme_io_writen(
[In] int fd,
[In] byte[] buffer,
[In] UIntPtr count);

public static NativeMethodsWrapper CreateWrapper()
{
return new NativeMethodsWrapper
Expand Down Expand Up @@ -899,11 +924,13 @@ public static NativeMethodsWrapper CreateWrapper()
gpgme_get_include_certs = gpgme_get_include_certs,
gpgme_get_key = gpgme_get_key,
gpgme_get_keylist_mode = gpgme_get_keylist_mode,
gpgme_get_pinentry_mode = gpgme_get_pinentry_mode,
gpgme_get_protocol_name = gpgme_get_protocol_name,
gpgme_get_textmode = gpgme_get_textmode,
gpgme_get_protocol = gpgme_get_protocol,
gpgme_hash_algo_name = gpgme_hash_algo_name,
gpgme_io_write = gpgme_io_write,
gpgme_io_writen = gpgme_io_writen,
gpgme_key_release = gpgme_key_release,
gpgme_new = gpgme_new,
gpgme_op_decrypt = gpgme_op_decrypt,
Expand Down Expand Up @@ -938,6 +965,7 @@ public static NativeMethodsWrapper CreateWrapper()
gpgme_set_keylist_mode = gpgme_set_keylist_mode,
gpgme_set_passphrase_cb = gpgme_set_passphrase_cb,
gpgme_set_textmode = gpgme_set_textmode,
gpgme_set_pinentry_mode = gpgme_set_pinentry_mode,
gpgme_set_protocol = gpgme_set_protocol,
gpgme_sig_notation_add = gpgme_sig_notation_add,
gpgme_sig_notation_clear = gpgme_sig_notation_clear,
Expand Down
51 changes: 49 additions & 2 deletions gpgme-sharp/Context.cs
Expand Up @@ -142,6 +142,53 @@ private void Initialize(ref ContextSigners signers, ref ContextSignatureNotation
}
}
}

public PinentryMode PinentryMode
{
get
{
if (IsValid)
{
lock (CtxLock)
{
var mode = libgpgme.NativeMethods.gpgme_get_pinentry_mode(CtxPtr);
return (PinentryMode)mode;
}
}
throw new InvalidContextException();
}
set
{
if (IsValid)
{
lock (CtxLock)
{
var mode = (gpgme_pinentry_mode_t)value;
int err = libgpgme.NativeMethods.gpgme_set_pinentry_mode(CtxPtr, mode);

gpg_err_code_t errcode = libgpgme.gpgme_err_code(err);
if (errcode != gpg_err_code_t.GPG_ERR_NO_ERROR)
{
string errmsg;
try
{
Gpgme.GetStrError(err, out errmsg);
}
catch
{
errmsg = "No error message available.";
}
throw new ArgumentException(errmsg + " Error: " + err.ToString(CultureInfo.InvariantCulture));
}
}
}
else
{
throw new InvalidContextException();
}
}
}

public EngineInfo EngineInfo {
get {
if (IsValid) {
Expand Down Expand Up @@ -350,8 +397,8 @@ private void Initialize(ref ContextSigners signers, ref ContextSignatureNotation
if (fd > 0) {
byte[] utf8_passwd = Gpgme.ConvertCharArrayToUTF8(passwd, 0);

libgpgme.NativeMethods.gpgme_io_write(fd, utf8_passwd, (UIntPtr)utf8_passwd.Length);
libgpgme.NativeMethods.gpgme_io_write(fd, new[] { (byte)0 }, (UIntPtr)1);
libgpgme.NativeMethods.gpgme_io_writen(fd, utf8_passwd, (UIntPtr)utf8_passwd.Length);
libgpgme.NativeMethods.gpgme_io_writen(fd, new[] { (byte)'\n' }, (UIntPtr)1);

// try to wipe the passwords
int i;
Expand Down

0 comments on commit 31a7350

Please sign in to comment.