Initial Revision
[krb5.git] / src / lib / gssapi / init_sec.c
1 /*
2  * init_sec.c --- initialize security context
3  * 
4  * $Source$
5  * $Author$
6  * $Header$
7  * 
8  * Copyright 1991 by the Massachusetts Institute of Technology.
9  * All Rights Reserved.
10  *
11  * For copying and distribution information, please see the file
12  * <krb5/copyright.h>.
13  *
14  */
15
16 #include <gssapi.h>
17
18 extern krb5_flags    krb5_kdc_default_options;
19
20 /*
21  * To do in the future:
22  *
23  *      * Support replay cache
24  *
25  *      * Support delegation of credentials
26  *
27  *      * Do something with time_rec
28  *
29  *      * Should handle Kerberos error packets being sent back and
30  *      forth.
31  */
32
33 gss_cred_id_t   gss_default_credentials = {
34         (krb5_principal) NULL, (gss_OID) NULL, 0, (krb5_ccache) NULL,
35         (krb5_kvno) 0, { (krb5_keytype) 0, 0, (krb5_octet *) NULL }
36 };
37                 
38
39 OM_uint32 gss_init_sec_context(minor_status, claimant_cred_handle,
40                                context_handle, target_name,
41                                mech_type, req_flags, time_req,
42                                channel, input_token,
43                                actual_mech_type, output_token,
44                                ret_flags, time_rec)
45         OM_uint32       *minor_status;
46         gss_cred_id_t   claimant_cred_handle;
47         gss_ctx_id_t    *context_handle;
48         gss_name_t      target_name;
49         gss_OID         mech_type;
50         int             req_flags;
51         int             time_req;
52         gss_channel_bindings    channel;
53         gss_buffer_t    input_token;
54         gss_OID         *actual_mech_type;
55         gss_buffer_t    output_token;
56         int             *ret_flags;
57         OM_uint32       *time_rec;
58 {
59         krb5_flags              kdc_options = krb5_kdc_default_options;
60         krb5_flags              ap_req_options = 0;
61         krb5_ccache             ccache;
62         krb5_creds              creds;
63         krb5_authenticator      authent;
64         krb5_data               inbuf, outbuf;
65         krb5_ap_rep_enc_part    *repl;
66         OM_uint32               retval;
67         gss_ctx_id_t    context;
68         
69         *minor_status = 0;
70
71         if (!context_handle) {
72                 /*
73                  * This is first call to init_sec_context
74                  *
75                  * We only handle Kerberos V5...
76                  */
77                 if ((mech_type != GSS_C_NULL_OID) &&
78                     !gss_compare_OID(mech_type, &gss_OID_krb5)) {
79                         return(gss_make_re(GSS_RE_BAD_MECH));
80                 }
81                 if (actual_mech_type)
82                         *actual_mech_type = &gss_OID_krb5;
83                 /*
84                  * Sanitize the incoming flags
85                  *
86                  * We don't support delegation or replay detection --- yet.
87                  */
88                 req_flags &= ~GSS_C_DELEG_FLAG;
89                 req_flags &= ~GSS_C_REPLAY_FLAG; 
90                 /*
91                  * If no credentials were passed in, get our own
92                  */
93                 if (claimant_cred_handle.ccache)
94                         ccache = claimant_cred_handle.ccache;
95                 else {
96                         /*
97                          * Default (or NULL) credentials, we need to
98                          * fill in with defaults.
99                          */
100                         if (*minor_status = krb5_cc_default(&ccache)) {
101                                 return(gss_make_re(GSS_RE_FAILURE));
102                         }
103                         claimant_cred_handle.ccache = ccache;
104                         if (*minor_status =
105                             krb5_cc_get_principal(ccache,
106                                                   &claimant_cred_handle.principal))
107                                 return(gss_make_re(GSS_RE_FAILURE));
108                 }
109                 /*
110                  * Allocate the context handle structure
111                  */
112                 if (!(context = malloc(sizeof(struct gss_ctx_id_desc)))) {
113                         *minor_status = ENOMEM;
114                         return(gss_make_re(GSS_RE_FAILURE));
115                 }
116                 context->mech_type = &gss_OID_krb5;
117                 context->state =  GSS_KRB_STATE_DOWN;
118                 /*
119                  * Fill in context handle structure
120                  */
121                 if (*minor_status =
122                     krb5_copy_principal(claimant_cred_handle.principal,
123                                         &context->me))
124                         return(gss_make_re(GSS_RE_FAILURE));
125                 if (*minor_status =
126                     krb5_copy_principal(target_name,
127                                         &context->him))
128                         return(gss_make_re(GSS_RE_FAILURE));
129                 context->flags = req_flags | GSS_C_CONF_FLAG;;
130                 context->am_client = 1;
131                 context->session_key = NULL;
132                 context->my_address.addrtype = channel.sender_addrtype;
133                 context->my_address.length = channel.sender_address.length;
134                 if (!(context->my_address.contents =
135                       malloc(context->my_address.length))) {
136                         xfree(context);
137                         return(gss_make_re(GSS_RE_FAILURE));
138                 }
139                 memcpy((char *) context->my_address.contents,
140                        (char *) channel.sender_address.value,
141                        context->my_address.length);
142                 context->his_address.addrtype = channel.receiver_addrtype;
143                 context->his_address.length = channel.receiver_address.length;
144                 if (!(context->his_address.contents =
145                       malloc(context->my_address.length))) {
146                         xfree(context->my_address.contents);
147                         xfree(context);
148                         return(gss_make_re(GSS_RE_FAILURE));
149                 }
150                 memcpy((char *) context->his_address.contents,
151                        (char *) channel.receiver_address.value,
152                        context->his_address.length);
153                 /*
154                  * Generate a random sequence number
155                  */
156                 if (*minor_status =
157                     krb5_generate_seq_number(&creds.keyblock,
158                                              &context->my_seq_num)) {
159                         xfree(context->his_address.contents);
160                         xfree(context->my_address.contents);
161                         free((char *)context);
162                         return(make_gss_re(GSS_RE_FAILURE));
163                 }
164                 context->his_seq_num = 0;
165                 /*
166                  * Make a credentials structure
167                  */
168                 memset((char *)&creds, 0, sizeof(creds));
169                 creds.server = context->him;
170                 creds.client = context->me;
171                 /* creds.times.endtime = 0; -- memset 0 takes care of this
172                                         zero means "as long as possible" */
173                 /* creds.keyblock.keytype = 0; -- as well as this.
174                                         zero means no session keytype
175                                         preference */
176                 if (*minor_status = krb5_get_credentials(0,
177                                                          ccache,
178                                                          &creds)) {
179                         krb5_free_cred_contents(&creds);
180                         free((char *)context);
181                         return(gss_make_re(GSS_RE_FAILURE));
182                 }
183                 /*
184                  * Setup the ap_req_options
185                  */
186                 if ((req_flags & GSS_C_MUTUAL_FLAG) ||
187                     (req_flags & GSS_C_SEQUENCE_FLAG))
188                         ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
189                 /*
190                  * OK, get the authentication header!
191                  */
192                 if (*minor_status = krb5_mk_req_extended(ap_req_options, 0,
193                                                   &creds.times,
194                                                   kdc_options,
195                                                   context->my_seq_num, 0,
196                                                   ccache, &creds, &authent,
197                                                   &outbuf)) {
198                         memset((char *)&authent, 0, sizeof(authent));
199                         krb5_free_cred_contents(&creds);
200                         free((char *)context);
201                         return(gss_make_re(GSS_RE_FAILURE));    
202                 }
203                 context->cusec = authent.cusec;
204                 context->ctime = authent.ctime;
205                 memset((char *)&authent, 0, sizeof(authent));
206                 
207                 if (*minor_status =
208                     krb5_copy_keyblock(&creds.keyblock,
209                                        &context->session_key)) {
210                         xfree(outbuf.data);
211                         krb5_free_cred_contents(&creds);
212                         free((char *)context);
213                         return(gss_make_re(GSS_RE_FAILURE));
214                 }
215                 
216                 if (*minor_status = gss_make_token(minor_status,
217                                                    GSS_API_KRB5_TYPE,
218                                                    GSS_API_KRB5_REQ,
219                                                    outbuf.length,
220                                                    outbuf.data,
221                                                    output_token)) {
222                         xfree(outbuf.data);
223                         krb5_free_cred_contents(&creds);
224                         free((char *) context);
225                         return(gss_make_re(GSS_RE_FAILURE));
226                 }
227                 /*
228                  * Send over the requested flags information
229                  */
230                 ((char *) output_token->value)[4] = context->flags;
231                 xfree(outbuf.data);
232                 *context_handle = context;
233                 context->state = GSS_KRB_STATE_DOWN;
234                 *ret_flags = context->flags;
235                 /*
236                  * Don't free server and client because we need them
237                  * for the context structure.
238                  */
239                 creds.server = 0;
240                 creds.client = 0;
241                 krb5_free_cred_contents(&creds);
242                 if (ap_req_options & AP_OPTS_MUTUAL_REQUIRED) {
243                         context->state = GSS_KRB_STATE_MUTWAIT;
244                         return(GSS_SS_CONTINUE_NEEDED);
245                 } else {
246                         context->state = GSS_KRB_STATE_UP;
247                         return(GSS_S_COMPLETE);
248                 }
249                 
250         } else {
251                 context = *context_handle;
252
253                 if (context->state != GSS_KRB_STATE_MUTWAIT)
254                         return(gss_make_re(GSS_RE_FAILURE));
255                 if (retval = gss_check_token(minor_status, input_token,
256                                              GSS_API_KRB5_TYPE,
257                                              GSS_API_KRB5_REP))
258                         return(retval);
259                 inbuf.length = input_token->length-4;
260                 inbuf.data = ((char *)input_token->value)+4;
261                 
262                 if (*minor_status = krb5_rd_rep(&inbuf, context->session_key,
263                                                 &repl))
264                         return(gss_make_re(GSS_RE_FAILURE));
265                 if ((repl->ctime != context->ctime) ||
266                     (repl->cusec != context->cusec)) {
267                         *minor_status = KRB5_SENDAUTH_MUTUAL_FAILED;
268                         return(gss_make_re(GSS_RE_FAILURE));
269                 }
270                 context->his_seq_num = repl->seq_number;
271                 context->state = GSS_KRB_STATE_UP;
272                 krb5_free_ap_rep_enc_part(repl);
273                 return(GSS_S_COMPLETE);
274         }
275 }