Skip to content
Permalink
Browse files

Do not send server-controlled values in tmux integration mode.

CVE-2019-9535

- Use session number everywhere rather than session name
- Do not poll tmux for the set-titles-string, status-left, and status-right and
  then request the values of the returned format strings. Use ${T:} eval
  instead. These features are now only available for tmux 2.9 and later.
- Hex-encode options saved in the tmux server to make them unexploitable (e.g.,
  hotkeys, window affinities, window origins, etc.). The old values are
  accepted as inputs but will never be produced as output.
  • Loading branch information...
George Nachman authored and gnachman committed Sep 26, 2019
1 parent 6f1fab7 commit 538d570ea54614d3a2b5724f820953d717fbeb0c
@@ -1376,6 +1376,8 @@
53E184F21FE32F2800DB78F3 /* iTermMetalBufferPool.m in Sources */ = {isa = PBXBuildFile; fileRef = 53E184F01FE32F2800DB78F3 /* iTermMetalBufferPool.m */; };
53E8F36F2244A58800F3770F /* iTermActionsModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 53E8F36D2244A58800F3770F /* iTermActionsModel.h */; };
53E8F3702244A58800F3770F /* iTermActionsModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 53E8F36E2244A58800F3770F /* iTermActionsModel.m */; };
53E98E7B233C6B760094D8A9 /* iTermTmuxSessionObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 53E98E79233C6B760094D8A9 /* iTermTmuxSessionObject.h */; };
53E98E7C233C6B760094D8A9 /* iTermTmuxSessionObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 53E98E7A233C6B760094D8A9 /* iTermTmuxSessionObject.m */; };
53E9DFE0220D518E0070C9C0 /* AlertTrigger.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D9DCC15142D7FC10016228A /* AlertTrigger.m */; };
53E9DFE1220D51D50070C9C0 /* CoprocessTrigger.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D9DDD91142E5AC600275650 /* CoprocessTrigger.m */; };
53E9DFE2220D52730070C9C0 /* PasswordTrigger.m in Sources */ = {isa = PBXBuildFile; fileRef = 1DABA03219253FEA00A228D8 /* PasswordTrigger.m */; };
@@ -4429,6 +4431,8 @@
53E184F01FE32F2800DB78F3 /* iTermMetalBufferPool.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = iTermMetalBufferPool.m; sourceTree = "<group>"; };
53E8F36D2244A58800F3770F /* iTermActionsModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = iTermActionsModel.h; sourceTree = "<group>"; };
53E8F36E2244A58800F3770F /* iTermActionsModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = iTermActionsModel.m; sourceTree = "<group>"; };
53E98E79233C6B760094D8A9 /* iTermTmuxSessionObject.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = iTermTmuxSessionObject.h; sourceTree = "<group>"; };
53E98E7A233C6B760094D8A9 /* iTermTmuxSessionObject.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = iTermTmuxSessionObject.m; sourceTree = "<group>"; };
53EBF29B1DCBFF7C00766613 /* iTermAPIServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iTermAPIServer.h; sourceTree = "<group>"; };
53EBF29C1DCBFF7C00766613 /* iTermAPIServer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = iTermAPIServer.m; sourceTree = "<group>"; };
53EBF29F1DCCF4AC00766613 /* iTermIPV4Address.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iTermIPV4Address.h; sourceTree = "<group>"; };
@@ -7010,6 +7014,8 @@
A6F718B4226438FC0053488E /* iTermInitialDirectory+Tmux.m */,
53D68F7E2283EE7C0018710D /* iTermTmuxLayoutBuilder.h */,
53D68F7F2283EE7C0018710D /* iTermTmuxLayoutBuilder.m */,
53E98E79233C6B760094D8A9 /* iTermTmuxSessionObject.h */,
53E98E7A233C6B760094D8A9 /* iTermTmuxSessionObject.m */,
);
name = tmux;
sourceTree = "<group>";
@@ -9987,6 +9993,7 @@
A6153D4C21F30A9C002976FC /* iTermJobTreeViewController.h in Headers */,
5370678F21C9D2780088D0F3 /* SIGSHA2VerificationAlgorithm.h in Headers */,
A66719161DCE36C3000CE608 /* NSURL+iTerm.h in Headers */,
53E98E7B233C6B760094D8A9 /* iTermTmuxSessionObject.h in Headers */,
A66719171DCE36C3000CE608 /* NSDate+iTerm.h in Headers */,
A6BCAAD821F6F53E0000CD29 /* iTermTextPopoverViewController.h in Headers */,
A66719181DCE36C3000CE608 /* iTermBaseHotKey.h in Headers */,
@@ -12429,6 +12436,7 @@
A6AC5D241E9036D70097C0A7 /* iTermURLMark.m in Sources */,
535EA51420D8C1EB00FC81E0 /* iTermProfilesWindowController.m in Sources */,
A6A4866F20B67A1600493302 /* iTermSemanticHistoryPrefsController.m in Sources */,
53E98E7C233C6B760094D8A9 /* iTermTmuxSessionObject.m in Sources */,
A66719621DCE3772000CE608 /* iTermSocketAddress.m in Sources */,
A6A4867E20B67FD100493302 /* SmartSelectionController.m in Sources */,
53AFFC8E1DD2A04100E6CEC6 /* iTermLSOF.m in Sources */,
@@ -4726,7 +4726,7 @@ - (void)profileSessionNameDidEndEditing:(NSNotification *)notification {
} else {
// Legacy code path for pre tmux 2.6
[_tmuxController renameWindowWithId:_delegate.tmuxWindow
inSession:nil
inSessionNumber:nil
toName:profile[KEY_NAME]];
}
_tmuxTitleOutOfSync = NO;
@@ -5910,14 +5910,18 @@ - (void)replaceWorkingDirectoryPollerWithTmuxWorkingDirectoryPoller {

- (void)installTmuxStatusBarMonitor {
assert(!_tmuxStatusBarMonitor);
_tmuxStatusBarMonitor = [[iTermTmuxStatusBarMonitor alloc] initWithGateway:_tmuxController.gateway
scope:self.variablesScope];
_tmuxStatusBarMonitor.active = [iTermProfilePreferences boolForKey:KEY_SHOW_STATUS_BAR inProfile:self.profile];
if ([iTermPreferences boolForKey:kPreferenceKeyUseTmuxStatusBar] ||
[iTermStatusBarLayout shouldOverrideLayout:self.profile[KEY_STATUS_BAR_LAYOUT]]) {
[self setSessionSpecificProfileValues:@{ KEY_STATUS_BAR_LAYOUT: [[iTermStatusBarLayout tmuxLayoutWithController:_tmuxController
scope:nil
window:self.delegate.tmuxWindow] dictionaryValue] }];

if (_tmuxController.gateway.minimumServerVersion.doubleValue >= 2.9) {
// Just use the built-in status bar for older versions of tmux because they don't support ${T:xxx} or ${E:xxx}
_tmuxStatusBarMonitor = [[iTermTmuxStatusBarMonitor alloc] initWithGateway:_tmuxController.gateway
scope:self.variablesScope];
_tmuxStatusBarMonitor.active = [iTermProfilePreferences boolForKey:KEY_SHOW_STATUS_BAR inProfile:self.profile];
if ([iTermPreferences boolForKey:kPreferenceKeyUseTmuxStatusBar] ||
[iTermStatusBarLayout shouldOverrideLayout:self.profile[KEY_STATUS_BAR_LAYOUT]]) {
[self setSessionSpecificProfileValues:@{ KEY_STATUS_BAR_LAYOUT: [[iTermStatusBarLayout tmuxLayoutWithController:_tmuxController
scope:nil
window:self.delegate.tmuxWindow] dictionaryValue] }];
}
}
}

@@ -5929,7 +5933,8 @@ - (void)installTmuxTitleMonitor {
}
__weak __typeof(self) weakSelf = self;
_tmuxTitleMonitor = [[iTermTmuxOptionMonitor alloc] initWithGateway:_tmuxController.gateway
scope:self.variablesScope
scope:self.variablesScope
fallbackVariableName:nil
format:@"#{pane_title}"
target:[NSString stringWithFormat:@"%%%@", @(self.tmuxPane)]
variableName:iTermVariableKeySessionTmuxPaneTitle
@@ -6308,8 +6313,7 @@ - (void)tmuxInitialCommandDidCompleteSuccessfully {
[_tmuxController ping];
[_tmuxController validateOptions];
[_tmuxController checkForUTF8];
[_tmuxController guessVersion];
[_tmuxController loadTitleFormat];
[_tmuxController guessVersion]; // NOTE: This kicks off more stuff that depends on knowing the version number.
}

- (void)tmuxInitialCommandDidFailWithError:(NSString *)error {
@@ -3690,11 +3690,12 @@ - (void)installTmuxTitleMonitor {
return;
}
_tmuxTitleMonitor = [[iTermTmuxOptionMonitor alloc] initWithGateway:tmuxController_.gateway
scope:self.variablesScope
format:tmuxController_.setTitlesString
target:[NSString stringWithFormat:@"@%@", @(self.tmuxWindow)]
variableName:iTermVariableKeyTabTmuxWindowTitle
block:nil];
scope:self.variablesScope
fallbackVariableName:iTermVariableKeySessionWindowName
format:@"#{T:set-titles-string}"
target:[NSString stringWithFormat:@"@%@", @(self.tmuxWindow)]
variableName:iTermVariableKeyTabTmuxWindowTitle
block:nil];
[_tmuxTitleMonitor updateOnce];
if (self.titleOverride.length == 0) {
// Show the tmux window title if both the tmux option set-titles is on and the user hasn't
@@ -4312,7 +4313,7 @@ - (void)setTitleOverride:(NSString *)titleOverride {
if (self.tmuxTab) {
if (titleOverride) {
[self.tmuxController renameWindowWithId:self.tmuxWindow
inSession:nil
inSessionNumber:nil
toName:titleOverride];
}
return;
@@ -3018,8 +3018,8 @@ - (BOOL)loadArrangement:(NSDictionary *)arrangement sessions:(NSArray *)sessions
if ([arrangement objectForKey:TERMINAL_GUID] &&
[[arrangement objectForKey:TERMINAL_GUID] isKindOfClass:[NSString class]]) {
NSString *savedGUID = [arrangement objectForKey:TERMINAL_GUID];
if ([[iTermController sharedInstance] terminalWithGuid:savedGUID]) {
// Refuse to create a window with an already-used guid.
if ([[iTermController sharedInstance] terminalWithGuid:savedGUID] || ![self stringIsValidTerminalGuid:savedGUID]) {
// Refuse to create a window with an already-used or invalid guid.
self.terminalGuid = [NSString stringWithFormat:@"pty-%@", [NSString uuid]];
} else {
self.terminalGuid = savedGUID;
@@ -3039,6 +3039,11 @@ - (BOOL)loadArrangement:(NSDictionary *)arrangement sessions:(NSArray *)sessions
return YES;
}

- (BOOL)stringIsValidTerminalGuid:(NSString *)string {
NSCharacterSet *characterSet = [NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-"];
return [string rangeOfCharacterFromSet:characterSet.invertedSet].location == NSNotFound;
}

- (BOOL)restoreTabsFromArrangement:(NSDictionary *)arrangement sessions:(NSArray<PTYSession *> *)sessions {
for (NSDictionary *tabArrangement in arrangement[TERMINAL_ARRANGEMENT_TABS]) {
NSDictionary<NSString *, PTYSession *> *sessionMap = nil;
@@ -8,6 +8,7 @@
#import <Cocoa/Cocoa.h>
#import "ProfileModel.h"
#import "iTermInitialDirectory.h"
#import "iTermTmuxSessionObject.h"
#import "TmuxGateway.h"
#import "WindowControllerInterface.h"

@@ -33,15 +34,15 @@ extern NSString *const kTmuxControllerWindowDidClose;
extern NSString *const kTmuxControllerAttachedSessionDidChange;
// Posted when a session changes name
extern NSString *const kTmuxControllerSessionWasRenamed;
// Posted when set-titles-string option changes. Object is tmux controller.
// Posted when set-titles option changes. Object is tmux controller.
extern NSString *const kTmuxControllerDidFetchSetTitlesStringOption;

@interface TmuxController : NSObject

@property(nonatomic, readonly) TmuxGateway *gateway;
@property(nonatomic, retain) NSMutableDictionary *windowPositions;
@property(nonatomic, copy) NSString *sessionName;
@property(nonatomic, retain) NSArray *sessions;
@property(nonatomic, copy) NSArray<iTermTmuxSessionObject *> *sessionObjects;
@property(nonatomic, assign) BOOL ambiguousIsDoubleWidth;
@property(nonatomic, assign) NSInteger unicodeVersion;
@property(nonatomic, readonly) NSString *clientName;
@@ -53,7 +54,6 @@ extern NSString *const kTmuxControllerDidFetchSetTitlesStringOption;
@property(nonatomic, readonly) NSDictionary *sharedFontOverrides;
@property(nonatomic, readonly) NSString *sessionGuid;
@property(nonatomic, readonly) BOOL variableWindowSize;
@property(nonatomic, readonly) NSString *setTitlesString;
@property(nonatomic, readonly) BOOL shouldSetTitles;
@property(nonatomic, readonly) BOOL serverIsLocal;

@@ -120,9 +120,9 @@ extern NSString *const kTmuxControllerDidFetchSetTitlesStringOption;
scope:(iTermVariableScope *)scope
initialDirectory:(iTermInitialDirectory *)initialDirectory;

- (void)newWindowInSession:(NSString *)targetSession
scope:(iTermVariableScope *)scope
initialDirectory:(iTermInitialDirectory *)initialDirectory;
- (void)newWindowInSessionNumber:(NSNumber *)sessionNumber
scope:(iTermVariableScope *)scope
initialDirectory:(iTermInitialDirectory *)initialDirectory;

- (void)selectPane:(int)windowPane;

@@ -150,9 +150,11 @@ extern NSString *const kTmuxControllerDidFetchSetTitlesStringOption;

- (void)killWindowPane:(int)windowPane;
- (void)killWindow:(int)window;
- (void)unlinkWindowWithId:(int)windowId inSession:(NSString *)sessionName;
- (void)unlinkWindowWithId:(int)windowId;
- (void)requestDetach;
- (void)renameWindowWithId:(int)windowId inSession:(NSString *)sessionName toName:(NSString *)newName;
- (void)renameWindowWithId:(int)windowId
inSessionNumber:(NSNumber *)sessionNumber
toName:(NSString *)newName;
- (BOOL)canRenamePane;
- (void)renamePane:(int)windowPane toTitle:(NSString *)newTitle;
- (void)setHotkeyForWindowPane:(int)windowPane to:(NSDictionary *)hotkey;
@@ -162,21 +164,25 @@ extern NSString *const kTmuxControllerDidFetchSetTitlesStringOption;
- (NSString *)tabColorStringForWindowPane:(int)windowPane;

- (void)linkWindowId:(int)windowId
inSession:(NSString *)sessionName
toSession:(NSString *)targetSession;
inSessionNumber:(int)sessionNumber
toSessionNumber:(int)targetSession;

- (void)moveWindowId:(int)windowId
inSession:(NSString *)sessionName
toSession:(NSString *)targetSession;
inSessionNumber:(int)sessionNumber
toSessionNumber:(int)targetSessionNumber;

- (void)renameSessionNumber:(int)sessionNumber
to:(NSString *)newName;

- (void)renameSession:(NSString *)oldName to:(NSString *)newName;
- (void)killSession:(NSString *)sessionName;
- (void)attachToSession:(NSString *)sessionName;
- (void)killSessionNumber:(int)sessionNumber;
- (void)attachToSessionWithNumber:(int)sessionNumber;
- (void)addSessionWithName:(NSString *)sessionName;
// NOTE: If the session name is bogus (or any other error occurs) the selector will not be called.
- (void)listWindowsInSession:(NSString *)sessionName
target:(id)target
selector:(SEL)selector
object:(id)object;
// NOTE: If anything goes wrong the selector will not be called.
- (void)listWindowsInSessionNumber:(int)sessionNumber
target:(id)target
selector:(SEL)selector
object:(id)object;

- (void)listSessions;
- (void)saveAffinities;
- (void)saveWindowOrigins;
@@ -200,7 +206,6 @@ extern NSString *const kTmuxControllerDidFetchSetTitlesStringOption;
- (void)setLayoutInWindow:(int)window toLayout:(NSString *)layout;
- (NSArray<PTYSession *> *)clientSessions;

- (void)setSize:(NSSize)size windows:(NSArray<NSString *> *)windows;
- (void)setSize:(NSSize)size window:(int)window;

@end

0 comments on commit 538d570

Please sign in to comment.
You can’t perform that action at this time.