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 #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 }; 126 127 /* Encode User-Password attribute. */ 128 static krb5_error_code 129 user_password_encode(krb5_context ctx, const char *secret, 130 const unsigned char *auth, const krb5_data *in, 131 unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen) 132 { 133 const unsigned char *indx; 134 krb5_error_code retval; 135 unsigned int seclen; 136 krb5_checksum sum; 137 size_t blck, len, i; 138 krb5_data tmp; 139 140 /* Copy the input buffer to the (zero-padded) output buffer. */ 141 len = (in->length + BLOCKSIZE - 1) / BLOCKSIZE * BLOCKSIZE; 142 if (len > MAX_ATTRSIZE) 143 return ENOBUFS; 144 memset(outbuf, 0, len); 145 memcpy(outbuf, in->data, in->length); 146 147 /* Create our temporary space for processing each block. */ 148 seclen = strlen(secret); 149 retval = alloc_data(&tmp, seclen + BLOCKSIZE); 150 if (retval != 0) 151 return retval; 152 153 memcpy(tmp.data, secret, seclen); 154 for (blck = 0, indx = auth; blck * BLOCKSIZE < len; blck++) { 155 memcpy(tmp.data + seclen, indx, BLOCKSIZE); 156 157 retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &tmp, 158 &sum); 159 if (retval != 0) { 160 zap(tmp.data, tmp.length); 161 zap(outbuf, len); 162 krb5_free_data_contents(ctx, &tmp); 163 return retval; 164 } 165 166 for (i = 0; i < BLOCKSIZE; i++) 167 outbuf[blck * BLOCKSIZE + i] ^= sum.contents[i]; 168 krb5_free_checksum_contents(ctx, &sum); 169 170 indx = &outbuf[blck * BLOCKSIZE]; 171 } 172 173 zap(tmp.data, tmp.length); 174 krb5_free_data_contents(ctx, &tmp); 175 *outlen = len; 176 return 0; 177 } 178 179 /* Decode User-Password attribute. */ 180 static krb5_error_code 181 user_password_decode(krb5_context ctx, const char *secret, 182 const unsigned char *auth, const krb5_data *in, 183 unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen) 184 { 185 const unsigned char *indx; 186 krb5_error_code retval; 187 unsigned int seclen; 188 krb5_checksum sum; 189 ssize_t blck, i; 190 krb5_data tmp; 191 192 if (in->length % BLOCKSIZE != 0) 193 return EINVAL; 194 if (in->length > MAX_ATTRSIZE) 195 return ENOBUFS; 196 197 /* Create our temporary space for processing each block. */ 198 seclen = strlen(secret); 199 retval = alloc_data(&tmp, seclen + BLOCKSIZE); 200 if (retval != 0) 201 return retval; 202 203 memcpy(tmp.data, secret, seclen); 204 for (blck = 0, indx = auth; blck * BLOCKSIZE < in->length; blck++) { 205 memcpy(tmp.data + seclen, indx, BLOCKSIZE); 206 207 retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, 208 &tmp, &sum); 209 if (retval != 0) { 210 zap(tmp.data, tmp.length); 211 zap(outbuf, in->length); 212 krb5_free_data_contents(ctx, &tmp); 213 return retval; 214 } 215 216 for (i = 0; i < BLOCKSIZE; i++) { 217 outbuf[blck * BLOCKSIZE + i] = in->data[blck * BLOCKSIZE + i] ^ 218 sum.contents[i]; 219 } 220 krb5_free_checksum_contents(ctx, &sum); 221 222 indx = (const unsigned char *)&in->data[blck * BLOCKSIZE]; 223 } 224 225 /* Strip off trailing NULL bytes. */ 226 *outlen = in->length; 227 while (*outlen > 0 && outbuf[*outlen - 1] == '\0') 228 (*outlen)--; 229 230 krb5_free_data_contents(ctx, &tmp); 231 return 0; 232 } 233 234 krb5_error_code 235 kr_attr_valid(krad_attr type, const krb5_data *data) 236 { 237 const attribute_record *ar; 238 239 if (type == 0) 240 return EINVAL; 241 242 ar = &attributes[type - 1]; 243 return (data->length >= ar->minval && data->length <= ar->maxval) ? 0 : 244 EMSGSIZE; 245 } 246 247 krb5_error_code 248 kr_attr_encode(krb5_context ctx, const char *secret, 249 const unsigned char *auth, krad_attr type, 250 const krb5_data *in, unsigned char outbuf[MAX_ATTRSIZE], 251 size_t *outlen) 252 { 253 krb5_error_code retval; 254 255 retval = kr_attr_valid(type, in); 256 if (retval != 0) 257 return retval; 258 259 if (attributes[type - 1].encode == NULL) { 260 if (in->length > MAX_ATTRSIZE) 261 return ENOBUFS; 262 263 *outlen = in->length; 264 memcpy(outbuf, in->data, in->length); 265 return 0; 266 } 267 268 return attributes[type - 1].encode(ctx, secret, auth, in, outbuf, outlen); 269 } 270 271 krb5_error_code 272 kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth, 273 krad_attr type, const krb5_data *in, 274 unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen) 275 { 276 krb5_error_code retval; 277 278 retval = kr_attr_valid(type, in); 279 if (retval != 0) 280 return retval; 281 282 if (attributes[type - 1].encode == NULL) { 283 if (in->length > MAX_ATTRSIZE) 284 return ENOBUFS; 285 286 *outlen = in->length; 287 memcpy(outbuf, in->data, in->length); 288 return 0; 289 } 290 291 return attributes[type - 1].decode(ctx, secret, auth, in, outbuf, outlen); 292 } 293 294 krad_attr 295 krad_attr_name2num(const char *name) 296 { 297 unsigned char i; 298 299 for (i = 0; i < UCHAR_MAX; i++) { 300 if (attributes[i].name == NULL) 301 continue; 302 303 if (strcmp(attributes[i].name, name) == 0) 304 return i + 1; 305 } 306 307 return 0; 308 } 309 310 const char * 311 krad_attr_num2name(krad_attr type) 312 { 313 if (type == 0) 314 return NULL; 315 316 return attributes[type - 1].name; 317 } 318