xref: /freebsd/crypto/krb5/src/lib/krad/attrset.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
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