some work on fast reauth
[mech_eap.git] / util_reauth.c
1 /*
2  * Copyright (c) 2010, JANET(UK)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of JANET(UK) nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32
33 #include "gssapiP_eap.h"
34
35 /*
36  * Fast reauthentication support for EAP GSS.
37  */
38
39 #define KRB5_AUTHDATA_RADIUS_AVP        513
40
41 static krb5_error_code
42 getAcceptorKey(krb5_context krbContext,
43                gss_ctx_id_t ctx,
44                gss_cred_id_t cred,
45                krb5_principal *princ,
46                krb5_keyblock *key)
47 {
48     krb5_error_code code;
49     krb5_keytab keytab = NULL;
50     krb5_keytab_entry ktent;
51     krb5_kt_cursor cursor = NULL;
52
53     *princ = NULL;
54     memset(key, 0, sizeof(*key));
55     memset(&ktent, 0, sizeof(ktent));
56
57     code = krb5_kt_default(krbContext, &keytab);
58     if (code != 0)
59         goto cleanup;
60
61     if (cred != GSS_C_NO_CREDENTIAL && cred->name != GSS_C_NO_NAME) {
62         code = krb5_kt_get_entry(krbContext, keytab,
63                                  cred->name->krbPrincipal, 0, 
64                                  ctx->encryptionType, &ktent);
65         if (code != 0)
66             goto cleanup;
67     } else {
68         code = krb5_kt_start_seq_get(krbContext, keytab, &cursor);
69         if (code != 0)
70             goto cleanup;
71
72         while ((code = krb5_kt_next_entry(krbContext, keytab,
73                                           &ktent, &cursor)) == 0) {
74             if (ktent.key.enctype != ctx->encryptionType) {
75                 krb5_free_keytab_entry_contents(krbContext, &ktent);
76                 continue;
77             }
78         }
79     }
80
81     code = krb5_copy_principal(krbContext, ktent.principal, princ);
82     if (code != 0)
83         goto cleanup;
84
85     code = krb5_copy_keyblock_contents(krbContext, &ktent.key, key);
86     if (code != 0)
87         goto cleanup;
88
89 cleanup:
90     if (cred == GSS_C_NO_CREDENTIAL || cred->name == GSS_C_NO_NAME)
91         krb5_kt_end_seq_get(krbContext, keytab, &cursor);
92
93     krb5_free_keytab_entry_contents(krbContext, &ktent);
94     krb5_kt_end_seq_get(krbContext, keytab, &cursor);
95     krb5_kt_close(krbContext, keytab);
96
97     if (code != 0) {
98         if (*princ != NULL) {
99             krb5_free_principal(krbContext, *princ);
100             *princ = NULL;
101         }
102         krb5_free_keyblock_contents(krbContext, key),
103         memset(key, 0, sizeof(key));
104     }
105
106     return code; 
107 }
108
109 static OM_uint32
110 makeReauthCreds(OM_uint32 *minor,
111                 gss_ctx_id_t ctx,
112                 gss_cred_id_t cred,
113                 gss_buffer_t credBuf)
114 {
115     OM_uint32 major = GSS_S_COMPLETE, code;
116     krb5_context krbContext = NULL;
117     krb5_ticket ticket = { 0 };
118     krb5_keyblock session, acceptorKey = { 0 };
119     krb5_enc_tkt_part enc_part = { 0 };
120     gss_buffer_desc attrBuf = GSS_C_EMPTY_BUFFER;
121     krb5_authdata *authData[2], authDatum = { 0 };
122     krb5_data *ticketData = NULL, *credsData = NULL;
123     krb5_creds creds = { 0 };
124     krb5_auth_context authContext = NULL;
125  
126     credBuf->length = 0;
127     credBuf->value = NULL;
128  
129     GSSEAP_KRB_INIT(&krbContext);
130
131     code = getAcceptorKey(krbContext, ctx, cred,
132                           &ticket.server, &acceptorKey);
133     if (code != 0)
134         goto cleanup;
135
136     enc_part.flags = TKT_FLG_INITIAL;
137
138     code = krb5_c_make_random_key(krbContext, ctx->encryptionType,
139                                   &session);
140     if (code != 0)
141         goto cleanup;
142
143     enc_part.session = &session;
144     enc_part.client = ctx->initiatorName->krbPrincipal;
145     enc_part.times.authtime = time(NULL);
146     enc_part.times.starttime = enc_part.times.authtime;
147     enc_part.times.endtime = ctx->expiryTime
148                              ? ctx->expiryTime
149                              : KRB5_INT32_MAX;
150     enc_part.times.renew_till = 0;
151
152     major = gssEapExportAttrContext(minor, ctx->initiatorName,
153                                     &attrBuf);
154     if (GSS_ERROR(major))
155         goto cleanup;
156
157     authDatum.ad_type = KRB5_AUTHDATA_RADIUS_AVP;
158     authDatum.length = attrBuf.length;
159     authDatum.contents = attrBuf.value;
160     authData[0] = &authDatum;
161     authData[1] = NULL;
162     enc_part.authorization_data = authData;
163
164     ticket.enc_part2 = &enc_part;
165
166     code = encode_krb5_ticket(&ticket, &ticketData);
167     if (code != 0)
168         goto cleanup;
169
170     creds.client = enc_part.client;
171     creds.server = ticket.server;
172     creds.keyblock = session;
173     creds.times = enc_part.times;
174     creds.ticket_flags = enc_part.flags;
175     creds.ticket = *ticketData;
176     creds.authdata = authData;
177
178     code = krb5_auth_con_init(krbContext, &authContext);
179     if (code != 0)
180         goto cleanup;
181
182     code = krb5_auth_con_setsendsubkey(krbContext, authContext, &ctx->rfc3961Key);
183     if (code != 0)
184         goto cleanup;
185
186     code = krb5_mk_1cred(krbContext, authContext, &creds, &credsData, NULL);
187     if (code != 0)
188         goto cleanup;
189
190     krbDataToGssBuffer(credsData, credBuf);
191
192     code = krb5_encrypt_tkt_part(krbContext, acceptorKey, &ticket);
193     if (code != 0)
194         goto cleanup;
195
196 cleanup:
197     *minor = code;
198
199     if (ticket.enc_part.ciphertext.data != NULL)
200         GSSEAP_FREE(ticket.enc_part.ciphertext.data);
201
202     krb5_free_keyblock_contents(krbContext, &session);
203     krb5_free_keyblock_contents(krbContext, &acceptorKey);
204     gss_release_buffer(&code, &attrBuf);
205     krb5_free_data(krbContext, ticketData);
206     krb5_auth_con_free(krbContext, authContext);
207     if (credsData != NULL)
208         GSSEAP_FREE(credsData);
209
210     if (major == GSS_S_COMPLETE)
211         major = *minor ? GSS_S_FAILURE : GSS_S_COMPLETE;
212
213     return major;
214 }
215
216 static OM_uint32
217 storeReauthCreds(OM_uint32 *minor,
218                  gss_ctx_id_t ctx,
219                  gss_cred_id_t cred,
220                  gss_buffer_t credBuf)
221 {
222     OM_uint32 major = GSS_S_COMPLETE, code;
223     krb5_context krbContext = NULL;
224     krb5_auth_context authContext = NULL;
225     krb5_data credData = { 0 };
226     krb5_creds **creds = NULL;
227     int i;
228
229     if (credBuf->length == 0 || cred == GSS_C_NO_CREDENTIAL)
230         return GSS_S_COMPLETE;
231
232     GSSEAP_KRB_INIT(&krbContext);
233
234     code = krb5_auth_con_init(krbContext, &authContext);
235     if (code != 0)
236         goto cleanup;
237
238     code = krb5_auth_con_setrecvsubkey(krbContext, authContext,
239                                        &ctx->rfc3961Key);
240     if (code != 0)
241         goto cleanup;
242
243     gssBufferToKrbData(credBuf, &credData);
244
245     code = krb5_rd_cred(krbContext, authContext, &credData, &creds, NULL);
246     if (code != 0)
247         goto cleanup;
248
249
250
251 /*
252 OM_uint32 KRB5_CALLCONV
253 gss_krb5_import_cred(OM_uint32 *minor_status,
254                      krb5_ccache id,
255                      krb5_principal keytab_principal,
256                      krb5_keytab keytab,
257                      gss_cred_id_t *cred);
258 */
259
260     if (creds != NULL && creds[0] != NULL) {
261     }    
262
263 cleanup:
264     *minor = code;
265
266     krb5_auth_con_free(krbContext, authContext);
267     if (creds != NULL) {
268         for (i = 0; creds[i] != NULL; i++)
269             krb5_free_creds(krbContext, creds[i]);
270     }
271     if (major == GSS_S_COMPLETE)
272         major = *minor ? GSS_S_FAILURE : GSS_S_COMPLETE;
273
274     return major;
275 }