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