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
acquire_accept_cred_with_pw(context,minor_status,desired_name,password,cred)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
acquire_init_cred_with_pw(context,minor_status,desired_name,password,cred)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
krb5_gss_acquire_cred_with_password(minor_status,desired_name,password,time_req,desired_mechs,cred_usage,output_cred_handle,actual_mechs,time_rec)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
gssspi_acquire_cred_with_password(ctx,minor_status,desired_name,password,time_req,desired_mechs,cred_usage,output_cred_handle,actual_mechs,time_rec)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