Permalink
Browse files

Force pinentry-program to be pinentry-mac

[#172 state:fixed assigned:mento milestone:0.8.8]
  • Loading branch information...
Mento committed Jan 24, 2019
1 parent 5e2fc45 commit 1957c3f4dcb4dc3132eb05220583d4fc74e720a6
@@ -117,6 +117,7 @@
@property (nonatomic) BOOL autoKeyRetrieve;
@property (nonatomic) BOOL allowNonSelfsignedUid;
@property (nonatomic) BOOL allowWeakDigestAlgos;
@property (nonatomic) BOOL ignoreCustomPinentryProgram;
@property (nonatomic, readonly) NSDictionary *statusDict;
@property (nonatomic, readonly) GPGHashAlgorithm hashAlgorithm;
@property (nonatomic, readonly, retain) GPGTask *gpgTask;
@@ -218,6 +218,7 @@ - (id)init {
keyserverTimeout = 30; // Give the slow keyservers some time to answer.
asyncProxy = [[AsyncProxy alloc] initWithRealObject:self];
useDefaultComments = YES;
_ignoreCustomPinentryProgram = YES;

GPGOptions *options = [GPGOptions sharedOptions];
id value;
@@ -3093,6 +3094,10 @@ - (void)addArgumentsForComments {
}

- (void)addArgumentsForOptions {
if (_ignoreCustomPinentryProgram) {
[[GPGOptions sharedOptions] forceDefaultPinentry];
}

[gpgTask addArgument:useArmor ? @"--armor" : @"--no-armor"];
[gpgTask addArgument:useTextMode ? @"--textmode" : @"--no-textmode"];
[gpgTask addArgument:printVersion ? @"--emit-version" : @"--no-emit-version"];
@@ -113,6 +113,12 @@ typedef enum {
- (void)gpgAgentFlush;
- (void)gpgAgentTerminate;

/*
* Enforce the use of our default pinentry.
* Reloads the gpg-agent if it's using another pinentry.
*/
- (void)forceDefaultPinentry;

+ (NSString *)standardizedKey:(NSString *)key;
- (GPGOptionsDomain)domainForKey:(NSString *)key;
+ (BOOL)isKnownKey:(NSString *)key inDomain:(GPGOptionsDomain)domain;
@@ -32,10 +32,15 @@
#import "GPGTask.h"
#import "GPGTaskHelper.h"
#import "GPGController.h"
#import "GPGTaskHelperXPC.h"


NSString * const GPGOptionsChangedNotification = @"GPGOptionsChangedNotification";
NSString * const GPGConfigurationModifiedNotification = @"GPGConfigurationModifiedNotification";

static NSString * const GPGDefaultPinentryPath = @"/usr/local/MacGPG2/libexec/pinentry-mac.app/Contents/MacOS/pinentry-mac";


@interface GPGOptions ()
@property (nonatomic, readonly) NSMutableDictionary *commonDefaults;
@property (nonatomic, readonly) NSMutableDictionary *standardDefaults;
@@ -674,15 +679,18 @@ - (NSString *)pinentryPath {

// Remove an invalid path from gpg-agent.conf.
// A pinentry in Libmacgpg is an old version, don't use it anymore.
if ([pinentryPath rangeOfString:@"/Libmacgpg.framework/"].length > 0 || ![fileManager isExecutableFileAtPath:pinentryPath]) {
NSDictionary<NSFileAttributeKey, id> *attributes = [fileManager attributesOfItemAtPath:pinentryPath error:nil];
BOOL executable = !!([attributes[NSFilePosixPermissions] shortValue] & 0x40);

if ([pinentryPath rangeOfString:@"/Libmacgpg.framework/"].length > 0 || !executable) {
pinentryPath = nil;
[self setValueInGPGAgentConf:nil forKey:kPinentry_program];
[self gpgAgentFlush];
}
}

if (!pinentryPath) {
pinentryPath = @"/usr/local/MacGPG2/libexec/pinentry-mac.app/Contents/MacOS/pinentry-mac";
pinentryPath = GPGDefaultPinentryPath;
}

NSString *temp = _pinentryPath;
@@ -720,15 +728,97 @@ + (NSString *)standardizedKey:(NSString *)key {
}

- (void)gpgAgentFlush {
system("killall -HUP gpg-agent");
// Do not use system anymore. It's better to use gpgconf.
[GPGTask launchGeneralTask:@"/usr/local/MacGPG2/bin/gpgconf" withArguments:@[@"--reload", @"gpg-agent"] wait:YES];
}

- (void)gpgAgentTerminate {
system("killall gpg-agent");
// Do not use system anymore. It's better to use gpgconf.
[GPGTask launchGeneralTask:@"/usr/local/MacGPG2/bin/gpgconf" withArguments:@[@"--kill", @"gpg-agent"] wait:YES];
}

- (void)forceDefaultPinentry {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if ([self.pinentryPath isEqualToString:GPGDefaultPinentryPath]) {
// Our default pinentry is already used. Nothing to do.
return;
}

// Get te PID of gpg-agent.
int pid = 0;
if ([GPGTask sandboxed]) {
GPGTaskHelperXPC *taskHelper = [[GPGTaskHelperXPC alloc] init];
@try {
pid = [taskHelper gpgAgentPID];
@throw [NSException exceptionWithName:@"asd" reason:@"asasdf" userInfo:@{}];
} @catch (NSException *exception) {
// Ignore.
} @finally {
[taskHelper release];
}
} else {
pid = [GPGTaskHelper gpgAgentPID];
}


if (pid != 0) {
// gpg-agent is running. Check if it was called with --pinentry-program /usr/local/MacGPG2/libexec/pinentry-mac.app/Contents/MacOS/pinentry-mac.

NSString *needle = @"--pinentry-program";
NSUInteger needleLength = needle.length;

NSArray *arguments = [GPGTaskHelper argumentsOfProcessWithID:pid];
NSString *pinentryProgram = nil;
NSUInteger count = arguments.count;

// Search the --pinentry-program argument and get it's value.
for (NSUInteger i = 0; i < count; i++) {
NSString *argument = arguments[i];
if ([argument rangeOfString:needle options:NSAnchoredSearch].length > 0) {
// This is the --pinentry-program argument.

if (argument.length > needleLength) {
// "--pinentry-program" is followed by something.
if (argument.length <= needleLength + 2 || [argument characterAtIndex:needleLength] != '=') {
// "--pinentry-program" is not followed by "=" and a path.
continue;
}
// Get the string after "--pinentry-program=".
argument = [argument substringFromIndex:needleLength + 1];
} else {
i++;
if (i >= count) {
// Missing argument. Should not happen.
break;
}
// Get the next arguemtn after "--pinentry-program".
argument = arguments[i];
}

pinentryProgram = argument;
break;
}
}

if ([pinentryProgram isEqualToString:GPGDefaultPinentryPath]) {
// gpg-agent uses our default pinentry.
return;
}

// Kill the gpg-agent which uses the wrong pinentry.
[self gpgAgentTerminate];
}

// Start gpg-agent with our default pinentry.
[GPGTask launchGeneralTask:@"/usr/local/MacGPG2/bin/gpg-agent" withArguments:@[@"--daemon", @"--pinentry-program", GPGDefaultPinentryPath] wait:YES];
});
}


- (void)dirmngrFlush {
system("killall -HUP dirmngr");
// Do not use system anymore. It's better to use gpgconf.
[GPGTask launchGeneralTask:@"/usr/local/MacGPG2/bin/gpgconf" withArguments:@[@"--reload", @"dirmngr"] wait:YES];
}


@@ -141,6 +141,18 @@ typedef void (^lp_progress_handler_t)(NSUInteger processedBytes, NSUInteger tota
+ (BOOL)launchGeneralTask:(NSString *)path withArguments:(NSArray *)arguments wait:(BOOL)wait;

+ (NSString *)gpgAgentSocket;

/**
Return the process id of the running gpg-agent.
Returns 0 if no running gpg-agent was found.
*/
+ (int)gpgAgentPID;

/**
Returns the arguments (argv) of a given process.
*/
+ (NSArray *)argumentsOfProcessWithID:(int)pid;

+ (BOOL)isPassphraseInGPGAgentCache:(id)key;

+ (NSDictionary *)statusCodes;
@@ -49,6 +49,7 @@
#include <sys/socket.h>
#include <sys/un.h>
#import <sys/stat.h>
#import <sys/sysctl.h>


static const NSUInteger kDataBufferSize = 65536;
@@ -957,6 +958,153 @@ + (NSString *)gpgAgentSocket {
return nil;
}

+ (int)gpgAgentPID {
NSString *socketPath = [GPGTaskHelper gpgAgentSocket];

// No socket path available? Can't get the pid, so bail.
if(!socketPath) {
return 0;
}

__block int sock = -1;
if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("+[GPGTaskHelper gpgAgentPID] failed to initiate socket");
return 0;
}

int (^cleanup)(int) = ^(int result){
if (sock != -1) {
close(sock);
}
return result;
};

const char *socketPathName = [socketPath UTF8String];
unsigned long socketPathLength = [socketPath lengthOfBytesUsingEncoding:NSUTF8StringEncoding];

struct sockaddr_un server;
bzero((char *)&server, sizeof(struct sockaddr_un));
server.sun_family = AF_UNIX;
strncpy(server.sun_path, socketPathName, socketPathLength);
// On BSD systems, sun_path is not to be expected to be terminated with a null byte.
if (connect(sock, (struct sockaddr *)&server, sizeof(struct sockaddr_un)) < 0) {
perror("+[GPGTaskHelper gpgAgentPID] failed to connect to gpg-agent socket");
return cleanup(0);
}

struct timeval socketTimeout;
socketTimeout.tv_usec = 0;
socketTimeout.tv_sec = 2;
if(setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &socketTimeout, sizeof(socketTimeout)) < 0) {
perror("+[GPGTaskHelper gpgAgentPID] failed to configure socket timeout");
return cleanup(0);
}

char buffer[101];
NSString *command = @"GETINFO pid\nBYE\n";

// Request the process id of gpg-agent.
send(sock, [command UTF8String], [command lengthOfBytesUsingEncoding:NSUTF8StringEncoding], 0);

size_t pos = 0;
ssize_t length = 0;
bzero(&buffer, sizeof(buffer));

while ((length = recv(sock, buffer+pos, 100-pos, 0)) > 0) {
pos += length;
}

NSString *response = [NSString stringWithUTF8String:buffer];

// Find the process id in the response.
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^D ([0-9]+)$" options:NSRegularExpressionAnchorsMatchLines error:nil];
NSTextCheckingResult *match = [regex firstMatchInString:response options:0 range:NSMakeRange(0, response.length)];
int pid = 0;
if (match) {
NSRange pidRange = [match rangeAtIndex:1];
pid = [[response substringWithRange:pidRange] intValue];
}


return cleanup(pid);
}

+ (NSArray *)argumentsOfProcessWithID:(int)pid {
int name[3], maxArgumentsLength = 0;

name[0] = CTL_KERN;
name[1] = KERN_ARGMAX;
size_t size = sizeof(maxArgumentsLength);
if (sysctl(name, 2, &maxArgumentsLength, &size, nil, 0) == -1) {
return nil;
}


NSMutableData *argumentsData = [NSMutableData dataWithLength:maxArgumentsLength];
if (!argumentsData) {
return nil;
}
char *processArguments = argumentsData.mutableBytes;


name[0] = CTL_KERN;
name[1] = KERN_PROCARGS2;
name[2] = pid;
size_t argumentsLength = (size_t)maxArgumentsLength;
if (sysctl(name, 3, processArguments, &argumentsLength, nil, 0) == -1) {
return nil;
}
if (argumentsLength < 5) {
return nil;
}

// Get the number of arguments.
int argumentsCount = ((int *)processArguments)[0];
NSUInteger index = 4;

// Skip full exec path.
while (processArguments[index] != 0) {
index++;
if (index >= argumentsLength) {
// Unexpected end of data.
return nil;
}
}
// Skip null-bytes.
while (processArguments[index] == 0) {
index++;
if (index >= argumentsLength) {
// Unexpected end of data.
return nil;
}
}


NSMutableArray *arguments = [NSMutableArray array];
for (NSUInteger i = 0; i < argumentsCount; i++) {
NSUInteger argumentStart = index;

// Look for the end of the argument.
while (processArguments[index] != 0) {
index++;
if (index >= argumentsLength) {
// Unexpected end of data.
return nil;
}
}
index++;

NSString *argument = [NSString stringWithUTF8String:processArguments+argumentStart];
if (!argument) {
return nil;
}
[arguments addObject:argument];
}

return arguments;
}


+ (BOOL)isPassphraseInGPGAgentCache:(id)key {
if(![key respondsToSelector:@selector(description)])
return NO;
@@ -66,6 +66,7 @@
- (NSDictionary *)loadUserDefaultsForName:(NSString *)domainName;
- (void)setUserDefaults:(NSDictionary *)domain forName:(NSString *)domainName;
- (BOOL)isPassphraseForKeyInGPGAgentCache:(NSString *)key;
- (int)gpgAgentPID;
- (BOOL)validSupportContractAvailableForProduct:(NSString *)identifier activationInfo:(NSDictionary **)activationInfo;
- (BOOL)activateSupportContractWithEmail:(NSString *)email activationCode:(NSString *)activationCode error:(NSError **)error;
- (BOOL)startTrial;
@@ -341,6 +341,23 @@ - (BOOL)isPassphraseForKeyInGPGAgentCache:(NSString *)key {

return inCache;
}
- (int)gpgAgentPID {
[self prepareTask];

int __block pid = 0;

[_jailfree gpgAgentPID:^(int result) {
pid = result;

[self completeTaskWithSuccess];
}];

[self waitForTaskToCompleteAndShutdown:YES throwExceptionIfNecessary:YES];

return pid;
}



- (void)processStatusWithKey:(NSString *)keyword value:(NSString *)value reply:(void (^)(NSData *))reply {
// If process status is not set, we still have to reply otherwise the request might hang forever.
@@ -40,6 +40,7 @@
- (void)loadUserDefaultsForName:(NSString *)domainName reply:(void (^)(NSDictionary *))reply;
- (void)setUserDefaults:(NSDictionary *)domain forName:(NSString *)domainName reply:(void (^)(BOOL result))reply;
- (void)isPassphraseForKeyInGPGAgentCache:(NSString *)key reply:(void (^)(BOOL result))reply;
- (void)gpgAgentPID:(void (^)(int))reply;
- (void)validSupportContractAvailableForProduct:(NSString *)identifier reply:(void (^)(BOOL, NSDictionary *))reply;
- (void)startTrialWithReply:(void (^)(BOOL))reply;
- (void)activateProductWithEmail:(NSString *)email activationCode:(NSString *)activationCode reply:(void (^)(BOOL, NSError *))reply;
Oops, something went wrong.

0 comments on commit 1957c3f

Please sign in to comment.