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 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 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 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 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 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 * 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 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 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 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 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