CVE-2016-6190: Exposure of Sensitive Information to an Unauthorized Actor (CWE-200)
value: SOGo before 2.3.12 and 3.x before 3.1.1 does not restrict access to the UID and DTSTAMP attributes, which allows remote authenticated users to obtain sensitive information about appointments with the "View the Date & Time" restriction, as demonstrated by correlating UIDs and DTSTAMPs between all users.
CWE description: The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information.
CVSS v3 base score: 4.3
Published: 2017-02-17T17:59Z
Repository: https://github.com/inverse-inc/sogo
Language: Objective-C
Files changed: SoObjects/Appointments/SOGoCalendarComponent.m, SoObjects/SOGo/SOGoUserSettings.h, SoObjects/SOGo/SOGoUserSettings.m
Commit date: 2016-05-27 10:53:16 -0400
Commit message: (fix) improved previous commit for attributes stripping and UID generation (fixes #3695 and #3696)

Diff: diff --git a/SoObjects/Appointments/SOGoCalendarComponent.m b/SoObjects/Appointments/SOGoCalendarComponent.m
index eb6ccebaf0..49a779beca 100644
--- a/SoObjects/Appointments/SOGoCalendarComponent.m
+++ b/SoObjects/Appointments/SOGoCalendarComponent.m
@@ -39,16 +39,18 @@
 
 #import <SOGo/NSCalendarDate+SOGo.h>
 #import <SOGo/NSDictionary+Utilities.h>
 #import <SOGo/NSObject+DAV.h>
 #import <SOGo/NSObject+Utilities.h>
+#import <SOGo/NSString+Crypto.h>
 #import <SOGo/NSString+Utilities.h>
 #import <SOGo/SOGoBuild.h>
 #import <SOGo/SOGoMailer.h>
 #import <SOGo/SOGoGroup.h>
 #import <SOGo/SOGoPermissions.h>
 #import <SOGo/SOGoUser.h>
+#import <SOGo/SOGoUserSettings.h>
 #import <SOGo/SOGoSystemDefaults.h>
 #import <SOGo/SOGoUserManager.h>
 #import <SOGo/SOGoWebDAVAclManager.h>
 #import <SOGo/WORequest+SOGo.h>
 #import <Appointments/SOGoAppointmentFolder.h>
@@ -200,18 +202,25 @@ - (void) setComponentTag: (NSString *) theTag
   ASSIGN(componentTag, theTag);
 }
 
 - (void) _filterComponent: (iCalEntityObject *) component
 {
-  NSString *type, *summary, *tag;
-  NSArray *children;
+  NSString *type, *summary, *tag, *uid;
+  SOGoUserSettings *settings;
+  SOGoUser *calendarOwner;
+  NSEnumerator *children;
+  CardElement *element;
+  NSArray *tags;
 
-  int classification, i;
+  int classification;
 
   type = @"vtodo";
   classification = 0;
 
+  calendarOwner = [SOGoUser userWithLogin: [self ownerInContext: context]];
+  settings = [calendarOwner userSettings];
+
   if ([component isKindOfClass: [iCalEvent class]])
     type = @"vevent";
   
   if ([component symbolicAccessClass] == iCalAccessPrivate)
     classification = 1;
@@ -219,29 +228,27 @@ - (void) _filterComponent: (iCalEntityObject *) component
     classification = 2;
 
   summary = [self labelForKey: [NSString stringWithFormat: @"%@_class%d",
                                          type, classification]
                     inContext: context];
-  [component setSummary: summary];
-  [component setComment: @""];
-  [component setUserComment: @""];
-  [component setLocation: @""];
-  [component setCategories: [NSArray array]];
-  [component setUrl: @""];
-  [component setOrganizer: nil];
-  [component removeAllAttendees];
-  [component removeAllAlarms];
-
-  // We strip all X- tags
-  children = [component children];
-
-  for (i = 0; i < [children count]; i++)
+
+  tags = [NSArray arrayWithObjects: @"DTSTAMP", @"DTSTART", @"DTEND", @"DUE", @"EXDATE", @"EXRULE", @"RRULE", nil];
+  uid = [[component uid] asCryptedPassUsingScheme: @"ssha256"
+                                         withSalt: [[settings userSalt] dataUsingEncoding: NSASCIIStringEncoding]
+                                      andEncoding: encHex];
+
+  children = [[[[component children] copy] autorelease] objectEnumerator];
+
+  while ((element = [children nextObject]))
     {
-      tag = [[children objectAtIndex: i] tag];
-      if ([[tag uppercaseString] hasPrefix: @"X-"])
-        [component removeChild: [children objectAtIndex: i]];
+      tag = [element tag];
+      if (![tags containsObject: [tag uppercaseString]])
+        [component removeChild: element];
     }
+
+  [component setSummary: summary];
+  [component setUid: uid];
 }
 
 - (NSString *) secureContentAsString
 {
   iCalRepeatableEntityObject *tmpComponent;
diff --git a/SoObjects/SOGo/SOGoUserSettings.h b/SoObjects/SOGo/SOGoUserSettings.h
index 25de5aaea9..7a3958ae3d 100644
--- a/SoObjects/SOGo/SOGoUserSettings.h
+++ b/SoObjects/SOGo/SOGoUserSettings.h
@@ -1,8 +1,8 @@
 /* SOGoUserSettings.h - this file is part of SOGo
  *
- * Copyright (C) 2009-2014 Inverse inc.
+ * Copyright (C) 2009-2016 Inverse inc.
  *
  * 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.
@@ -31,9 +31,10 @@
 
 + (SOGoUserSettings *) settingsForUser: (NSString *) userId;
 
 - (NSArray *) subscribedCalendars;
 - (NSArray *) subscribedAddressBooks;
+- (NSString *) userSalt;
 
 @end
 
 #endif /* SOGOUSERSETTINGS_H */
diff --git a/SoObjects/SOGo/SOGoUserSettings.m b/SoObjects/SOGo/SOGoUserSettings.m
index 7f0af9bc40..eb71d84c1f 100644
--- a/SoObjects/SOGo/SOGoUserSettings.m
+++ b/SoObjects/SOGo/SOGoUserSettings.m
@@ -1,8 +1,8 @@
 /* SOGoUserSettings.m - this file is part of SOGo
  *
- * Copyright (C) 2009-2014 Inverse inc.
+ * Copyright (C) 2009-2016 Inverse inc.
  *
  * 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.
@@ -17,13 +17,15 @@
  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
  */
 
 #import <Foundation/NSDictionary.h>
+#import <Foundation/NSProcessInfo.h>
 #import <Foundation/NSString.h>
 
 #import "SOGoUserProfile.h"
+#import "NSString+Crypto.h"
 
 #import "SOGoUserSettings.h"
 
 static Class SOGoUserProfileKlass = Nil;
 
@@ -66,6 +68,30 @@ - (NSArray *) subscribedCalendars
 - (NSArray *) subscribedAddressBooks
 {
   return [self _subscribedFoldersForModule: @"Contacts"];
 }
 
+- (NSString *) userSalt
+{
+  NSMutableDictionary *values;
+  NSString *salt;
+
+  salt = [[self dictionaryForKey: @"General"] objectForKey: @"Salt"];
+
+  if (!salt)
+    {
+      salt = [[[NSProcessInfo processInfo] globallyUniqueString] asSHA1String];
+      values = [self objectForKey: @"General"];
+
+      if (!values)
+        values = [NSMutableDictionary dictionary];
+
+      [values setObject: salt  forKey: @"Salt"];
+      [self setObject:  values forKey: @"General"];
+      [self synchronize];
+    }
+
+  return salt;
+}
+
+
 @end

Vulnerable code:   NSString *type, *summary, *tag;
  NSArray *children;
  int classification, i;
  [component setSummary: summary];
  [component setComment: @""];
  [component setUserComment: @""];
  [component setLocation: @""];
  [component setCategories: [NSArray array]];
  [component setUrl: @""];
  [component setOrganizer: nil];
  [component removeAllAttendees];
  [component removeAllAlarms];

  // We strip all X- tags
  children = [component children];

  for (i = 0; i < [children count]; i++)
      tag = [[children objectAtIndex: i] tag];
      if ([[tag uppercaseString] hasPrefix: @"X-"])
        [component removeChild: [children objectAtIndex: i]];
 * Copyright (C) 2009-2014 Inverse inc.
 * Copyright (C) 2009-2014 Inverse inc.
Fixed code: #import <SOGo/NSString+Crypto.h>
#import <SOGo/SOGoUserSettings.h>
  NSString *type, *summary, *tag, *uid;
  SOGoUserSettings *settings;
  SOGoUser *calendarOwner;
  NSEnumerator *children;
  CardElement *element;
  NSArray *tags;
  int classification;
  calendarOwner = [SOGoUser userWithLogin: [self ownerInContext: context]];
  settings = [calendarOwner userSettings];


  tags = [NSArray arrayWithObjects: @"DTSTAMP", @"DTSTART", @"DTEND", @"DUE", @"EXDATE", @"EXRULE", @"RRULE", nil];
  uid = [[component uid] asCryptedPassUsingScheme: @"ssha256"
                                         withSalt: [[settings userSalt] dataUsingEncoding: NSASCIIStringEncoding]
                                      andEncoding: encHex];

  children = [[[[component children] copy] autorelease] objectEnumerator];

  while ((element = [children nextObject]))
      tag = [element tag];
      if (![tags containsObject: [tag uppercaseString]])
        [component removeChild: element];

  [component setSummary: summary];
  [component setUid: uid];
 * Copyright (C) 2009-2016 Inverse inc.
- (NSString *) userSalt;
 * Copyright (C) 2009-2016 Inverse inc.
#import <Foundation/NSProcessInfo.h>
#import "NSString+Crypto.h"
- (NSString *) userSalt
{
  NSMutableDictionary *values;
  NSString *salt;

  salt = [[self dictionaryForKey: @"General"] objectForKey: @"Salt"];

  if (!salt)
    {
      salt = [[[NSProcessInfo processInfo] globallyUniqueString] asSHA1String];
      values = [self objectForKey: @"General"];

      if (!values)
        values = [NSMutableDictionary dictionary];

      [values setObject: salt  forKey: @"Salt"];
      [self setObject:  values forKey: @"General"];
      [self synchronize];
    }

  return salt;
}