Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/plugins/preauth/otp/main.c
34907 views
1
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
/* plugins/preauth/otp/main.c - OTP kdcpreauth module definition */
3
/*
4
* Copyright 2011 NORDUnet A/S. All rights reserved.
5
* Copyright 2013 Red Hat, Inc. 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 are met:
9
*
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
*
13
* 2. Redistributions in binary form must reproduce the above copyright
14
* notice, this list of conditions and the following disclaimer in
15
* the documentation and/or other materials provided with the
16
* distribution.
17
*
18
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
19
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
21
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
*/
30
31
#include "k5-int.h"
32
#include "k5-json.h"
33
#include <krb5/preauth_plugin.h>
34
#include "otp_state.h"
35
36
#include <errno.h>
37
#include <ctype.h>
38
39
static krb5_preauthtype otp_pa_type_list[] =
40
{ KRB5_PADATA_OTP_REQUEST, 0 };
41
42
struct request_state {
43
krb5_context context;
44
krb5_kdcpreauth_verify_respond_fn respond;
45
void *arg;
46
krb5_enc_tkt_part *enc_tkt_reply;
47
krb5_kdcpreauth_callbacks preauth_cb;
48
krb5_kdcpreauth_rock rock;
49
};
50
51
static krb5_error_code
52
decrypt_encdata(krb5_context context, krb5_keyblock *armor_key,
53
krb5_pa_otp_req *req, krb5_data *out)
54
{
55
krb5_error_code retval;
56
krb5_data plaintext;
57
58
if (req == NULL)
59
return EINVAL;
60
61
retval = alloc_data(&plaintext, req->enc_data.ciphertext.length);
62
if (retval)
63
return retval;
64
65
retval = krb5_c_decrypt(context, armor_key, KRB5_KEYUSAGE_PA_OTP_REQUEST,
66
NULL, &req->enc_data, &plaintext);
67
if (retval != 0) {
68
com_err("otp", retval, "Unable to decrypt encData in PA-OTP-REQUEST");
69
free(plaintext.data);
70
return retval;
71
}
72
73
*out = plaintext;
74
return 0;
75
}
76
77
static krb5_error_code
78
nonce_verify(krb5_context ctx, krb5_keyblock *armor_key,
79
const krb5_data *nonce)
80
{
81
krb5_error_code retval;
82
krb5_timestamp ts;
83
krb5_data *er = NULL;
84
85
if (armor_key == NULL || nonce->data == NULL) {
86
retval = EINVAL;
87
goto out;
88
}
89
90
/* Decode the PA-OTP-ENC-REQUEST structure. */
91
retval = decode_krb5_pa_otp_enc_req(nonce, &er);
92
if (retval != 0)
93
goto out;
94
95
/* Make sure the nonce is exactly the same size as the one generated. */
96
if (er->length != armor_key->length + sizeof(krb5_timestamp))
97
goto out;
98
99
/* Check to make sure the timestamp at the beginning is still valid. */
100
ts = load_32_be(er->data);
101
retval = krb5_check_clockskew(ctx, ts);
102
103
out:
104
krb5_free_data(ctx, er);
105
return retval;
106
}
107
108
static krb5_error_code
109
timestamp_verify(krb5_context ctx, const krb5_data *nonce)
110
{
111
krb5_error_code retval = EINVAL;
112
krb5_pa_enc_ts *et = NULL;
113
114
if (nonce->data == NULL)
115
goto out;
116
117
/* Decode the PA-ENC-TS-ENC structure. */
118
retval = decode_krb5_pa_enc_ts(nonce, &et);
119
if (retval != 0)
120
goto out;
121
122
/* Check the clockskew. */
123
retval = krb5_check_clockskew(ctx, et->patimestamp);
124
125
out:
126
krb5_free_pa_enc_ts(ctx, et);
127
return retval;
128
}
129
130
static krb5_error_code
131
nonce_generate(krb5_context ctx, unsigned int length, krb5_data *nonce_out)
132
{
133
krb5_data nonce;
134
krb5_error_code retval;
135
krb5_timestamp now;
136
137
retval = krb5_timeofday(ctx, &now);
138
if (retval != 0)
139
return retval;
140
141
retval = alloc_data(&nonce, sizeof(now) + length);
142
if (retval != 0)
143
return retval;
144
145
retval = krb5_c_random_make_octets(ctx, &nonce);
146
if (retval != 0) {
147
free(nonce.data);
148
return retval;
149
}
150
151
store_32_be(now, nonce.data);
152
*nonce_out = nonce;
153
return 0;
154
}
155
156
static void
157
on_response(void *data, krb5_error_code retval, otp_response response,
158
char *const *indicators)
159
{
160
struct request_state rs = *(struct request_state *)data;
161
krb5_context context = rs.context;
162
krb5_keyblock *armor_key;
163
char *const *ind;
164
165
free(data);
166
167
if (retval == 0 && response != otp_response_success)
168
retval = KRB5_PREAUTH_FAILED;
169
if (retval)
170
goto done;
171
172
rs.enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
173
armor_key = rs.preauth_cb->fast_armor(context, rs.rock);
174
if (armor_key == NULL) {
175
retval = ENOENT;
176
goto done;
177
}
178
179
retval = rs.preauth_cb->replace_reply_key(context, rs.rock, armor_key,
180
FALSE);
181
if (retval)
182
goto done;
183
184
for (ind = indicators; ind != NULL && *ind != NULL; ind++) {
185
retval = rs.preauth_cb->add_auth_indicator(context, rs.rock, *ind);
186
if (retval)
187
goto done;
188
}
189
190
done:
191
rs.respond(rs.arg, retval, NULL, NULL, NULL);
192
}
193
194
static krb5_error_code
195
otp_init(krb5_context context, krb5_kdcpreauth_moddata *moddata_out,
196
const char **realmnames)
197
{
198
krb5_error_code retval;
199
otp_state *state;
200
201
retval = otp_state_new(context, &state);
202
if (retval)
203
return retval;
204
*moddata_out = (krb5_kdcpreauth_moddata)state;
205
return 0;
206
}
207
208
static void
209
otp_fini(krb5_context context, krb5_kdcpreauth_moddata moddata)
210
{
211
otp_state_free((otp_state *)moddata);
212
}
213
214
static int
215
otp_flags(krb5_context context, krb5_preauthtype pa_type)
216
{
217
return PA_REPLACES_KEY;
218
}
219
220
static void
221
otp_edata(krb5_context context, krb5_kdc_req *request,
222
krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
223
krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type,
224
krb5_kdcpreauth_edata_respond_fn respond, void *arg)
225
{
226
krb5_otp_tokeninfo ti, *tis[2] = { &ti, NULL };
227
krb5_keyblock *armor_key = NULL;
228
krb5_pa_otp_challenge chl;
229
krb5_pa_data *pa = NULL;
230
krb5_error_code retval;
231
krb5_data *encoding, nonce = empty_data();
232
char *config;
233
234
/* Determine if otp is enabled for the user. */
235
retval = cb->get_string(context, rock, "otp", &config);
236
if (retval == 0 && config == NULL)
237
retval = ENOENT;
238
if (retval != 0)
239
goto out;
240
cb->free_string(context, rock, config);
241
242
/* Get the armor key. This indicates the length of random data to use in
243
* the nonce. */
244
armor_key = cb->fast_armor(context, rock);
245
if (armor_key == NULL) {
246
retval = ENOENT;
247
goto out;
248
}
249
250
/* Build the (mostly empty) challenge. */
251
memset(&ti, 0, sizeof(ti));
252
memset(&chl, 0, sizeof(chl));
253
chl.tokeninfo = tis;
254
ti.format = -1;
255
ti.length = -1;
256
ti.iteration_count = -1;
257
258
/* Generate the nonce. */
259
retval = nonce_generate(context, armor_key->length, &nonce);
260
if (retval != 0)
261
goto out;
262
chl.nonce = nonce;
263
264
/* Build the output pa-data. */
265
retval = encode_krb5_pa_otp_challenge(&chl, &encoding);
266
if (retval != 0)
267
goto out;
268
pa = k5alloc(sizeof(krb5_pa_data), &retval);
269
if (pa == NULL) {
270
krb5_free_data(context, encoding);
271
goto out;
272
}
273
pa->pa_type = KRB5_PADATA_OTP_CHALLENGE;
274
pa->contents = (krb5_octet *)encoding->data;
275
pa->length = encoding->length;
276
free(encoding);
277
278
out:
279
krb5_free_data_contents(context, &nonce);
280
(*respond)(arg, retval, pa);
281
}
282
283
static void
284
otp_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
285
krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *pa,
286
krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
287
krb5_kdcpreauth_moddata moddata,
288
krb5_kdcpreauth_verify_respond_fn respond, void *arg)
289
{
290
krb5_keyblock *armor_key = NULL;
291
krb5_pa_otp_req *req = NULL;
292
struct request_state *rs;
293
krb5_error_code retval;
294
krb5_data d, plaintext;
295
char *config;
296
297
/* Get the FAST armor key. */
298
armor_key = cb->fast_armor(context, rock);
299
if (armor_key == NULL) {
300
retval = KRB5KDC_ERR_PREAUTH_FAILED;
301
com_err("otp", retval, "No armor key found when verifying padata");
302
goto error;
303
}
304
305
/* Decode the request. */
306
d = make_data(pa->contents, pa->length);
307
retval = decode_krb5_pa_otp_req(&d, &req);
308
if (retval != 0) {
309
com_err("otp", retval, "Unable to decode OTP request");
310
goto error;
311
}
312
313
/* Decrypt the nonce from the request. */
314
retval = decrypt_encdata(context, armor_key, req, &plaintext);
315
if (retval != 0) {
316
com_err("otp", retval, "Unable to decrypt nonce");
317
goto error;
318
}
319
320
/* Verify the nonce or timestamp. */
321
retval = nonce_verify(context, armor_key, &plaintext);
322
if (retval != 0)
323
retval = timestamp_verify(context, &plaintext);
324
krb5_free_data_contents(context, &plaintext);
325
if (retval != 0) {
326
com_err("otp", retval, "Unable to verify nonce or timestamp");
327
goto error;
328
}
329
330
/* Create the request state. Save the response callback, and the
331
* enc_tkt_reply pointer so we can set the TKT_FLG_PRE_AUTH flag later. */
332
rs = k5alloc(sizeof(struct request_state), &retval);
333
if (rs == NULL)
334
goto error;
335
rs->context = context;
336
rs->arg = arg;
337
rs->respond = respond;
338
rs->enc_tkt_reply = enc_tkt_reply;
339
rs->preauth_cb = cb;
340
rs->rock = rock;
341
342
/* Get the principal's OTP configuration string. */
343
retval = cb->get_string(context, rock, "otp", &config);
344
if (retval == 0 && config == NULL)
345
retval = KRB5_PREAUTH_FAILED;
346
if (retval != 0) {
347
free(rs);
348
goto error;
349
}
350
351
/* Send the request. */
352
otp_state_verify((otp_state *)moddata, cb->event_context(context, rock),
353
cb->client_name(context, rock), config, req, on_response,
354
rs);
355
cb->free_string(context, rock, config);
356
357
k5_free_pa_otp_req(context, req);
358
return;
359
360
error:
361
k5_free_pa_otp_req(context, req);
362
(*respond)(arg, retval, NULL, NULL, NULL);
363
}
364
365
krb5_error_code
366
kdcpreauth_otp_initvt(krb5_context context, int maj_ver, int min_ver,
367
krb5_plugin_vtable vtable);
368
369
krb5_error_code
370
kdcpreauth_otp_initvt(krb5_context context, int maj_ver, int min_ver,
371
krb5_plugin_vtable vtable)
372
{
373
krb5_kdcpreauth_vtable vt;
374
375
if (maj_ver != 1)
376
return KRB5_PLUGIN_VER_NOTSUPP;
377
378
vt = (krb5_kdcpreauth_vtable)vtable;
379
vt->name = "otp";
380
vt->pa_type_list = otp_pa_type_list;
381
vt->init = otp_init;
382
vt->fini = otp_fini;
383
vt->flags = otp_flags;
384
vt->edata = otp_edata;
385
vt->verify = otp_verify;
386
387
com_err("otp", 0, "Loaded");
388
389
return 0;
390
}
391
392