Imported Upstream version 1.15.1
[platform/upstream/krb5.git] / src / kdc / kdc_preauth_ec.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* kdc/kdc_preauth_ec.c - Encrypted challenge kdcpreauth module */
3 /*
4  * Copyright (C) 2009 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/kdcpreauth_plugin.h>
34 #include "kdc_util.h"
35
36 static void
37 ec_edata(krb5_context context, krb5_kdc_req *request,
38          krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
39          krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type,
40          krb5_kdcpreauth_edata_respond_fn respond, void *arg)
41 {
42     krb5_keyblock *armor_key = cb->fast_armor(context, rock);
43
44     /* Encrypted challenge only works with FAST, and requires a client key. */
45     if (armor_key == NULL || !cb->have_client_keys(context, rock))
46         (*respond)(arg, ENOENT, NULL);
47     else
48         (*respond)(arg, 0, NULL);
49 }
50
51 static void
52 ec_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
53           krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *data,
54           krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
55           krb5_kdcpreauth_moddata moddata,
56           krb5_kdcpreauth_verify_respond_fn respond, void *arg)
57 {
58     krb5_error_code retval = 0;
59     krb5_timestamp now;
60     krb5_enc_data *enc = NULL;
61     krb5_data scratch, plain;
62     krb5_keyblock *armor_key = cb->fast_armor(context, rock);
63     krb5_pa_enc_ts *ts = NULL;
64     krb5_keyblock *client_keys = NULL;
65     krb5_keyblock *challenge_key = NULL;
66     krb5_keyblock *kdc_challenge_key;
67     krb5_kdcpreauth_modreq modreq = NULL;
68     int i = 0;
69
70     plain.data = NULL;
71
72     if (armor_key == NULL) {
73         retval = ENOENT;
74         k5_setmsg(context, ENOENT,
75                   _("Encrypted Challenge used outside of FAST tunnel"));
76     }
77     scratch.data = (char *) data->contents;
78     scratch.length = data->length;
79     if (retval == 0)
80         retval = decode_krb5_enc_data(&scratch, &enc);
81     if (retval == 0) {
82         plain.data =  malloc(enc->ciphertext.length);
83         plain.length = enc->ciphertext.length;
84         if (plain.data == NULL)
85             retval = ENOMEM;
86     }
87     if (retval == 0)
88         retval = cb->client_keys(context, rock, &client_keys);
89     if (retval == 0) {
90         for (i = 0; client_keys[i].enctype&& (retval == 0); i++ ) {
91             retval = krb5_c_fx_cf2_simple(context,
92                                           armor_key, "clientchallengearmor",
93                                           &client_keys[i], "challengelongterm",
94                                           &challenge_key);
95             if (retval == 0)
96                 retval  = krb5_c_decrypt(context, challenge_key,
97                                          KRB5_KEYUSAGE_ENC_CHALLENGE_CLIENT,
98                                          NULL, enc, &plain);
99             if (challenge_key)
100                 krb5_free_keyblock(context, challenge_key);
101             challenge_key = NULL;
102             if (retval == 0)
103                 break;
104             /*We failed to decrypt. Try next key*/
105             retval = 0;
106         }
107         if (client_keys[i].enctype == 0) {
108             retval = KRB5KDC_ERR_PREAUTH_FAILED;
109             k5_setmsg(context, retval,
110                       _("Incorrect password in encrypted challenge"));
111         }
112     }
113     if (retval == 0)
114         retval = decode_krb5_pa_enc_ts(&plain, &ts);
115     if (retval == 0)
116         retval = krb5_timeofday(context, &now);
117     if (retval == 0) {
118         if (labs(now-ts->patimestamp) < context->clockskew) {
119             enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
120             /*
121              * If this fails, we won't generate a reply to the client.  That
122              * may cause the client to fail, but at this point the KDC has
123              * considered this a success, so the return value is ignored.
124              */
125             if (krb5_c_fx_cf2_simple(context, armor_key, "kdcchallengearmor",
126                                      &client_keys[i], "challengelongterm",
127                                      &kdc_challenge_key) == 0)
128                 modreq = (krb5_kdcpreauth_modreq)kdc_challenge_key;
129         } else { /*skew*/
130             retval = KRB5KRB_AP_ERR_SKEW;
131         }
132     }
133     cb->free_keys(context, rock, client_keys);
134     if (plain.data)
135         free(plain.data);
136     if (enc)
137         krb5_free_enc_data(context, enc);
138     if (ts)
139         krb5_free_pa_enc_ts(context, ts);
140
141     (*respond)(arg, retval, modreq, NULL, NULL);
142 }
143
144 static krb5_error_code
145 ec_return(krb5_context context, krb5_pa_data *padata, krb5_data *req_pkt,
146           krb5_kdc_req *request, krb5_kdc_rep *reply,
147           krb5_keyblock *encrypting_key, krb5_pa_data **send_pa,
148           krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
149           krb5_kdcpreauth_moddata moddata, krb5_kdcpreauth_modreq modreq)
150 {
151     krb5_error_code retval = 0;
152     krb5_keyblock *challenge_key = (krb5_keyblock *)modreq;
153     krb5_pa_enc_ts ts;
154     krb5_data *plain = NULL;
155     krb5_enc_data enc;
156     krb5_data *encoded = NULL;
157     krb5_pa_data *pa = NULL;
158
159     if (challenge_key == NULL)
160         return 0;
161     enc.ciphertext.data = NULL; /* In case of error pass through */
162
163     retval = krb5_us_timeofday(context, &ts.patimestamp, &ts.pausec);
164     if (retval == 0)
165         retval = encode_krb5_pa_enc_ts(&ts, &plain);
166     if (retval == 0)
167         retval = krb5_encrypt_helper(context, challenge_key,
168                                      KRB5_KEYUSAGE_ENC_CHALLENGE_KDC,
169                                      plain, &enc);
170     if (retval == 0)
171         retval = encode_krb5_enc_data(&enc, &encoded);
172     if (retval == 0) {
173         pa = calloc(1, sizeof(krb5_pa_data));
174         if (pa == NULL)
175             retval = ENOMEM;
176     }
177     if (retval == 0) {
178         pa->pa_type = KRB5_PADATA_ENCRYPTED_CHALLENGE;
179         pa->contents = (unsigned char *) encoded->data;
180         pa->length = encoded->length;
181         encoded->data = NULL;
182         *send_pa = pa;
183         pa = NULL;
184     }
185     if (challenge_key)
186         krb5_free_keyblock(context, challenge_key);
187     if (encoded)
188         krb5_free_data(context, encoded);
189     if (plain)
190         krb5_free_data(context, plain);
191     if (enc.ciphertext.data)
192         krb5_free_data_contents(context, &enc.ciphertext);
193     return retval;
194 }
195
196 static krb5_preauthtype ec_types[] = {
197     KRB5_PADATA_ENCRYPTED_CHALLENGE, 0};
198
199 krb5_error_code
200 kdcpreauth_encrypted_challenge_initvt(krb5_context context, int maj_ver,
201                                       int min_ver, krb5_plugin_vtable vtable)
202 {
203     krb5_kdcpreauth_vtable vt;
204
205     if (maj_ver != 1)
206         return KRB5_PLUGIN_VER_NOTSUPP;
207     vt = (krb5_kdcpreauth_vtable)vtable;
208     vt->name = "encrypted_challenge";
209     vt->pa_type_list = ec_types;
210     vt->edata = ec_edata;
211     vt->verify = ec_verify;
212     vt->return_padata = ec_return;
213     return 0;
214 }