1/* SOGoSession.m - this file is part of SOGo 2 * 3 * Copyright (C) 2010-2014 Inverse inc. 4 * 5 * This file is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2, or (at your option) 8 * any later version. 9 * 10 * This file is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; see the file COPYING. If not, write to 17 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 18 * Boston, MA 02111-1307, USA. 19 */ 20 21#include "SOGoSession.h" 22 23#include "SOGoCache.h" 24 25#import <NGObjWeb/WOCookie.h> 26 27#import <Foundation/NSArray.h> 28#import <Foundation/NSCalendarDate.h> 29#import <Foundation/NSData.h> 30#import <Foundation/NSDictionary.h> 31 32#import <GDLContentStore/GCSSessionsFolder.h> 33#import <GDLContentStore/GCSFolder.h> 34#import <GDLContentStore/GCSFolderManager.h> 35 36#import <NGExtensions/NGBase64Coding.h> 37 38#include <sys/types.h> 39#include <sys/stat.h> 40#include <fcntl.h> 41#include <unistd.h> 42 43#import "SOGoSystemDefaults.h" 44#import "SOGoUserManager.h" 45 46@implementation SOGoSession 47 48+ (NSString *) valueForSessionKey: (NSString *) theSessionKey 49{ 50 NSString *value, *key; 51 SOGoCache *cache; 52 53 cache = [SOGoCache sharedCache]; 54 55 key = [NSString stringWithFormat: @"session:%@", theSessionKey]; 56 value = [cache valueForKey: key]; 57 58 // We go check in the database 59 if (!value) 60 { 61 GCSSessionsFolder *folder; 62 NSDictionary *d; 63 64 folder = [[GCSFolderManager defaultFolderManager] sessionsFolder]; 65 d = [folder recordForEntryWithID: theSessionKey]; 66 67 if (d) 68 { 69 // We cache back the result in memcached 70 value = [d objectForKey: @"c_value"]; 71 [cache setValue: value forKey: key]; 72 73 // We update c_lastseen. We do this only when we get a cache miss from memcached 74 // and when the data was reloaded from the database to avoid updating it too often. 75 // This is good enough since this information would be mostly used to cleanup 76 // dead sessions - and not really to know when was the last time a user 77 // invoke an action method on SOGo. 78 [folder writeRecordForEntryWithID: theSessionKey 79 value: value 80 creationDate: [NSDate dateWithTimeIntervalSince1970: [[d objectForKey: @"c_creationdate"] intValue]] 81 lastSeenDate: [NSCalendarDate date]]; 82 } 83 } 84 85 return value; 86} 87 88+ (void) setValue: (NSString *) theValue 89 forSessionKey: (NSString *) theSessionKey 90{ 91 GCSSessionsFolder *folder; 92 NSCalendarDate *now; 93 SOGoCache *cache; 94 NSString *key; 95 96 cache = [SOGoCache sharedCache]; 97 98 key = [NSString stringWithFormat: @"session:%@", theSessionKey]; 99 100 [cache setValue: theValue forKey: key]; 101 102 // We store it inside the database 103 folder = [[GCSFolderManager defaultFolderManager] sessionsFolder]; 104 105 now = [NSCalendarDate date]; 106 [folder writeRecordForEntryWithID: theSessionKey 107 value: theValue 108 creationDate: now 109 lastSeenDate: now]; 110} 111 112// 113// 114// 115+ (void) deleteValueForSessionKey: (NSString *) theSessionKey 116{ 117 GCSSessionsFolder *folder; 118 119 folder = [[GCSFolderManager defaultFolderManager] sessionsFolder]; 120 121 [folder deleteRecordForEntryWithID: theSessionKey]; 122 [[SOGoCache sharedCache] removeValueForKey: [NSString stringWithFormat: @"session:%@", theSessionKey]]; 123} 124 125// 126// 127// 128+ (NSString *) generateKeyForLength: (unsigned int) theLength 129{ 130 char *buf; 131 int fd; 132 133 fd = open("/dev/urandom", O_RDONLY); 134 135 if (fd > 0) 136 { 137 NSData *data; 138 NSString *s; 139 140 buf = (char *)malloc(theLength); 141 read(fd, buf, theLength); 142 close(fd); 143 144 // We encode the bytes in base64 with a line lenght fixed to 1024 since 145 // we want to avoid folding the values 146 data = [NSData dataWithBytesNoCopy: buf length: theLength freeWhenDone: YES]; 147 148 s = [[NSString alloc] initWithData: [data dataByEncodingBase64WithLineLength: 1024] 149 encoding: NSASCIIStringEncoding]; 150 return [s autorelease]; 151 } 152 153 return nil; 154} 155 156// 157// The key will likely be longer than the password. We don't care 158// that much about this for now. 159// 160+ (NSString *) securedValue: (NSString *) theValue 161 usingKey: (NSString *) theKey 162{ 163 NSData *data; 164 NSString *s; 165 166 char *buf, *key, *pass; 167 int i, klen; 168 169 // Get the key length and its bytes 170 data = [theKey dataByDecodingBase64]; 171 key = (char *)[data bytes]; 172 klen = [data length]; 173 174 // Get the key - padding it with 0 with key length 175 pass = (char *) calloc(klen, sizeof(char)); 176 [theValue getCString: pass maxLength: klen encoding: NSUTF8StringEncoding]; 177 178 // Target buffer 179 buf = (char *)malloc(klen); 180 181 for (i = 0; i < klen; i++) 182 { 183 buf[i] = key[i] ^ pass[i]; 184 } 185 186 free(pass); 187 188 data = [NSData dataWithBytesNoCopy: buf length: klen freeWhenDone: YES]; 189 190 s = [[NSString alloc] initWithData: [data dataByEncodingBase64WithLineLength: 1024] 191 encoding: NSASCIIStringEncoding]; 192 return [s autorelease]; 193} 194 195 196+ (NSString *) valueFromSecuredValue: (NSString *) theValue 197 usingKey: (NSString *) theKey 198{ 199 NSData *dataKey, *dataValue; 200 NSString *s; 201 202 char *buf, *key, *value; 203 size_t i, klen, vlen; 204 205 // Get the key length and its bytes 206 dataKey = [theKey dataByDecodingBase64]; 207 key = (char *)[dataKey bytes]; 208 klen = [dataKey length]; 209 210 // Get the secured value length and its bytes 211 dataValue = [theValue dataByDecodingBase64]; 212 value = (char *)[dataValue bytes]; 213 vlen = [dataValue length]; 214 215 // Target buffer 216 buf = (char *) calloc(klen, sizeof(char)); 217 218 for (i = 0; i < klen && i < vlen; i++) 219 { 220 buf[i] = key[i] ^ value[i]; 221 } 222 223 // buf is now our C string in UTF8 224 s = [NSString stringWithCString: buf encoding: NSUTF8StringEncoding]; 225 free(buf); 226 227 return s; 228} 229 230/** 231 * 232 * @param theValue 233 * @param theKey 234 * @param theLogin 235 * @param theDomain 236 * @param thePassword 237 * @see [SOGoUser initWithLogin:roles:trust:] 238 */ 239+ (void) decodeValue: (NSString *) theValue 240 usingKey: (NSString *) theKey 241 login: (NSString **) theLogin 242 domain: (NSString **) theDomain 243 password: (NSString **) thePassword 244{ 245 NSString *decodedValue; 246 NSRange r; 247 SOGoSystemDefaults *sd; 248 249 decodedValue = [SOGoSession valueFromSecuredValue: theValue 250 usingKey: theKey]; 251 252 r = [decodedValue rangeOfString: @":"]; 253 *theLogin = [decodedValue substringToIndex: r.location]; 254 *thePassword = [decodedValue substringFromIndex: r.location+1]; 255 *theDomain = nil; 256 257 sd = [SOGoSystemDefaults sharedSystemDefaults]; 258 if ([sd enableDomainBasedUID]) 259 { 260 r = [*theLogin rangeOfString: @"@" options: NSBackwardsSearch]; 261 if (r.location != NSNotFound) 262 { 263 // The domain is probably appended to the username; 264 // make sure it is defined as a domain in the configuration. 265 *theDomain = [*theLogin substringFromIndex: (r.location + r.length)]; 266 if (![[SOGoUserManager sharedUserManager] isDomainDefined: *theDomain]) 267 *theDomain = nil; 268 } 269 } 270} 271 272@end 273