6a9c76ad9d483bd31d18ef8203da0b2d91d4438a
[krb5.git] / src / lib / krb5 / krb / preauth_ec.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/krb/preauth_ec.c - Encrypted Challenge clpreauth module */
3 /*
4  * Copyright (C) 2009, 2011 by the Massachusetts Institute of Technology.
5  * All rights reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26
27 /*
28  * Implement Encrypted Challenge fast factor from
29  * draft-ietf-krb-wg-preauth-framework
30  */
31
32 #include <k5-int.h>
33 #include <krb5/preauth_plugin.h>
34 #include "int-proto.h"
35
36 static int
37 preauth_flags(krb5_context context, krb5_preauthtype pa_type)
38 {
39     return PA_REAL;
40 }
41
42 static krb5_error_code
43 process_preauth(krb5_context context, krb5_clpreauth_moddata moddata,
44                 krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt,
45                 krb5_clpreauth_callbacks cb,
46                 krb5_clpreauth_rock rock, krb5_kdc_req *request,
47                 krb5_data *encoded_request_body,
48                 krb5_data *encoded_previous_request, krb5_pa_data *padata,
49                 krb5_prompter_fct prompter, void *prompter_data,
50                 krb5_clpreauth_get_as_key_fn gak_fct, void *gak_data,
51                 krb5_data *salt, krb5_data *s2kparams, krb5_keyblock *as_key,
52                 krb5_pa_data ***out_padata)
53 {
54     krb5_error_code retval = 0;
55     krb5_enctype enctype;
56     krb5_keyblock *challenge_key = NULL, *armor_key;
57
58     armor_key = cb->fast_armor(context, rock);
59     enctype = cb->get_etype(context, rock);
60     if (as_key->length == 0 ||as_key->enctype != enctype) {
61         retval = gak_fct(context, request->client,
62                          enctype, prompter, prompter_data,
63                          salt, s2kparams,
64                          as_key, gak_data);
65     }
66     if (retval == 0 && padata->length) {
67         krb5_enc_data *enc = NULL;
68         krb5_data scratch;
69         scratch.length = padata->length;
70         scratch.data = (char *) padata->contents;
71         retval = krb5_c_fx_cf2_simple(context,armor_key, "kdcchallengearmor",
72                                       as_key, "challengelongterm",
73                                       &challenge_key);
74         if (retval == 0)
75             retval = decode_krb5_enc_data(&scratch, &enc);
76         scratch.data = NULL;
77         if (retval == 0) {
78             scratch.data = malloc(enc->ciphertext.length);
79             scratch.length = enc->ciphertext.length;
80             if (scratch.data == NULL)
81                 retval = ENOMEM;
82         }
83         if (retval == 0)
84             retval = krb5_c_decrypt(context, challenge_key,
85                                     KRB5_KEYUSAGE_ENC_CHALLENGE_KDC, NULL,
86                                     enc, &scratch);
87         /*
88          * Per draft 11 of the preauth framework, the client MAY but is not
89          * required to actually check the timestamp from the KDC other than to
90          * confirm it decrypts. This code does not perform that check.
91          */
92         if (scratch.data)
93             krb5_free_data_contents(context, &scratch);
94         /* If we had a callback to assert that the KDC is verified, we would
95          * call it here. */
96         if (enc)
97             krb5_free_enc_data(context, enc);
98     } else if (retval == 0) { /*No padata; we send*/
99         krb5_enc_data enc;
100         krb5_pa_data *pa = NULL;
101         krb5_pa_data **pa_array = NULL;
102         krb5_data *encoded_ts = NULL;
103         krb5_pa_enc_ts ts;
104         enc.ciphertext.data = NULL;
105         retval = krb5_us_timeofday(context, &ts.patimestamp, &ts.pausec);
106         if (retval == 0)
107             retval = encode_krb5_pa_enc_ts(&ts, &encoded_ts);
108         if (retval == 0)
109             retval = krb5_c_fx_cf2_simple(context,
110                                           armor_key, "clientchallengearmor",
111                                           as_key, "challengelongterm",
112                                           &challenge_key);
113         if (retval == 0)
114             retval = krb5_encrypt_helper(context, challenge_key,
115                                          KRB5_KEYUSAGE_ENC_CHALLENGE_CLIENT,
116                                          encoded_ts, &enc);
117         if (encoded_ts)
118             krb5_free_data(context, encoded_ts);
119         encoded_ts = NULL;
120         if (retval == 0) {
121             retval = encode_krb5_enc_data(&enc, &encoded_ts);
122             krb5_free_data_contents(context, &enc.ciphertext);
123         }
124         if (retval == 0) {
125             pa = calloc(1, sizeof(krb5_pa_data));
126             if (pa == NULL)
127                 retval = ENOMEM;
128         }
129         if (retval == 0) {
130             pa_array = calloc(2, sizeof(krb5_pa_data *));
131             if (pa_array == NULL)
132                 retval = ENOMEM;
133         }
134         if (retval == 0) {
135             pa->length = encoded_ts->length;
136             pa->contents = (unsigned char *) encoded_ts->data;
137             pa->pa_type = KRB5_PADATA_ENCRYPTED_CHALLENGE;
138             free(encoded_ts);
139             encoded_ts = NULL;
140             pa_array[0] = pa;
141             pa = NULL;
142             *out_padata = pa_array;
143             pa_array = NULL;
144         }
145         if (pa)
146             free(pa);
147         if (encoded_ts)
148             krb5_free_data(context, encoded_ts);
149         if (pa_array)
150             free(pa_array);
151     }
152     if (challenge_key)
153         krb5_free_keyblock(context, challenge_key);
154     return retval;
155 }
156
157
158 krb5_preauthtype supported_pa_types[] = {
159     KRB5_PADATA_ENCRYPTED_CHALLENGE, 0};
160
161 krb5_error_code
162 clpreauth_encrypted_challenge_initvt(krb5_context context, int maj_ver,
163                                      int min_ver, krb5_plugin_vtable vtable)
164 {
165     krb5_clpreauth_vtable vt;
166
167     if (maj_ver != 1)
168         return KRB5_PLUGIN_VER_NOTSUPP;
169     vt = (krb5_clpreauth_vtable)vtable;
170     vt->name = "encrypted_challenge";
171     vt->pa_type_list = supported_pa_types;
172     vt->flags = preauth_flags;
173     vt->process = process_preauth;
174     return 0;
175 }