mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-11-08 02:07:09 +00:00
AngularJS (EOL since 2022-01) and AngularJS Material no longer receive
security fixes, so any new vulnerability in them will stay unpatched.
In AngularJS, templates are code: any attacker-controlled text that
ends up in a compiled template is evaluated as an expression, and
since 1.6 there is no expression sandbox, so a single
{{constructor.constructor('...')()}} gives full script execution.
SOGo renders untrusted data (mail bodies, invitations, attachment
names, contact fields) into such templates and currently relies on
server-side filtering, like stripping "{{" and "}}", to prevent this.
Any gap in that filtering, or a bypass of $sanitize/ng-bind-html,
would mean XSS in the webmail session: reading and sending mail as
the user, and access to calendars and contacts.
A Content Security Policy adds a second layer in the browser that
doesn't depend on catching every injection. The new
SOGoContentSecurityPolicy system default ("report-only" or "enforce",
disabled when unset) sends a strict policy with the pages of the web
interface:
script-src 'self' 'nonce-<random>' (no 'unsafe-inline'/'unsafe-eval')
style-src 'self' 'unsafe-inline' (Angular Material, CKEditor 5)
object-src 'none'; base-uri 'self'; frame-ancestors 'self'; ...
The policy is generated by sogod, since a fresh 128-bit nonce is needed
per response: it is shared by all components of the response and set
on the inline configuration scripts of the page frame and module views.
The page also gets ng-csp="no-unsafe-eval", so AngularJS evaluates
expressions with its interpreter instead of Function().
With the policy enforced, an expression injected into a compiled
template, such as {{constructor.constructor('...')()}}, can no longer
reach Function() and fails with an EvalError, and injected inline
scripts are blocked. Injected expressions are limited to calling
functions available on the scope.
Tested with Chromium in both modes on login, Mail (HTML messages,
remote content, invitations, compose), Calendar and Contacts editors,
Preferences and Administration: no violations from SOGo's own code.
With the default unset, the generated output is unchanged.
form-action is deliberately not restricted, so SAML2/CAS/OpenID
logins posting to an external IdP keep working. Scripts listed in
SOGoUIAdditionalJSFiles are served from WebServerResources and allowed,
but must not load scripts from other origins, use eval() or rely on
inline handlers; their dependencies have to be installed and listed
alongside them.
1188 lines
25 KiB
Objective-C
1188 lines
25 KiB
Objective-C
/* SOGoSystemDefaults.m - this file is part of SOGo
|
|
*
|
|
* Copyright (C) 2009-2021 Inverse inc.
|
|
* Copyright (C) 2012 Jeroen Dekkers <jeroen@dekkers.ch>
|
|
*
|
|
* This file is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This file is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#import <dlfcn.h>
|
|
|
|
#import <Foundation/NSBundle.h>
|
|
#import <Foundation/NSFileManager.h>
|
|
#import <Foundation/NSFileManager.h>
|
|
#import <Foundation/NSUserDefaults.h>
|
|
#import <Foundation/NSProcessInfo.h>
|
|
|
|
#import <NGExtensions/NSObject+Logs.h>
|
|
|
|
#import "NSArray+Utilities.h"
|
|
#import "NSString+Crypto.h"
|
|
#import "NSDictionary+Utilities.h"
|
|
#import "SOGoStartupLogger.h"
|
|
|
|
#import "SOGoSystemDefaults.h"
|
|
#import "SOGoConstants.h"
|
|
|
|
@implementation SOGoSystemDefaults
|
|
|
|
#if defined(LDAP_CONFIG)
|
|
#endif
|
|
|
|
typedef void (*NSUserDefaultsInitFunction) ();
|
|
|
|
#define DIR_SEP "/"
|
|
|
|
#ifndef NSUIntegerMax
|
|
#define NSUIntegerMax UINTPTR_MAX
|
|
#endif
|
|
|
|
static void
|
|
BootstrapNSUserDefaults ()
|
|
{
|
|
char *filename;
|
|
NSUserDefaultsInitFunction SOGoNSUserDefaultsBootstrap;
|
|
void *handle;
|
|
|
|
filename = SOGO_LIBDIR DIR_SEP "libSOGoNSUserDefaults.so.1";
|
|
handle = dlopen (filename, RTLD_NOW | RTLD_GLOBAL);
|
|
if (handle)
|
|
{
|
|
SOGoNSUserDefaultsBootstrap = dlsym (handle,
|
|
"SOGoNSUserDefaultsBootstrap");
|
|
if (SOGoNSUserDefaultsBootstrap)
|
|
SOGoNSUserDefaultsBootstrap ();
|
|
}
|
|
}
|
|
|
|
static void
|
|
_injectConfigurationFromFile (NSMutableDictionary *defaultsDict,
|
|
NSString *filename, NSObject *logger)
|
|
{
|
|
NSDictionary *newConfig, *fileAttrs;
|
|
NSFileManager *fm;
|
|
|
|
fm = [NSFileManager defaultManager];
|
|
if ([fm fileExistsAtPath: filename])
|
|
{
|
|
fileAttrs = [fm fileAttributesAtPath: filename
|
|
traverseLink: YES];
|
|
if (![fileAttrs objectForKey: @"NSFileSize"])
|
|
{
|
|
[logger errorWithFormat:
|
|
@"Can't get file attributes from '%@'",
|
|
filename];
|
|
exit(1);
|
|
}
|
|
if ([[fileAttrs objectForKey: @"NSFileSize"] intValue] == 0 )
|
|
{
|
|
[logger warnWithFormat:
|
|
@"Empty file: '%@'. Skipping",
|
|
filename];
|
|
}
|
|
else
|
|
{
|
|
newConfig = [NSDictionary dictionaryWithContentsOfFile: filename];
|
|
if (newConfig)
|
|
[defaultsDict addEntriesFromDictionary: newConfig];
|
|
else
|
|
{
|
|
[logger errorWithFormat:
|
|
@"Cannot read configuration from '%@'. Aborting",
|
|
filename];
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ (void) prepareUserDefaults
|
|
{
|
|
/* Load settings from configuration files and
|
|
* enforce the following order of precedence.
|
|
* First match wins
|
|
* 1. Command line arguments
|
|
* 2. .GNUstepDefaults
|
|
* 3. /etc/sogo/{debconf,sogo}.conf
|
|
* 4. SOGoDefaults.plist
|
|
*
|
|
* The default standardUserDefaults search list is as follows:
|
|
* GSPrimaryDomain
|
|
* NSArgumentDomain (command line arguments)
|
|
* applicationDomain (sogod)
|
|
* NSGlobalDomain
|
|
* GSConfigDomain
|
|
* (languages)
|
|
* NSRegistrationDomain
|
|
*
|
|
* We'll end up with this search list:
|
|
* NSArgumentDomain (command line arguments)
|
|
* sogodRuntimeDomain (config from all config files)
|
|
* GSPrimaryDomain
|
|
* NSGlobalDomain
|
|
* GSConfigDomain
|
|
* (languages)
|
|
* NSRegistrationDomain (SOPE loads its defaults in this one)
|
|
*/
|
|
|
|
NSDictionary *sogodDomain;
|
|
NSMutableDictionary *configFromFiles;
|
|
NSUserDefaults *ud;
|
|
SOGoStartupLogger *logger;
|
|
NSBundle *bundle;
|
|
NSString *confFiles[] = {@"/etc/sogo/debconf.conf",
|
|
@"/etc/sogo/sogo.conf"};
|
|
NSString *filename, *redirectURL;
|
|
NSUInteger count;
|
|
|
|
logger = [SOGoStartupLogger sharedLogger];
|
|
|
|
/* Load the configuration from the standard user default files */
|
|
ud = [NSUserDefaults standardUserDefaults];
|
|
|
|
/* Populate configFromFiles with default values from SOGoDefaults.plist */
|
|
configFromFiles = [NSMutableDictionary dictionaryWithCapacity:0];
|
|
bundle = [NSBundle bundleForClass: self];
|
|
filename = [bundle pathForResource: @"SOGoDefaults" ofType: @"plist"];
|
|
if (filename)
|
|
_injectConfigurationFromFile (configFromFiles, filename, logger);
|
|
|
|
/* Fill/Override configFromFiles values with configuration stored
|
|
* in "/etc" */
|
|
for (count = 0; count < sizeof(confFiles)/sizeof(confFiles[0]); count++)
|
|
_injectConfigurationFromFile (configFromFiles, confFiles[count], logger);
|
|
|
|
/* This dance is required to let other appplications (sogo-tool) use
|
|
* options from the sogod domain while preserving the order of precedence
|
|
* - remove the 'sogod' domain from the user defaults search list
|
|
* - Load the content of the sogod domain into configFromFiles
|
|
* Thereby overriding values from the config files loaded above
|
|
*/
|
|
[ud removeSuiteNamed: @"sogod"];
|
|
sogodDomain = [ud persistentDomainForName: @"sogod"];
|
|
if ([sogodDomain count])
|
|
[configFromFiles addEntriesFromDictionary: sogodDomain];
|
|
|
|
/* Add a volatile domain containing the config to the search list.
|
|
* The domain is added at the very front of the search list
|
|
*/
|
|
[ud setVolatileDomain: configFromFiles
|
|
forName: @"sogodRuntimeDomain"];
|
|
[ud addSuiteNamed: @"sogodRuntimeDomain"];
|
|
|
|
/* NSArgumentsDomain goes back in front of the search list */
|
|
[ud addSuiteNamed: @"NSArgumentDomain"];
|
|
|
|
/* issue a warning if WOApplicationRedirectURL is used */
|
|
redirectURL = [ud stringForKey: @"WOApplicationRedirectURL"];
|
|
if (redirectURL)
|
|
{
|
|
[logger warnWithFormat:
|
|
@"Using obsolete 'WOApplicationRedirectURL' user default."];
|
|
[logger warnWithFormat:
|
|
@" Please configure the use of the x-webobjects-XXX headers"
|
|
@" with your webserver (see sample files)."];
|
|
if ([redirectURL hasSuffix: @"/"])
|
|
[ud setObject: [redirectURL substringToIndex: [redirectURL length] - 1]
|
|
forKey: @"WOApplicationRedirectURL"];
|
|
}
|
|
}
|
|
|
|
+ (void) initialize
|
|
{
|
|
BootstrapNSUserDefaults ();
|
|
[self prepareUserDefaults];
|
|
}
|
|
|
|
+ (SOGoSystemDefaults *) sharedSystemDefaults
|
|
{
|
|
static SOGoSystemDefaults *sharedSystemDefaults = nil;
|
|
NSUserDefaults *ud;
|
|
|
|
if (!sharedSystemDefaults)
|
|
{
|
|
ud = [NSUserDefaults standardUserDefaults];
|
|
sharedSystemDefaults = [self defaultsSourceWithSource: ud
|
|
andParentSource: nil];
|
|
[sharedSystemDefaults retain];
|
|
}
|
|
|
|
return sharedSystemDefaults;
|
|
}
|
|
|
|
- (id) init
|
|
{
|
|
if ((self = [super init]))
|
|
{
|
|
loginDomains = nil;
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void) dealloc
|
|
{
|
|
[loginDomains release];
|
|
[super dealloc];
|
|
}
|
|
|
|
- (BOOL) migrate
|
|
{
|
|
static NSDictionary *migratedKeys = nil;
|
|
|
|
if (!migratedKeys)
|
|
{
|
|
migratedKeys = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
@"SOGoProfileURL", @"AgenorProfileURL",
|
|
@"SOGoTimeZone", @"SOGoServerTimeZone",
|
|
nil];
|
|
[migratedKeys retain];
|
|
}
|
|
|
|
return ([self migrateOldDefaultsWithDictionary: migratedKeys]
|
|
| [super migrate]);
|
|
}
|
|
|
|
- (NSArray *) domainIds
|
|
{
|
|
NSDictionary *domains = [self dictionaryForKey: @"domains"];
|
|
return [domains allKeys];
|
|
}
|
|
|
|
- (BOOL) doesLoginTypeByDomain
|
|
{
|
|
return ([self dictionaryForKey: @"SOGoLoginTypeByDomain"] != nil);
|
|
}
|
|
|
|
- (NSString *) getLoginTypeForDomain: (NSString*) _domain
|
|
{
|
|
NSDictionary *domains, *config;
|
|
NSString *type;
|
|
if(![self doesLoginTypeByDomain])
|
|
return nil;
|
|
domains = [self dictionaryForKey: @"SOGoLoginTypeByDomain"];
|
|
if([domains objectForKey: _domain])
|
|
{
|
|
config = [domains objectForKey: _domain];
|
|
}
|
|
else if([domains objectForKey: @"login_default"])
|
|
{
|
|
config = [domains objectForKey: @"login_default"];
|
|
}
|
|
else
|
|
return nil;
|
|
|
|
if((type = [config objectForKey: @"type"]))
|
|
{
|
|
return type;
|
|
}
|
|
else
|
|
return nil;
|
|
}
|
|
|
|
- (NSString *) getImapAuthMechForDomain: (NSString*) _domain
|
|
{
|
|
NSDictionary *domains, *config;
|
|
NSString *type;
|
|
|
|
if(![self doesLoginTypeByDomain])
|
|
return nil;
|
|
|
|
domains = [self dictionaryForKey: @"SOGoLoginTypeByDomain"];
|
|
|
|
if([domains objectForKey: _domain])
|
|
{
|
|
config = [domains objectForKey: _domain];
|
|
}
|
|
else if([domains objectForKey: @"login_default"])
|
|
{
|
|
config = [domains objectForKey: @"login_default"];
|
|
}
|
|
else
|
|
return nil;
|
|
|
|
if((type = [config objectForKey: @"imapAuthMech"]))
|
|
{
|
|
return type;
|
|
}
|
|
else
|
|
return nil;
|
|
}
|
|
|
|
- (NSString *) getSmtpAuthMechForDomain: (NSString*) _domain
|
|
{
|
|
NSDictionary *domains, *config;
|
|
NSString *type;
|
|
|
|
if(![self doesLoginTypeByDomain])
|
|
return nil;
|
|
|
|
domains = [self dictionaryForKey: @"SOGoLoginTypeByDomain"];
|
|
|
|
if([domains objectForKey: _domain])
|
|
{
|
|
config = [domains objectForKey: _domain];
|
|
}
|
|
else if([domains objectForKey: @"login_default"])
|
|
{
|
|
config = [domains objectForKey: @"login_default"];
|
|
}
|
|
else
|
|
return nil;
|
|
|
|
if((type = [config objectForKey: @"smtpAuthMech"]))
|
|
{
|
|
return type;
|
|
}
|
|
else
|
|
return nil;
|
|
}
|
|
|
|
- (NSString *) getLoginConfigForDomain: (NSDictionary*) _domain
|
|
{
|
|
NSDictionary *domains, *config;
|
|
if(![self doesLoginTypeByDomain])
|
|
return nil;
|
|
domains = [self dictionaryForKey: @"SOGoLoginTypeByDomain"];
|
|
if([domains objectForKey: _domain])
|
|
{
|
|
config = [domains objectForKey: _domain];
|
|
}
|
|
else if([domains objectForKey: @"login_default"])
|
|
{
|
|
config = [domains objectForKey: @"login_default"];
|
|
}
|
|
|
|
if(config)
|
|
return config;
|
|
else
|
|
return nil;
|
|
}
|
|
|
|
- (BOOL) hasOpenIdType
|
|
{
|
|
if([self doesLoginTypeByDomain])
|
|
{
|
|
NSDictionary *domainsConfig;
|
|
NSEnumerator *e;
|
|
NSString *domain, *type;
|
|
if(![self doesLoginTypeByDomain])
|
|
return NO;
|
|
domainsConfig = [self dictionaryForKey: @"SOGoLoginTypeByDomain"];
|
|
e = [domainsConfig keyEnumerator];
|
|
while((domain = [e nextObject]))
|
|
{
|
|
if((type = [[domainsConfig objectForKey: domain] objectForKey: @"type"]))
|
|
{
|
|
if([type isEqualToString: @"openid"])
|
|
return YES;
|
|
}
|
|
}
|
|
return NO;
|
|
}
|
|
else
|
|
return [[self authenticationType] isEqualToString: @"openid"];
|
|
|
|
}
|
|
|
|
|
|
- (BOOL) enableDomainBasedUID
|
|
{
|
|
return [self boolForKey: @"SOGoEnableDomainBasedUID"];
|
|
}
|
|
|
|
- (BOOL) forbidUnknownDomainsAuth
|
|
{
|
|
return [self boolForKey: @"SOGoForbidUnknownDomainsAuth"];
|
|
}
|
|
|
|
- (NSArray *) domainsAllowed
|
|
{
|
|
return [NSMutableArray arrayWithArray: [self stringArrayForKey: @"SOGoDomainAllowed"]];
|
|
}
|
|
|
|
- (NSArray *) loginDomains
|
|
{
|
|
NSMutableArray *filteredLoginDomains;
|
|
NSArray *domains;
|
|
id currentObject;
|
|
int count;
|
|
|
|
if (self->loginDomains == nil)
|
|
{
|
|
filteredLoginDomains = [NSMutableArray arrayWithArray: [self stringArrayForKey: @"SOGoLoginDomains"]];
|
|
domains = [self domainIds];
|
|
count = [filteredLoginDomains count];
|
|
while (count > 0)
|
|
{
|
|
count--;
|
|
currentObject = [filteredLoginDomains objectAtIndex: count];
|
|
if (![domains containsObject: currentObject])
|
|
{
|
|
[filteredLoginDomains removeObject: currentObject];
|
|
[self warnWithFormat: @"SOGoLoginDomains contains an invalid domain : %@", currentObject];
|
|
}
|
|
}
|
|
|
|
ASSIGN (self->loginDomains, filteredLoginDomains);
|
|
}
|
|
|
|
return self->loginDomains;
|
|
}
|
|
|
|
- (NSArray *) visibleDomainsForDomain: (NSString *) domain
|
|
{
|
|
NSMutableArray *domains;
|
|
NSArray *definedDomains, *visibleDomains, *currentGroup;
|
|
NSEnumerator *groups;
|
|
NSString *currentDomain;
|
|
|
|
definedDomains = [self domainIds];
|
|
visibleDomains = [self arrayForKey: @"SOGoDomainsVisibility"];
|
|
domains = [NSMutableArray array];
|
|
groups = [visibleDomains objectEnumerator];
|
|
while ((currentGroup = (NSArray *)[groups nextObject]))
|
|
{
|
|
if ([currentGroup containsObject: domain])
|
|
[domains addObjectsFromArray: currentGroup];
|
|
}
|
|
|
|
// Remove lookup domain and invalid domains
|
|
groups = [domains objectEnumerator];
|
|
while ((currentDomain = [groups nextObject]))
|
|
{
|
|
if ([currentDomain isEqualToString: domain] || ![definedDomains containsObject: currentDomain])
|
|
[domains removeObject: currentDomain];
|
|
}
|
|
|
|
return [domains uniqueObjects];
|
|
}
|
|
|
|
/* System-level only */
|
|
|
|
- (BOOL) crashOnSessionCreate
|
|
{
|
|
return [self boolForKey: @"SOGoCrashOnSessionCreate"];
|
|
}
|
|
|
|
- (BOOL) debugRequests
|
|
{
|
|
return [self boolForKey: @"SOGoDebugRequests"];
|
|
}
|
|
|
|
- (BOOL) debugLeaks;
|
|
{
|
|
return [self boolForKey: @"SOGoDebugLeaks"];
|
|
}
|
|
|
|
- (int) vmemLimit
|
|
{
|
|
return [self integerForKey: @"SxVMemLimit"];
|
|
}
|
|
|
|
- (BOOL) trustProxyAuthentication;
|
|
{
|
|
return [self boolForKey: @"SOGoTrustProxyAuthentication"];
|
|
}
|
|
|
|
- (NSString *) encryptionKey;
|
|
{
|
|
return [self stringForKey: @"SOGoEncryptionKey"];
|
|
}
|
|
|
|
|
|
- (BOOL) isSogoSecretSet
|
|
{
|
|
NSString *type;
|
|
type = [self stringForKey: @"SOGoSecretType"];
|
|
if(!type || [type isEqualToString:@"none"])
|
|
return NO;
|
|
else
|
|
return YES;
|
|
}
|
|
|
|
- (NSString *) sogoSecretValue
|
|
{
|
|
NSString *value, *type;
|
|
NSDictionary *env;
|
|
|
|
type = [self stringForKey: @"SOGoSecretType"];
|
|
if(!type)
|
|
type = @"none";
|
|
|
|
if ([type isEqualToString:@"plain"])
|
|
{
|
|
value = [self stringForKey: @"SOGoSecretValue"];
|
|
}
|
|
else if ([type isEqualToString:@"env"])
|
|
{
|
|
value = [self stringForKey: @"SOGoSecretValue"];
|
|
[self errorWithFormat: @"SOGo env fetching %@", value];
|
|
if(!value || [value length] < 1)
|
|
{
|
|
[self errorWithFormat: @"SOGoSecretValue is not set!"];
|
|
return nil;
|
|
}
|
|
env = [[NSProcessInfo processInfo] environment];
|
|
value = [env objectForKey:value];
|
|
}
|
|
else if ([type isEqualToString:@"none"])
|
|
{
|
|
return nil;
|
|
}
|
|
else {
|
|
[self errorWithFormat: @"SOGo can't understand the type of secret SOGoSecretType"];
|
|
return nil;
|
|
}
|
|
|
|
if(!value || [value length] != 32){
|
|
[self errorWithFormat: @"SOGo doesn't have a correct secret value of 32 chars SOGoSecretValue"];
|
|
return nil;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
|
|
- (BOOL) useRelativeURLs
|
|
{
|
|
return [self boolForKey: @"WOUseRelativeURLs"];
|
|
}
|
|
|
|
- (NSString *) sieveFolderEncoding
|
|
{
|
|
return [self stringForKey: @"SOGoSieveFolderEncoding"];
|
|
}
|
|
|
|
|
|
- (BOOL) isWebAccessEnabled
|
|
{
|
|
return [self boolForKey: @"SOGoWebAccessEnabled"];
|
|
}
|
|
|
|
- (BOOL) isCalendarDAVAccessEnabled
|
|
{
|
|
return [self boolForKey: @"SOGoCalendarDAVAccessEnabled"];
|
|
}
|
|
|
|
- (BOOL) isCalendarJitsiLinkEnabled
|
|
{
|
|
return [self boolForKey: @"SOGoCalendarEnableJitsiLink"];
|
|
}
|
|
|
|
- (BOOL) isAddressBookDAVAccessEnabled
|
|
{
|
|
return [self boolForKey: @"SOGoAddressBookDAVAccessEnabled"];
|
|
}
|
|
|
|
- (BOOL) enableEMailAlarms
|
|
{
|
|
return [self boolForKey: @"SOGoEnableEMailAlarms"];
|
|
}
|
|
|
|
- (BOOL) disableOrganizerEventCheck
|
|
{
|
|
return [self boolForKey: @"SOGoDisableOrganizerEventCheck"];
|
|
}
|
|
|
|
|
|
- (NSString *) faviconRelativeURL
|
|
{
|
|
return [self stringForKey: @"SOGoFaviconRelativeURL"];
|
|
}
|
|
|
|
- (NSString *) zipPath
|
|
{
|
|
return [self stringForKey: @"SOGoZipPath"];
|
|
}
|
|
|
|
- (int) port
|
|
{
|
|
return [self integerForKey: @"WOPort"];
|
|
}
|
|
|
|
- (int) workers
|
|
{
|
|
return [self integerForKey: @"WOWorkersCount"];
|
|
}
|
|
|
|
- (NSString *) logFile
|
|
{
|
|
return [self stringForKey: @"WOLogFile"];
|
|
}
|
|
|
|
- (NSString *) pidFile
|
|
{
|
|
return [self stringForKey: @"WOPidFile"];
|
|
}
|
|
|
|
- (NSTimeInterval) cacheCleanupInterval
|
|
{
|
|
return [self floatForKey: @"SOGoCacheCleanupInterval"];
|
|
}
|
|
|
|
- (NSString *) memcachedHost
|
|
{
|
|
return [self stringForKey: @"SOGoMemcachedHost"];
|
|
}
|
|
|
|
- (BOOL) uixDebugEnabled
|
|
{
|
|
return [self boolForKey: @"SOGoUIxDebugEnabled"];
|
|
}
|
|
|
|
/* Returns "report-only", "enforce" or nil when disabled */
|
|
- (NSString *) contentSecurityPolicy
|
|
{
|
|
NSString *mode;
|
|
|
|
mode = [[self stringForKey: @"SOGoContentSecurityPolicy"] lowercaseString];
|
|
if ([mode isEqualToString: @"report-only"] || [mode isEqualToString: @"enforce"])
|
|
return mode;
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (BOOL) easDebugEnabled
|
|
{
|
|
return [self boolForKey: @"SOGoEASDebugEnabled"];
|
|
}
|
|
|
|
- (BOOL) openIdDebugEnabled
|
|
{
|
|
return [self boolForKey: @"SOGoOpenIDDebugEnabled"];
|
|
}
|
|
|
|
- (BOOL) apiDebugEnabled
|
|
{
|
|
return [self boolForKey: @"SOGoAPIDebugEnabled"];
|
|
}
|
|
|
|
- (BOOL) tnefDecoderDebugEnabled
|
|
{
|
|
return [self boolForKey: @"SOGoTnefDecoderDebugEnabled"];
|
|
}
|
|
|
|
- (BOOL) xsrfValidationEnabled
|
|
{
|
|
id o;
|
|
|
|
if (!(o = [self objectForKey: @"SOGoXSRFValidationEnabled"]))
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
return [o boolValue];
|
|
}
|
|
|
|
- (NSString *) pageTitle
|
|
{
|
|
return [self stringForKey: @"SOGoPageTitle"];
|
|
}
|
|
|
|
- (NSString *) helpURL
|
|
{
|
|
return [self stringForKey: @"SOGoHelpURL"];
|
|
}
|
|
|
|
NSComparisonResult languageSort(id el1, id el2, void *context)
|
|
{
|
|
NSString *t1, *t2;
|
|
|
|
t1 = [context labelForKey: el1];
|
|
t2 = [context labelForKey: el2];
|
|
|
|
return [t1 compare: t2 options: NSCaseInsensitiveSearch];
|
|
}
|
|
|
|
- (NSArray *) supportedLanguages
|
|
{
|
|
static NSArray *supportedLanguages = nil;
|
|
|
|
if (!supportedLanguages)
|
|
{
|
|
supportedLanguages = [self stringArrayForKey: @"SOGoSupportedLanguages"];
|
|
[supportedLanguages retain];
|
|
}
|
|
|
|
return supportedLanguages;
|
|
}
|
|
|
|
- (BOOL) userCanChangePassword
|
|
{
|
|
return [self boolForKey: SOGoPasswordChangeEnabled];
|
|
}
|
|
|
|
- (BOOL) uixAdditionalPreferences
|
|
{
|
|
return [self boolForKey: @"SOGoUIxAdditionalPreferences"];
|
|
}
|
|
|
|
- (NSString *) loginSuffix
|
|
{
|
|
return [self stringForKey: @"SOGoLoginSuffix"];
|
|
}
|
|
|
|
- (NSString *) authenticationType
|
|
{
|
|
return [[self stringForKey: @"SOGoAuthenticationType"] lowercaseString];
|
|
}
|
|
|
|
- (BOOL) isSsoUsed: (NSString *) domain
|
|
{
|
|
NSString* authType;
|
|
|
|
authType = [self getLoginTypeForDomain: domain];
|
|
if(!authType)
|
|
authType = [self authenticationType];
|
|
return ([authType isEqualToString: @"cas"] || [authType isEqualToString: @"saml2"] || [authType isEqualToString: @"openid"]);
|
|
}
|
|
|
|
- (NSString *) davAuthenticationType
|
|
{
|
|
return [[self stringForKey: @"SOGoDAVAuthenticationType"] lowercaseString];
|
|
}
|
|
|
|
- (NSString *) CASServiceURL
|
|
{
|
|
return [self stringForKey: @"SOGoCASServiceURL"];
|
|
}
|
|
|
|
- (BOOL) CASLogoutEnabled
|
|
{
|
|
return [self boolForKey: @"SOGoCASLogoutEnabled"];
|
|
}
|
|
|
|
/* OpenId Support */
|
|
- (NSString *) openIdConfigUrl
|
|
{
|
|
return [self stringForKey: @"SOGoOpenIdConfigUrl"];
|
|
}
|
|
|
|
- (NSString *) openIdScope
|
|
{
|
|
return [self stringForKey: @"SOGoOpenIdScope"];
|
|
}
|
|
|
|
- (NSString *) openIdClient
|
|
{
|
|
return [self stringForKey: @"SOGoOpenIdClient"];
|
|
}
|
|
|
|
- (NSString *) openIdClientSecret
|
|
{
|
|
return [self stringForKey: @"SOGoOpenIdClientSecret"];
|
|
}
|
|
|
|
- (NSString *) openIdEmailParam
|
|
{
|
|
NSString *emailParam;
|
|
emailParam = [self stringForKey: @"SOGoOpenIdEmailParam"];
|
|
if(!emailParam)
|
|
emailParam = @"email";
|
|
return emailParam;
|
|
}
|
|
|
|
- (BOOL) openIdLogoutEnabled: (NSString *) _domain
|
|
{
|
|
if(_domain && [self doesLoginTypeByDomain])
|
|
{
|
|
NSDictionary *config;
|
|
NSString *type;
|
|
id value;
|
|
if((config = [self getLoginConfigForDomain: _domain]))
|
|
{
|
|
if((type = [config objectForKey: @"type"]) && [type isEqualToString:@"openid"])
|
|
return [self boolForKey: @"SOGoOpenIdLogoutEnabled" andDict: config];
|
|
}
|
|
return NO;
|
|
}
|
|
return [self boolForKey: @"SOGoOpenIdLogoutEnabled"];
|
|
}
|
|
|
|
- (BOOL) openIdSendDomainInfo
|
|
{
|
|
return [self boolForKey: @"SOGoOpenIdSendDomainInfo"];
|
|
}
|
|
|
|
- (int) openIdTokenCheckInterval
|
|
{
|
|
|
|
int v;
|
|
|
|
v = [self integerForKey: @"SOGoOpenIdTokenCheckInterval"];
|
|
|
|
if (!v)
|
|
v = 0;
|
|
if(v<0)
|
|
v = 0;
|
|
|
|
return v;
|
|
}
|
|
|
|
- (BOOL) openIdEnableRefreshToken
|
|
{
|
|
return [self boolForKey: @"SOGoOpenIdEnableRefreshToken"];
|
|
}
|
|
|
|
/* SAML2 support */
|
|
- (NSString *) SAML2PrivateKeyLocation
|
|
{
|
|
return [self stringForKey: @"SOGoSAML2PrivateKeyLocation"];
|
|
}
|
|
|
|
- (NSString *) SAML2CertificateLocation;
|
|
{
|
|
return [self stringForKey: @"SOGoSAML2CertificateLocation"];
|
|
}
|
|
|
|
- (NSString *) SAML2IdpMetadataLocation
|
|
{
|
|
return [self stringForKey: @"SOGoSAML2IdpMetadataLocation"];
|
|
}
|
|
|
|
- (NSString *) SAML2IdpPublicKeyLocation
|
|
{
|
|
return [self stringForKey: @"SOGoSAML2IdpPublicKeyLocation"];
|
|
}
|
|
|
|
- (NSString *) SAML2IdpCertificateLocation
|
|
{
|
|
return [self stringForKey: @"SOGoSAML2IdpCertificateLocation"];
|
|
}
|
|
|
|
- (BOOL) SAML2LogoutEnabled
|
|
{
|
|
return [self boolForKey: @"SOGoSAML2LogoutEnabled"];
|
|
}
|
|
|
|
- (NSString *) SAML2LogoutURL
|
|
{
|
|
return [self stringForKey: @"SOGoSAML2LogoutURL"];
|
|
}
|
|
|
|
- (NSString *) SAML2LoginAttribute
|
|
{
|
|
return [self stringForKey: @"SOGoSAML2LoginAttribute"];
|
|
}
|
|
|
|
- (BOOL) enablePublicAccess
|
|
{
|
|
return [self boolForKey: @"SOGoEnablePublicAccess"];
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
- (int) maximumFailedLoginCount
|
|
{
|
|
return [self integerForKey: @"SOGoMaximumFailedLoginCount"];
|
|
}
|
|
|
|
- (int) maximumFailedLoginInterval
|
|
{
|
|
int v;
|
|
|
|
v = [self integerForKey: @"SOGoMaximumFailedLoginInterval"];
|
|
|
|
if (!v)
|
|
v = 10;
|
|
|
|
return v;
|
|
}
|
|
|
|
- (int) failedLoginBlockInterval
|
|
{
|
|
int v;
|
|
|
|
v = [self integerForKey: @"SOGoFailedLoginBlockInterval"];
|
|
|
|
if (!v)
|
|
v = 300;
|
|
|
|
return v;
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
- (int) maximumMessageSizeLimit
|
|
{
|
|
return [self integerForKey: @"SOGoMaximumMessageSizeLimit"];
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
- (NSUInteger) maximumMessageSubmissionCount
|
|
{
|
|
NSUInteger v;
|
|
|
|
v = [self integerForKey: @"SOGoMaximumMessageSubmissionCount"];
|
|
|
|
if (!v)
|
|
return NSUIntegerMax;
|
|
|
|
return v;
|
|
}
|
|
|
|
- (NSUInteger) maximumRecipientCount
|
|
{
|
|
NSUInteger v;
|
|
|
|
v = [self integerForKey: @"SOGoMaximumRecipientCount"];
|
|
|
|
if (!v)
|
|
return NSUIntegerMax;
|
|
|
|
return v;
|
|
}
|
|
|
|
- (int) maximumSubmissionInterval
|
|
{
|
|
int v;
|
|
|
|
v = [self integerForKey: @"SOGoMaximumSubmissionInterval"];
|
|
|
|
if (!v)
|
|
v = 30;
|
|
|
|
return v;
|
|
}
|
|
|
|
- (int) messageSubmissionBlockInterval
|
|
{
|
|
int v;
|
|
|
|
v = [self integerForKey: @"SOGoMessageSubmissionBlockInterval"];
|
|
|
|
if (!v)
|
|
v = 300;
|
|
|
|
return v;
|
|
}
|
|
|
|
//
|
|
// SOGo rate-limiting
|
|
//
|
|
- (int) maximumRequestCount
|
|
{
|
|
return [self integerForKey: @"SOGoMaximumRequestCount"];
|
|
}
|
|
|
|
- (int) maximumRequestInterval
|
|
{
|
|
int v;
|
|
|
|
v = [self integerForKey: @"SOGoMaximumRequestInterval"];
|
|
|
|
if (!v)
|
|
v = 30;
|
|
|
|
return v;
|
|
}
|
|
|
|
- (int) requestBlockInterval
|
|
{
|
|
int v;
|
|
|
|
v = [self integerForKey: @"SOGoRequestBlockInterval"];
|
|
|
|
if (!v)
|
|
v = 300;
|
|
|
|
return v;
|
|
}
|
|
|
|
|
|
//
|
|
// SOGo EAS settings
|
|
//
|
|
- (int) maximumPingInterval
|
|
{
|
|
int v;
|
|
|
|
v = [self integerForKey: @"SOGoMaximumPingInterval"];
|
|
|
|
if (!v)
|
|
v = 10;
|
|
|
|
return v;
|
|
}
|
|
|
|
- (int) maximumSyncInterval
|
|
{
|
|
int v;
|
|
|
|
v = [self integerForKey: @"SOGoMaximumSyncInterval"];
|
|
|
|
if (!v)
|
|
v = 30;
|
|
|
|
return v;
|
|
}
|
|
|
|
- (int) internalSyncInterval
|
|
{
|
|
int v;
|
|
|
|
v = [self integerForKey: @"SOGoInternalSyncInterval"];
|
|
|
|
if (!v)
|
|
v = 10;
|
|
|
|
return v;
|
|
}
|
|
|
|
- (int) maximumSyncWindowSize
|
|
{
|
|
return [self integerForKey: @"SOGoMaximumSyncWindowSize"];
|
|
}
|
|
|
|
- (int) maximumSyncResponseSize
|
|
{
|
|
int v;
|
|
|
|
v = [self integerForKey: @"SOGoMaximumSyncResponseSize"];
|
|
|
|
if (v > 0)
|
|
v = v * 1024;
|
|
|
|
return v;
|
|
}
|
|
|
|
- (BOOL) easSearchInBody
|
|
{
|
|
return [self boolForKey: @"SOGoEASSearchInBody"];
|
|
}
|
|
|
|
- (BOOL) isEasUIDisabled
|
|
{
|
|
return [self boolForKey: @"SOGoEASDisableUI"];
|
|
}
|
|
|
|
//
|
|
// See https://msdn.microsoft.com/en-us/library/gg672032(v=exchg.80).aspx
|
|
//
|
|
- (int) maximumPictureSize
|
|
{
|
|
int v;
|
|
|
|
v = [self integerForKey: @"SOGoMaximumPictureSize"];
|
|
|
|
if (!v)
|
|
v = 102400;
|
|
|
|
return v;
|
|
}
|
|
|
|
- (BOOL) isPasswordRecoveryEnabled
|
|
{
|
|
return [self boolForKey: @"SOGoPasswordRecoveryEnabled"];
|
|
}
|
|
|
|
- (NSArray *) passwordRecoveryBaseURLs
|
|
{
|
|
return [self stringArrayForKey: @"SOGoPasswordRecoveryBaseURLs"];
|
|
}
|
|
|
|
- (NSArray *) passwordRecoveryDomains
|
|
{
|
|
static NSArray *passwordRecoveryDomains = nil;
|
|
|
|
if (!passwordRecoveryDomains)
|
|
{
|
|
passwordRecoveryDomains = [self stringArrayForKey: @"SOGoPasswordRecoveryDomains"];
|
|
[passwordRecoveryDomains retain];
|
|
}
|
|
|
|
return passwordRecoveryDomains;
|
|
}
|
|
|
|
- (NSString *) JWTSecret
|
|
{
|
|
NSString *secret;
|
|
|
|
secret = [self stringForKey: @"SOGoJWTSecret"];
|
|
|
|
if (!secret)
|
|
secret = @"SOGo"; // Default secret
|
|
|
|
return secret;
|
|
}
|
|
|
|
- (NSArray *) disableSharing
|
|
{
|
|
static NSArray *disableSharing = nil;
|
|
|
|
if (!disableSharing)
|
|
{
|
|
disableSharing = [self stringArrayForKey: @"SOGoDisableSharing"];
|
|
[disableSharing retain];
|
|
}
|
|
|
|
return disableSharing;
|
|
}
|
|
|
|
- (BOOL)isURLEncryptionEnabled {
|
|
return [self boolForKey: @"SOGoURLEncryptionEnabled"];
|
|
}
|
|
|
|
- (NSString *) urlEncryptionPassphrase
|
|
{
|
|
NSString *passphrase;
|
|
|
|
passphrase = [self stringForKey: @"SOGoURLEncryptionPassphrase"];
|
|
|
|
if (!passphrase)
|
|
passphrase = @"SOGoSuperSecret0"; // Default passphrase
|
|
|
|
return passphrase;
|
|
}
|
|
|
|
- (NSArray *) disableSharingAnyAuthUser
|
|
{
|
|
static NSArray *disableSharingAnyAuthUser = nil;
|
|
|
|
if (!disableSharingAnyAuthUser)
|
|
{
|
|
disableSharingAnyAuthUser = [self stringArrayForKey: @"SOGoDisableSharingAnyAuthUser"];
|
|
[disableSharingAnyAuthUser retain];
|
|
}
|
|
|
|
return disableSharingAnyAuthUser;
|
|
}
|
|
|
|
- (NSArray *) disableExport
|
|
{
|
|
static NSArray *disableExport = nil;
|
|
|
|
if (!disableExport)
|
|
{
|
|
disableExport = [self stringArrayForKey: @"SOGoDisableExport"];
|
|
[disableExport retain];
|
|
}
|
|
|
|
return disableExport;
|
|
}
|
|
|
|
- (BOOL) enableMailCleaning
|
|
{
|
|
return [self boolForKey: @"SOGoEnableMailCleaning"];
|
|
}
|
|
|
|
@end
|