xref: /freebsd/crypto/krb5/src/lib/gssapi/mechglue/g_acquire_cred_with_pw.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 /* #pragma ident	"@(#)g_acquire_cred.c	1.22	04/02/23 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_with_password
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 static OM_uint32
val_acq_cred_pw_args(OM_uint32 * minor_status,const gss_name_t desired_name,const gss_buffer_t password,OM_uint32 time_req,const gss_OID_set desired_mechs,int cred_usage,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)39 val_acq_cred_pw_args(
40     OM_uint32 *minor_status,
41     const gss_name_t desired_name,
42     const gss_buffer_t password,
43     OM_uint32 time_req,
44     const gss_OID_set desired_mechs,
45     int cred_usage,
46     gss_cred_id_t *output_cred_handle,
47     gss_OID_set *actual_mechs,
48     OM_uint32 *time_rec)
49 {
50 
51     /* Initialize outputs. */
52 
53     if (minor_status != NULL)
54 	*minor_status = 0;
55 
56     if (output_cred_handle != NULL)
57 	*output_cred_handle = GSS_C_NO_CREDENTIAL;
58 
59     if (actual_mechs != NULL)
60 	*actual_mechs = GSS_C_NULL_OID_SET;
61 
62     if (time_rec != NULL)
63 	*time_rec = 0;
64 
65     /* Validate arguments. */
66 
67     if (desired_name == GSS_C_NO_NAME)
68 	return (GSS_S_BAD_NAME);
69 
70     if (minor_status == NULL)
71 	return (GSS_S_CALL_INACCESSIBLE_WRITE);
72 
73     if (output_cred_handle == NULL)
74 	return (GSS_S_CALL_INACCESSIBLE_WRITE);
75 
76     if (cred_usage != GSS_C_ACCEPT
77 	&& cred_usage != GSS_C_INITIATE
78 	&& cred_usage != GSS_C_BOTH) {
79 	if (minor_status) {
80 	    *minor_status = EINVAL;
81 	    map_errcode(minor_status);
82 	}
83 	return GSS_S_FAILURE;
84     }
85 
86     if (password == GSS_C_NO_BUFFER ||
87         password->length == 0 ||
88         password->value == NULL) {
89 	if (minor_status) {
90 	    *minor_status = EINVAL;
91 	    map_errcode(minor_status);
92 	}
93 	return GSS_S_FAILURE;
94     }
95 
96     return (GSS_S_COMPLETE);
97 }
98 
99 
100 OM_uint32 KRB5_CALLCONV
gss_acquire_cred_with_password(OM_uint32 * minor_status,const gss_name_t desired_name,const gss_buffer_t password,OM_uint32 time_req,const gss_OID_set desired_mechs,int cred_usage,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)101 gss_acquire_cred_with_password(OM_uint32 *minor_status,
102 			       const gss_name_t desired_name,
103 			       const gss_buffer_t password, OM_uint32 time_req,
104 			       const gss_OID_set desired_mechs, int cred_usage,
105 			       gss_cred_id_t *output_cred_handle,
106 			       gss_OID_set *actual_mechs, OM_uint32 *time_rec)
107 {
108     OM_uint32 major = GSS_S_FAILURE;
109     OM_uint32 initTimeOut, acceptTimeOut, outTime = GSS_C_INDEFINITE;
110     gss_OID_set_desc default_OID_set;
111     gss_OID_set mechs;
112     gss_OID_desc default_OID;
113     gss_mechanism mech;
114     unsigned int i;
115     gss_union_cred_t creds;
116 
117     major = val_acq_cred_pw_args(minor_status,
118 			         desired_name,
119 			         password,
120 			         time_req,
121 			         desired_mechs,
122 			         cred_usage,
123 			         output_cred_handle,
124 			         actual_mechs,
125 			         time_rec);
126     if (major != GSS_S_COMPLETE)
127 	return (major);
128 
129     /* Initial value needed below. */
130     major = GSS_S_FAILURE;
131 
132     /*
133      * if desired_mechs equals GSS_C_NULL_OID_SET, then pick an
134      * appropriate default.  We use the first mechanism in the
135      * mechanism list as the default. This set is created with
136      * statics thus needs not be freed
137      */
138     if(desired_mechs == GSS_C_NULL_OID_SET) {
139 	mech = gssint_get_mechanism(NULL);
140 	if (mech == NULL)
141 	    return (GSS_S_BAD_MECH);
142 
143 	mechs = &default_OID_set;
144 	default_OID_set.count = 1;
145 	default_OID_set.elements = &default_OID;
146 	default_OID.length = mech->mech_type.length;
147 	default_OID.elements = mech->mech_type.elements;
148     } else
149 	mechs = desired_mechs;
150 
151     if (mechs->count == 0)
152 	return (GSS_S_BAD_MECH);
153 
154     /* allocate the output credential structure */
155     creds = (gss_union_cred_t)malloc(sizeof (gss_union_cred_desc));
156     if (creds == NULL)
157 	return (GSS_S_FAILURE);
158 
159     /* initialize to 0s */
160     (void) memset(creds, 0, sizeof (gss_union_cred_desc));
161     creds->loopback = creds;
162 
163     /* for each requested mech attempt to obtain a credential */
164     for (i = 0; i < mechs->count; i++) {
165 	major = gss_add_cred_with_password(minor_status, (gss_cred_id_t)creds,
166 			     desired_name,
167 			     &mechs->elements[i],
168 			     password,
169 			     cred_usage, time_req, time_req, NULL,
170 			     NULL, &initTimeOut, &acceptTimeOut);
171 	if (major == GSS_S_COMPLETE) {
172 	    /* update the credential's time */
173 	    if (cred_usage == GSS_C_ACCEPT) {
174 		if (outTime > acceptTimeOut)
175 		    outTime = acceptTimeOut;
176 	    } else if (cred_usage == GSS_C_INITIATE) {
177 		if (outTime > initTimeOut)
178 		    outTime = initTimeOut;
179 	    } else {
180 		/*
181 		 * time_rec is the lesser of the
182 		 * init/accept times
183 		 */
184 		if (initTimeOut > acceptTimeOut)
185 		    outTime = (outTime > acceptTimeOut) ?
186 			acceptTimeOut : outTime;
187 		else
188 		    outTime = (outTime > initTimeOut) ?
189 			initTimeOut : outTime;
190 	    }
191 	}
192     } /* for */
193 
194     /* ensure that we have at least one credential element */
195     if (creds->count < 1) {
196 	free(creds);
197 	return (major);
198     }
199 
200     /*
201      * fill in output parameters
202      * setup the actual mechs output parameter
203      */
204     if (actual_mechs != NULL) {
205 	major = gssint_make_public_oid_set(minor_status, creds->mechs_array,
206 					   creds->count, actual_mechs);
207 	if (GSS_ERROR(major)) {
208 	    (void) gss_release_cred(minor_status,
209 				    (gss_cred_id_t *)&creds);
210 	    return (major);
211 	}
212     }
213 
214     if (time_rec)
215 	*time_rec = outTime;
216 
217 
218     creds->loopback = creds;
219     *output_cred_handle = (gss_cred_id_t)creds;
220     return (GSS_S_COMPLETE);
221 }
222 
223 static OM_uint32
val_add_cred_pw_args(OM_uint32 * minor_status,gss_cred_id_t input_cred_handle,const gss_name_t desired_name,const gss_OID desired_mech,const gss_buffer_t password,gss_cred_usage_t cred_usage,OM_uint32 initiator_time_req,OM_uint32 acceptor_time_req,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * initiator_time_rec,OM_uint32 * acceptor_time_rec)224 val_add_cred_pw_args(
225     OM_uint32 *minor_status,
226     gss_cred_id_t input_cred_handle,
227     const gss_name_t desired_name,
228     const gss_OID desired_mech,
229     const gss_buffer_t password,
230     gss_cred_usage_t cred_usage,
231     OM_uint32 initiator_time_req,
232     OM_uint32 acceptor_time_req,
233     gss_cred_id_t *output_cred_handle,
234     gss_OID_set *actual_mechs,
235     OM_uint32 *initiator_time_rec,
236     OM_uint32 *acceptor_time_rec)
237 {
238 
239     /* Initialize outputs. */
240 
241     if (minor_status != NULL)
242 	*minor_status = 0;
243 
244     if (output_cred_handle != NULL)
245 	*output_cred_handle = GSS_C_NO_CREDENTIAL;
246 
247     if (actual_mechs != NULL)
248 	*actual_mechs = GSS_C_NO_OID_SET;
249 
250     if (acceptor_time_rec != NULL)
251 	*acceptor_time_rec = 0;
252 
253     if (initiator_time_rec != NULL)
254 	*initiator_time_rec = 0;
255 
256     /* Validate arguments. */
257 
258     if (desired_name == GSS_C_NO_NAME)
259 	return (GSS_S_BAD_NAME);
260 
261     if (minor_status == NULL)
262 	return (GSS_S_CALL_INACCESSIBLE_WRITE);
263 
264     if (input_cred_handle == GSS_C_NO_CREDENTIAL &&
265 	output_cred_handle == NULL)
266 	return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CRED);
267 
268     if (cred_usage != GSS_C_ACCEPT
269 	&& cred_usage != GSS_C_INITIATE
270 	&& cred_usage != GSS_C_BOTH) {
271 	if (minor_status) {
272 	    *minor_status = EINVAL;
273 	    map_errcode(minor_status);
274 	}
275 	return GSS_S_FAILURE;
276     }
277 
278     if (password == GSS_C_NO_BUFFER ||
279         password->length == 0 ||
280         password->value == NULL) {
281 	if (minor_status) {
282 	    *minor_status = EINVAL;
283 	    map_errcode(minor_status);
284 	}
285 	return GSS_S_FAILURE;
286     }
287 
288 
289     return (GSS_S_COMPLETE);
290 }
291 
292 
293 /* V2 KRB5_CALLCONV */
294 OM_uint32 KRB5_CALLCONV
gss_add_cred_with_password(OM_uint32 * minor_status,const gss_cred_id_t input_cred_handle,const gss_name_t desired_name,const gss_OID desired_mech,const gss_buffer_t password,gss_cred_usage_t cred_usage,OM_uint32 initiator_time_req,OM_uint32 acceptor_time_req,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * initiator_time_rec,OM_uint32 * acceptor_time_rec)295 gss_add_cred_with_password(
296     OM_uint32 *minor_status,
297     const gss_cred_id_t input_cred_handle,
298     const gss_name_t desired_name,
299     const gss_OID desired_mech,
300     const gss_buffer_t password,
301     gss_cred_usage_t cred_usage,
302     OM_uint32 initiator_time_req,
303     OM_uint32 acceptor_time_req,
304     gss_cred_id_t *output_cred_handle,
305     gss_OID_set *actual_mechs,
306     OM_uint32 *initiator_time_rec,
307     OM_uint32 *acceptor_time_rec)
308 {
309     OM_uint32		status, temp_minor_status;
310     OM_uint32		time_req, time_rec;
311     gss_union_name_t	union_name;
312     gss_union_cred_t	new_union_cred, union_cred;
313     gss_name_t		internal_name = GSS_C_NO_NAME;
314     gss_name_t		allocated_name = GSS_C_NO_NAME;
315     gss_mechanism       mech;
316     gss_cred_id_t	cred = NULL;
317     gss_OID		new_mechs_array = NULL;
318     gss_cred_id_t *	new_cred_array = NULL;
319     gss_OID_set		target_mechs = GSS_C_NO_OID_SET;
320     gss_OID		selected_mech = GSS_C_NO_OID;
321 
322     status = val_add_cred_pw_args(minor_status,
323 			          input_cred_handle,
324 			          desired_name,
325 			          desired_mech,
326 			          password,
327 			          cred_usage,
328 			          initiator_time_req,
329 			          acceptor_time_req,
330 			          output_cred_handle,
331 			          actual_mechs,
332 			          initiator_time_rec,
333 			          acceptor_time_rec);
334     if (status != GSS_S_COMPLETE)
335 	return (status);
336 
337     status = gssint_select_mech_type(minor_status, desired_mech,
338 				     &selected_mech);
339     if (status != GSS_S_COMPLETE)
340 	return (status);
341 
342     mech = gssint_get_mechanism(selected_mech);
343     if (!mech)
344 	return GSS_S_BAD_MECH;
345     if (!mech->gssspi_acquire_cred_with_password)
346 	return GSS_S_UNAVAILABLE;
347 
348     if (input_cred_handle == GSS_C_NO_CREDENTIAL) {
349 	union_cred = malloc(sizeof (gss_union_cred_desc));
350 	if (union_cred == NULL)
351 	    return (GSS_S_FAILURE);
352 
353 	(void) memset(union_cred, 0, sizeof (gss_union_cred_desc));
354 
355 	/* for default credentials we will use GSS_C_NO_NAME */
356 	internal_name = GSS_C_NO_NAME;
357     } else {
358 	union_cred = (gss_union_cred_t)input_cred_handle;
359 	if (gssint_get_mechanism_cred(union_cred, selected_mech) !=
360 	    GSS_C_NO_CREDENTIAL)
361 	    return (GSS_S_DUPLICATE_ELEMENT);
362     }
363 
364     /* may need to create a mechanism specific name */
365     union_name = (gss_union_name_t)desired_name;
366     if (union_name->mech_type &&
367 	g_OID_equal(union_name->mech_type, selected_mech))
368 	internal_name = union_name->mech_name;
369     else {
370 	if (gssint_import_internal_name(minor_status,
371 					selected_mech, union_name,
372 					&allocated_name) != GSS_S_COMPLETE)
373 	    return (GSS_S_BAD_NAME);
374 	internal_name = allocated_name;
375     }
376 
377     if (cred_usage == GSS_C_ACCEPT)
378 	time_req = acceptor_time_req;
379     else if (cred_usage == GSS_C_INITIATE)
380 	time_req = initiator_time_req;
381     else if (cred_usage == GSS_C_BOTH)
382 	time_req = (acceptor_time_req > initiator_time_req) ?
383 	    acceptor_time_req : initiator_time_req;
384     else
385 	time_req = 0;
386 
387     status = gss_create_empty_oid_set(minor_status, &target_mechs);
388     if (status != GSS_S_COMPLETE)
389 	goto errout;
390 
391     status = gss_add_oid_set_member(minor_status,
392 				    gssint_get_public_oid(selected_mech),
393 				    &target_mechs);
394     if (status != GSS_S_COMPLETE)
395 	goto errout;
396 
397     status = mech->gssspi_acquire_cred_with_password(minor_status,
398 						     internal_name,
399 						     password,
400 						     time_req,
401 						     target_mechs,
402 						     cred_usage,
403 						     &cred,
404 						     NULL,
405 						     &time_rec);
406     if (status != GSS_S_COMPLETE) {
407 	map_error(minor_status, mech);
408 	goto errout;
409     }
410 
411     /* now add the new credential elements */
412     new_mechs_array = (gss_OID)
413 	malloc(sizeof (gss_OID_desc) * (union_cred->count+1));
414 
415     new_cred_array = (gss_cred_id_t *)
416 	malloc(sizeof (gss_cred_id_t) * (union_cred->count+1));
417 
418     if (!new_mechs_array || !new_cred_array) {
419 	status = GSS_S_FAILURE;
420 	goto errout;
421     }
422 
423     if (acceptor_time_rec)
424 	if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH)
425 	    *acceptor_time_rec = time_rec;
426     if (initiator_time_rec)
427 	if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH)
428 	    *initiator_time_rec = time_rec;
429 
430     /*
431      * OK, expand the mechanism array and the credential array
432      */
433     (void) memcpy(new_mechs_array, union_cred->mechs_array,
434 		  sizeof (gss_OID_desc) * union_cred->count);
435     (void) memcpy(new_cred_array, union_cred->cred_array,
436 		  sizeof (gss_cred_id_t) * union_cred->count);
437 
438     new_cred_array[union_cred->count] = cred;
439     if ((new_mechs_array[union_cred->count].elements =
440 	 malloc(selected_mech->length)) == NULL)
441 	goto errout;
442 
443     g_OID_copy(&new_mechs_array[union_cred->count], selected_mech);
444 
445     if (actual_mechs != NULL) {
446 	status = gssint_make_public_oid_set(minor_status, new_mechs_array,
447 					    union_cred->count + 1,
448 					    actual_mechs);
449 	if (GSS_ERROR(status)) {
450 	    free(new_mechs_array[union_cred->count].elements);
451 	    goto errout;
452 	}
453     }
454 
455     if (output_cred_handle == NULL) {
456 	free(union_cred->mechs_array);
457 	free(union_cred->cred_array);
458 	new_union_cred = union_cred;
459     } else {
460 	new_union_cred = malloc(sizeof (gss_union_cred_desc));
461 	if (new_union_cred == NULL) {
462 	    free(new_mechs_array[union_cred->count].elements);
463 	    goto errout;
464 	}
465 	*new_union_cred = *union_cred;
466 	*output_cred_handle = (gss_cred_id_t)new_union_cred;
467     }
468 
469     new_union_cred->mechs_array = new_mechs_array;
470     new_union_cred->cred_array = new_cred_array;
471     new_union_cred->count++;
472     new_union_cred->loopback = new_union_cred;
473 
474     /* We're done with the internal name. Free it if we allocated it. */
475 
476     if (allocated_name)
477 	(void) gssint_release_internal_name(&temp_minor_status,
478 					    selected_mech,
479 					   &allocated_name);
480 
481     if (target_mechs)
482 	(void)gss_release_oid_set(&temp_minor_status, &target_mechs);
483 
484     return (GSS_S_COMPLETE);
485 
486 errout:
487     if (new_mechs_array)
488 	free(new_mechs_array);
489     if (new_cred_array)
490 	free(new_cred_array);
491 
492     if (cred != NULL && mech->gss_release_cred)
493 	mech->gss_release_cred(&temp_minor_status, &cred);
494 
495     if (allocated_name)
496 	(void) gssint_release_internal_name(&temp_minor_status,
497 					    selected_mech, &allocated_name);
498 
499     if (target_mechs)
500 	(void)gss_release_oid_set(&temp_minor_status, &target_mechs);
501 
502     if (input_cred_handle == GSS_C_NO_CREDENTIAL && union_cred)
503 	free(union_cred);
504 
505     return (status);
506 }
507