Camino only - Bug 361463: Validate saved passwords against form action before filling. r=josh sr=mento

This commit is contained in:
stuart.morgan%alumni.case.edu 2007-02-13 01:10:16 +00:00
Родитель 6707328527
Коммит a922024419
7 изменённых файлов: 140 добавлений и 48 удалений

Просмотреть файл

@ -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 сгенерированный

Двоичный файл не отображается.

Просмотреть файл

@ -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
//