Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/plugins/preauth/securid_sam2/grail.c
34889 views
1
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
/* plugins/preauth/securid_sam2/grail.c - Test method for SAM-2 preauth */
3
/*
4
* Copyright (C) 2012 by the Massachusetts Institute of Technology.
5
* All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
*
11
* * Redistributions of source code must retain the above copyright
12
* notice, this list of conditions and the following disclaimer.
13
*
14
* * Redistributions in binary form must reproduce the above copyright
15
* notice, this list of conditions and the following disclaimer in
16
* the documentation and/or other materials provided with the
17
* distribution.
18
*
19
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30
* OF THE POSSIBILITY OF SUCH DAMAGE.
31
*/
32
33
/*
34
* This test method exists to exercise the client SAM-2 code and some of the
35
* KDC SAM-2 code. We make up a weakly random number and presents it to the
36
* client in the prompt (in plain text), as well as encrypted in the track ID.
37
* To verify, we compare the decrypted track ID to the entered value.
38
*
39
* Do not use this method in production; it is not secure.
40
*/
41
42
#ifdef GRAIL_PREAUTH
43
44
#include "k5-int.h"
45
#include <kdb.h>
46
#include <adm_proto.h>
47
#include <ctype.h>
48
#include "extern.h"
49
50
static krb5_error_code
51
get_grail_key(krb5_context context, krb5_db_entry *client,
52
krb5_keyblock *key_out)
53
{
54
krb5_db_entry *grail_entry = NULL;
55
krb5_key_data *kd;
56
int sam_type = PA_SAM_TYPE_GRAIL;
57
krb5_error_code ret = 0;
58
59
ret = sam_get_db_entry(context, client->princ, &sam_type, &grail_entry);
60
if (ret)
61
return KRB5_PREAUTH_NO_KEY;
62
ret = krb5_dbe_find_enctype(context, grail_entry, -1, -1, -1, &kd);
63
if (ret)
64
goto cleanup;
65
ret = krb5_dbe_decrypt_key_data(context, NULL, kd, key_out, NULL);
66
if (ret)
67
goto cleanup;
68
69
cleanup:
70
if (grail_entry)
71
krb5_db_free_principal(context, grail_entry);
72
return ret;
73
}
74
75
static krb5_error_code
76
decrypt_track_data(krb5_context context, krb5_db_entry *client,
77
krb5_data *enc_track_data, krb5_data *output)
78
{
79
krb5_error_code ret;
80
krb5_keyblock sam_key;
81
krb5_enc_data enc;
82
krb5_data result = empty_data();
83
84
sam_key.contents = NULL;
85
*output = empty_data();
86
87
ret = get_grail_key(context, client, &sam_key);
88
if (ret != 0)
89
return ret;
90
enc.ciphertext = *enc_track_data;
91
enc.enctype = ENCTYPE_UNKNOWN;
92
enc.kvno = 0;
93
ret = alloc_data(&result, enc_track_data->length);
94
if (ret)
95
goto cleanup;
96
ret = krb5_c_decrypt(context, &sam_key,
97
KRB5_KEYUSAGE_PA_SAM_CHALLENGE_TRACKID, 0, &enc,
98
&result);
99
if (ret)
100
goto cleanup;
101
102
*output = result;
103
result = empty_data();
104
105
cleanup:
106
krb5_free_keyblock_contents(context, &sam_key);
107
krb5_free_data_contents(context, &result);
108
return ret;
109
}
110
111
static krb5_error_code
112
encrypt_track_data(krb5_context context, krb5_db_entry *client,
113
krb5_data *track_data, krb5_data *output)
114
{
115
krb5_error_code ret;
116
size_t olen;
117
krb5_keyblock sam_key;
118
krb5_enc_data enc;
119
120
*output = empty_data();
121
enc.ciphertext = empty_data();
122
sam_key.contents = NULL;
123
124
ret = get_grail_key(context, client, &sam_key);
125
if (ret != 0)
126
return ret;
127
128
ret = krb5_c_encrypt_length(context, sam_key.enctype,
129
track_data->length, &olen);
130
if (ret != 0)
131
goto cleanup;
132
assert(olen <= 65536);
133
ret = alloc_data(&enc.ciphertext, olen);
134
if (ret)
135
goto cleanup;
136
enc.enctype = sam_key.enctype;
137
enc.kvno = 0;
138
139
ret = krb5_c_encrypt(context, &sam_key,
140
KRB5_KEYUSAGE_PA_SAM_CHALLENGE_TRACKID, 0,
141
track_data, &enc);
142
if (ret)
143
goto cleanup;
144
145
*output = enc.ciphertext;
146
enc.ciphertext = empty_data();
147
148
cleanup:
149
krb5_free_keyblock_contents(context, &sam_key);
150
krb5_free_data_contents(context, &enc.ciphertext);
151
return ret;
152
}
153
154
krb5_error_code
155
get_grail_edata(krb5_context context, krb5_db_entry *client,
156
krb5_keyblock *client_key, krb5_sam_challenge_2 *sc2_out)
157
{
158
krb5_error_code ret;
159
krb5_data tmp_data, track_id = empty_data();
160
int tval = time(NULL) % 77777;
161
krb5_sam_challenge_2_body sc2b;
162
char tval_string[256], prompt[256];
163
164
snprintf(tval_string, sizeof(tval_string), "%d", tval);
165
snprintf(prompt, sizeof(prompt), "Enter %d", tval);
166
167
memset(&sc2b, 0, sizeof(sc2b));
168
sc2b.magic = KV5M_SAM_CHALLENGE_2;
169
sc2b.sam_track_id = empty_data();
170
sc2b.sam_flags = KRB5_SAM_SEND_ENCRYPTED_SAD;
171
sc2b.sam_type_name = empty_data();
172
sc2b.sam_challenge_label = empty_data();
173
sc2b.sam_challenge = empty_data();
174
sc2b.sam_response_prompt = string2data(prompt);
175
sc2b.sam_pk_for_sad = empty_data();
176
sc2b.sam_type = PA_SAM_TYPE_GRAIL;
177
sc2b.sam_etype = client_key->enctype;
178
179
tmp_data = string2data(tval_string);
180
ret = encrypt_track_data(context, client, &tmp_data, &track_id);
181
if (ret)
182
goto cleanup;
183
sc2b.sam_track_id = track_id;
184
185
tmp_data = make_data(&sc2b.sam_nonce, sizeof(sc2b.sam_nonce));
186
ret = krb5_c_random_make_octets(context, &tmp_data);
187
if (ret)
188
goto cleanup;
189
190
ret = sam_make_challenge(context, &sc2b, client_key, sc2_out);
191
192
cleanup:
193
krb5_free_data_contents(context, &track_id);
194
return ret;
195
}
196
197
krb5_error_code
198
verify_grail_data(krb5_context context, krb5_db_entry *client,
199
krb5_sam_response_2 *sr2, krb5_enc_tkt_part *enc_tkt_reply,
200
krb5_pa_data *pa, krb5_sam_challenge_2 **sc2_out)
201
{
202
krb5_error_code ret;
203
krb5_key_data *client_key_data = NULL;
204
krb5_keyblock client_key;
205
krb5_data scratch = empty_data(), track_id_data = empty_data();
206
krb5_enc_sam_response_enc_2 *esre2 = NULL;
207
208
*sc2_out = NULL;
209
memset(&client_key, 0, sizeof(client_key));
210
211
if ((sr2->sam_enc_nonce_or_sad.ciphertext.data == NULL) ||
212
(sr2->sam_enc_nonce_or_sad.ciphertext.length <= 0))
213
return KRB5KDC_ERR_PREAUTH_FAILED;
214
215
ret = krb5_dbe_find_enctype(context, client,
216
sr2->sam_enc_nonce_or_sad.enctype, -1,
217
sr2->sam_enc_nonce_or_sad.kvno,
218
&client_key_data);
219
if (ret)
220
goto cleanup;
221
222
ret = krb5_dbe_decrypt_key_data(context, NULL, client_key_data,
223
&client_key, NULL);
224
if (ret)
225
goto cleanup;
226
ret = alloc_data(&scratch, sr2->sam_enc_nonce_or_sad.ciphertext.length);
227
if (ret)
228
goto cleanup;
229
ret = krb5_c_decrypt(context, &client_key, KRB5_KEYUSAGE_PA_SAM_RESPONSE,
230
NULL, &sr2->sam_enc_nonce_or_sad, &scratch);
231
if (ret)
232
goto cleanup;
233
234
ret = decode_krb5_enc_sam_response_enc_2(&scratch, &esre2);
235
if (ret)
236
goto cleanup;
237
238
if (sr2->sam_nonce != esre2->sam_nonce) {
239
ret = KRB5KDC_ERR_PREAUTH_FAILED;
240
goto cleanup;
241
}
242
243
if (esre2->sam_sad.length == 0 || esre2->sam_sad.data == NULL) {
244
ret = KRB5KDC_ERR_PREAUTH_FAILED;
245
goto cleanup;
246
}
247
248
ret = decrypt_track_data(context, client, &sr2->sam_track_id,
249
&track_id_data);
250
if (ret)
251
goto cleanup;
252
253
/* Some enctypes aren't length-preserving; try to work anyway. */
254
while (track_id_data.length > 0 &&
255
!isdigit(track_id_data.data[track_id_data.length - 1]))
256
track_id_data.length--;
257
258
if (!data_eq(track_id_data, esre2->sam_sad)) {
259
ret = KRB5KDC_ERR_PREAUTH_FAILED;
260
goto cleanup;
261
}
262
263
enc_tkt_reply->flags |= (TKT_FLG_HW_AUTH | TKT_FLG_PRE_AUTH);
264
265
cleanup:
266
krb5_free_keyblock_contents(context, &client_key);
267
krb5_free_data_contents(context, &scratch);
268
krb5_free_enc_sam_response_enc_2(context, esre2);
269
return ret;
270
}
271
272
#endif /* GRAIL_PREAUTH */
273
274