зеркало из https://github.com/mozilla/pjs.git
Camino only - Bug 361463: Validate saved passwords against form action before filling. r=josh sr=mento
This commit is contained in:
Родитель
6707328527
Коммит
a922024419
|
@ -2,11 +2,21 @@
|
|||
IBClasses = (
|
||||
{CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
|
||||
{CLASS = KeychainBrowserListener; LANGUAGE = ObjC; SUPERCLASS = NSObject; },
|
||||
{
|
||||
ACTIONS = {shutdown = id; };
|
||||
CLASS = KeychainDenyList;
|
||||
LANGUAGE = ObjC;
|
||||
SUPERCLASS = NSObject;
|
||||
},
|
||||
{
|
||||
ACTIONS = {hitButtonCancel = id; hitButtonOK = id; hitButtonOther = id; shutdown = id; };
|
||||
CLASS = KeychainService;
|
||||
LANGUAGE = ObjC;
|
||||
OUTLETS = {confirmChangePasswordPanel = id; confirmStorePasswordPanel = id; };
|
||||
OUTLETS = {
|
||||
mConfirmChangePasswordPanel = id;
|
||||
mConfirmFillPasswordPanel = id;
|
||||
mConfirmStorePasswordPanel = id;
|
||||
};
|
||||
SUPERCLASS = NSObject;
|
||||
}
|
||||
);
|
||||
|
|
|
@ -3,15 +3,16 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IBDocumentLocation</key>
|
||||
<string>108 19 356 240 0 0 1024 746 </string>
|
||||
<string>175 24 356 240 0 0 1440 878 </string>
|
||||
<key>IBFramework Version</key>
|
||||
<string>446.1</string>
|
||||
<key>IBOpenObjects</key>
|
||||
<array>
|
||||
<integer>18</integer>
|
||||
<integer>14</integer>
|
||||
<integer>45</integer>
|
||||
</array>
|
||||
<key>IBSystem Version</key>
|
||||
<string>8I127</string>
|
||||
<string>8J2135a</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
Двоичные данные
camino/resources/localized/English.lproj/Keychain.nib/keyedobjects.nib
сгенерированный
Двоичные данные
camino/resources/localized/English.lproj/Keychain.nib/keyedobjects.nib
сгенерированный
Двоичный файл не отображается.
|
@ -48,6 +48,7 @@
|
|||
NSString* mPassword; // strong
|
||||
NSString* mHost; // strong
|
||||
NSString* mComment; // strong
|
||||
NSArray* mSecurityDomains; // strong
|
||||
SecProtocolType mPort;
|
||||
SecProtocolType mProtocol;
|
||||
SecAuthenticationType mAuthenticationType;
|
||||
|
@ -91,6 +92,8 @@
|
|||
- (void)setCreator:(OSType)creator;
|
||||
- (NSString*)comment;
|
||||
- (void)setComment:(NSString*)comment;
|
||||
- (void)setSecurityDomains:(NSArray*)securityDomains;
|
||||
- (NSArray*)securityDomains;
|
||||
|
||||
- (void)removeFromKeychain;
|
||||
|
||||
|
|
|
@ -45,6 +45,8 @@
|
|||
- (BOOL)setAttributeType:(SecKeychainAttrType)type toValue:(void*)valuePtr withLength:(UInt32)length;
|
||||
@end
|
||||
|
||||
NSString* const kSecurityDomainSeparator = @";";
|
||||
|
||||
@implementation KeychainItem
|
||||
|
||||
+ (KeychainItem*)keychainItemForHost:(NSString*)host
|
||||
|
@ -181,6 +183,7 @@
|
|||
[mPassword release];
|
||||
[mHost release];
|
||||
[mComment release];
|
||||
[mSecurityDomains release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
|
@ -189,14 +192,15 @@
|
|||
if (!mKeychainItemRef)
|
||||
return;
|
||||
SecKeychainAttributeInfo attrInfo;
|
||||
UInt32 tags[7];
|
||||
UInt32 tags[8];
|
||||
tags[0] = kSecAccountItemAttr;
|
||||
tags[1] = kSecServerItemAttr;
|
||||
tags[2] = kSecPortItemAttr;
|
||||
tags[3] = kSecProtocolItemAttr;
|
||||
tags[4] = kSecAuthenticationTypeItemAttr;
|
||||
tags[5] = kSecCreatorItemAttr;
|
||||
tags[6] = kSecCommentItemAttr;
|
||||
tags[5] = kSecSecurityDomainItemAttr;
|
||||
tags[6] = kSecCreatorItemAttr;
|
||||
tags[7] = kSecCommentItemAttr;
|
||||
attrInfo.count = sizeof(tags)/sizeof(UInt32);
|
||||
attrInfo.tag = tags;
|
||||
attrInfo.format = NULL;
|
||||
|
@ -215,6 +219,8 @@
|
|||
mHost = nil;
|
||||
[mComment autorelease];
|
||||
mComment = nil;
|
||||
[mSecurityDomains autorelease];
|
||||
mSecurityDomains = nil;
|
||||
|
||||
if (result != noErr) {
|
||||
NSLog(@"Couldn't load keychain data");
|
||||
|
@ -222,6 +228,7 @@
|
|||
mPassword = [[NSString alloc] init];
|
||||
mHost = [[NSString alloc] init];
|
||||
mComment = [[NSString alloc] init];
|
||||
mSecurityDomains = [[NSArray alloc] init];
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -241,6 +248,13 @@
|
|||
mAuthenticationType = *((SecAuthenticationType*)(attr.data));
|
||||
else if (attr.tag == kSecCreatorItemAttr)
|
||||
mCreator = attr.data ? *((OSType*)(attr.data)) : 0;
|
||||
else if (attr.tag == kSecSecurityDomainItemAttr) {
|
||||
NSString* domainsString = [[[NSString alloc] initWithBytes:(char*)(attr.data) length:attr.length encoding:NSUTF8StringEncoding] autorelease];
|
||||
if ([domainsString isEqualToString:@""])
|
||||
mSecurityDomains = [[NSArray alloc] init];
|
||||
else
|
||||
mSecurityDomains = [[domainsString componentsSeparatedByString:kSecurityDomainSeparator] retain];
|
||||
}
|
||||
}
|
||||
mPassword = [[NSString alloc] initWithBytes:passwordData length:passwordLength encoding:NSUTF8StringEncoding];
|
||||
SecKeychainItemFreeAttributesAndData(attrList, (void*)passwordData);
|
||||
|
@ -379,6 +393,26 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (void)setSecurityDomains:(NSArray*)securityDomains
|
||||
{
|
||||
NSString* domainsString = [securityDomains componentsJoinedByString:kSecurityDomainSeparator];
|
||||
if([self setAttributeType:kSecSecurityDomainItemAttr toString:domainsString]) {
|
||||
[mSecurityDomains autorelease];
|
||||
mSecurityDomains = [securityDomains retain];
|
||||
}
|
||||
else {
|
||||
NSLog(@"Couldn't update keychain item security domains");
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray*)securityDomains
|
||||
{
|
||||
if (!mDataLoaded)
|
||||
[self loadKeychainData];
|
||||
return mSecurityDomains;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)setAttributeType:(SecKeychainAttrType)type toString:(NSString*)value {
|
||||
const char* cString = [value UTF8String];
|
||||
UInt32 length = cString ? strlen(cString) : 0;
|
||||
|
|
|
@ -58,8 +58,9 @@ enum KeychainPromptResult { kSave, kDontRemember, kNeverRemember } ;
|
|||
|
||||
@interface KeychainService : NSObject
|
||||
{
|
||||
IBOutlet id confirmStorePasswordPanel;
|
||||
IBOutlet id confirmChangePasswordPanel;
|
||||
IBOutlet id mConfirmStorePasswordPanel;
|
||||
IBOutlet id mConfirmChangePasswordPanel;
|
||||
IBOutlet id mConfirmFillPasswordPanel;
|
||||
|
||||
BOOL mFormPasswordFillIsEnabled;
|
||||
|
||||
|
@ -74,7 +75,8 @@ enum KeychainPromptResult { kSave, kDontRemember, kNeverRemember } ;
|
|||
- (IBAction)hitButtonOther:(id)sender;
|
||||
|
||||
- (KeychainPromptResult)confirmStorePassword:(NSWindow*)parent;
|
||||
- (BOOL)confirmChangedPassword:(NSWindow*)parent;
|
||||
- (BOOL)confirmChangePassword:(NSWindow*)parent;
|
||||
- (BOOL)confirmFillPassword:(NSWindow*)parent;
|
||||
|
||||
- (KeychainItem*)findKeychainEntryForHost:(NSString*)host
|
||||
port:(PRInt32)port
|
||||
|
@ -83,6 +85,7 @@ enum KeychainPromptResult { kSave, kDontRemember, kNeverRemember } ;
|
|||
- (void)storeUsername:(NSString*)username
|
||||
password:(NSString*)password
|
||||
forHost:(NSString*)host
|
||||
securityDomain:(NSString*)host
|
||||
port:(PRInt32)port
|
||||
scheme:(NSString*)scheme
|
||||
isForm:(BOOL)isForm;
|
||||
|
@ -162,13 +165,6 @@ public:
|
|||
|
||||
// NS_DECL_NSIFORMSUBMITOBSERVER
|
||||
NS_IMETHOD Notify(nsIContent* formNode, nsIDOMWindowInternal* window, nsIURI* actionURL, PRBool* cancelSubmit);
|
||||
|
||||
private:
|
||||
|
||||
static KeychainPromptResult CheckStorePasswordYN(nsIDOMWindowInternal*);
|
||||
static BOOL CheckChangeDataYN(nsIDOMWindowInternal*);
|
||||
|
||||
static NSWindow* GetNSWindow(nsIDOMWindowInternal* inWindow);
|
||||
};
|
||||
|
||||
//
|
||||
|
|
|
@ -91,6 +91,9 @@ nsresult
|
|||
FindUsernamePasswordFields(nsIDOMHTMLFormElement* inFormElement, nsIDOMHTMLInputElement** outUsername,
|
||||
nsIDOMHTMLInputElement** outPassword, PRBool inStopWhenFound);
|
||||
|
||||
NSWindow*
|
||||
GetNSWindow(nsIDOMWindow* inWindow);
|
||||
|
||||
@interface KeychainService(Private)
|
||||
- (KeychainItem*)findLegacyKeychainEntryForHost:(NSString*)host port:(PRInt32)port;
|
||||
@end
|
||||
|
@ -260,6 +263,7 @@ int KeychainPrefChangedCallback(const char* inPref, void* unused)
|
|||
- (void)storeUsername:(NSString*)username
|
||||
password:(NSString*)password
|
||||
forHost:(NSString*)host
|
||||
securityDomain:(NSString*)securityDomain
|
||||
port:(PRInt32)port
|
||||
scheme:(NSString*)scheme
|
||||
isForm:(BOOL)isForm
|
||||
|
@ -276,6 +280,8 @@ int KeychainPrefChangedCallback(const char* inPref, void* unused)
|
|||
withUsername:username
|
||||
password:password];
|
||||
[newItem setCreator:kCaminoKeychainCreatorCode];
|
||||
if (securityDomain)
|
||||
[newItem setSecurityDomains:[NSArray arrayWithObject:securityDomain]];
|
||||
}
|
||||
|
||||
// Stores changes to a site's stored account. Because we don't handle multiple accounts, we want to
|
||||
|
@ -375,8 +381,8 @@ int KeychainPrefChangedCallback(const char* inPref, void* unused)
|
|||
//
|
||||
- (KeychainPromptResult)confirmStorePassword:(NSWindow*)parent
|
||||
{
|
||||
int result = [NSApp runModalForWindow:confirmStorePasswordPanel relativeToWindow:parent];
|
||||
[confirmStorePasswordPanel close];
|
||||
int result = [NSApp runModalForWindow:mConfirmStorePasswordPanel relativeToWindow:parent];
|
||||
[mConfirmStorePasswordPanel close];
|
||||
|
||||
KeychainPromptResult keychainAction = kDontRemember;
|
||||
switch (result) {
|
||||
|
@ -390,18 +396,32 @@ int KeychainPrefChangedCallback(const char* inPref, void* unused)
|
|||
}
|
||||
|
||||
//
|
||||
// confirmChangedPassword:
|
||||
// confirmChangePassword:
|
||||
//
|
||||
// The password stored in the keychain differs from what the user typed
|
||||
// in. Ask what they want to do to resolve the issue.
|
||||
//
|
||||
- (BOOL)confirmChangedPassword:(NSWindow*)parent
|
||||
- (BOOL)confirmChangePassword:(NSWindow*)parent
|
||||
{
|
||||
int result = [NSApp runModalForWindow:confirmChangePasswordPanel relativeToWindow:parent];
|
||||
[confirmChangePasswordPanel close];
|
||||
int result = [NSApp runModalForWindow:mConfirmChangePasswordPanel relativeToWindow:parent];
|
||||
[mConfirmChangePasswordPanel close];
|
||||
return (result == NSAlertDefaultReturn);
|
||||
}
|
||||
|
||||
//
|
||||
// confirmFillPassword:
|
||||
//
|
||||
// The password stored in the keychain has an action domain that
|
||||
// doesn't match the stored value; ask the user whether to fill.
|
||||
//
|
||||
- (BOOL)confirmFillPassword:(NSWindow*)parent
|
||||
{
|
||||
int result = [NSApp runModalForWindow:mConfirmFillPasswordPanel relativeToWindow:parent];
|
||||
[mConfirmFillPasswordPanel close];
|
||||
// Default is not to fill
|
||||
return (result != NSAlertDefaultReturn);
|
||||
}
|
||||
|
||||
|
||||
- (void)addHostToDenyList:(NSString*)host
|
||||
{
|
||||
|
@ -622,7 +642,7 @@ KeychainPrompt::ProcessPrompt(const PRUnichar* realm, bool checked, PRUnichar* u
|
|||
// choice and whether or not we found the username/password in the
|
||||
// keychain.
|
||||
if (checked && !existingEntry)
|
||||
[keychain storeUsername:username password:password forHost:(NSString*)host port:port scheme:scheme isForm:NO];
|
||||
[keychain storeUsername:username password:password forHost:(NSString*)host securityDomain:nil port:port scheme:scheme isForm:NO];
|
||||
else if (checked && existingEntry && (![[existingEntry username] isEqualToString:username] ||
|
||||
![[existingEntry password] isEqualToString:password]))
|
||||
[keychain updateKeychainEntry:existingEntry withUsername:username password:password scheme:scheme isForm:NO];
|
||||
|
@ -783,6 +803,13 @@ KeychainFormSubmitObserver::Notify(nsIContent* node, nsIDOMWindowInternal* windo
|
|||
PRInt32 port = -1;
|
||||
docURL->GetPort(&port);
|
||||
|
||||
nsAutoString action;
|
||||
formNode->GetAction(action);
|
||||
NSString* actionHost = [[NSURL URLWithString:[NSString stringWith_nsAString:action]] host];
|
||||
// Forms without an action specified submit to the page
|
||||
if (!actionHost)
|
||||
actionHost = host;
|
||||
|
||||
//
|
||||
// If there's already an entry in the keychain, check if the username
|
||||
// and password match. If not, ask the user what they want to do and replace
|
||||
|
@ -792,13 +819,18 @@ KeychainFormSubmitObserver::Notify(nsIContent* node, nsIDOMWindowInternal* windo
|
|||
KeychainItem* existingEntry = [keychain findKeychainEntryForHost:host port:port scheme:scheme isForm:YES];
|
||||
if (existingEntry) {
|
||||
if (!([[existingEntry username] isEqualToString:username] && [[existingEntry password] isEqualToString:password]))
|
||||
if (CheckChangeDataYN(window))
|
||||
if ([keychain confirmChangePassword:GetNSWindow(window)])
|
||||
[keychain updateKeychainEntry:existingEntry withUsername:username password:password scheme:scheme isForm:YES];
|
||||
// If the password doesn't have an action host associated with it,
|
||||
// add the host for the first form it is submitted to.
|
||||
if ([[existingEntry securityDomains] count] == 0) {
|
||||
[existingEntry setSecurityDomains:[NSArray arrayWithObject:actionHost]];
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (CheckStorePasswordYN(window)) {
|
||||
switch ([keychain confirmStorePassword:GetNSWindow(window)]) {
|
||||
case kSave:
|
||||
[keychain storeUsername:username password:password forHost:host port:port scheme:scheme isForm:YES];
|
||||
[keychain storeUsername:username password:password forHost:host securityDomain:actionHost port:port scheme:scheme isForm:YES];
|
||||
break;
|
||||
|
||||
case kNeverRemember:
|
||||
|
@ -817,28 +849,6 @@ KeychainFormSubmitObserver::Notify(nsIContent* node, nsIDOMWindowInternal* windo
|
|||
}
|
||||
|
||||
|
||||
NSWindow*
|
||||
KeychainFormSubmitObserver::GetNSWindow(nsIDOMWindowInternal* inWindow)
|
||||
{
|
||||
CHBrowserView* browserView = [CHBrowserView browserViewFromDOMWindow:inWindow];
|
||||
return [browserView nativeWindow];
|
||||
}
|
||||
|
||||
KeychainPromptResult
|
||||
KeychainFormSubmitObserver::CheckStorePasswordYN(nsIDOMWindowInternal* window)
|
||||
{
|
||||
NSWindow* nswindow = GetNSWindow(window);
|
||||
return [[KeychainService instance] confirmStorePassword:nswindow];
|
||||
}
|
||||
|
||||
|
||||
BOOL
|
||||
KeychainFormSubmitObserver::CheckChangeDataYN(nsIDOMWindowInternal* window)
|
||||
{
|
||||
NSWindow* nswindow = GetNSWindow(window);
|
||||
return [[KeychainService instance] confirmChangedPassword:nswindow];
|
||||
}
|
||||
|
||||
@implementation KeychainBrowserListener
|
||||
|
||||
- (id)initWithBrowser:(CHBrowserView*)aBrowser
|
||||
|
@ -885,6 +895,7 @@ KeychainFormSubmitObserver::CheckChangeDataYN(nsIDOMWindowInternal* window)
|
|||
if (NS_FAILED(rv) || !forms)
|
||||
return;
|
||||
|
||||
BOOL silentlyDenySuspiciousForms = NO;
|
||||
PRUint32 numForms;
|
||||
forms->GetLength(&numForms);
|
||||
|
||||
|
@ -929,6 +940,31 @@ KeychainFormSubmitObserver::CheckChangeDataYN(nsIDOMWindowInternal* window)
|
|||
|
||||
KeychainItem* keychainEntry = [keychain findKeychainEntryForHost:host port:port scheme:scheme isForm:YES];
|
||||
if (keychainEntry) {
|
||||
// To help prevent password stealing on sites that allow user-created HTML (but not JS),
|
||||
// only fill if the form's action host is one that has been authorized by the user.
|
||||
// If the keychain entry doesn't have any authorized hosts, either because it pre-dates
|
||||
// this code or because it's a non-Camino entry, fill any form.
|
||||
nsAutoString action;
|
||||
formElement->GetAction(action);
|
||||
NSString* actionHost = [[NSURL URLWithString:[NSString stringWith_nsAString:action]] host];
|
||||
if (!actionHost)
|
||||
actionHost = host;
|
||||
NSArray* allowedActionHosts = [keychainEntry securityDomains];
|
||||
if ([allowedActionHosts count] > 0 && ![allowedActionHosts containsObject:actionHost]) {
|
||||
// The form has an un-authorized action domain. If we haven't
|
||||
// asked the user about this page, ask. If we have and they said
|
||||
// no, don't ask (to prevent a malicious page from throwing
|
||||
// dialogs until the user tries the other button).
|
||||
if (silentlyDenySuspiciousForms)
|
||||
continue;
|
||||
if (![keychain confirmFillPassword:GetNSWindow(inDOMWindow)]) {
|
||||
silentlyDenySuspiciousForms = YES;
|
||||
continue;
|
||||
}
|
||||
// Remember the approval
|
||||
[keychainEntry setSecurityDomains:[allowedActionHosts arrayByAddingObject:actionHost]];
|
||||
}
|
||||
|
||||
nsAutoString user, pwd;
|
||||
[[keychainEntry username] assignTo_nsAString:user];
|
||||
[[keychainEntry password] assignTo_nsAString:pwd];
|
||||
|
@ -1026,6 +1062,18 @@ KeychainFormSubmitObserver::CheckChangeDataYN(nsIDOMWindowInternal* window)
|
|||
|
||||
@end
|
||||
|
||||
//
|
||||
// GetNSWindow
|
||||
//
|
||||
// Finds the native window for the given DOM window
|
||||
//
|
||||
NSWindow*
|
||||
GetNSWindow(nsIDOMWindow* inWindow)
|
||||
{
|
||||
CHBrowserView* browserView = [CHBrowserView browserViewFromDOMWindow:inWindow];
|
||||
return [browserView nativeWindow];
|
||||
}
|
||||
|
||||
//
|
||||
// FindUsernamePasswordFields
|
||||
//
|
||||
|
|
Загрузка…
Ссылка в новой задаче