1 /*
2  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /*
9  * Copyright 2000 by the Massachusetts Institute of Technology.
10  * All Rights Reserved.
11  *
12  * Export of this software from the United States of America may
13  *   require a specific license from the United States Government.
14  *   It is the responsibility of any person or organization contemplating
15  *   export to obtain such a license before exporting.
16  *
17  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
18  * distribute this software and its documentation for any purpose and
19  * without fee is hereby granted, provided that the above copyright
20  * notice appear in all copies and that both that copyright notice and
21  * this permission notice appear in supporting documentation, and that
22  * the name of M.I.T. not be used in advertising or publicity pertaining
23  * to distribution of the software without specific, written prior
24  * permission.  Furthermore if you modify this software you must label
25  * your software as modified software and not distribute it in such a
26  * fashion that it might be confused with the original M.I.T. software.
27  * M.I.T. makes no representations about the suitability of
28  * this software for any purpose.  It is provided "as is" without express
29  * or implied warranty.
30  *
31  */
32 
33 /*
34  * Copyright 1993 by OpenVision Technologies, Inc.
35  *
36  * Permission to use, copy, modify, distribute, and sell this software
37  * and its documentation for any purpose is hereby granted without fee,
38  * provided that the above copyright notice appears in all copies and
39  * that both that copyright notice and this permission notice appear in
40  * supporting documentation, and that the name of OpenVision not be used
41  * in advertising or publicity pertaining to distribution of the software
42  * without specific, written prior permission. OpenVision makes no
43  * representations about the suitability of this software for any
44  * purpose.  It is provided "as is" without express or implied warranty.
45  *
46  * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
47  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
48  * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
49  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
50  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
51  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
52  * PERFORMANCE OF THIS SOFTWARE.
53  */
54 
55 /*
56  * Copyright (C) 1998 by the FundsXpress, INC.
57  *
58  * All rights reserved.
59  *
60  * Export of this software from the United States of America may require
61  * a specific license from the United States Government.  It is the
62  * responsibility of any person or organization contemplating export to
63  * obtain such a license before exporting.
64  *
65  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
66  * distribute this software and its documentation for any purpose and
67  * without fee is hereby granted, provided that the above copyright
68  * notice appear in all copies and that both that copyright notice and
69  * this permission notice appear in supporting documentation, and that
70  * the name of FundsXpress. not be used in advertising or publicity pertaining
71  * to distribution of the software without specific, written prior
72  * permission.  FundsXpress makes no representations about the suitability of
73  * this software for any purpose.  It is provided "as is" without express
74  * or implied warranty.
75  *
76  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
77  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
78  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
79  */
80 
81 #include "gss_libinit.h"
82 #include <gssapiP_krb5.h>
83 #include <k5-int.h>
84 
85 #ifdef HAVE_STRING_H
86 #include <string.h>
87 #else
88 #include <strings.h>
89 #endif
90 
91 /*
92  * $Id: acquire_cred.c,v 1.25.6.2 2000/05/22 20:41:32 meeroh Exp $
93  */
94 
95 /* ARGSUSED */
96 static OM_uint32
acquire_accept_cred_with_pw(context,minor_status,desired_name,password,cred)97 acquire_accept_cred_with_pw(context, minor_status, desired_name, password, cred)
98 krb5_context context;
99 OM_uint32 *minor_status;
100 krb5_principal desired_name;
101 const gss_buffer_t password;
102 krb5_gss_cred_id_rec *cred;
103 {
104 	/*
105 	 * We could add support for this, but we'd need a "memory" based
106 	 * keytab, which we lack support for.
107 	 */
108 	return (GSS_S_UNAVAILABLE);
109 }
110 
111 static OM_uint32
acquire_init_cred_with_pw(context,minor_status,desired_name,password,cred)112 acquire_init_cred_with_pw(context, minor_status, desired_name, password, cred)
113 krb5_context context;
114 OM_uint32 *minor_status;
115 krb5_principal desired_name;
116 const gss_buffer_t password;
117 krb5_gss_cred_id_rec *cred;
118 {
119 	krb5_error_code code = 0;
120 	krb5_ccache ccache1 = NULL;
121 	krb5_ccache ccache2 = NULL;
122 	krb5_creds creds;
123 	char *pw;
124 
125 	cred->ccache = NULL;
126 
127 	if (password == NULL || password->length == 0 ||
128 	    password->value == NULL)
129 		pw = strdup("");
130 	else if (*((char *)password->value + (password->length - 1)) == '\0')
131 		pw = strdup(password->value);
132 	else {
133 		pw = malloc(password->length + 1);
134 		if (pw == NULL) {
135 			code = ENOMEM;
136 			goto out;
137 		}
138 		*pw = '\0';
139 		(void) strlcat(pw, password->value, password->length + 1);
140 	}
141 
142 	if (pw == NULL) {
143 		code = ENOMEM;
144 		goto out;
145 	}
146 
147 	(void) memset(&creds, 0, sizeof (creds));
148 
149 	code = krb5_get_init_creds_password(context, &creds, desired_name, pw,
150 			NULL,	/* no prompter callback */
151 			NULL,	/* no prompter callback data */
152 			0,	/* start time (now) */
153 			NULL,	/* target princ; NULL -> TGS */
154 			NULL);	/* no options; use defaults/config */
155 
156 	if (code)
157 		goto out;
158 
159 	/* Got a TGT, now make a MEMORY ccache, stuff in the TGT */
160 
161 	if ((code = krb5_cc_resolve(context, "MEMORY:GSSAPI", &ccache1)))
162 		goto out;
163 
164 	/*
165 	 * Weirdness: there's no way to gen a new ccache without first
166 	 * opening another of well-known name.  A bug in the krb5 API,
167 	 * really which will have to be fixed in coordination with MIT.
168 	 *
169 	 * So we first krb5_cc_resolve() "MEMORY:GSSAPI", then we
170 	 * krb5_cc_gen_new(), which is a macro that finds the memory
171 	 * ccache ops from the first ccache but generates a new one.  If
172 	 * we don't close that first ccache it will leak.
173 	 */
174 	ccache2 = ccache1;
175 	if ((code = krb5_cc_gen_new(context, &ccache2)) != 0)
176 		goto out;
177 
178 	(void) krb5_cc_close(context, ccache1);	    /* avoid leak; see above */
179 
180 	if ((code = krb5_cc_initialize(context, ccache2, creds.client)) != 0)
181 		goto out;
182 
183 	if ((code = krb5_cc_store_cred(context, ccache2, &creds)) != 0)
184 		goto out;
185 
186 	krb5_free_cred_contents(context, &creds);
187 
188 	cred->ccache = ccache2;
189 
190 out:
191 	if (pw)
192 		free(pw);
193 
194 	*minor_status = code;
195 
196 	if (code == 0)
197 		return (GSS_S_COMPLETE);
198 
199 	if (ccache2 != NULL)
200 		(void) krb5_cc_close(context, ccache2);
201 
202 	return (GSS_S_FAILURE);
203 }
204 
205 /*ARGSUSED*/
206 OM_uint32
krb5_gss_acquire_cred_with_password(minor_status,desired_name,password,time_req,desired_mechs,cred_usage,output_cred_handle,actual_mechs,time_rec)207 krb5_gss_acquire_cred_with_password(minor_status,
208 				desired_name, password, time_req,
209 				desired_mechs, cred_usage,
210 				output_cred_handle, actual_mechs,
211 				time_rec)
212 OM_uint32 *minor_status;
213 gss_name_t desired_name;
214 const gss_buffer_t password;
215 OM_uint32 time_req;
216 gss_OID_set desired_mechs;
217 gss_cred_usage_t cred_usage;
218 gss_cred_id_t *output_cred_handle;
219 gss_OID_set *actual_mechs;
220 OM_uint32 *time_rec;
221 {
222 	krb5_context context;
223 	size_t i;
224 	krb5_gss_cred_id_t cred;
225 	gss_OID_set ret_mechs = GSS_C_NULL_OID_SET;
226 	const gss_OID_set_desc  * valid_mechs;
227 	int req_old, req_new;
228 	OM_uint32 ret;
229 	krb5_error_code code;
230 
231 	if (desired_name == GSS_C_NO_NAME)
232 		return (GSS_S_BAD_NAME);
233 
234 	code = gssint_initialize_library();
235 	if (code) {
236 		*minor_status = code;
237 		return (GSS_S_FAILURE);
238 	}
239 
240 	code = krb5_gss_init_context(&context);
241 	if (code) {
242 		*minor_status = code;
243 		return (GSS_S_FAILURE);
244 	}
245 
246 	/* make sure all outputs are valid */
247 
248 	*output_cred_handle = NULL;
249 	if (actual_mechs)
250 		*actual_mechs = NULL;
251 	if (time_rec)
252 		*time_rec = 0;
253 
254 	/* validate the name */
255 	if (!kg_validate_name(desired_name)) {
256 		*minor_status = (OM_uint32) G_VALIDATE_FAILED;
257 		krb5_free_context(context);
258 		return (GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
259 	}
260 
261 	/*
262 	 * verify that the requested mechanism set is the default, or
263 	 * contains krb5
264 	 */
265 
266 	if (desired_mechs == GSS_C_NULL_OID_SET) {
267 		valid_mechs = gss_mech_set_krb5_both;
268 		req_old = 1;
269 		req_new = 1;
270 	} else {
271 		req_old = 0;
272 		req_new = 0;
273 
274 		for (i = 0; i < desired_mechs->count; i++) {
275 			if (g_OID_equal(gss_mech_krb5_old,
276 				    &(desired_mechs->elements[i])))
277 				req_old++;
278 			if (g_OID_equal(gss_mech_krb5,
279 				    &(desired_mechs->elements[i])))
280 				req_new++;
281 		}
282 
283 		if (!req_old && !req_new) {
284 			*minor_status = 0;
285 			krb5_free_context(context);
286 			return (GSS_S_BAD_MECH);
287 		}
288 	}
289 
290 	/* create the gss cred structure */
291 	if ((cred = (krb5_gss_cred_id_t)
292 		    xmalloc(sizeof (krb5_gss_cred_id_rec))) == NULL) {
293 		*minor_status = ENOMEM;
294 		krb5_free_context(context);
295 		return (GSS_S_FAILURE);
296 	}
297 	memset(cred, 0, sizeof (krb5_gss_cred_id_rec));
298 
299 	cred->usage = cred_usage;
300 	cred->princ = NULL;
301 	cred->prerfc_mech = req_old;
302 	cred->rfc_mech = req_new;
303 
304 	cred->keytab = NULL;
305 	cred->ccache = NULL;
306 
307 	if ((cred_usage != GSS_C_INITIATE) &&
308 			(cred_usage != GSS_C_ACCEPT) &&
309 			(cred_usage != GSS_C_BOTH)) {
310 		xfree(cred);
311 		*minor_status = (OM_uint32) G_BAD_USAGE;
312 		krb5_free_context(context);
313 		return (GSS_S_FAILURE);
314 	}
315 
316 	/*
317 	 * If requested, acquire credentials for accepting.  This will
318 	 * fill in cred->princ if the desired_name is not specified.
319 	 */
320 
321 	if ((cred_usage == GSS_C_ACCEPT) ||
322 			(cred_usage == GSS_C_BOTH))
323 		if ((ret = acquire_accept_cred_with_pw(context, minor_status,
324 						(krb5_principal) desired_name,
325 						password, cred))
326 				!= GSS_S_COMPLETE) {
327 			if (cred->princ)
328 				krb5_free_principal(context, cred->princ);
329 			xfree(cred);
330 			krb5_free_context(context);
331 			/* minor_status set by acquire_accept_cred() */
332 			return (ret);
333 		}
334 
335 	/*
336 	 * If requested, acquire credentials for initiation.  This will
337 	 * fill in cred->princ if it wasn't set above, and the
338 	 * desired_name is not specified.
339 	 */
340 
341 	if ((cred_usage == GSS_C_INITIATE) ||
342 			(cred_usage == GSS_C_BOTH))
343 		if ((ret = acquire_init_cred_with_pw(context, minor_status,
344 				cred->princ ? cred->princ : (krb5_principal)
345 				desired_name, password, cred))
346 				!= GSS_S_COMPLETE) {
347 			if (cred->keytab)
348 				(void) krb5_kt_close(context, cred->keytab);
349 			if (cred->princ)
350 				krb5_free_principal(context, cred->princ);
351 			xfree(cred);
352 			krb5_free_context(context);
353 			/* minor_status set by acquire_init_cred() */
354 			return (ret);
355 		}
356 
357 	/* if the princ wasn't filled in already, fill it in now */
358 
359 	if (!cred->princ)
360 		if ((code = krb5_copy_principal(context, (krb5_principal)
361 				desired_name, &(cred->princ)))) {
362 			if (cred->ccache)
363 				(void) krb5_cc_close(context, cred->ccache);
364 			if (cred->keytab)
365 				(void) krb5_kt_close(context, cred->keytab);
366 			xfree(cred);
367 			*minor_status = code;
368 			krb5_free_context(context);
369 			return (GSS_S_FAILURE);
370 		}
371 
372 	/* at this point, the cred structure has been completely created */
373 
374 	/* compute time_rec */
375 
376 	if (cred_usage == GSS_C_ACCEPT) {
377 		if (time_rec)
378 			*time_rec = GSS_C_INDEFINITE;
379 	} else {
380 		krb5_timestamp now;
381 
382 		if ((code = krb5_timeofday(context, &now))) {
383 			if (cred->ccache)
384 				(void) krb5_cc_close(context, cred->ccache);
385 			if (cred->keytab)
386 				(void) krb5_kt_close(context, cred->keytab);
387 			if (cred->princ)
388 				krb5_free_principal(context, cred->princ);
389 			xfree(cred);
390 			*minor_status = code;
391 			krb5_free_context(context);
392 			return (GSS_S_FAILURE);
393 		}
394 
395 		if (time_rec)
396 			*time_rec = (cred->tgt_expire > now) ?
397 				(cred->tgt_expire - now) : 0;
398 	}
399 
400 	/* create mechs */
401 
402 	if (actual_mechs) {
403 		if (GSS_ERROR(ret = gss_create_empty_oid_set(minor_status,
404 						&ret_mechs)) ||
405 		    (cred->prerfc_mech && GSS_ERROR(ret =
406 					gss_add_oid_set_member(minor_status,
407 					    (gss_OID) gss_mech_krb5_old,
408 					    &ret_mechs))) ||
409 		    (cred->rfc_mech && GSS_ERROR(ret =
410 					gss_add_oid_set_member(minor_status,
411 					    (gss_OID) gss_mech_krb5,
412 					    &ret_mechs)))) {
413 			if (cred->ccache)
414 				(void) krb5_cc_close(context, cred->ccache);
415 			if (cred->keytab)
416 				(void) krb5_kt_close(context, cred->keytab);
417 			if (cred->princ)
418 				krb5_free_principal(context, cred->princ);
419 			xfree(cred);
420 			krb5_free_context(context);
421 			/* (*minor_status) set above */
422 			return (ret);
423 		}
424 	}
425 
426 	/* intern the credential handle */
427 
428 	if (! kg_save_cred_id((gss_cred_id_t)cred)) {
429 		(void) gss_release_oid_set(NULL, &ret_mechs);
430 		free(ret_mechs->elements);
431 		free(ret_mechs);
432 		if (cred->ccache)
433 			(void) krb5_cc_close(context, cred->ccache);
434 		if (cred->keytab)
435 			(void) krb5_kt_close(context, cred->keytab);
436 		if (cred->princ)
437 			krb5_free_principal(context, cred->princ);
438 		xfree(cred);
439 		krb5_free_context(context);
440 		*minor_status = (OM_uint32) G_VALIDATE_FAILED;
441 		return (GSS_S_FAILURE);
442 	}
443 
444 	krb5_free_context(context);
445 
446 	/* return success */
447 	*minor_status = 0;
448 	*output_cred_handle = (gss_cred_id_t)cred;
449 	if (actual_mechs)
450 		*actual_mechs = ret_mechs;
451 	return (GSS_S_COMPLETE);
452 }
453 
454 /*ARGSUSED*/
455 OM_uint32
gssspi_acquire_cred_with_password(ctx,minor_status,desired_name,password,time_req,desired_mechs,cred_usage,output_cred_handle,actual_mechs,time_rec)456 gssspi_acquire_cred_with_password(ctx, minor_status, desired_name,
457 		password, time_req, desired_mechs, cred_usage,
458 		output_cred_handle, actual_mechs, time_rec)
459 void *ctx;
460 OM_uint32 *minor_status;
461 gss_name_t desired_name;
462 const gss_buffer_t password;
463 OM_uint32 time_req;
464 gss_OID_set desired_mechs;
465 gss_cred_usage_t cred_usage;
466 gss_cred_id_t *output_cred_handle;
467 gss_OID_set *actual_mechs;
468 OM_uint32 *time_rec;
469 {
470 	OM_uint32 ret;
471 
472 	ret = krb5_gss_acquire_cred_with_password(minor_status,
473 			desired_name, password, time_req, desired_mechs,
474 			cred_usage, output_cred_handle, actual_mechs, time_rec);
475 	return (ret);
476 }
477