2 * Copyright 1993 by OpenVision Technologies, Inc.
4 * Permission to use, copy, modify, distribute, and sell this software
5 * and its documentation for any purpose is hereby granted without fee,
6 * provided that the above copyright notice appears in all copies and
7 * that both that copyright notice and this permission notice appear in
8 * supporting documentation, and that the name of OpenVision not be used
9 * in advertising or publicity pertaining to distribution of the software
10 * without specific, written prior permission. OpenVision makes no
11 * representations about the suitability of this software for any
12 * purpose. It is provided "as is" without express or implied warranty.
14 * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
18 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
19 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20 * PERFORMANCE OF THIS SOFTWARE.
23 #include "gssapiP_krb5.h"
26 static krb5_error_code
27 make_ap_req(context, auth_context, cred, server, endtime, chan_bindings,
28 do_mutual, flags, token)
30 krb5_auth_context ** auth_context;
31 krb5_gss_cred_id_t cred;
32 krb5_principal server;
33 krb5_timestamp *endtime;
34 gss_channel_bindings_t chan_bindings;
39 krb5_flags mk_req_flags;
41 krb5_data checksum_data;
43 krb5_creds in_creds, * out_creds;
46 unsigned char ckbuf[24]; /* see the token formats doc */
50 /* build the checksum buffer */
52 /* compute the hash of the channel bindings */
54 if (code = kg_checksum_channel_bindings(chan_bindings, &md5, 0))
59 TWRITE_INT(ptr, md5.length, 0);
60 TWRITE_STR(ptr, (unsigned char *) md5.contents, md5.length);
61 TWRITE_INT(ptr, do_mutual?GSS_C_MUTUAL_FLAG:0, 0);
63 /* done with this, free it */
66 checksum_data.data = (char *) ckbuf;
67 checksum_data.length = sizeof(ckbuf);
69 /* fill in the necessary fields in creds */
71 memset((char *) &in_creds, 0, sizeof(krb5_creds));
72 if (code = krb5_copy_principal(context, cred->princ, &in_creds.client))
74 if (code = krb5_copy_principal(context, server, &in_creds.server)) {
75 krb5_free_cred_contents(context, &in_creds);
78 in_creds.times.endtime = *endtime;
81 * Get the credential..., I don't know in 0 is a good value for the
84 if (code = krb5_get_credentials(context, 0, cred->ccache,
85 &in_creds, &out_creds)) {
86 krb5_free_cred_contents(context, &in_creds);
90 krb5_free_cred_contents(context, &in_creds);
92 /* get an auth_context structure */
93 if (code = krb5_auth_con_init(context, auth_context))
96 krb5_auth_con_setcksumtype(context, *auth_context, CKSUMTYPE_KG_CB);
99 /* call mk_req. subkey and ap_req need to be used or destroyed */
101 mk_req_flags |= AP_OPTS_USE_SUBKEY;
104 mk_req_flags |= AP_OPTS_MUTUAL_REQUIRED;
106 if (code = krb5_mk_req_extended(context, auth_context, mk_req_flags,
107 &checksum_data, out_creds, &ap_req)) {
108 krb5_auth_con_free(context, *auth_context);
109 krb5_free_creds(context, out_creds);
113 /* store the interesting stuff from creds and authent */
114 *endtime = out_creds->times.endtime;
115 *flags = out_creds->ticket_flags;
117 /* free stuff which was created */
118 krb5_free_creds(context, out_creds);
120 /* build up the token */
122 /* allocate space for the token */
123 tlen = g_token_size(gss_mech_krb5, ap_req.length);
125 if ((t = (unsigned char *) xmalloc(tlen)) == NULL) {
126 krb5_auth_con_free(context, *auth_context);
131 /* fill in the buffer */
135 g_make_token_header(gss_mech_krb5, ap_req.length,
136 &ptr, KG_TOK_CTX_AP_REQ);
138 TWRITE_STR(ptr, (unsigned char *) ap_req.data, ap_req.length);
140 /* free the ap_req */
144 token->length = tlen;
145 token->value = (void *) t;
151 krb5_gss_init_sec_context(context, minor_status, claimant_cred_handle,
152 context_handle, target_name, mech_type,
153 req_flags, time_req, input_chan_bindings,
154 input_token, actual_mech_type, output_token,
156 krb5_context context;
157 OM_uint32 *minor_status;
158 gss_cred_id_t claimant_cred_handle;
159 gss_ctx_id_t *context_handle;
160 gss_name_t target_name;
161 const_gss_OID mech_type;
164 gss_channel_bindings_t input_chan_bindings;
165 gss_buffer_t input_token;
166 gss_OID *actual_mech_type;
167 gss_buffer_t output_token;
171 krb5_gss_cred_id_t cred;
172 krb5_error_code code;
173 krb5_gss_ctx_id_rec *ctx;
175 gss_buffer_desc token;
177 /* Remove this when server is fixed and this function goes away */
178 krb5_error_code krb5_auth_con_setkey ();
180 /* set up return values so they can be "freed" successfully */
182 output_token->length = 0;
183 output_token->value = NULL;
184 if (actual_mech_type)
185 *actual_mech_type = (gss_OID) gss_mech_krb5;
187 /* verify the mech_type */
189 if ((mech_type != GSS_C_NULL_OID) &&
190 (! g_OID_equal(mech_type, gss_mech_krb5))) {
192 return(GSS_S_BAD_MECH);
195 /* verify the credential, or use the default */
197 if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) {
200 if ((major = kg_get_defcred(minor_status, &claimant_cred_handle)) &&
205 if (! kg_validate_cred_id(claimant_cred_handle)) {
206 *minor_status = (OM_uint32) G_VALIDATE_FAILED;
207 return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_DEFECTIVE_CREDENTIAL);
211 cred = (krb5_gss_cred_id_t) claimant_cred_handle;
213 /* verify that the target_name is valid and usable */
215 if (! kg_validate_name(target_name)) {
216 *minor_status = (OM_uint32) G_VALIDATE_FAILED;
217 return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
220 /* is this a new connection or not? */
223 if (*context_handle == GSS_C_NO_CONTEXT) {
224 /* make sure the cred is usable for init */
226 if ((cred->usage != GSS_C_INITIATE) &&
227 (cred->usage != GSS_C_BOTH)) {
229 return(GSS_S_NO_CRED);
232 /* complain if the input token is nonnull */
234 if (input_token != GSS_C_NO_BUFFER) {
236 return(GSS_S_DEFECTIVE_TOKEN);
241 if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec)))
243 *minor_status = ENOMEM;
244 return(GSS_S_FAILURE);
247 /* fill in the ctx */
249 ctx->context = context;
250 ctx->auth_context = NULL;
252 ctx->mutual = req_flags & GSS_C_MUTUAL_FLAG;
255 ctx->big_endian = 0; /* all initiators do little-endian, as per spec */
257 if (time_req == 0 || time_req == GSS_C_INDEFINITE) {
260 if (code = krb5_timeofday(context, &now)) {
262 *minor_status = code;
263 return(GSS_S_FAILURE);
265 ctx->endtime = now + time_req;
268 if (code = krb5_copy_principal(context, cred->princ, &ctx->here)) {
270 *minor_status = code;
271 return(GSS_S_FAILURE);
274 if (code = krb5_copy_principal(context, (krb5_principal) target_name,
276 krb5_free_principal(context, ctx->here);
278 *minor_status = code;
279 return(GSS_S_FAILURE);
282 if (code = make_ap_req(context, &(ctx->auth_context), ctx->cred,
283 ctx->there, &ctx->endtime, input_chan_bindings,
284 ctx->mutual, &ctx->flags, &token)) {
285 krb5_free_principal(context, ctx->here);
286 krb5_free_principal(context, ctx->there);
288 *minor_status = code;
289 return(GSS_S_FAILURE);
292 krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &ctx->seq_send);
293 krb5_auth_con_getlocalsubkey(context, ctx->auth_context, &ctx->subkey);
295 /* fill in the encryption descriptors */
297 /* the encryption key is the session key XOR 0xf0f0f0f0f0f0f0f0 */
299 krb5_use_cstype(context, &ctx->enc.eblock, ETYPE_RAW_DES_CBC);
300 ctx->enc.processed = 0;
301 if (code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc.key))
303 for (i=0; i<ctx->enc.key->length; i++)
305 ctx->enc.key->contents[i] ^= 0xf0;
307 krb5_use_cstype(context, &ctx->seq.eblock, ETYPE_RAW_DES_CBC);
308 ctx->seq.processed = 0;
309 ctx->seq.key = ctx->subkey;
311 /* at this point, the context is constructed and valid,
312 hence, releaseable */
314 /* intern the context handle */
316 if (! kg_save_ctx_id((gss_ctx_id_t) ctx)) {
318 krb5_free_keyblock(context, ctx->subkey);
319 krb5_free_principal(context, ctx->here);
320 krb5_free_principal(context, ctx->there);
323 *minor_status = (OM_uint32) G_VALIDATE_FAILED;
324 return(GSS_S_FAILURE);
327 /* compute time_rec */
330 if (code = krb5_timeofday(context, &now)) {
332 (void)krb5_gss_delete_sec_context(context, minor_status,
333 (gss_ctx_id_t) ctx, NULL);
334 *minor_status = code;
335 return(GSS_S_FAILURE);
337 *time_rec = ctx->endtime - now;
340 /* set the other returns */
342 *context_handle = (gss_ctx_id_t) ctx;
344 *output_token = token;
347 *ret_flags = ((req_flags & GSS_C_MUTUAL_FLAG) |
348 GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG);
350 /* return successfully */
354 ctx->established = 0;
355 return(GSS_S_CONTINUE_NEEDED);
357 ctx->seq_recv = ctx->seq_send;
358 ctx->established = 1;
359 return(GSS_S_COMPLETE);
365 krb5_ap_rep_enc_part *ap_rep_data;
367 /* validate the context handle */
369 if (! kg_validate_ctx_id(*context_handle)) {
370 *minor_status = (OM_uint32) G_VALIDATE_FAILED;
371 return(GSS_S_NO_CONTEXT);
374 ctx = (gss_ctx_id_t) *context_handle;
376 /* make sure the context is non-established, and that certain
377 arguments are unchanged */
379 if ((ctx->established) ||
380 (((gss_cred_id_t) ctx->cred) != claimant_cred_handle) ||
381 ((req_flags & GSS_C_MUTUAL_FLAG) == 0)) {
382 (void)krb5_gss_delete_sec_context(context, minor_status,
383 context_handle, NULL);
384 *minor_status = KG_CONTEXT_ESTABLISHED;
385 return(GSS_S_FAILURE);
388 if (! krb5_principal_compare(context, ctx->there,
389 (krb5_principal) target_name)) {
390 (void)krb5_gss_delete_sec_context(context, minor_status,
391 context_handle, NULL);
393 return(GSS_S_BAD_NAME);
396 /* verify the token and leave the AP_REP message in ap_rep */
398 if (input_token == GSS_C_NO_BUFFER) {
399 (void)krb5_gss_delete_sec_context(context, minor_status,
400 context_handle, NULL);
402 return(GSS_S_DEFECTIVE_TOKEN);
405 ptr = (unsigned char *) input_token->value;
407 if (! g_verify_token_header(gss_mech_krb5, &(ap_rep.length),
408 &ptr, KG_TOK_CTX_AP_REP,
409 input_token->length)) {
411 return(GSS_S_DEFECTIVE_TOKEN);
414 sptr = (char *) ptr; /* PC compiler bug */
415 TREAD_STR(sptr, ap_rep.data, ap_rep.length);
417 /* A hack. Don't forget to remove the prototype for it above */
418 krb5_auth_con_setkey(context, ctx->auth_context, ctx->subkey);
419 /* decode the ap_rep */
420 if (code = krb5_rd_rep(context,ctx->auth_context,&ap_rep,&ap_rep_data)) {
421 (void)krb5_gss_delete_sec_context(context, minor_status,
422 context_handle, NULL);
423 *minor_status = code;
424 return(GSS_S_FAILURE);
427 /* store away the sequence number */
428 ctx->seq_recv = ap_rep_data->seq_number;
430 /* free the ap_rep_data */
431 krb5_free_ap_rep_enc_part(context, ap_rep_data);
433 /* set established */
434 ctx->established = 1;
439 if (code = krb5_timeofday(context, &now)) {
440 (void)krb5_gss_delete_sec_context(context, minor_status,
443 *minor_status = code;
444 return(GSS_S_FAILURE);
446 *time_rec = ctx->endtime - now;
450 *ret_flags = GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG | GSS_C_MUTUAL_FLAG;
455 return(GSS_S_COMPLETE);