Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/lib/krad/packet.c
39536 views
1
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
/* lib/krad/packet.c - Packet functions for libkrad */
3
/*
4
* Copyright 2013 Red Hat, Inc. All rights reserved.
5
*
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions 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
14
* the documentation and/or other materials provided with the
15
* distribution.
16
*
17
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
*/
29
30
#include "internal.h"
31
32
#include <string.h>
33
34
#include <arpa/inet.h>
35
36
typedef unsigned char uchar;
37
38
/* RFC 2865 */
39
#define MSGAUTH_SIZE (2 + MD5_DIGEST_SIZE)
40
#define OFFSET_CODE 0
41
#define OFFSET_ID 1
42
#define OFFSET_LENGTH 2
43
#define OFFSET_AUTH 4
44
#define OFFSET_ATTR 20
45
#define AUTH_FIELD_SIZE (OFFSET_ATTR - OFFSET_AUTH)
46
47
#define offset(d, o) (&(d)->data[o])
48
#define pkt_code_get(p) (*(krad_code *)offset(&(p)->pkt, OFFSET_CODE))
49
#define pkt_code_set(p, v) (*(krad_code *)offset(&(p)->pkt, OFFSET_CODE)) = v
50
#define pkt_id_get(p) (*(uchar *)offset(&(p)->pkt, OFFSET_ID))
51
#define pkt_id_set(p, v) (*(uchar *)offset(&(p)->pkt, OFFSET_ID)) = v
52
#define pkt_len_get(p) load_16_be(offset(&(p)->pkt, OFFSET_LENGTH))
53
#define pkt_len_set(p, v) store_16_be(v, offset(&(p)->pkt, OFFSET_LENGTH))
54
#define pkt_auth(p) ((uchar *)offset(&(p)->pkt, OFFSET_AUTH))
55
#define pkt_attr(p) ((unsigned char *)offset(&(p)->pkt, OFFSET_ATTR))
56
57
struct krad_packet_st {
58
char buffer[KRAD_PACKET_SIZE_MAX];
59
krad_attrset *attrset;
60
krb5_data pkt;
61
};
62
63
typedef struct {
64
uchar x[(UCHAR_MAX + 1) / 8];
65
} idmap;
66
67
/* Ensure the map is empty. */
68
static inline void
69
idmap_init(idmap *map)
70
{
71
memset(map, 0, sizeof(*map));
72
}
73
74
/* Set an id as already allocated. */
75
static inline void
76
idmap_set(idmap *map, uchar id)
77
{
78
map->x[id / 8] |= 1 << (id % 8);
79
}
80
81
/* Determine whether or not an id is used. */
82
static inline krb5_boolean
83
idmap_isset(const idmap *map, uchar id)
84
{
85
return (map->x[id / 8] & (1 << (id % 8))) != 0;
86
}
87
88
/* Find an unused id starting the search at the value specified in id.
89
* NOTE: For optimal security, the initial value of id should be random. */
90
static inline krb5_error_code
91
idmap_find(const idmap *map, uchar *id)
92
{
93
krb5_int16 i;
94
95
for (i = *id; i >= 0 && i <= UCHAR_MAX; (*id % 2 == 0) ? i++ : i--) {
96
if (!idmap_isset(map, i))
97
goto success;
98
}
99
100
for (i = *id; i >= 0 && i <= UCHAR_MAX; (*id % 2 == 1) ? i++ : i--) {
101
if (!idmap_isset(map, i))
102
goto success;
103
}
104
105
return ERANGE;
106
107
success:
108
*id = i;
109
return 0;
110
}
111
112
/* Generate size bytes of random data into the buffer. */
113
static inline krb5_error_code
114
randomize(krb5_context ctx, void *buffer, unsigned int size)
115
{
116
krb5_data rdata = make_data(buffer, size);
117
return krb5_c_random_make_octets(ctx, &rdata);
118
}
119
120
/* Generate a radius packet id. */
121
static krb5_error_code
122
id_generate(krb5_context ctx, krad_packet_iter_cb cb, void *data, uchar *id)
123
{
124
krb5_error_code retval;
125
const krad_packet *tmp;
126
idmap used;
127
uchar i;
128
129
retval = randomize(ctx, &i, sizeof(i));
130
if (retval != 0) {
131
if (cb != NULL)
132
(*cb)(data, TRUE);
133
return retval;
134
}
135
136
if (cb != NULL) {
137
idmap_init(&used);
138
for (tmp = (*cb)(data, FALSE); tmp != NULL; tmp = (*cb)(data, FALSE))
139
idmap_set(&used, tmp->pkt.data[1]);
140
141
retval = idmap_find(&used, &i);
142
if (retval != 0)
143
return retval;
144
}
145
146
*id = i;
147
return 0;
148
}
149
150
/* Generate a random authenticator field. */
151
static krb5_error_code
152
auth_generate_random(krb5_context ctx, uchar *rauth)
153
{
154
krb5_ui_4 trunctime;
155
time_t currtime;
156
157
/* Get the least-significant four bytes of the current time. */
158
currtime = time(NULL);
159
if (currtime == (time_t)-1)
160
return errno;
161
trunctime = (krb5_ui_4)currtime;
162
memcpy(rauth, &trunctime, sizeof(trunctime));
163
164
/* Randomize the rest of the buffer. */
165
return randomize(ctx, rauth + sizeof(trunctime),
166
AUTH_FIELD_SIZE - sizeof(trunctime));
167
}
168
169
/* Generate a response authenticator field. */
170
static krb5_error_code
171
auth_generate_response(krb5_context ctx, const char *secret,
172
const krad_packet *response, const uchar *auth,
173
uchar *rauth)
174
{
175
krb5_error_code retval;
176
krb5_checksum hash;
177
krb5_data data;
178
179
/* Allocate the temporary buffer. */
180
retval = alloc_data(&data, response->pkt.length + strlen(secret));
181
if (retval != 0)
182
return retval;
183
184
/* Encoded RADIUS packet with the request's
185
* authenticator and the secret at the end. */
186
memcpy(data.data, response->pkt.data, response->pkt.length);
187
memcpy(data.data + OFFSET_AUTH, auth, AUTH_FIELD_SIZE);
188
memcpy(data.data + response->pkt.length, secret, strlen(secret));
189
190
/* Hash it. */
191
retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &data,
192
&hash);
193
free(data.data);
194
if (retval != 0)
195
return retval;
196
197
memcpy(rauth, hash.contents, AUTH_FIELD_SIZE);
198
krb5_free_checksum_contents(ctx, &hash);
199
return 0;
200
}
201
202
/* Create a new packet. */
203
static krad_packet *
204
packet_new(void)
205
{
206
krad_packet *pkt;
207
208
pkt = calloc(1, sizeof(krad_packet));
209
if (pkt == NULL)
210
return NULL;
211
pkt->pkt = make_data(pkt->buffer, sizeof(pkt->buffer));
212
213
return pkt;
214
}
215
216
/* Set the attrset object by decoding the packet. */
217
static krb5_error_code
218
packet_set_attrset(krb5_context ctx, const char *secret, krad_packet *pkt)
219
{
220
krb5_data tmp;
221
222
tmp = make_data(pkt_attr(pkt), pkt->pkt.length - OFFSET_ATTR);
223
return kr_attrset_decode(ctx, &tmp, secret, pkt_auth(pkt), &pkt->attrset);
224
}
225
226
/* Determine if a packet requires a Message-Authenticator attribute. */
227
static inline krb5_boolean
228
requires_msgauth(const char *secret, krad_code code)
229
{
230
/* If no secret is provided, assume that the transport is a UNIX socket.
231
* Message-Authenticator is required only on UDP and TCP connections. */
232
if (*secret == '\0')
233
return FALSE;
234
235
/*
236
* Per draft-ietf-radext-deprecating-radius-03 sections 5.2.1 and 5.2.4,
237
* Message-Authenticator is required in Access-Request packets and all
238
* potential responses when UDP or TCP transport is used.
239
*/
240
return code == KRAD_CODE_ACCESS_REQUEST ||
241
code == KRAD_CODE_ACCESS_ACCEPT || code == KRAD_CODE_ACCESS_REJECT ||
242
code == KRAD_CODE_ACCESS_CHALLENGE;
243
}
244
245
/* Check if the packet has a Message-Authenticator attribute. */
246
static inline krb5_boolean
247
has_pkt_msgauth(const krad_packet *pkt)
248
{
249
return krad_attrset_get(pkt->attrset, KRAD_ATTR_MESSAGE_AUTHENTICATOR,
250
0) != NULL;
251
}
252
253
/* Return the beginning of the Message-Authenticator attribute in pkt, or NULL
254
* if no such attribute is present. */
255
static const uint8_t *
256
lookup_msgauth_addr(const krad_packet *pkt)
257
{
258
size_t i;
259
uint8_t *p;
260
261
i = OFFSET_ATTR;
262
while (i + 2 < pkt->pkt.length) {
263
p = (uint8_t *)offset(&pkt->pkt, i);
264
if (*p == KRAD_ATTR_MESSAGE_AUTHENTICATOR)
265
return p;
266
i += p[1];
267
}
268
269
return NULL;
270
}
271
272
/*
273
* Calculate the message authenticator MAC for pkt as specified in RFC 2869
274
* section 5.14, placing the result in mac_out. Use the provided authenticator
275
* auth, which may be from pkt or from a corresponding request.
276
*/
277
static krb5_error_code
278
calculate_mac(const char *secret, const krad_packet *pkt,
279
const uint8_t auth[AUTH_FIELD_SIZE],
280
uint8_t mac_out[MD5_DIGEST_SIZE])
281
{
282
const uint8_t *msgauth_attr, *msgauth_end, *pkt_end;
283
krb5_crypto_iov input[5];
284
krb5_data ksecr, mac;
285
static const uint8_t zeroed_msgauth[MSGAUTH_SIZE] = {
286
KRAD_ATTR_MESSAGE_AUTHENTICATOR, MSGAUTH_SIZE
287
};
288
289
msgauth_attr = lookup_msgauth_addr(pkt);
290
if (msgauth_attr == NULL)
291
return EINVAL;
292
msgauth_end = msgauth_attr + MSGAUTH_SIZE;
293
pkt_end = (const uint8_t *)pkt->pkt.data + pkt->pkt.length;
294
295
/* Read code, id, and length from the packet. */
296
input[0].flags = KRB5_CRYPTO_TYPE_DATA;
297
input[0].data = make_data(pkt->pkt.data, OFFSET_AUTH);
298
299
/* Read the provided authenticator. */
300
input[1].flags = KRB5_CRYPTO_TYPE_DATA;
301
input[1].data = make_data((uint8_t *)auth, AUTH_FIELD_SIZE);
302
303
/* Read any attributes before Message-Authenticator. */
304
input[2].flags = KRB5_CRYPTO_TYPE_DATA;
305
input[2].data = make_data(pkt_attr(pkt), msgauth_attr - pkt_attr(pkt));
306
307
/* Read Message-Authenticator with the data bytes all set to zero, per RFC
308
* 2869 section 5.14. */
309
input[3].flags = KRB5_CRYPTO_TYPE_DATA;
310
input[3].data = make_data((uint8_t *)zeroed_msgauth, MSGAUTH_SIZE);
311
312
/* Read any attributes after Message-Authenticator. */
313
input[4].flags = KRB5_CRYPTO_TYPE_DATA;
314
input[4].data = make_data((uint8_t *)msgauth_end, pkt_end - msgauth_end);
315
316
mac = make_data(mac_out, MD5_DIGEST_SIZE);
317
ksecr = string2data((char *)secret);
318
return k5_hmac_md5(&ksecr, input, 5, &mac);
319
}
320
321
ssize_t
322
krad_packet_bytes_needed(const krb5_data *buffer)
323
{
324
size_t len;
325
326
if (buffer->length < OFFSET_AUTH)
327
return OFFSET_AUTH - buffer->length;
328
329
len = load_16_be(offset(buffer, OFFSET_LENGTH));
330
if (len > KRAD_PACKET_SIZE_MAX)
331
return -1;
332
333
return (buffer->length > len) ? 0 : len - buffer->length;
334
}
335
336
void
337
krad_packet_free(krad_packet *pkt)
338
{
339
if (pkt)
340
krad_attrset_free(pkt->attrset);
341
free(pkt);
342
}
343
344
/* Create a new request packet. */
345
krb5_error_code
346
krad_packet_new_request(krb5_context ctx, const char *secret, krad_code code,
347
const krad_attrset *set, krad_packet_iter_cb cb,
348
void *data, krad_packet **request)
349
{
350
krb5_error_code retval;
351
krad_packet *pkt;
352
uchar id;
353
size_t attrset_len;
354
krb5_boolean msgauth_required;
355
356
pkt = packet_new();
357
if (pkt == NULL) {
358
if (cb != NULL)
359
(*cb)(data, TRUE);
360
return ENOMEM;
361
}
362
363
/* Generate the ID. */
364
retval = id_generate(ctx, cb, data, &id);
365
if (retval != 0)
366
goto error;
367
pkt_id_set(pkt, id);
368
369
/* Generate the authenticator. */
370
retval = auth_generate_random(ctx, pkt_auth(pkt));
371
if (retval != 0)
372
goto error;
373
374
/* Determine if Message-Authenticator is required. */
375
msgauth_required = (*secret != '\0' && code == KRAD_CODE_ACCESS_REQUEST);
376
377
/* Encode the attributes. */
378
retval = kr_attrset_encode(set, secret, pkt_auth(pkt), msgauth_required,
379
pkt_attr(pkt), &attrset_len);
380
if (retval != 0)
381
goto error;
382
383
/* Set the code, ID and length. */
384
pkt->pkt.length = attrset_len + OFFSET_ATTR;
385
pkt_code_set(pkt, code);
386
pkt_len_set(pkt, pkt->pkt.length);
387
388
if (msgauth_required) {
389
/* Calculate and set the Message-Authenticator MAC. */
390
retval = calculate_mac(secret, pkt, pkt_auth(pkt), pkt_attr(pkt) + 2);
391
if (retval != 0)
392
goto error;
393
}
394
395
/* Copy the attrset for future use. */
396
retval = packet_set_attrset(ctx, secret, pkt);
397
if (retval != 0)
398
goto error;
399
400
*request = pkt;
401
return 0;
402
403
error:
404
free(pkt);
405
return retval;
406
}
407
408
/* Create a new request packet. */
409
krb5_error_code
410
krad_packet_new_response(krb5_context ctx, const char *secret, krad_code code,
411
const krad_attrset *set, const krad_packet *request,
412
krad_packet **response)
413
{
414
krb5_error_code retval;
415
krad_packet *pkt;
416
size_t attrset_len;
417
krb5_boolean msgauth_required;
418
419
pkt = packet_new();
420
if (pkt == NULL)
421
return ENOMEM;
422
423
/* Determine if Message-Authenticator is required. */
424
msgauth_required = requires_msgauth(secret, code);
425
426
/* Encode the attributes. */
427
retval = kr_attrset_encode(set, secret, pkt_auth(request),
428
msgauth_required, pkt_attr(pkt), &attrset_len);
429
if (retval != 0)
430
goto error;
431
432
/* Set the code, ID and length. */
433
pkt->pkt.length = attrset_len + OFFSET_ATTR;
434
pkt_code_set(pkt, code);
435
pkt_id_set(pkt, pkt_id_get(request));
436
pkt_len_set(pkt, pkt->pkt.length);
437
438
/* Generate the authenticator. */
439
retval = auth_generate_response(ctx, secret, pkt, pkt_auth(request),
440
pkt_auth(pkt));
441
if (retval != 0)
442
goto error;
443
444
if (msgauth_required) {
445
/*
446
* Calculate and replace the Message-Authenticator MAC. Per RFC 2869
447
* section 5.14, use the authenticator from the request, not from the
448
* response.
449
*/
450
retval = calculate_mac(secret, pkt, pkt_auth(request),
451
pkt_attr(pkt) + 2);
452
if (retval != 0)
453
goto error;
454
}
455
456
/* Copy the attrset for future use. */
457
retval = packet_set_attrset(ctx, secret, pkt);
458
if (retval != 0)
459
goto error;
460
461
*response = pkt;
462
return 0;
463
464
error:
465
free(pkt);
466
return retval;
467
}
468
469
/* Verify the Message-Authenticator value in pkt, using the provided
470
* authenticator (which may be from pkt or from a corresponding request). */
471
static krb5_error_code
472
verify_msgauth(const char *secret, const krad_packet *pkt,
473
const uint8_t auth[AUTH_FIELD_SIZE])
474
{
475
uint8_t mac[MD5_DIGEST_SIZE];
476
const krb5_data *msgauth;
477
krb5_error_code retval;
478
479
msgauth = krad_packet_get_attr(pkt, KRAD_ATTR_MESSAGE_AUTHENTICATOR, 0);
480
/* XXX ENODATA does not exist in FreeBSD. The closest thing we have to */
481
/* XXX ENODATA is ENOATTR. We use that instead. */
482
#define ENODATA ENOATTR
483
if (msgauth == NULL)
484
return ENODATA;
485
486
retval = calculate_mac(secret, pkt, auth, mac);
487
if (retval)
488
return retval;
489
490
if (msgauth->length != MD5_DIGEST_SIZE)
491
return EMSGSIZE;
492
493
if (k5_bcmp(mac, msgauth->data, MD5_DIGEST_SIZE) != 0)
494
return EBADMSG;
495
496
return 0;
497
}
498
499
/* Decode a packet. */
500
static krb5_error_code
501
decode_packet(krb5_context ctx, const char *secret, const krb5_data *buffer,
502
krad_packet **pkt)
503
{
504
krb5_error_code retval;
505
krad_packet *tmp;
506
krb5_ui_2 len;
507
508
tmp = packet_new();
509
if (tmp == NULL) {
510
retval = ENOMEM;
511
goto error;
512
}
513
514
/* Ensure a proper message length. */
515
retval = (buffer->length < OFFSET_ATTR) ? EMSGSIZE : 0;
516
if (retval != 0)
517
goto error;
518
len = load_16_be(offset(buffer, OFFSET_LENGTH));
519
retval = (len < OFFSET_ATTR) ? EBADMSG : 0;
520
if (retval != 0)
521
goto error;
522
retval = (len > buffer->length || len > tmp->pkt.length) ? EBADMSG : 0;
523
if (retval != 0)
524
goto error;
525
526
/* Copy over the buffer. */
527
tmp->pkt.length = len;
528
memcpy(tmp->pkt.data, buffer->data, len);
529
530
/* Parse the packet to ensure it is well-formed. */
531
retval = packet_set_attrset(ctx, secret, tmp);
532
if (retval != 0)
533
goto error;
534
535
*pkt = tmp;
536
return 0;
537
538
error:
539
krad_packet_free(tmp);
540
return retval;
541
}
542
543
krb5_error_code
544
krad_packet_decode_request(krb5_context ctx, const char *secret,
545
const krb5_data *buffer, krad_packet_iter_cb cb,
546
void *data, const krad_packet **duppkt,
547
krad_packet **reqpkt)
548
{
549
const krad_packet *tmp = NULL;
550
krad_packet *req;
551
krb5_error_code retval;
552
553
retval = decode_packet(ctx, secret, buffer, &req);
554
if (retval)
555
return retval;
556
557
/* Verify Message-Authenticator if present. */
558
if (has_pkt_msgauth(req)) {
559
retval = verify_msgauth(secret, req, pkt_auth(req));
560
if (retval) {
561
krad_packet_free(req);
562
return retval;
563
}
564
}
565
566
if (cb != NULL) {
567
for (tmp = (*cb)(data, FALSE); tmp != NULL; tmp = (*cb)(data, FALSE)) {
568
if (pkt_id_get(*reqpkt) == pkt_id_get(tmp))
569
break;
570
}
571
572
if (tmp != NULL)
573
(*cb)(data, TRUE);
574
}
575
576
*reqpkt = req;
577
*duppkt = tmp;
578
return 0;
579
}
580
581
krb5_error_code
582
krad_packet_decode_response(krb5_context ctx, const char *secret,
583
const krb5_data *buffer, krad_packet_iter_cb cb,
584
void *data, const krad_packet **reqpkt,
585
krad_packet **rsppkt)
586
{
587
uchar auth[AUTH_FIELD_SIZE];
588
const krad_packet *tmp = NULL;
589
krb5_error_code retval;
590
591
retval = decode_packet(ctx, secret, buffer, rsppkt);
592
if (cb != NULL && retval == 0) {
593
for (tmp = (*cb)(data, FALSE); tmp != NULL; tmp = (*cb)(data, FALSE)) {
594
if (pkt_id_get(*rsppkt) != pkt_id_get(tmp))
595
continue;
596
597
/* Response */
598
retval = auth_generate_response(ctx, secret, *rsppkt,
599
pkt_auth(tmp), auth);
600
if (retval != 0) {
601
krad_packet_free(*rsppkt);
602
break;
603
}
604
605
/* Verify the response authenticator. */
606
if (k5_bcmp(pkt_auth(*rsppkt), auth, sizeof(auth)) != 0)
607
continue;
608
609
/* Verify Message-Authenticator if present. */
610
if (has_pkt_msgauth(*rsppkt)) {
611
if (verify_msgauth(secret, *rsppkt, pkt_auth(tmp)) != 0)
612
continue;
613
}
614
615
break;
616
}
617
}
618
619
if (cb != NULL && (retval != 0 || tmp != NULL))
620
(*cb)(data, TRUE);
621
622
*reqpkt = tmp;
623
return retval;
624
}
625
626
const krb5_data *
627
krad_packet_encode(const krad_packet *pkt)
628
{
629
return &pkt->pkt;
630
}
631
632
krad_code
633
krad_packet_get_code(const krad_packet *pkt)
634
{
635
if (pkt == NULL)
636
return 0;
637
638
return pkt_code_get(pkt);
639
}
640
641
const krb5_data *
642
krad_packet_get_attr(const krad_packet *pkt, krad_attr type, size_t indx)
643
{
644
return krad_attrset_get(pkt->attrset, type, indx);
645
}
646
647