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