Coverage Report

Created: 2026-10-17 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/krb5/src/lib/krad/attr.c
Line
Count
Source
1
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
/* lib/krad/attr.c - RADIUS attribute 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 <k5-int.h>
31
#include "internal.h"
32
33
#include <string.h>
34
35
/* RFC 2865 */
36
165k
#define BLOCKSIZE 16
37
38
typedef krb5_error_code
39
(*attribute_transform_fn)(krb5_context ctx, const char *secret,
40
                          const unsigned char *auth, const krb5_data *in,
41
                          unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
42
43
typedef struct {
44
    const char *name;
45
    unsigned char minval;
46
    unsigned char maxval;
47
    attribute_transform_fn encode;
48
    attribute_transform_fn decode;
49
} attribute_record;
50
51
static krb5_error_code
52
user_password_encode(krb5_context ctx, const char *secret,
53
                     const unsigned char *auth, const krb5_data *in,
54
                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
55
56
static krb5_error_code
57
user_password_decode(krb5_context ctx, const char *secret,
58
                     const unsigned char *auth, const krb5_data *in,
59
                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
60
61
static const attribute_record attributes[UCHAR_MAX] = {
62
    {"User-Name", 1, MAX_ATTRSIZE, NULL, NULL},
63
    {"User-Password", 1, 128, user_password_encode, user_password_decode},
64
    {"CHAP-Password", 17, 17, NULL, NULL},
65
    {"NAS-IP-Address", 4, 4, NULL, NULL},
66
    {"NAS-Port", 4, 4, NULL, NULL},
67
    {"Service-Type", 4, 4, NULL, NULL},
68
    {"Framed-Protocol", 4, 4, NULL, NULL},
69
    {"Framed-IP-Address", 4, 4, NULL, NULL},
70
    {"Framed-IP-Netmask", 4, 4, NULL, NULL},
71
    {"Framed-Routing", 4, 4, NULL, NULL},
72
    {"Filter-Id", 1, MAX_ATTRSIZE, NULL, NULL},
73
    {"Framed-MTU", 4, 4, NULL, NULL},
74
    {"Framed-Compression", 4, 4, NULL, NULL},
75
    {"Login-IP-Host", 4, 4, NULL, NULL},
76
    {"Login-Service", 4, 4, NULL, NULL},
77
    {"Login-TCP-Port", 4, 4, NULL, NULL},
78
    {NULL, 0, 0, NULL, NULL}, /* Unassigned */
79
    {"Reply-Message", 1, MAX_ATTRSIZE, NULL, NULL},
80
    {"Callback-Number", 1, MAX_ATTRSIZE, NULL, NULL},
81
    {"Callback-Id", 1, MAX_ATTRSIZE, NULL, NULL},
82
    {NULL, 0, 0, NULL, NULL}, /* Unassigned */
83
    {"Framed-Route", 1, MAX_ATTRSIZE, NULL, NULL},
84
    {"Framed-IPX-Network", 4, 4, NULL, NULL},
85
    {"State", 1, MAX_ATTRSIZE, NULL, NULL},
86
    {"Class", 1, MAX_ATTRSIZE, NULL, NULL},
87
    {"Vendor-Specific", 5, MAX_ATTRSIZE, NULL, NULL},
88
    {"Session-Timeout", 4, 4, NULL, NULL},
89
    {"Idle-Timeout", 4, 4, NULL, NULL},
90
    {"Termination-Action", 4, 4, NULL, NULL},
91
    {"Called-Station-Id", 1, MAX_ATTRSIZE, NULL, NULL},
92
    {"Calling-Station-Id", 1, MAX_ATTRSIZE, NULL, NULL},
93
    {"NAS-Identifier", 1, MAX_ATTRSIZE, NULL, NULL},
94
    {"Proxy-State", 1, MAX_ATTRSIZE, NULL, NULL},
95
    {"Login-LAT-Service", 1, MAX_ATTRSIZE, NULL, NULL},
96
    {"Login-LAT-Node", 1, MAX_ATTRSIZE, NULL, NULL},
97
    {"Login-LAT-Group", 32, 32, NULL, NULL},
98
    {"Framed-AppleTalk-Link", 4, 4, NULL, NULL},
99
    {"Framed-AppleTalk-Network", 4, 4, NULL, NULL},
100
    {"Framed-AppleTalk-Zone", 1, MAX_ATTRSIZE, NULL, NULL},
101
    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
102
    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
103
    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
104
    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
105
    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
106
    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
107
    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
108
    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
109
    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
110
    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
111
    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
112
    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
113
    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
114
    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
115
    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
116
    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
117
    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
118
    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
119
    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
120
    {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
121
    {"CHAP-Challenge", 5, MAX_ATTRSIZE, NULL, NULL},
122
    {"NAS-Port-Type", 4, 4, NULL, NULL},
123
    {"Port-Limit", 4, 4, NULL, NULL},
124
    {"Login-LAT-Port", 1, MAX_ATTRSIZE, NULL, NULL},
125
    {NULL, 0, 0, NULL, NULL}, /* Reserved for tunnelling */
126
    {NULL, 0, 0, NULL, NULL}, /* Reserved for tunnelling */
127
    {NULL, 0, 0, NULL, NULL}, /* Reserved for tunnelling */
128
    {NULL, 0, 0, NULL, NULL}, /* Reserved for tunnelling */
129
    {NULL, 0, 0, NULL, NULL}, /* Reserved for tunnelling */
130
    {NULL, 0, 0, NULL, NULL}, /* Reserved for tunnelling */
131
    {NULL, 0, 0, NULL, NULL}, /* Reserved for Apple Remote Access Protocol */
132
    {NULL, 0, 0, NULL, NULL}, /* Reserved for Apple Remote Access Protocol */
133
    {NULL, 0, 0, NULL, NULL}, /* Reserved for Apple Remote Access Protocol */
134
    {NULL, 0, 0, NULL, NULL}, /* Reserved for Apple Remote Access Protocol */
135
    {NULL, 0, 0, NULL, NULL}, /* Reserved for Apple Remote Access Protocol */
136
    {NULL, 0, 0, NULL, NULL}, /* Password-Retry */
137
    {NULL, 0, 0, NULL, NULL}, /* Prompt */
138
    {NULL, 0, 0, NULL, NULL}, /* Connect-Info */
139
    {NULL, 0, 0, NULL, NULL}, /* Configuration-Token */
140
    {NULL, 0, 0, NULL, NULL}, /* EAP-Message */
141
    {"Message-Authenticator", MD5_DIGEST_SIZE, MD5_DIGEST_SIZE, NULL, NULL},
142
};
143
144
/* Encode User-Password attribute. */
145
static krb5_error_code
146
user_password_encode(krb5_context ctx, const char *secret,
147
                     const unsigned char *auth, const krb5_data *in,
148
                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
149
256
{
150
256
    const unsigned char *indx;
151
256
    krb5_error_code retval;
152
256
    unsigned int seclen;
153
256
    krb5_checksum sum;
154
256
    size_t blck, len, i;
155
256
    krb5_data tmp;
156
157
    /* Copy the input buffer to the (zero-padded) output buffer. */
158
256
    len = (in->length + BLOCKSIZE - 1) / BLOCKSIZE * BLOCKSIZE;
159
256
    if (len > MAX_ATTRSIZE)
160
0
        return ENOBUFS;
161
256
    memset(outbuf, 0, len);
162
256
    memcpy(outbuf, in->data, in->length);
163
164
    /* Create our temporary space for processing each block. */
165
256
    seclen = strlen(secret);
166
256
    retval = alloc_data(&tmp, seclen + BLOCKSIZE);
167
256
    if (retval != 0)
168
0
        return retval;
169
170
256
    memcpy(tmp.data, secret, seclen);
171
822
    for (blck = 0, indx = auth; blck * BLOCKSIZE < len; blck++) {
172
566
        memcpy(tmp.data + seclen, indx, BLOCKSIZE);
173
174
566
        retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &tmp,
175
566
                                      &sum);
176
566
        if (retval != 0) {
177
0
            zap(tmp.data, tmp.length);
178
0
            zap(outbuf, len);
179
0
            krb5_free_data_contents(ctx, &tmp);
180
0
            return retval;
181
0
        }
182
183
9.62k
        for (i = 0; i < BLOCKSIZE; i++)
184
9.05k
            outbuf[blck * BLOCKSIZE + i] ^= sum.contents[i];
185
566
        krb5_free_checksum_contents(ctx, &sum);
186
187
566
        indx = &outbuf[blck * BLOCKSIZE];
188
566
    }
189
190
256
    zap(tmp.data, tmp.length);
191
256
    krb5_free_data_contents(ctx, &tmp);
192
256
    *outlen = len;
193
256
    return 0;
194
256
}
195
196
/* Decode User-Password attribute. */
197
static krb5_error_code
198
user_password_decode(krb5_context ctx, const char *secret,
199
                     const unsigned char *auth, const krb5_data *in,
200
                     unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
201
1.28k
{
202
1.28k
    const unsigned char *indx;
203
1.28k
    krb5_error_code retval;
204
1.28k
    unsigned int seclen;
205
1.28k
    krb5_checksum sum;
206
1.28k
    ssize_t blck, i;
207
1.28k
    krb5_data tmp;
208
209
1.28k
    if (in->length % BLOCKSIZE != 0)
210
30
        return EINVAL;
211
1.25k
    if (in->length > MAX_ATTRSIZE)
212
0
        return ENOBUFS;
213
214
    /* Create our temporary space for processing each block. */
215
1.25k
    seclen = strlen(secret);
216
1.25k
    retval = alloc_data(&tmp, seclen + BLOCKSIZE);
217
1.25k
    if (retval != 0)
218
0
        return retval;
219
220
1.25k
    memcpy(tmp.data, secret, seclen);
221
3.94k
    for (blck = 0, indx = auth; blck * BLOCKSIZE < in->length; blck++) {
222
2.69k
        memcpy(tmp.data + seclen, indx, BLOCKSIZE);
223
224
2.69k
        retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0,
225
2.69k
                                      &tmp, &sum);
226
2.69k
        if (retval != 0) {
227
0
            zap(tmp.data, tmp.length);
228
0
            zap(outbuf, in->length);
229
0
            krb5_free_data_contents(ctx, &tmp);
230
0
            return retval;
231
0
        }
232
233
45.7k
        for (i = 0; i < BLOCKSIZE; i++) {
234
43.0k
            outbuf[blck * BLOCKSIZE + i] = in->data[blck * BLOCKSIZE + i] ^
235
43.0k
                sum.contents[i];
236
43.0k
        }
237
2.69k
        krb5_free_checksum_contents(ctx, &sum);
238
239
2.69k
        indx = (const unsigned char *)&in->data[blck * BLOCKSIZE];
240
2.69k
    }
241
242
    /* Strip off trailing NULL bytes. */
243
1.25k
    *outlen = in->length;
244
2.67k
    while (*outlen > 0 && outbuf[*outlen - 1] == '\0')
245
1.42k
        (*outlen)--;
246
247
1.25k
    krb5_free_data_contents(ctx, &tmp);
248
1.25k
    return 0;
249
1.25k
}
250
251
krb5_error_code
252
kr_attr_valid(krad_attr type, const krb5_data *data)
253
11.2k
{
254
11.2k
    const attribute_record *ar;
255
256
11.2k
    if (type == 0)
257
8
        return EINVAL;
258
259
11.2k
    ar = &attributes[type - 1];
260
11.2k
    return (data->length >= ar->minval && data->length <= ar->maxval) ? 0 :
261
11.2k
        EMSGSIZE;
262
11.2k
}
263
264
krb5_error_code
265
kr_attr_encode(krb5_context ctx, const char *secret,
266
               const unsigned char *auth, krad_attr type,
267
               const krb5_data *in, unsigned char outbuf[MAX_ATTRSIZE],
268
               size_t *outlen)
269
867
{
270
867
    krb5_error_code retval;
271
272
867
    retval = kr_attr_valid(type, in);
273
867
    if (retval != 0)
274
0
        return retval;
275
276
867
    if (attributes[type - 1].encode == NULL) {
277
611
        if (in->length > MAX_ATTRSIZE)
278
0
            return ENOBUFS;
279
280
611
        *outlen = in->length;
281
611
        memcpy(outbuf, in->data, in->length);
282
611
        return 0;
283
611
    }
284
285
256
    return attributes[type - 1].encode(ctx, secret, auth, in, outbuf, outlen);
286
867
}
287
288
krb5_error_code
289
kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
290
               krad_attr type, const krb5_data *in,
291
               unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
292
5.23k
{
293
5.23k
    krb5_error_code retval;
294
295
5.23k
    retval = kr_attr_valid(type, in);
296
5.23k
    if (retval != 0)
297
85
        return retval;
298
299
5.14k
    if (attributes[type - 1].encode == NULL) {
300
3.86k
        if (in->length > MAX_ATTRSIZE)
301
0
            return ENOBUFS;
302
303
3.86k
        *outlen = in->length;
304
3.86k
        memcpy(outbuf, in->data, in->length);
305
3.86k
        return 0;
306
3.86k
    }
307
308
1.28k
    return attributes[type - 1].decode(ctx, secret, auth, in, outbuf, outlen);
309
5.14k
}
310
311
krad_attr
312
krad_attr_name2num(const char *name)
313
0
{
314
0
    unsigned char i;
315
316
0
    for (i = 0; i < UCHAR_MAX; i++) {
317
0
        if (attributes[i].name == NULL)
318
0
            continue;
319
320
0
        if (strcmp(attributes[i].name, name) == 0)
321
0
            return i + 1;
322
0
    }
323
324
0
    return 0;
325
0
}
326
327
const char *
328
krad_attr_num2name(krad_attr type)
329
0
{
330
0
    if (type == 0)
331
0
        return NULL;
332
333
0
    return attributes[type - 1].name;
334
0
}