1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krad/attrset.c - RADIUS attribute set 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 <k5-queue.h>
32 #include "internal.h"
33
34 #include <string.h>
35
36 K5_TAILQ_HEAD(attr_head, attr_st);
37
38 typedef struct attr_st attr;
39 struct attr_st {
40 K5_TAILQ_ENTRY(attr_st) list;
41 krad_attr type;
42 krb5_data attr;
43 char buffer[MAX_ATTRSIZE];
44 };
45
46 struct krad_attrset_st {
47 krb5_context ctx;
48 struct attr_head list;
49 };
50
51 krb5_error_code
krad_attrset_new(krb5_context ctx,krad_attrset ** set)52 krad_attrset_new(krb5_context ctx, krad_attrset **set)
53 {
54 krad_attrset *tmp;
55
56 tmp = calloc(1, sizeof(krad_attrset));
57 if (tmp == NULL)
58 return ENOMEM;
59 tmp->ctx = ctx;
60 K5_TAILQ_INIT(&tmp->list);
61
62 *set = tmp;
63 return 0;
64 }
65
66 void
krad_attrset_free(krad_attrset * set)67 krad_attrset_free(krad_attrset *set)
68 {
69 attr *a;
70
71 if (set == NULL)
72 return;
73
74 while (!K5_TAILQ_EMPTY(&set->list)) {
75 a = K5_TAILQ_FIRST(&set->list);
76 K5_TAILQ_REMOVE(&set->list, a, list);
77 zap(a->buffer, sizeof(a->buffer));
78 free(a);
79 }
80
81 free(set);
82 }
83
84 krb5_error_code
krad_attrset_add(krad_attrset * set,krad_attr type,const krb5_data * data)85 krad_attrset_add(krad_attrset *set, krad_attr type, const krb5_data *data)
86 {
87 krb5_error_code retval;
88 attr *tmp;
89
90 retval = kr_attr_valid(type, data);
91 if (retval != 0)
92 return retval;
93
94 tmp = calloc(1, sizeof(attr));
95 if (tmp == NULL)
96 return ENOMEM;
97
98 tmp->type = type;
99 tmp->attr = make_data(tmp->buffer, data->length);
100 memcpy(tmp->attr.data, data->data, data->length);
101
102 K5_TAILQ_INSERT_TAIL(&set->list, tmp, list);
103 return 0;
104 }
105
106 krb5_error_code
krad_attrset_add_number(krad_attrset * set,krad_attr type,krb5_ui_4 num)107 krad_attrset_add_number(krad_attrset *set, krad_attr type, krb5_ui_4 num)
108 {
109 krb5_data data;
110
111 num = htonl(num);
112 data = make_data(&num, sizeof(num));
113 return krad_attrset_add(set, type, &data);
114 }
115
116 void
krad_attrset_del(krad_attrset * set,krad_attr type,size_t indx)117 krad_attrset_del(krad_attrset *set, krad_attr type, size_t indx)
118 {
119 attr *a;
120
121 K5_TAILQ_FOREACH(a, &set->list, list) {
122 if (a->type == type && indx-- == 0) {
123 K5_TAILQ_REMOVE(&set->list, a, list);
124 zap(a->buffer, sizeof(a->buffer));
125 free(a);
126 return;
127 }
128 }
129 }
130
131 const krb5_data *
krad_attrset_get(const krad_attrset * set,krad_attr type,size_t indx)132 krad_attrset_get(const krad_attrset *set, krad_attr type, size_t indx)
133 {
134 attr *a;
135
136 K5_TAILQ_FOREACH(a, &set->list, list) {
137 if (a->type == type && indx-- == 0)
138 return &a->attr;
139 }
140
141 return NULL;
142 }
143
144 krb5_error_code
krad_attrset_copy(const krad_attrset * set,krad_attrset ** copy)145 krad_attrset_copy(const krad_attrset *set, krad_attrset **copy)
146 {
147 krb5_error_code retval;
148 krad_attrset *tmp;
149 attr *a;
150
151 retval = krad_attrset_new(set->ctx, &tmp);
152 if (retval != 0)
153 return retval;
154
155 K5_TAILQ_FOREACH(a, &set->list, list) {
156 retval = krad_attrset_add(tmp, a->type, &a->attr);
157 if (retval != 0) {
158 krad_attrset_free(tmp);
159 return retval;
160 }
161 }
162
163 *copy = tmp;
164 return 0;
165 }
166
167 /* Place an encoded attributes into outbuf at position *i. Increment *i by the
168 * length of the encoding. */
169 static krb5_error_code
append_attr(krb5_context ctx,const char * secret,const uint8_t * auth,krad_attr type,const krb5_data * data,uint8_t outbuf[MAX_ATTRSETSIZE],size_t * i)170 append_attr(krb5_context ctx, const char *secret,
171 const uint8_t *auth, krad_attr type, const krb5_data *data,
172 uint8_t outbuf[MAX_ATTRSETSIZE], size_t *i)
173 {
174 uint8_t buffer[MAX_ATTRSIZE];
175 size_t attrlen;
176 krb5_error_code retval;
177
178 retval = kr_attr_encode(ctx, secret, auth, type, data, buffer, &attrlen);
179 if (retval)
180 return retval;
181
182 if (attrlen > MAX_ATTRSETSIZE - *i - 2)
183 return EMSGSIZE;
184
185 outbuf[(*i)++] = type;
186 outbuf[(*i)++] = attrlen + 2;
187 memcpy(outbuf + *i, buffer, attrlen);
188 *i += attrlen;
189
190 return 0;
191 }
192
193 krb5_error_code
kr_attrset_encode(const krad_attrset * set,const char * secret,const uint8_t * auth,krb5_boolean add_msgauth,unsigned char outbuf[MAX_ATTRSETSIZE],size_t * outlen)194 kr_attrset_encode(const krad_attrset *set, const char *secret,
195 const uint8_t *auth, krb5_boolean add_msgauth,
196 unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen)
197 {
198 krb5_error_code retval;
199 const uint8_t zeroes[MD5_DIGEST_SIZE] = { 0 };
200 krb5_data zerodata;
201 size_t i = 0;
202 attr *a;
203
204 if (set == NULL) {
205 *outlen = 0;
206 return 0;
207 }
208
209 if (add_msgauth) {
210 /* Encode Message-Authenticator as the first attribute, per
211 * draft-ietf-radext-deprecating-radius-03 section 5.2. */
212 zerodata = make_data((uint8_t *)zeroes, MD5_DIGEST_SIZE);
213 retval = append_attr(set->ctx, secret, auth,
214 KRAD_ATTR_MESSAGE_AUTHENTICATOR, &zerodata,
215 outbuf, &i);
216 if (retval)
217 return retval;
218 }
219
220 K5_TAILQ_FOREACH(a, &set->list, list) {
221 retval = append_attr(set->ctx, secret, auth, a->type, &a->attr,
222 outbuf, &i);
223 if (retval)
224 return retval;
225 }
226
227 *outlen = i;
228 return 0;
229 }
230
231 krb5_error_code
kr_attrset_decode(krb5_context ctx,const krb5_data * in,const char * secret,const unsigned char * auth,krad_attrset ** set_out)232 kr_attrset_decode(krb5_context ctx, const krb5_data *in, const char *secret,
233 const unsigned char *auth, krad_attrset **set_out)
234 {
235 unsigned char buffer[MAX_ATTRSIZE];
236 krb5_data tmp;
237 krb5_error_code retval;
238 krad_attr type;
239 krad_attrset *set;
240 size_t i, len;
241
242 *set_out = NULL;
243
244 retval = krad_attrset_new(ctx, &set);
245 if (retval != 0)
246 return retval;
247
248 for (i = 0; i + 2 < in->length; ) {
249 type = in->data[i++];
250 tmp = make_data(&in->data[i + 1], (uint8_t)in->data[i] - 2);
251 i += tmp.length + 1;
252
253 retval = (in->length < i) ? EBADMSG : 0;
254 if (retval != 0)
255 goto cleanup;
256
257 retval = kr_attr_decode(ctx, secret, auth, type, &tmp, buffer, &len);
258 if (retval != 0)
259 goto cleanup;
260
261 tmp = make_data(buffer, len);
262 retval = krad_attrset_add(set, type, &tmp);
263 if (retval != 0)
264 goto cleanup;
265 }
266
267 *set_out = set;
268 set = NULL;
269
270 cleanup:
271 zap(buffer, sizeof(buffer));
272 krad_attrset_free(set);
273 return retval;
274 }
275