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