From 1603d8d995220f0b81dcd98915f3d6c8ffe25895 Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Sat, 7 Dec 2019 16:35:07 -0500 Subject: [PATCH] For file/line breakpoints, create secure bookmarks for maintaining access. Under the app sandbox, the program only has access to user-selected files, unless it creates bookmarks for them. Each file bookmark (even for the same file, for simplicity) now gets a bookmark when it is created. https://developer.apple.com/library/archive/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW16 --- MacGDBp.entitlements | 4 ++ Source/BSLineNumberRulerView.mm | 17 +++++--- Source/Breakpoint.h | 13 ++++++ Source/Breakpoint.m | 77 +++++++++++++++++++++++++++++++-- Source/BreakpointController.m | 22 +++++++--- Source/BreakpointManager.m | 63 +++++++++++++++++---------- 6 files changed, 160 insertions(+), 36 deletions(-) diff --git a/MacGDBp.entitlements b/MacGDBp.entitlements index 7a2230d..0d6bac6 100644 --- a/MacGDBp.entitlements +++ b/MacGDBp.entitlements @@ -4,6 +4,10 @@ com.apple.security.app-sandbox + com.apple.security.files.bookmarks.app-scope + + com.apple.security.files.user-selected.read-only + com.apple.security.network.client com.apple.security.network.server diff --git a/Source/BSLineNumberRulerView.mm b/Source/BSLineNumberRulerView.mm index 7b1d253..e912b02 100644 --- a/Source/BSLineNumberRulerView.mm +++ b/Source/BSLineNumberRulerView.mm @@ -141,11 +141,15 @@ const CGFloat kRulerRightPadding = 2.5; { [self computeLineIndex]; - // Determine the width of the ruler based on the line count. - NSUInteger lastElement = lineIndex_.back() + 1; - NSAttributedString* lastElementString = [self attributedStringForLineNumber:lastElement]; - NSSize boundingSize = [lastElementString size]; - [self setRuleThickness:std::max(kDefaultWidth, boundingSize.width)]; + if (lineIndex_.empty()) { + [self setRuleThickness:kDefaultWidth]; + } else { + // Determine the width of the ruler based on the line count. + NSUInteger lastElement = lineIndex_.back() + 1; + NSAttributedString* lastElementString = [self attributedStringForLineNumber:lastElement]; + NSSize boundingSize = [lastElementString size]; + [self setRuleThickness:std::max(kDefaultWidth, boundingSize.width)]; + } [self setNeedsDisplay:YES]; } @@ -209,6 +213,9 @@ const CGFloat kRulerRightPadding = 2.5; index = NSMaxRange([text lineRangeForRange:NSMakeRange(index, 0)]); } + if (lineIndex_.empty()) + return; + NSUInteger lineEnd, contentEnd; [text getLineStart:NULL end:&lineEnd diff --git a/Source/Breakpoint.h b/Source/Breakpoint.h index fe2e217..4a53497 100644 --- a/Source/Breakpoint.h +++ b/Source/Breakpoint.h @@ -35,6 +35,7 @@ extern NSString* const kBreakpointTypeFunctionEntry; // kBreakpointTypeFile: @property (readonly) NSString* file; @property (readonly) unsigned long line; +@property (copy) NSData* secureBookmark; // kBreakpointTypeFunctionEntry: @property (readonly) NSString* functionName; @@ -50,4 +51,16 @@ extern NSString* const kBreakpointTypeFunctionEntry; // Creates a dictionary representation for use in NSUserDefaults. - (NSDictionary*)dictionary; +// For kBreakpointTypeFile: //////////////////////////////////////////////////// + +// Creates a new secure bookmark for maintaining access to the file in the App +// Sandbox across relaunches. +- (BOOL)createSecureBookmark; + +// Call to enable read-only access to the file. +- (BOOL)startSecureFileAccess; + +// Call when done accessing the file. +- (BOOL)stopSecureFileAccess; + @end diff --git a/Source/Breakpoint.m b/Source/Breakpoint.m index 744cf5d..f3cc675 100644 --- a/Source/Breakpoint.m +++ b/Source/Breakpoint.m @@ -26,6 +26,8 @@ NSString* const kBreakpointTypeFunctionEntry = @"call"; unsigned long _debuggerId; NSString* _file; + NSData* _secureBookmark; + NSURL* _secureFileAccess; NSString* _functionName; } @@ -55,6 +57,7 @@ NSString* const kBreakpointTypeFunctionEntry = @"call"; _type = kBreakpointTypeFile; _file = [[dict valueForKey:@"file"] copy]; _line = [[dict valueForKey:@"line"] intValue]; + _secureBookmark = [[dict valueForKey:@"secureBookmark"] copy]; } else if ([type isEqualToString:kBreakpointTypeFunctionEntry]) { _type = kBreakpointTypeFunctionEntry; _functionName = [[dict valueForKey:@"function"] copy]; @@ -66,6 +69,11 @@ NSString* const kBreakpointTypeFunctionEntry = @"call"; return self; } +- (void)dealloc { + if (_secureFileAccess) + [self stopSecureFileAccess]; +} + /** * Returns the string to display in the breakpoints list. */ @@ -124,11 +132,14 @@ NSString* const kBreakpointTypeFunctionEntry = @"call"; - (NSDictionary*)dictionary { if (self.type == kBreakpointTypeFile) { - return @{ + NSMutableDictionary* dict = [NSMutableDictionary dictionaryWithDictionary:@{ @"type" : self.type, @"file" : self.file, - @"line" : @(self.line) - }; + @"line" : @(self.line), + }]; + if (self.secureBookmark) + [dict setObject:self.secureBookmark forKey:@"secureBookmark"]; + return dict; } else if (self.type == kBreakpointTypeFunctionEntry) { return @{ @"type" : self.type, @@ -138,6 +149,66 @@ NSString* const kBreakpointTypeFunctionEntry = @"call"; return nil; } +- (BOOL)createSecureBookmark +{ + NSURL* fileURL = [NSURL fileURLWithPath:self.file]; + return [self _createSecureBookmarkWithURL:fileURL]; +} + +- (BOOL)_createSecureBookmarkWithURL:(NSURL*)url +{ + NSError* error; + NSData* secureBookmark = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope | NSURLBookmarkCreationSecurityScopeAllowOnlyReadAccess + includingResourceValuesForKeys:nil + relativeToURL:nil + error:&error]; + if (secureBookmark) { + self.secureBookmark = secureBookmark; + return YES; + } else { + NSLog(@"Failed to create secure bookmark: %@", error); + return NO; + } +} + +- (BOOL)startSecureFileAccess +{ + assert(self.type == kBreakpointTypeFile); + if (_secureFileAccess) + return YES; + if (!_secureBookmark) + return NO; + + BOOL isStale; + NSError* error; + _secureFileAccess = [NSURL URLByResolvingBookmarkData:_secureBookmark + options:NSURLBookmarkResolutionWithSecurityScope + relativeToURL:nil + bookmarkDataIsStale:&isStale + error:&error]; + if (error) { + NSLog(@"Failed to access file via secure bookmark: %@", error); + return NO; + } + if (isStale) + [self _createSecureBookmarkWithURL:_secureFileAccess]; + + return [_secureFileAccess startAccessingSecurityScopedResource]; +} + +- (BOOL)stopSecureFileAccess +{ + assert(self.type == kBreakpointTypeFile); + if (!_secureFileAccess) + return YES; + if (!_secureBookmark) + return NO; + + [_secureFileAccess stopAccessingSecurityScopedResource]; + _secureFileAccess = nil; + return YES; +} + - (NSString*)description { return [NSString stringWithFormat:@"Breakpoint %@", [[self dictionary] description]]; diff --git a/Source/BreakpointController.m b/Source/BreakpointController.m index b7047e0..9e539c8 100644 --- a/Source/BreakpointController.m +++ b/Source/BreakpointController.m @@ -21,6 +21,7 @@ @implementation BreakpointController { BreakpointManager* _manager; + Breakpoint* _selection; BSSourceView* _sourceView; @@ -102,19 +103,28 @@ */ - (void)tableViewSelectionDidChange:(NSNotification*)notif { + if (_selection.type == kBreakpointTypeFile) { + [_selection stopSecureFileAccess]; + _selection = nil; + } + NSArray* selection = [_arrayController selectedObjects]; if ([selection count] < 1) { + [_sourceView setString:@"" asFile:nil]; + [_sourceView setMarkers:[NSSet set]]; return; } - - Breakpoint* bp = [selection objectAtIndex:0]; - if (bp.type != kBreakpointTypeFile) { + + _selection = [selection objectAtIndex:0]; + if (_selection.type != kBreakpointTypeFile) { return; } - [_sourceView setFile:[bp file]]; - [_sourceView scrollToLine:[bp line]]; - [_sourceView setMarkers:[_manager breakpointsForFile:bp.file]]; + [_selection startSecureFileAccess]; + + [_sourceView setFile:[_selection file]]; + [_sourceView scrollToLine:[_selection line]]; + [_sourceView setMarkers:[_manager breakpointsForFile:_selection.file]]; } @end diff --git a/Source/BreakpointManager.m b/Source/BreakpointManager.m index 0cfea23..bb53511 100644 --- a/Source/BreakpointManager.m +++ b/Source/BreakpointManager.m @@ -53,39 +53,58 @@ */ - (void)addBreakpoint:(Breakpoint*)bp; { - if (![_breakpoints containsObject:bp]) - { - [self willChangeValueForKey:@"breakpoints"]; - [_breakpoints addObject:bp]; - [self didChangeValueForKey:@"breakpoints"]; + if ([_breakpoints containsObject:bp]) + return; + + if (bp.type == kBreakpointTypeFile && !bp.secureBookmark) { + // There is no secure bookmark for this file, so first see if any other + // bookmarks exist for the same file. If not, try and create a bookmark. + if (!bp.secureBookmark) { + [bp createSecureBookmark]; + /* + for (Breakpoint* other in _breakpoints) { + if ([other.file isEqualToString:bp.file] && other.secureBookmark) { + bp.secureBookmark = other.secureBookmark; + break; + } + } - [_connection addBreakpoint:bp]; + if (!bp.secureBookmark) { + [bp createSecureBookmark]; + } + */ + } + } - [_savedBreakpoints addObject:[bp dictionary]]; - [[NSUserDefaults standardUserDefaults] setObject:_savedBreakpoints forKey:kPrefBreakpoints]; + [self willChangeValueForKey:@"breakpoints"]; + [_breakpoints addObject:bp]; + [self didChangeValueForKey:@"breakpoints"]; - [self updateDisplaysForFile:[bp file]]; - } + [_connection addBreakpoint:bp]; + + [_savedBreakpoints addObject:[bp dictionary]]; + [[NSUserDefaults standardUserDefaults] setObject:_savedBreakpoints forKey:kPrefBreakpoints]; + [self updateDisplaysForFile:[bp file]]; } - (Breakpoint*)removeBreakpoint:(Breakpoint*)bp { - if ([_breakpoints containsObject:bp]) { - [self willChangeValueForKey:@"breakpoints"]; - [_breakpoints removeObject:bp]; - [self didChangeValueForKey:@"breakpoints"]; + if (![_breakpoints containsObject:bp]) + return nil; - [_connection removeBreakpoint:bp]; + [self willChangeValueForKey:@"breakpoints"]; + [_breakpoints removeObject:bp]; + [self didChangeValueForKey:@"breakpoints"]; - [_savedBreakpoints removeObject:[bp dictionary]]; - [[NSUserDefaults standardUserDefaults] setObject:_savedBreakpoints forKey:kPrefBreakpoints]; + [_connection removeBreakpoint:bp]; - if (bp.file) - [self updateDisplaysForFile:bp.file]; + [_savedBreakpoints removeObject:[bp dictionary]]; + [[NSUserDefaults standardUserDefaults] setObject:_savedBreakpoints forKey:kPrefBreakpoints]; - return bp; - } - return nil; + if (bp.file) + [self updateDisplaysForFile:bp.file]; + + return bp; } /** -- 2.22.5