1 /*
2  * Portions of this file Copyright 1999-2005 University of Chicago
3  * Portions of this file Copyright 1999-2005 The University of Southern California.
4  *
5  * This file or a portion of this file is licensed under the
6  * terms of the Globus Toolkit Public License, found at
7  * http://www.globus.org/toolkit/download/license.html.
8  * If you redistribute this file, with or without
9  * modifications, you must include this notice in the file.
10  */
11
12
13 #include "globus_i_handler_wsse_cred.h"
14 #include "globus_ws_security.h"
15 #include "openssl/x509.h"
16
17 #define WSSE_CACHED_USER_SUBJECT_HASH "WSSE_CACHED_USER_SUBJECT_HASH"
18
19 static unsigned long int                       cached_hash = 0;
20 static globus_i_handler_wsse_cache_t           pkipath_table = NULL;
21 static globus_mutex_t                          pkipath_table_mutex;
22
23 globus_result_t
24 globus_i_handler_wsse_cred_pkipath_table_init()
25 0 {
26 0     globus_reltime_t                    expiration;
27 0     globus_mutex_init(&pkipath_table_mutex, NULL);
28 0     globus_mutex_lock(&pkipath_table_mutex);
29
30 0     cached_hash = 0;
31 0     pkipath_table = NULL;
32
33     /* expiration = 5 mins */
34 0     GlobusTimeReltimeSet(expiration, (5 * 60), 0);
35 0     globus_i_handler_wsse_cache_init(
36 &pkipath_table,
37 20, /* cache size for creds */
38 expiration);
39 0     globus_mutex_unlock(&pkipath_table_mutex);
40
41 0     return GLOBUS_SUCCESS;
42 }
43
44 void
45 globus_i_handler_wsse_cred_pkipath_table_destroy()
46 0 {
47 0     globus_mutex_lock(&pkipath_table_mutex);
48 0     globus_i_handler_wsse_cache_destroy(pkipath_table);
49 0     globus_mutex_unlock(&pkipath_table_mutex);
50
51 0     globus_mutex_destroy(&pkipath_table_mutex);
52
53 0     cached_hash = 0;
54 0     pkipath_table = NULL;
55 }
56
57 unsigned long
58 globus_i_handler_wsse_cred_subject_hash(
59     globus_gsi_cred_handle_t            credential)
60 0 {
61 0     unsigned long                       hash;
62 0     X509_NAME *                         name;
63
64 0     globus_gsi_cred_get_X509_subject_name(credential, &name);
65 0     hash = X509_NAME_hash(name);
66 0     X509_NAME_free(name);
67 0     return hash;
68 }
69
70 void 
71 globus_i_handler_wsse_cache_init (
72     globus_i_handler_wsse_cache_t *     new_cache,
73     int                                 capacity,
74     globus_reltime_t                    reltime_limit)
75 0 {
76 0     globus_i_handler_wsse_cache_t       cache;
77
78 0     if ( new_cache == NULL ) return;
79
80
81 0     cache = globus_malloc(sizeof(struct globus_i_handler_wsse_cache_s));
82 0     if(!cache)
83     {
84 0         *new_cache = NULL;
85 0         return;
86     }
87
88 0     globus_hashtable_init (&(cache->handlemap),
89                            capacity,
90                            globus_hashtable_ulong_hash,
91                            globus_hashtable_ulong_keyeq);
92 0     globus_fifo_init (&(cache->handles));
93
94 0     cache->capacity_limit = capacity;
95 0     cache->entry_count = 0;
96 0     GlobusTimeReltimeCopy(cache->time_limit, reltime_limit);
97
98 0     *new_cache = cache;
99 }
100
101 void
102 globus_i_handler_wsse_cache_destroy (
103     globus_i_handler_wsse_cache_t       cache)
104 0 {
105 0     if(cache == NULL) return;
106
107 0     while(!globus_fifo_empty(&(cache->handles)))
108     {
109 0         globus_gsi_cred_handle_t        cred;
110 0         char *                          pkipath_buff;
111 0         int                             pkipath_length;
112 0         globus_i_handler_wsse_cache_remove(
113             cache,
114             (int)globus_fifo_dequeue(&(cache->handles)),
115             &cred, &pkipath_buff, &pkipath_length);
116         
117 0         if(cred)
118         {
119 0             globus_gsi_cred_handle_destroy(cred);
120         }
121
122 0         if(pkipath_buff)
123         {
124 0             free(pkipath_buff);
125         }
126     }
127 0     globus_hashtable_destroy_all(
128 &(cache->handlemap),
129 globus_i_handler_wsse_pkipath_entry_destroy);
130 0     globus_fifo_destroy (&(cache->handles));
131
132 0     globus_free(cache);
133 }
134
135 globus_result_t
136 globus_i_handler_wsse_cache_insert(
137     globus_i_handler_wsse_cache_t       cache,
138     globus_gsi_cred_handle_t            credential,
139     unsigned long *                     hash)
140 0 {
141 0     globus_i_handler_wsse_pkipath_entry_t * entry;
142 0     globus_result_t                     result = GLOBUS_SUCCESS;
143 0     unsigned long                       subject_hash;
144
145 0     if(credential == NULL) return GLOBUS_FAILURE;
146
147 0     subject_hash = globus_i_handler_wsse_cred_subject_hash(
148         credential);
149     
150 0     *hash = subject_hash;
151
152 0     if (cache == NULL) return GLOBUS_FAILURE;
153
154 0     if ( cache->entry_count > cache->capacity_limit ) 
155     {
156 0         globus_gsi_cred_handle_t        cred;
157 0         char *                          pkipath_buff;
158 0         globus_i_handler_wsse_cache_remove ( 
159             cache,
160             (int)globus_fifo_dequeue ( &(cache->handles) ),
161             &cred, &pkipath_buff, NULL);
162         
163 0         if(cred)
164         {
165 0             globus_gsi_cred_handle_destroy(cred);
166         }
167
168 0         if(pkipath_buff)
169         {
170 0             globus_free(pkipath_buff);
171         }
172     }
173
174 0     entry = globus_malloc(sizeof(globus_i_handler_wsse_pkipath_entry_t));
175 0     if(!entry)
176     {
177 0         result = GlobusWSSEErrorOutOfMemory(
178             sizeof(globus_i_handler_wsse_pkipath_entry_t));
179 0         return result;
180     }
181 0     memset(entry, 0, sizeof(globus_i_handler_wsse_pkipath_entry_t));
182
183 0     entry->subject = subject_hash;
184 0     result = globus_gsi_cred_handle_copy(credential, &entry->credential);
185 0     if(result != GLOBUS_SUCCESS)
186     {
187 0         goto free_entry;
188     }
189
190 0     result = globus_ws_security_create_pkipath(
191         entry->credential, &entry->pkipath_buffer, &entry->pkipath_length);
192 0     if(result != GLOBUS_SUCCESS)
193     {
194 0         goto free_entry;
195     }
196
197 0     GlobusTimeAbstimeGetCurrent(entry->entry_time);
198
199 0     globus_hashtable_insert(
200         &(cache->handlemap),
201         (void *)entry->subject, 
202         (void *)entry);
203 0     globus_fifo_enqueue (&(cache->handles), (void *)entry->subject);
204
205 0     cache->entry_count += 1;
206
207 0     goto exit;
208
209  free_entry:
210
211 0     globus_i_handler_wsse_pkipath_entry_destroy(entry);
212
213  exit:
214
215 0     return result;
216 }
217
218 globus_result_t
219 globus_i_handler_wsse_cache_lookup (
220     globus_i_handler_wsse_cache_t       cache,
221     unsigned long                       subject_hash,
222     globus_gsi_cred_handle_t *          credential,
223     char **                             pkipath_buffer,
224     int *                               pkipath_length)
225 0 {
226 0     globus_i_handler_wsse_pkipath_entry_t * entry;
227 0     globus_result_t                     result = GLOBUS_SUCCESS;
228
229 0     if ( cache == NULL ) return GLOBUS_FAILURE;
230
231 0     entry = globus_hashtable_lookup (&(cache->handlemap), 
232                                      (void *)subject_hash);
233 0     if(entry)
234     {
235 0         if(!globus_time_reltime_is_infinity(&cache->time_limit))
236         {
237 0             char *                      tmp_buff;
238 0             int                         tmp_length;
239 0             globus_gsi_cred_handle_t    cred;
240 0             globus_reltime_t            diff;
241 0             globus_abstime_t            current;
242 0             GlobusTimeAbstimeGetCurrent(current);
243 0             GlobusTimeAbstimeDiff(diff, entry->entry_time, current);
244 0             if(globus_reltime_cmp(&diff, &cache->time_limit) >= 0)
245             {
246                 /* expired cache entry.  must remove */
247 0                 globus_i_handler_wsse_cache_remove(cache, subject_hash, 
248                                                    &cred,
249                                                    &tmp_buff, &tmp_length);
250 0                 if(tmp_buff)
251                 {
252 0                     globus_free(tmp_buff);
253                 }
254
255 0                 if(cred)
256                 {
257 0                     globus_gsi_cred_handle_destroy(cred);
258                 }
259
260 0                 result = GLOBUS_FAILURE;
261 0                 goto exit;
262             }
263         }
264
265 0         if(credential)
266         {
267 0             *credential = entry->credential;
268         }
269         
270 0         if(pkipath_buffer)
271         {
272 0             *pkipath_buffer = entry->pkipath_buffer;
273 0             *pkipath_length = entry->pkipath_length;
274         }
275     }
276     else
277     {
278 0         result = GLOBUS_FAILURE;
279     }
280
281  exit:
282
283 0     return result;
284 }
285
286 globus_result_t
287 globus_i_handler_wsse_cache_remove (
288     globus_i_handler_wsse_cache_t       cache,
289     unsigned long                       subject_hash,
290     globus_gsi_cred_handle_t *          credential,
291     char **                             pkipath_buffer,
292     int *                               pkipath_length)
293 0 {
294 0     globus_result_t                     result = GLOBUS_SUCCESS;
295 0     globus_i_handler_wsse_pkipath_entry_t * entry;
296
297 0     if ( cache == NULL ) return GLOBUS_FAILURE;
298
299 0     entry = globus_hashtable_remove (&(cache->handlemap), 
300                                      (void *)subject_hash);
301 0     globus_fifo_remove (&(cache->handles), (void *)subject_hash);
302 0     if ( entry != NULL ) cache->entry_count -= 1;
303
304 0     if(entry)
305     {
306 0         if(pkipath_buffer)
307         {
308 0             *pkipath_buffer = entry->pkipath_buffer;
309 0             *pkipath_length = entry->pkipath_length;
310         }
311         else
312         {
313 0             globus_free(entry->pkipath_buffer);
314         }
315
316 0         if(credential)
317         {
318 0             *credential = entry->credential;
319         }
320         else
321         {
322 0             globus_gsi_cred_handle_destroy(entry->credential);
323         }
324
325 0         globus_free(entry);
326     }
327
328 0     return result;
329 }
330
331 void
332 globus_i_handler_wsse_pkipath_entry_destroy(
333     globus_i_handler_wsse_pkipath_entry_t * entry)
334 0 {
335 0     if(entry)
336     {
337 0         if(entry->pkipath_buffer)
338         {
339 0             globus_free(entry->pkipath_buffer);
340         }
341
342 0         if(entry->credential)
343         {
344 0             globus_gsi_cred_handle_destroy(
345                 entry->credential);
346         }
347
348 0         globus_free(entry);
349     }
350 }
351
352 globus_result_t
353 globus_i_handler_wsse_load_local_cred(
354     globus_soap_message_handle_t        message_handle,
355     globus_gsi_cred_handle_t *          credential,
356     char **                             pkipath,
357     int *                               pkipath_length)
358 0 {
359 0     globus_gsi_cred_handle_t            user_cred = NULL;
360 0     globus_result_t                     result = GLOBUS_SUCCESS;
361 0     unsigned long                       hash;
362 0     int                                 user_cred_initialized = 0;
363 0     X509_NAME *                         x509n = NULL;
364
365
366 0     user_cred = globus_soap_message_handle_get_attr(
367         message_handle,
368         GLOBUS_HANDLER_WSSE_USER_CREDENTIAL_KEY);
369 0     if(!user_cred)
370     {
371 0         char *                          desired_subject;
372         
373 0         desired_subject =
374         globus_soap_message_handle_get_attr(
375             message_handle,
376             GLOBUS_HANDLER_WSSE_DESIRED_SUBJECT_KEY);
377
378 0         if(desired_subject)
379         {
380 0             x509n = X509_NAME_new();
381 0             result = globus_gsi_cert_utils_get_x509_name(
382                 desired_subject, strlen(desired_subject), x509n);
383 0             if(result != GLOBUS_SUCCESS)
384             {
385 0                 result = GlobusWSSEErrorPKIPath(
386                     result,
387                     "Failed to get PKIPath from local user credential");
388 0                 goto free_x509n;
389             }
390
391 0             globus_mutex_lock(&pkipath_table_mutex);
392 0             result = globus_i_handler_wsse_cache_lookup(
393                 pkipath_table, X509_NAME_hash(x509n),
394                 credential, pkipath, pkipath_length);
395 0             if(result == GLOBUS_SUCCESS)
396             {
397 0                 globus_mutex_unlock(&pkipath_table_mutex);
398 0                 goto free_x509n;
399             }
400 0             globus_mutex_unlock(&pkipath_table_mutex);
401         }
402            
403 0 if(cached_hash != 0)
404 {
405 0     result = globus_i_handler_wsse_cache_lookup(
406 pkipath_table, cached_hash,
407 credential, pkipath, pkipath_length);
408 0     if(result == GLOBUS_SUCCESS)
409     {
410 0 globus_mutex_unlock(&pkipath_table_mutex);
411 0 goto exit;
412     }
413 0     globus_mutex_unlock(&pkipath_table_mutex);
414 }
415
416 0    user_cred_initialized = 1; 
417 0         result = globus_gsi_cred_handle_init(&user_cred, NULL);
418 0         if(result != GLOBUS_SUCCESS)
419         {
420 0             result = GlobusWSSEErrorPKIPath(
421                 result, "Failed to load local user credential");
422 0             goto free_x509n;
423         }
424         
425 0         result = globus_gsi_cred_read(user_cred, x509n);
426 0         if(result != GLOBUS_SUCCESS)
427         {
428 0             result = GlobusWSSEErrorPKIPath(
429                 result, "Failed to load local user credential");
430 0             goto free_handle;
431         }
432
433 0         globus_mutex_lock(&pkipath_table_mutex);
434 0         result = globus_i_handler_wsse_cache_insert(
435             pkipath_table, 
436             user_cred,
437             &hash);
438 0         if(result != GLOBUS_SUCCESS)
439         {
440 0             globus_mutex_unlock(&pkipath_table_mutex);
441 0             result = GlobusWSSEErrorPKIPath(
442                 result, "Failed to insert user credential into cache");
443 0             goto free_handle;
444         }
445
446 0         result = globus_i_handler_wsse_cache_lookup(
447             pkipath_table,
448             hash,
449             credential,
450             pkipath,
451             pkipath_length);
452 0         if(result == GLOBUS_SUCCESS)
453         {
454 0     globus_mutex_unlock(&pkipath_table_mutex);
455 0     cached_hash = hash;
456 0     goto free_handle;
457         }
458 0 globus_mutex_unlock(&pkipath_table_mutex);
459     }
460     else
461     {
462 0         result = globus_gsi_cred_get_X509_subject_name(user_cred, &x509n);
463 0         if(result != GLOBUS_SUCCESS)
464         {
465 0             result = GlobusWSSEErrorPKIPath(
466                 result, "Failed to load user cred");
467 0             goto free_handle;
468         }
469         
470 0         globus_mutex_lock(&pkipath_table_mutex);
471 0         result = globus_i_handler_wsse_cache_lookup(
472             pkipath_table, 
473             X509_NAME_hash(x509n),
474             credential,
475             pkipath, 
476             pkipath_length);
477 0         if(result == GLOBUS_SUCCESS)
478         {
479 0             globus_mutex_unlock(&pkipath_table_mutex);
480 0             goto free_handle;
481         }
482 0         globus_mutex_unlock(&pkipath_table_mutex);
483     }
484
485 0     globus_mutex_lock(&pkipath_table_mutex);
486 0     result = globus_i_handler_wsse_cache_insert(
487         pkipath_table,
488         user_cred,
489         &hash);
490 0     if(result != GLOBUS_SUCCESS)
491     {
492 0         globus_mutex_unlock(&pkipath_table_mutex);
493 0         result = GlobusWSSEErrorPKIPath(
494             result, "Failed to insert credential into cache");
495 0         goto free_handle;
496     }
497
498 0     result = globus_i_handler_wsse_cache_lookup(
499         pkipath_table,
500         hash,
501         credential,
502         pkipath,
503         pkipath_length);
504 0     if(result != GLOBUS_SUCCESS)
505     {
506 0         globus_mutex_unlock(&pkipath_table_mutex);
507 0         result = GlobusWSSEErrorPKIPath(
508             result, "Failed to get credential from cache");
509 0         goto free_handle;
510     }
511
512 0     globus_mutex_unlock(&pkipath_table_mutex);
513
514  free_handle:
515
516 0     if(user_cred_initialized && user_cred)
517     {
518 0         globus_gsi_cred_handle_destroy(user_cred);
519     }
520
521  free_x509n:
522
523 0     if(x509n)
524     {
525 0         X509_NAME_free(x509n);
526     }
527
528  exit:
529
530 0     return result;