1 /*
2  * Copyright 2005 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 <gssapiP_krb5.h>
82 #include <k5-int.h>
83 
84 #ifdef HAVE_STRING_H
85 #include <string.h>
86 #else
87 #include <strings.h>
88 #endif
89 
90 /*
91  * $Id: acquire_cred.c,v 1.25.6.2 2000/05/22 20:41:32 meeroh Exp $
92  */
93 
94 /* ARGSUSED */
95 static OM_uint32
96 acquire_accept_cred_with_pw(context, minor_status, desired_name, password, cred)
97 krb5_context context;
98 OM_uint32 *minor_status;
99 krb5_principal desired_name;
100 const gss_buffer_t password;
101 krb5_gss_cred_id_rec *cred;
102 {
103 	/*
104 	 * We could add support for this, but we'd need a "memory" based
105 	 * keytab, which we lack support for.
106 	 */
107 	return (GSS_S_UNAVAILABLE);
108 }
109 
110 static OM_uint32
111 acquire_init_cred_with_pw(context, minor_status, desired_name, password, cred)
112 krb5_context context;
113 OM_uint32 *minor_status;
114 krb5_principal desired_name;
115 const gss_buffer_t password;
116 krb5_gss_cred_id_rec *cred;
117 {
118 	krb5_error_code code = 0;
119 	krb5_ccache ccache1 = NULL;
120 	krb5_ccache ccache2 = NULL;
121 	krb5_creds creds;
122 	char *pw;
123 
124 	cred->ccache = NULL;
125 
126 	if (password == NULL || password->length == NULL ||
127 	    password->value == NULL)
128 		pw = strdup("");
129 	else if (*((char *)password->value + (password->length - 1)) == '\0')
130 		pw = strdup(password->value);
131 	else {
132 		pw = malloc(password->length + 1);
133 		if (pw == NULL) {
134 			code = ENOMEM;
135 			goto out;
136 		}
137 		*pw = '\0';
138 		(void) strlcat(pw, password->value, password->length + 1);
139 	}
140 
141 	if (pw == NULL) {
142 		code = ENOMEM;
143 		goto out;
144 	}
145 
146 	(void) memset(&creds, 0, sizeof (creds));
147 
148 	code = krb5_get_init_creds_password(context, &creds, desired_name, pw,
149 			NULL,	/* no prompter callback */
150 			NULL,	/* no prompter callback data */
151 			0,	/* start time (now) */
152 			NULL,	/* target princ; NULL -> TGS */
153 			NULL);	/* no options; use defaults/config */
154 
155 	if (code)
156 		goto out;
157 
158 	/* Got a TGT, now make a MEMORY ccache, stuff in the TGT */
159 
160 	if ((code = krb5_cc_resolve(context, "MEMORY:GSSAPI", &ccache1)))
161 		goto out;
162 
163 	/*
164 	 * Weirdness: there's no way to gen a new ccache without first
165 	 * opening another of well-known name.  A bug in the krb5 API,
166 	 * really which will have to be fixed in coordination with MIT.
167 	 *
168 	 * So we first krb5_cc_resolve() "MEMORY:GSSAPI", then we
169 	 * krb5_cc_gen_new(), which is a macro that finds the memory
170 	 * ccache ops from the first ccache but generates a new one.  If
171 	 * we don't close that first ccache it will leak.
172 	 */
173 	ccache2 = ccache1;
174 	if ((code = krb5_cc_gen_new(context, &ccache2)) != 0)
175 		goto out;
176 
177 	(void) krb5_cc_close(context, ccache1);	    /* avoid leak; see above */
178 
179 	if ((code = krb5_cc_initialize(context, ccache2, creds.client)) != 0)
180 		goto out;
181 
182 	if ((code = krb5_cc_store_cred(context, ccache2, &creds)) != 0)
183 		goto out;
184 
185 	krb5_free_cred_contents(context, &creds);
186 
187 	cred->ccache = ccache2;
188 
189 out:
190 	if (pw)
191 		free(pw);
192 
193 	*minor_status = code;
194 
195 	if (code == 0)
196 		return (GSS_S_COMPLETE);
197 
198 	if (ccache2 != NULL)
199 		(void) krb5_cc_close(context, ccache2);
200 
201 	return (GSS_S_FAILURE);
202 }
203 
204 /*ARGSUSED*/
205 OM_uint32
206 krb5_gss_acquire_cred_with_password_no_lock(ctx, minor_status,
207 				desired_name, password, time_req,
208 				desired_mechs, cred_usage,
209 				output_cred_handle, actual_mechs,
210 				time_rec)
211 void *ctx;
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 0
232 	if (GSS_ERROR(kg_get_context(minor_status, &context)))
233 		return (GSS_S_FAILURE);
234 #endif
235 
236 	context = ctx;
237 
238 	if (desired_name == GSS_C_NO_NAME)
239 		return (GSS_S_BAD_NAME);
240 
241 	/* make sure all outputs are valid */
242 
243 	*output_cred_handle = NULL;
244 	if (actual_mechs)
245 		*actual_mechs = NULL;
246 	if (time_rec)
247 		*time_rec = 0;
248 
249 	/* validate the name */
250 	if (!kg_validate_name(desired_name)) {
251 		*minor_status = (OM_uint32) G_VALIDATE_FAILED;
252 		return (GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
253 	}
254 
255 	/*
256 	 * verify that the requested mechanism set is the default, or
257 	 * contains krb5
258 	 */
259 
260 	if (desired_mechs == GSS_C_NULL_OID_SET) {
261 		valid_mechs = gss_mech_set_krb5_both;
262 		req_old = 1;
263 		req_new = 1;
264 	} else {
265 		req_old = 0;
266 		req_new = 0;
267 
268 		for (i = 0; i < desired_mechs->count; i++) {
269 			if (g_OID_equal(gss_mech_krb5_old,
270 				    &(desired_mechs->elements[i])))
271 				req_old++;
272 			if (g_OID_equal(gss_mech_krb5,
273 				    &(desired_mechs->elements[i])))
274 				req_new++;
275 		}
276 
277 		if (!req_old && !req_new) {
278 			*minor_status = 0;
279 			return (GSS_S_BAD_MECH);
280 		}
281 	}
282 
283 	/* create the gss cred structure */
284 	if ((cred = (krb5_gss_cred_id_t)
285 		    xmalloc(sizeof (krb5_gss_cred_id_rec))) == NULL) {
286 		*minor_status = ENOMEM;
287 		return (GSS_S_FAILURE);
288 	}
289 	memset(cred, 0, sizeof (krb5_gss_cred_id_rec));
290 
291 	cred->usage = cred_usage;
292 	cred->princ = NULL;
293 	cred->actual_mechs = valid_mechs;
294 	cred->prerfc_mech = req_old;
295 	cred->rfc_mech = req_new;
296 
297 	cred->keytab = NULL;
298 	cred->ccache = NULL;
299 
300 	if ((cred_usage != GSS_C_INITIATE) &&
301 			(cred_usage != GSS_C_ACCEPT) &&
302 			(cred_usage != GSS_C_BOTH)) {
303 		xfree(cred);
304 		*minor_status = (OM_uint32) G_BAD_USAGE;
305 		return (GSS_S_FAILURE);
306 	}
307 
308 	/*
309 	 * If requested, acquire credentials for accepting.  This will
310 	 * fill in cred->princ if the desired_name is not specified.
311 	 */
312 
313 	if ((cred_usage == GSS_C_ACCEPT) ||
314 			(cred_usage == GSS_C_BOTH))
315 		if ((ret = acquire_accept_cred_with_pw(context, minor_status,
316 						(krb5_principal) desired_name,
317 						password, cred))
318 				!= GSS_S_COMPLETE) {
319 			if (cred->princ)
320 				krb5_free_principal(context, cred->princ);
321 			xfree(cred);
322 			/* minor_status set by acquire_accept_cred() */
323 			return (ret);
324 		}
325 
326 	/*
327 	 * If requested, acquire credentials for initiation.  This will
328 	 * fill in cred->princ if it wasn't set above, and the
329 	 * desired_name is not specified.
330 	 */
331 
332 	if ((cred_usage == GSS_C_INITIATE) ||
333 			(cred_usage == GSS_C_BOTH))
334 		if ((ret = acquire_init_cred_with_pw(context, minor_status,
335 				cred->princ ? cred->princ : (krb5_principal)
336 				desired_name, password, cred))
337 				!= GSS_S_COMPLETE) {
338 			if (cred->keytab)
339 				(void) krb5_kt_close(context, cred->keytab);
340 			if (cred->princ)
341 				krb5_free_principal(context, cred->princ);
342 			xfree(cred);
343 			/* minor_status set by acquire_init_cred() */
344 			return (ret);
345 		}
346 
347 	/* if the princ wasn't filled in already, fill it in now */
348 
349 	if (!cred->princ)
350 		if ((code = krb5_copy_principal(context, (krb5_principal)
351 				desired_name, &(cred->princ)))) {
352 			if (cred->ccache)
353 				(void) krb5_cc_close(context, cred->ccache);
354 			if (cred->keytab)
355 				(void) krb5_kt_close(context, cred->keytab);
356 			xfree(cred);
357 			*minor_status = code;
358 			return (GSS_S_FAILURE);
359 		}
360 
361 	/* at this point, the cred structure has been completely created */
362 
363 	/* compute time_rec */
364 
365 	if (cred_usage == GSS_C_ACCEPT) {
366 		if (time_rec)
367 			*time_rec = GSS_C_INDEFINITE;
368 	} else {
369 		krb5_timestamp now;
370 
371 		if ((code = krb5_timeofday(context, &now))) {
372 			if (cred->ccache)
373 				(void) krb5_cc_close(context, cred->ccache);
374 			if (cred->keytab)
375 				(void) krb5_kt_close(context, cred->keytab);
376 			if (cred->princ)
377 				krb5_free_principal(context, cred->princ);
378 			xfree(cred);
379 			*minor_status = code;
380 			return (GSS_S_FAILURE);
381 		}
382 
383 		if (time_rec)
384 			*time_rec = (cred->tgt_expire > now) ?
385 				(cred->tgt_expire - now) : 0;
386 	}
387 
388 	/* create mechs */
389 
390 	if (actual_mechs) {
391 		if (GSS_ERROR(ret = gss_create_empty_oid_set(minor_status,
392 						&ret_mechs)) ||
393 		    (cred->prerfc_mech && GSS_ERROR(ret =
394 					gss_add_oid_set_member(minor_status,
395 					    (gss_OID) gss_mech_krb5_old,
396 					    &ret_mechs))) ||
397 		    (cred->rfc_mech && GSS_ERROR(ret =
398 					gss_add_oid_set_member(minor_status,
399 					    (gss_OID) gss_mech_krb5,
400 					    &ret_mechs)))) {
401 			if (cred->ccache)
402 				(void) krb5_cc_close(context, cred->ccache);
403 			if (cred->keytab)
404 				(void) krb5_kt_close(context, cred->keytab);
405 			if (cred->princ)
406 				krb5_free_principal(context, cred->princ);
407 			xfree(cred);
408 			/* (*minor_status) set above */
409 			return (ret);
410 		}
411 	}
412 
413 	/* intern the credential handle */
414 
415 	if (! kg_save_cred_id((gss_cred_id_t)cred)) {
416 		(void) gss_release_oid_set(NULL, &ret_mechs);
417 		free(ret_mechs->elements);
418 		free(ret_mechs);
419 		if (cred->ccache)
420 			(void) krb5_cc_close(context, cred->ccache);
421 		if (cred->keytab)
422 			(void) krb5_kt_close(context, cred->keytab);
423 		if (cred->princ)
424 			krb5_free_principal(context, cred->princ);
425 		xfree(cred);
426 		*minor_status = (OM_uint32) G_VALIDATE_FAILED;
427 		return (GSS_S_FAILURE);
428 	}
429 
430 	/* return success */
431 
432 	*minor_status = 0;
433 	*output_cred_handle = (gss_cred_id_t)cred;
434 	if (actual_mechs)
435 		*actual_mechs = ret_mechs;
436 	return (GSS_S_COMPLETE);
437 }
438 
439 OM_uint32
440 gssspi_acquire_cred_with_password(ctx, minor_status, desired_name,
441 		password, time_req, desired_mechs, cred_usage,
442 		output_cred_handle, actual_mechs, time_rec)
443 void *ctx;
444 OM_uint32 *minor_status;
445 gss_name_t desired_name;
446 const gss_buffer_t password;
447 OM_uint32 time_req;
448 gss_OID_set desired_mechs;
449 gss_cred_usage_t cred_usage;
450 gss_cred_id_t *output_cred_handle;
451 gss_OID_set *actual_mechs;
452 OM_uint32 *time_rec;
453 {
454 	OM_uint32 ret;
455 
456 	mutex_lock(&krb5_mutex);
457 	ret = krb5_gss_acquire_cred_with_password_no_lock(ctx, minor_status,
458 			desired_name, password, time_req, desired_mechs,
459 			cred_usage, output_cred_handle, actual_mechs, time_rec);
460 	mutex_unlock(&krb5_mutex);
461 	return (ret);
462 }
463