1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #import "chrome/browser/ui/cocoa/certificate_viewer_mac.h" 6 7 #include <Security/Security.h> 8 #include <SecurityInterface/SFCertificatePanel.h> 9 #include <vector> 10 11 #include "base/mac/foundation_util.h" 12 #include "base/mac/scoped_cftyperef.h" 13 #include "chrome/browser/certificate_viewer.h" 14 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_mac.h" 15 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet.h" 16 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller.h" 17 #include "net/cert/x509_certificate.h" 18 #include "net/cert/x509_util_mac.h" 19 #import "ui/base/cocoa/window_size_constants.h" 20 21 class SSLCertificateViewerCocoaBridge; 22 23 @interface SFCertificatePanel (SystemPrivate) 24 // A system-private interface that dismisses a panel whose sheet was started by 25 // -beginSheetForWindow: 26 // modalDelegate: 27 // didEndSelector: 28 // contextInfo: 29 // certificates: 30 // showGroup: 31 // as though the user clicked the button identified by returnCode. Verified 32 // present in 10.8. 33 - (void)_dismissWithCode:(NSInteger)code; 34 @end 35 36 @interface SSLCertificateViewerCocoa () 37 - (void)onConstrainedWindowClosed; 38 @end 39 40 class SSLCertificateViewerCocoaBridge : public ConstrainedWindowMacDelegate { 41 public: 42 explicit SSLCertificateViewerCocoaBridge(SSLCertificateViewerCocoa * 43 controller) 44 : controller_(controller) { 45 } 46 47 virtual ~SSLCertificateViewerCocoaBridge() {} 48 49 // ConstrainedWindowMacDelegate implementation: 50 virtual void OnConstrainedWindowClosed( 51 ConstrainedWindowMac * window) OVERRIDE { 52 // |onConstrainedWindowClosed| will delete the sheet which might be still 53 // in use higher up the call stack. Wait for the next cycle of the event 54 // loop to call this function. 55 [controller_ performSelector:@selector(onConstrainedWindowClosed) 56 withObject:nil 57 afterDelay:0]; 58 } 59 60 private: 61 SSLCertificateViewerCocoa* controller_; // weak 62 63 DISALLOW_COPY_AND_ASSIGN(SSLCertificateViewerCocoaBridge); 64 }; 65 66 void ShowCertificateViewer(content::WebContents* web_contents, 67 gfx::NativeWindow parent, 68 net::X509Certificate* cert) { 69 // SSLCertificateViewerCocoa will manage its own lifetime and will release 70 // itself when the dialog is closed. 71 // See -[SSLCertificateViewerCocoa onConstrainedWindowClosed]. 72 SSLCertificateViewerCocoa* viewer = 73 [[SSLCertificateViewerCocoa alloc] initWithCertificate:cert]; 74 [viewer displayForWebContents:web_contents]; 75 } 76 77 @implementation SSLCertificateViewerCocoa 78 79 - (id)initWithCertificate:(net::X509Certificate*)certificate { 80 if ((self = [super init])) { 81 base::ScopedCFTypeRef<CFArrayRef> cert_chain( 82 certificate->CreateOSCertChainForCert()); 83 NSArray* certificates = base::mac::CFToNSCast(cert_chain.get()); 84 certificates_.reset([certificates retain]); 85 } 86 return self; 87 } 88 89 - (void)sheetDidEnd:(NSWindow*)parent 90 returnCode:(NSInteger)returnCode 91 context:(void*)context { 92 if (!closePending_) 93 constrainedWindow_->CloseWebContentsModalDialog(); 94 } 95 96 - (void)displayForWebContents:(content::WebContents*)webContents { 97 // Explicitly disable revocation checking, regardless of user preferences 98 // or system settings. The behaviour of SFCertificatePanel is to call 99 // SecTrustEvaluate on the certificate(s) supplied, effectively 100 // duplicating the behaviour of net::X509Certificate::Verify(). However, 101 // this call stalls the UI if revocation checking is enabled in the 102 // Keychain preferences or if the cert may be an EV cert. By disabling 103 // revocation checking, the stall is limited to the time taken for path 104 // building and verification, which should be minimized due to the path 105 // being provided in |certificates|. This does not affect normal 106 // revocation checking from happening, which is controlled by 107 // net::X509Certificate::Verify() and user preferences, but will prevent 108 // the certificate viewer UI from displaying which certificate is revoked. 109 // This is acceptable, as certificate revocation will still be shown in 110 // the page info bubble if a certificate in the chain is actually revoked. 111 base::ScopedCFTypeRef<CFMutableArrayRef> policies( 112 CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks)); 113 if (!policies.get()) { 114 NOTREACHED(); 115 return; 116 } 117 // Add a basic X.509 policy, in order to match the behaviour of 118 // SFCertificatePanel when no policies are specified. 119 SecPolicyRef basic_policy = NULL; 120 OSStatus status = net::x509_util::CreateBasicX509Policy(&basic_policy); 121 if (status != noErr) { 122 NOTREACHED(); 123 return; 124 } 125 CFArrayAppendValue(policies, basic_policy); 126 CFRelease(basic_policy); 127 128 status = net::x509_util::CreateRevocationPolicies(false, false, policies); 129 if (status != noErr) { 130 NOTREACHED(); 131 return; 132 } 133 134 panel_.reset([[SFCertificatePanel alloc] init]); 135 [panel_ setPolicies:(id) policies.get()]; 136 137 constrainedWindow_.reset( 138 new ConstrainedWindowMac(observer_.get(), webContents, self)); 139 } 140 141 - (NSWindow*)overlayWindow { 142 return overlayWindow_; 143 } 144 145 - (void)showSheetForWindow:(NSWindow*)window { 146 overlayWindow_.reset([window retain]); 147 [panel_ beginSheetForWindow:window 148 modalDelegate:self 149 didEndSelector:@selector(sheetDidEnd: 150 returnCode: 151 context:) 152 contextInfo:NULL 153 certificates:certificates_ 154 showGroup:YES]; 155 } 156 157 - (void)closeSheetWithAnimation:(BOOL)withAnimation { 158 closePending_ = YES; 159 overlayWindow_.reset(); 160 // Closing the sheet using -[NSApp endSheet:] doesn't work so use the private 161 // method. 162 [panel_ _dismissWithCode:NSFileHandlingPanelCancelButton]; 163 } 164 165 - (void)hideSheet { 166 NSWindow* sheetWindow = [overlayWindow_ attachedSheet]; 167 [sheetWindow setAlphaValue:0.0]; 168 169 oldResizesSubviews_ = [[sheetWindow contentView] autoresizesSubviews]; 170 [[sheetWindow contentView] setAutoresizesSubviews:NO]; 171 172 oldSheetFrame_ = [sheetWindow frame]; 173 NSRect overlayFrame = [overlayWindow_ frame]; 174 oldSheetFrame_.origin.x -= NSMinX(overlayFrame); 175 oldSheetFrame_.origin.y -= NSMinY(overlayFrame); 176 [sheetWindow setFrame:ui::kWindowSizeDeterminedLater display:NO]; 177 } 178 179 - (void)unhideSheet { 180 NSWindow* sheetWindow = [overlayWindow_ attachedSheet]; 181 NSRect overlayFrame = [overlayWindow_ frame]; 182 oldSheetFrame_.origin.x += NSMinX(overlayFrame); 183 oldSheetFrame_.origin.y += NSMinY(overlayFrame); 184 [sheetWindow setFrame:oldSheetFrame_ display:NO]; 185 [[sheetWindow contentView] setAutoresizesSubviews:oldResizesSubviews_]; 186 [[overlayWindow_ attachedSheet] setAlphaValue:1.0]; 187 } 188 189 - (void)pulseSheet { 190 // NOOP 191 } 192 193 - (void)makeSheetKeyAndOrderFront { 194 [[overlayWindow_ attachedSheet] makeKeyAndOrderFront:nil]; 195 } 196 197 - (void)updateSheetPosition { 198 // NOOP 199 } 200 201 - (void)onConstrainedWindowClosed { 202 panel_.reset(); 203 constrainedWindow_.reset(); 204 [self release]; 205 } 206 207 @end 208