50087db4dc2818f9faee81271fe9f406bac0c799
[nfs-utils.git] / support / gssapi / g_acquire_cred.c
1 /* #ident  "@(#)gss_acquire_cred.c 1.19     95/08/07 SMI" */
2
3 /*
4  * Copyright 1996 by Sun Microsystems, Inc.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software
7  * and its documentation for any purpose is hereby granted without fee,
8  * provided that the above copyright notice appears in all copies and
9  * that both that copyright notice and this permission notice appear in
10  * supporting documentation, and that the name of Sun Microsystems not be used
11  * in advertising or publicity pertaining to distribution of the software
12  * without specific, written prior permission. Sun Microsystems makes no
13  * representations about the suitability of this software for any
14  * purpose.  It is provided "as is" without express or implied warranty.
15  *
16  * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18  * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
20  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
21  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22  * PERFORMANCE OF THIS SOFTWARE.
23  */
24
25 /*
26  *  glue routine for gss_acquire_cred
27  */
28
29 #include "mglueP.h"
30 #include <stdio.h>
31 #ifdef HAVE_STDLIB_H
32 #include <stdlib.h>
33 #endif
34 #include <string.h>
35 #include <errno.h>
36 #include <time.h>
37
38 #define g_OID_equal(o1,o2) \
39    (((o1)->length == (o2)->length) && \
40     (memcmp((o1)->elements,(o2)->elements,(int) (o1)->length) == 0))
41
42 static gss_OID_set
43 create_actual_mechs(creds)
44     gss_union_cred_t    creds;
45 {
46     gss_OID_set         actual_mechs;
47     int                 i;
48
49     actual_mechs = (gss_OID_set) malloc(sizeof(gss_OID_set_desc));
50     if (!actual_mechs)
51         return NULL;
52
53     actual_mechs->elements = (gss_OID)
54             malloc(sizeof(gss_OID_desc) * creds->count);
55     if (!actual_mechs->elements) {
56         free(actual_mechs);
57         return NULL;
58     }
59
60     actual_mechs->count = creds->count;
61
62     for (i=0; i < creds->count; i++) {
63         actual_mechs->elements[i].length = creds->mechs_array[i].length;
64         actual_mechs->elements[i].elements = (void *)
65             malloc(creds->mechs_array[i].length);
66         memcpy(actual_mechs->elements[i].elements,
67                creds->mechs_array[i].elements, creds->mechs_array[i].length);
68     }
69
70     return actual_mechs;
71 }
72
73
74 OM_uint32 KRB5_CALLCONV
75 gss_acquire_cred(minor_status,
76                  desired_name,
77                  time_req,
78                  desired_mechs,
79                  cred_usage,
80                  output_cred_handle,
81                  actual_mechs,
82                  time_rec)
83
84 OM_uint32 *             minor_status;
85 gss_name_t              desired_name;
86 OM_uint32               time_req;
87 gss_OID_set             desired_mechs;
88 int                     cred_usage;
89 gss_cred_id_t *         output_cred_handle;
90 gss_OID_set *           actual_mechs;
91 OM_uint32 *             time_rec;
92
93 {
94     OM_uint32           status, temp_minor_status, temp_time_rec = ~0;
95     unsigned int        i, j, creds_acquired = 0;
96     int                 k;
97     gss_union_name_t    union_name;
98     gss_name_t          internal_name;
99     gss_union_cred_t    creds;
100     gss_OID_set_desc    default_OID_set;
101     gss_OID_desc        default_OID;
102     gss_OID             specific_mech_type = 0;
103     gss_mechanism       mech;
104
105     /*
106      * This struct is used to keep track of which mech_types are
107      * actually available and to store the credentials returned
108      * from them by each mechanism specific gss_acquire_cred() call.
109      * The results are used to construct the final union_cred
110      * structure returned by the glue layer gss_acquire_cred() call
111      * and the actual_mechs gss_OID_set returned.
112      */
113
114     struct creds_returned {
115         unsigned char   available;
116         gss_cred_id_t   cred;
117     } *creds_returned;
118
119     gss_initialize();
120
121     /* Set this to NULL for now */
122
123     if (actual_mechs)
124         *actual_mechs = GSS_C_NULL_OID_SET;
125
126     if (minor_status)
127         *minor_status = 0;
128
129     /* No need to continue if we don't have a place to store the creds */
130     if (output_cred_handle == NULL)
131         return GSS_S_COMPLETE;
132
133     /* get desired_name cast as a union_name type */
134
135     union_name = (gss_union_name_t) desired_name;
136
137     if (union_name)
138             specific_mech_type = union_name->mech_type;
139
140     /*
141      * if desired_mechs equals GSS_C_NULL_OID_SET, then pick an
142      * appropriate default.
143      */
144     if(desired_mechs == GSS_C_NULL_OID_SET) {
145         /*
146          * If union_name->mech_type is NULL then we get the default
147          * mechanism; otherwise, we get the mechanism for the
148          * mechanism-specific name.
149          */
150         mech = __gss_get_mechanism(specific_mech_type);
151         if (mech == NULL)
152             return (GSS_S_BAD_MECH);
153
154         desired_mechs = &default_OID_set;
155         default_OID_set.count = 1 ;
156         default_OID_set.elements = &default_OID;
157         default_OID.length = mech->mech_type.length;
158         default_OID.elements = mech->mech_type.elements;
159     }
160
161     /*
162      * Now allocate the creds returned array. There is one element
163      * for each member of the desired_mechs argument.
164      */
165
166     creds_returned = (struct creds_returned *)
167         malloc(sizeof(struct creds_returned) * desired_mechs->count);
168
169     /*
170      * For each requested mechanism in desired_mechs, determine if it
171      * is supported. If so, mark the corresponding element in
172      * creds_returned->available as 1 and call the mechanism
173      * specific gss_acquire_cred(), placing the returned cred in
174      * creds_returned->cred. If not, mark creds_returned->available as
175      * 0.
176      */
177     status = GSS_S_BAD_MECH;
178     for (j=0; j < desired_mechs->count; j++) {
179         creds_returned[j].available = 0;
180
181         mech = __gss_get_mechanism (&desired_mechs->elements[j]);
182         if (!mech || !mech->gss_acquire_cred)
183             continue;
184         /*
185          * If this is a mechanism-specific name, then only use the
186          * mechanism of the name.
187          */
188         if (specific_mech_type && !g_OID_equal(specific_mech_type,
189                                                &mech->mech_type))
190             continue;
191         /*
192          * If this is not a mechanism-specific name, then we need to
193          * do an import the external name in union_name first.
194          */
195         if (union_name == 0)
196             internal_name = (gss_name_t) 0;
197         else if (!union_name->mech_type) {
198             if (__gss_import_internal_name(&temp_minor_status,
199                                            &mech->mech_type,
200                                            union_name, &internal_name)) {
201                 continue;
202             }
203         } else
204             internal_name = union_name->mech_name;
205
206 #ifdef USE_MECH_CONTEXT
207         status = mech->gss_acquire_cred(mech->context, minor_status,
208 #else
209         status = mech->gss_acquire_cred(minor_status,
210 #endif
211                                         internal_name, time_req,
212                                         desired_mechs, cred_usage,
213                                         &creds_returned[j].cred,
214                                         NULL, &temp_time_rec);
215
216         /* Release the internal name, if allocated above */
217         if (union_name && !union_name->mech_type) {
218             (void) __gss_release_internal_name(&temp_minor_status,
219                                                &mech->mech_type,
220                                                &internal_name);
221         }
222
223         if (status != GSS_S_COMPLETE)
224             continue;
225
226         /*
227          * Add this into the creds_returned structure, if we got
228          * a good credential for this mechanism.
229          */
230         if (time_rec) {
231             *time_rec = *time_rec > temp_time_rec ? temp_time_rec : *time_rec;
232             temp_time_rec = *time_rec;
233         }
234
235         creds_returned[j].available = 1;
236         creds_acquired++;
237
238         /*
239          * If union_name is set, then we're done.  Continue, and
240          * declare success.  Otherwise, if do an inquire credentials
241          * from the first mechanism that succeeds and use that as the
242          * union name.
243          */
244         if (union_name)
245             continue;
246
247 #ifdef USE_MECH_CONTEXT
248         status = mech->gss_inquire_cred(mech->context, &temp_minor_status,
249 #else
250         status = mech->gss_inquire_cred(&temp_minor_status,
251 #endif
252                                         creds_returned[j].cred,
253                                         &internal_name, 0, 0, 0);
254         if (status) {
255             /* Should never happen */
256             creds_returned[j].available = 0;
257             creds_acquired--;
258             if (mech->gss_release_cred)
259 #ifdef USE_MECH_CONTEXT
260                 mech->gss_release_cred(mech->context, minor_status,
261 #else
262                 mech->gss_release_cred(minor_status,
263 #endif
264                                        &creds_returned[j].cred);
265             continue;
266         }
267
268         status = __gss_convert_name_to_union_name(&temp_minor_status, mech,
269                                                   internal_name,
270                                                   (gss_name_t *) &union_name);
271     }
272
273     /*
274      * Now allocate the creds struct, which will be cast as a gss_cred_id_t
275      * and returned in the output_cred_handle argument. If there were
276      * no credentials found, return an error. Also, allocate the
277      * actual_mechs data.
278      */
279     if (creds_acquired == 0) {
280         free (creds_returned);
281         return (status);
282     }
283
284     creds = (gss_union_cred_t) malloc(sizeof(gss_union_cred_desc));
285
286     creds->count = creds_acquired;
287
288     creds->mechs_array = (gss_OID)
289         malloc(sizeof(gss_OID_desc) * creds_acquired);
290
291     creds->cred_array = (gss_cred_id_t *)
292         malloc(sizeof(gss_cred_id_t) * creds_acquired);
293
294     if(actual_mechs != NULL) {
295         *actual_mechs = (gss_OID_set) malloc(sizeof(gss_OID_set_desc));
296
297         (*actual_mechs)->count = creds_acquired;
298
299         (*actual_mechs)->elements = (gss_OID)
300             malloc(sizeof(gss_OID_desc) * creds_acquired);
301     }
302
303     /*
304      * copy the mechanisms found and their allocated credentials into the
305      * creds structure. At the same time, build up the actual_mechs
306      * data.
307      */
308
309     j = 0;
310
311     for (i=0; i<desired_mechs->count; i++) {
312         if(creds_returned[i].available) {
313
314             creds->mechs_array[j].length =
315                 desired_mechs->elements[i].length;
316             creds->mechs_array[j].elements = (void *)
317                 malloc(desired_mechs->elements[i].length);
318             memcpy(creds->mechs_array[j].elements,
319                    desired_mechs->elements[i].elements,
320                    desired_mechs->elements[i].length);
321             creds->cred_array[j] = creds_returned[i].cred;
322             if (actual_mechs) {
323                     (*actual_mechs)->elements[j].length =
324                         desired_mechs->elements[i].length;
325                     (*actual_mechs)->elements[j].elements = (void *)
326                         malloc(desired_mechs->elements[i].length);
327                     memcpy((*actual_mechs)->elements[j].elements,
328                            desired_mechs->elements[i].elements,
329                            desired_mechs->elements[i].length);
330             }
331             j++;
332         }
333     }
334
335     /* free the creds_returned struct, since we are done with it. */
336
337     free(creds_returned);
338
339     /* record the information needed for gss_inquire_cred() */
340
341     creds->auxinfo.creation_time = time(0);
342     creds->auxinfo.time_rec = temp_time_rec;
343     creds->auxinfo.cred_usage =  cred_usage;
344
345     /*
346      * we can't just record the internal name, desired_name, since
347      * it may be destroyed between now and the time gss_inquire_cred()
348      * is called.  So we must record the printable name in a
349      * gss_buffer_t, calling gss_display_name() to fill it in. When
350      * gss_inquire_name() is called, we must then call gss_import_name()
351      * to get the internal name that is required at that point.
352      */
353     if (desired_name) {
354         status = gss_display_name(&temp_minor_status, desired_name,
355                                   &creds->auxinfo.name,
356                                   &creds->auxinfo.name_type);
357         if (status) {
358             status = GSS_S_BAD_NAME;
359             goto error_out;
360         }
361     } else {
362         status = gss_display_name(&temp_minor_status, union_name,
363                                   &creds->auxinfo.name,
364                                   &creds->auxinfo.name_type);
365         if (status) {
366             status = GSS_S_BAD_NAME;
367             goto error_out;
368         }
369     }
370
371     *output_cred_handle = (gss_cred_id_t) creds;
372     return(GSS_S_COMPLETE);
373
374 error_out:
375     for (k=0; k < creds->count; k++) {
376         free(creds->mechs_array[k].elements);
377         if (actual_mechs)
378             free((*actual_mechs)->elements[k].elements);
379     }
380
381     if (actual_mechs) {
382         free((*actual_mechs)->elements);
383         free(*actual_mechs);
384         *actual_mechs = GSS_C_NULL_OID_SET;
385     }
386     free(creds->cred_array);
387     free(creds->mechs_array);
388     free(creds);
389
390     return(status);
391 }
392
393 /* V2 KRB5_CALLCONV */
394 OM_uint32 KRB5_CALLCONV
395 gss_add_cred(minor_status, input_cred_handle,
396                   desired_name, desired_mech, cred_usage,
397                   initiator_time_req, acceptor_time_req,
398                   output_cred_handle, actual_mechs,
399                   initiator_time_rec, acceptor_time_rec)
400     OM_uint32           *minor_status;
401     gss_cred_id_t       input_cred_handle;
402     gss_name_t          desired_name;
403     gss_OID             desired_mech;
404     gss_cred_usage_t    cred_usage;
405     OM_uint32           initiator_time_req;
406     OM_uint32           acceptor_time_req;
407     gss_cred_id_t       *output_cred_handle;
408     gss_OID_set         *actual_mechs;
409     OM_uint32           *initiator_time_rec;
410     OM_uint32           *acceptor_time_rec;
411 {
412     OM_uint32           status, temp_minor_status;
413     OM_uint32           time_req, time_rec;
414     gss_union_name_t    union_name;
415     gss_union_cred_t    new_union_cred, union_cred;
416     gss_name_t          internal_name;
417     gss_mechanism       mech;
418     gss_cred_id_t       cred;
419     gss_OID             new_mechs_array;
420     gss_cred_id_t *     new_cred_array;
421
422     if (input_cred_handle == GSS_C_NO_CREDENTIAL)
423         return GSS_S_NO_CRED;
424
425     union_cred = (gss_union_cred_t) input_cred_handle;
426
427     mech = __gss_get_mechanism(desired_mech);
428     if (!mech)
429         return GSS_S_BAD_MECH;
430
431     if (__gss_get_mechanism_cred(union_cred, desired_mech) !=
432         GSS_C_NO_CREDENTIAL)
433         return GSS_S_DUPLICATE_ELEMENT;
434
435     union_name = (gss_union_name_t) desired_name;
436     if (union_name->mech_type) {
437         if (!g_OID_equal(desired_mech, union_name->mech_type))
438             return GSS_S_BAD_NAMETYPE;
439         internal_name = union_name->mech_name;
440     } else {
441         if (__gss_import_internal_name(minor_status, desired_mech,
442                                        union_name, &internal_name))
443             return (GSS_S_BAD_NAME);
444     }
445
446     if (cred_usage == GSS_C_ACCEPT)
447         time_req = acceptor_time_req;
448     else if (cred_usage == GSS_C_INITIATE)
449         time_req = initiator_time_req;
450     else if (cred_usage == GSS_C_BOTH)
451         time_req = (acceptor_time_req > initiator_time_req) ?
452             acceptor_time_req : initiator_time_req;
453
454 #ifdef USE_MECH_CONTEXT
455     status = mech->gss_acquire_cred(mech->context, minor_status,
456 #else
457     status = mech->gss_acquire_cred(minor_status,
458 #endif
459                                     internal_name, time_req,
460                                     GSS_C_NULL_OID_SET, cred_usage,
461                                     &cred, NULL, &time_rec);
462     if (status != GSS_S_COMPLETE)
463         goto errout;
464
465     new_mechs_array = (gss_OID)
466         malloc(sizeof(gss_OID_desc) * (union_cred->count+1));
467
468     new_cred_array = (gss_cred_id_t *)
469         malloc(sizeof(gss_cred_id_t) * (union_cred->count+1));
470
471     if (!new_mechs_array || !new_cred_array) {
472         *minor_status = ENOMEM;
473         status = GSS_S_FAILURE;
474         goto errout;
475     }
476
477
478     if (acceptor_time_rec)
479         if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH)
480             *acceptor_time_rec = time_rec;
481     if (initiator_time_rec)
482         if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH)
483             *initiator_time_rec = time_rec;
484
485     /*
486      * OK, expand the mechanism array in the union credentials
487      * (Look for the union label...)
488      */
489     memcpy(new_mechs_array, union_cred->mechs_array,
490            sizeof(gss_OID_desc) * union_cred->count);
491     memcpy(new_cred_array, union_cred->cred_array,
492            sizeof(gss_cred_id_t) * union_cred->count);
493
494     new_cred_array[union_cred->count] = cred;
495     new_mechs_array[union_cred->count].length = desired_mech->length;
496     new_mechs_array[union_cred->count].elements = malloc(desired_mech->length);
497     if (!new_mechs_array[union_cred->count].elements) {
498         *minor_status = ENOMEM;
499         goto errout;
500     }
501     memcpy(new_mechs_array[union_cred->count].elements, desired_mech->elements,
502            desired_mech->length);
503
504     if (output_cred_handle == NULL) {
505         free(union_cred->mechs_array);
506         free(union_cred->cred_array);
507         new_union_cred = union_cred;
508     } else {
509         new_union_cred = malloc(sizeof(gss_union_cred_desc));
510         if (new_union_cred == NULL) {
511             *minor_status = ENOMEM;
512             goto errout;
513         }
514         *new_union_cred = *union_cred;
515         *output_cred_handle = new_union_cred;
516     }
517     new_union_cred->mechs_array = new_mechs_array;
518     new_union_cred->cred_array = new_cred_array;
519     new_union_cred->count++;
520     new_mechs_array = 0;
521     new_cred_array = 0;
522
523     if (actual_mechs)
524         *actual_mechs = create_actual_mechs(new_union_cred);
525
526     status = GSS_S_COMPLETE;
527
528 errout:
529     if (new_mechs_array)
530         free(new_mechs_array);
531     if (new_cred_array)
532         free(new_cred_array);
533     if (!union_name->mech_type) {
534         (void) __gss_release_internal_name(&temp_minor_status,
535                                            desired_mech, &internal_name);
536     }
537
538     return(status);
539 }