Windows global stuff:
[krb5.git] / src / lib / gssapi / krb5 / init_sec_context.c
1 /*
2  * Copyright 1993 by OpenVision Technologies, Inc.
3  * 
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.
13  * 
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.
21  */
22
23 #include "gssapiP_krb5.h"
24 #include <memory.h>
25
26 static krb5_error_code
27 make_ap_req(context, auth_context, cred, server, endtime, chan_bindings, 
28             do_mutual, flags, token)
29     krb5_context context;
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;
35     int do_mutual;
36     krb5_flags *flags;
37     gss_buffer_t token;
38 {
39     krb5_flags mk_req_flags;
40     krb5_error_code code;
41     krb5_data checksum_data;
42     krb5_checksum md5;
43     krb5_creds in_creds, * out_creds;
44     krb5_data ap_req;
45     unsigned char *ptr;
46     unsigned char ckbuf[24];            /* see the token formats doc */
47     unsigned char *t;
48     int tlen;
49
50     /* build the checksum buffer */
51  
52     /* compute the hash of the channel bindings */
53
54     if (code = kg_checksum_channel_bindings(chan_bindings, &md5, 0))
55         return(code);
56
57     ptr = ckbuf;
58
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);
62
63     /* done with this, free it */
64     xfree(md5.contents);
65
66     checksum_data.data = (char *) ckbuf;
67     checksum_data.length = sizeof(ckbuf);
68
69     /* fill in the necessary fields in creds */
70
71     memset((char *) &in_creds, 0, sizeof(krb5_creds));
72     if (code = krb5_copy_principal(context, cred->princ, &in_creds.client))
73         return code;
74     if (code = krb5_copy_principal(context, server, &in_creds.server)) {
75         krb5_free_cred_contents(context, &in_creds);
76         return code;
77     }
78     in_creds.times.endtime = *endtime;
79
80     /*
81      * Get the credential..., I don't know in 0 is a good value for the
82      * kdcoptions
83      */
84     if (code = krb5_get_credentials(context, 0, cred->ccache, 
85                                     &in_creds, &out_creds)) {
86        krb5_free_cred_contents(context, &in_creds);
87        return code;
88     }
89
90     krb5_free_cred_contents(context, &in_creds);
91
92     /* get an auth_context structure */
93     if (code = krb5_auth_con_init(context, auth_context)) 
94         return(code);
95
96     krb5_auth_con_setcksumtype(context, *auth_context, CKSUMTYPE_KG_CB);
97
98
99     /* call mk_req.  subkey and ap_req need to be used or destroyed */
100
101     mk_req_flags |= AP_OPTS_USE_SUBKEY;
102
103     if (do_mutual)
104         mk_req_flags |= AP_OPTS_MUTUAL_REQUIRED;
105
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);
110        return(code);
111    }
112
113    /* store the interesting stuff from creds and authent */
114    *endtime = out_creds->times.endtime;
115    *flags = out_creds->ticket_flags;
116
117    /* free stuff which was created */
118    krb5_free_creds(context, out_creds);
119
120    /* build up the token */
121
122    /* allocate space for the token */
123    tlen = g_token_size(gss_mech_krb5, ap_req.length);
124
125    if ((t = (unsigned char *) xmalloc(tlen)) == NULL) {
126       krb5_auth_con_free(context, *auth_context);
127       xfree(ap_req.data);
128       return(ENOMEM);
129    }
130
131    /* fill in the buffer */
132
133    ptr = t;
134
135    g_make_token_header(gss_mech_krb5, ap_req.length,
136                        &ptr, KG_TOK_CTX_AP_REQ);
137
138    TWRITE_STR(ptr, (unsigned char *) ap_req.data, ap_req.length);
139
140    /* free the ap_req */
141    xfree(ap_req.data);
142
143    /* pass it back */
144    token->length = tlen;
145    token->value = (void *) t;
146
147    return(0);
148 }
149
150 OM_uint32
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,
155                         ret_flags, time_rec)
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;
162     int req_flags;
163     OM_uint32 time_req;
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;
168     int *ret_flags;
169     OM_uint32 *time_rec;
170 {
171     krb5_gss_cred_id_t    cred;
172     krb5_error_code       code; 
173     krb5_gss_ctx_id_rec *ctx;
174     krb5_timestamp now;
175     gss_buffer_desc token;
176     int i;
177 /* Remove this when server is fixed and this function goes away */
178 krb5_error_code krb5_auth_con_setkey (); 
179
180    /* set up return values so they can be "freed" successfully */
181
182    output_token->length = 0;
183    output_token->value = NULL;
184    if (actual_mech_type)
185       *actual_mech_type = (gss_OID) gss_mech_krb5;
186
187    /* verify the mech_type */
188
189    if ((mech_type != GSS_C_NULL_OID) &&
190        (! g_OID_equal(mech_type, gss_mech_krb5))) {
191       *minor_status = 0;
192       return(GSS_S_BAD_MECH);
193    }
194
195    /* verify the credential, or use the default */
196    /*SUPPRESS 29*/
197    if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) {
198       OM_uint32 major;
199
200       if ((major = kg_get_defcred(minor_status, &claimant_cred_handle)) &&
201           GSS_ERROR(major)) {
202          return(major);
203       }
204    } else {
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);
208       }
209    }
210
211    cred = (krb5_gss_cred_id_t) claimant_cred_handle;
212
213    /* verify that the target_name is valid and usable */
214
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);
218    }
219
220    /* is this a new connection or not? */
221
222    /*SUPPRESS 29*/
223    if (*context_handle == GSS_C_NO_CONTEXT) {
224       /* make sure the cred is usable for init */
225
226       if ((cred->usage != GSS_C_INITIATE) &&
227           (cred->usage != GSS_C_BOTH)) {
228          *minor_status = 0;
229          return(GSS_S_NO_CRED);
230       }
231
232       /* complain if the input token is nonnull */
233
234       if (input_token != GSS_C_NO_BUFFER) {
235          *minor_status = 0;
236          return(GSS_S_DEFECTIVE_TOKEN);
237       }
238
239       /* create the ctx */
240
241       if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec)))
242           == NULL) {
243          *minor_status = ENOMEM;
244          return(GSS_S_FAILURE);
245       }
246
247       /* fill in the ctx */
248
249       ctx->context = context;
250       ctx->auth_context = NULL;
251       ctx->initiate = 1;
252       ctx->mutual = req_flags & GSS_C_MUTUAL_FLAG;
253       ctx->seed_init = 0;
254       ctx->cred = cred;
255       ctx->big_endian = 0;  /* all initiators do little-endian, as per spec */
256
257       if (time_req == 0 || time_req == GSS_C_INDEFINITE) {
258          ctx->endtime = 0;
259       } else {
260          if (code = krb5_timeofday(context, &now)) {
261             free(ctx);
262             *minor_status = code;
263             return(GSS_S_FAILURE);
264          }
265          ctx->endtime = now + time_req;
266       }
267
268       if (code = krb5_copy_principal(context, cred->princ, &ctx->here)) {
269          xfree(ctx);
270          *minor_status = code;
271          return(GSS_S_FAILURE);
272       }
273       
274       if (code = krb5_copy_principal(context, (krb5_principal) target_name,
275                                      &ctx->there)) {
276          krb5_free_principal(context, ctx->here);
277          xfree(ctx);
278          *minor_status = code;
279          return(GSS_S_FAILURE);
280       }
281
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);
287          xfree(ctx);
288          *minor_status = code;
289          return(GSS_S_FAILURE);
290       }
291
292       krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &ctx->seq_send);
293       krb5_auth_con_getlocalsubkey(context, ctx->auth_context, &ctx->subkey);
294
295       /* fill in the encryption descriptors */
296
297       /* the encryption key is the session key XOR 0xf0f0f0f0f0f0f0f0 */
298
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))
302          return(code); 
303       for (i=0; i<ctx->enc.key->length; i++)
304          /*SUPPRESS 113*/
305          ctx->enc.key->contents[i] ^= 0xf0;
306
307       krb5_use_cstype(context, &ctx->seq.eblock, ETYPE_RAW_DES_CBC);
308       ctx->seq.processed = 0;
309       ctx->seq.key = ctx->subkey;
310
311       /* at this point, the context is constructed and valid,
312          hence, releaseable */
313
314       /* intern the context handle */
315
316       if (! kg_save_ctx_id((gss_ctx_id_t) ctx)) {
317          xfree(token.value);
318          krb5_free_keyblock(context, ctx->subkey);
319          krb5_free_principal(context, ctx->here);
320          krb5_free_principal(context, ctx->there);
321          xfree(ctx);
322
323          *minor_status = (OM_uint32) G_VALIDATE_FAILED;
324          return(GSS_S_FAILURE);
325       }
326
327       /* compute time_rec */
328
329       if (time_rec) {
330          if (code = krb5_timeofday(context, &now)) {
331             xfree(token.value);
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);
336          }
337          *time_rec = ctx->endtime - now;
338       }
339
340       /* set the other returns */
341
342       *context_handle = (gss_ctx_id_t) ctx;
343
344       *output_token = token;
345
346       if (ret_flags)
347          *ret_flags = ((req_flags & GSS_C_MUTUAL_FLAG) | 
348                        GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG);
349
350       /* return successfully */
351
352       *minor_status = 0;
353       if (ctx->mutual) {
354          ctx->established = 0;
355          return(GSS_S_CONTINUE_NEEDED);
356       } else {
357          ctx->seq_recv = ctx->seq_send;
358          ctx->established = 1;
359          return(GSS_S_COMPLETE);
360       }
361    } else {
362       unsigned char *ptr;
363       char *sptr;
364       krb5_data ap_rep;
365       krb5_ap_rep_enc_part *ap_rep_data;
366
367       /* validate the context handle */
368       /*SUPPRESS 29*/
369       if (! kg_validate_ctx_id(*context_handle)) {
370          *minor_status = (OM_uint32) G_VALIDATE_FAILED;
371          return(GSS_S_NO_CONTEXT);
372       }
373
374       ctx = (gss_ctx_id_t) *context_handle;
375
376       /* make sure the context is non-established, and that certain
377          arguments are unchanged */
378
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);
386       }
387
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);
392          *minor_status = 0;
393          return(GSS_S_BAD_NAME);
394       }
395
396       /* verify the token and leave the AP_REP message in ap_rep */
397
398       if (input_token == GSS_C_NO_BUFFER) {
399          (void)krb5_gss_delete_sec_context(context, minor_status, 
400                                            context_handle, NULL);
401          *minor_status = 0;
402          return(GSS_S_DEFECTIVE_TOKEN);
403       }
404
405       ptr = (unsigned char *) input_token->value;
406
407       if (! g_verify_token_header(gss_mech_krb5, &(ap_rep.length),
408                                   &ptr, KG_TOK_CTX_AP_REP,
409                                   input_token->length)) {
410          *minor_status = 0;
411          return(GSS_S_DEFECTIVE_TOKEN);
412       }
413
414       sptr = (char *) ptr;                      /* PC compiler bug */
415       TREAD_STR(sptr, ap_rep.data, ap_rep.length);
416
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);
425       }
426
427       /* store away the sequence number */
428       ctx->seq_recv = ap_rep_data->seq_number;
429
430       /* free the ap_rep_data */
431       krb5_free_ap_rep_enc_part(context, ap_rep_data);
432
433       /* set established */
434       ctx->established = 1;
435
436       /* set returns */
437
438       if (time_rec) {
439          if (code = krb5_timeofday(context, &now)) {
440             (void)krb5_gss_delete_sec_context(context, minor_status, 
441                                               (gss_ctx_id_t) ctx,
442                                          NULL);
443             *minor_status = code;
444             return(GSS_S_FAILURE);
445          }
446          *time_rec = ctx->endtime - now;
447       }
448
449       if (ret_flags)
450          *ret_flags = GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG | GSS_C_MUTUAL_FLAG;
451
452       /* success */
453
454       *minor_status = 0;
455       return(GSS_S_COMPLETE);
456    }
457 }