chap.c revision 274866
1/*- 2 * Copyright (c) 2014 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Edward Tomasz Napierala under sponsorship 6 * from the FreeBSD Foundation. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 */ 30 31#include <sys/cdefs.h> 32__FBSDID("$FreeBSD: stable/10/usr.sbin/iscsid/chap.c 274866 2014-11-22 17:39:39Z trasz $"); 33 34#include <assert.h> 35#include <string.h> 36#include <openssl/err.h> 37#include <openssl/md5.h> 38#include <openssl/rand.h> 39 40#include "iscsid.h" 41 42static void 43chap_compute_md5(const char id, const char *secret, 44 const void *challenge, size_t challenge_len, void *response, 45 size_t response_len) 46{ 47 MD5_CTX ctx; 48 int rv; 49 50 assert(response_len == MD5_DIGEST_LENGTH); 51 52 MD5_Init(&ctx); 53 MD5_Update(&ctx, &id, sizeof(id)); 54 MD5_Update(&ctx, secret, strlen(secret)); 55 MD5_Update(&ctx, challenge, challenge_len); 56 rv = MD5_Final(response, &ctx); 57 if (rv != 1) 58 log_errx(1, "MD5_Final"); 59} 60 61static int 62chap_hex2int(const char hex) 63{ 64 switch (hex) { 65 case '0': 66 return (0x00); 67 case '1': 68 return (0x01); 69 case '2': 70 return (0x02); 71 case '3': 72 return (0x03); 73 case '4': 74 return (0x04); 75 case '5': 76 return (0x05); 77 case '6': 78 return (0x06); 79 case '7': 80 return (0x07); 81 case '8': 82 return (0x08); 83 case '9': 84 return (0x09); 85 case 'a': 86 case 'A': 87 return (0x0a); 88 case 'b': 89 case 'B': 90 return (0x0b); 91 case 'c': 92 case 'C': 93 return (0x0c); 94 case 'd': 95 case 'D': 96 return (0x0d); 97 case 'e': 98 case 'E': 99 return (0x0e); 100 case 'f': 101 case 'F': 102 return (0x0f); 103 default: 104 return (-1); 105 } 106} 107 108/* 109 * XXX: Review this _carefully_. 110 */ 111static int 112chap_hex2bin(const char *hex, void **binp, size_t *bin_lenp) 113{ 114 int i, hex_len, nibble; 115 bool lo = true; /* As opposed to 'hi'. */ 116 char *bin; 117 size_t bin_off, bin_len; 118 119 if (strncasecmp(hex, "0x", strlen("0x")) != 0) { 120 log_warnx("malformed variable, should start with \"0x\""); 121 return (-1); 122 } 123 124 hex += strlen("0x"); 125 hex_len = strlen(hex); 126 if (hex_len < 1) { 127 log_warnx("malformed variable; doesn't contain anything " 128 "but \"0x\""); 129 return (-1); 130 } 131 132 bin_len = hex_len / 2 + hex_len % 2; 133 bin = calloc(bin_len, 1); 134 if (bin == NULL) 135 log_err(1, "calloc"); 136 137 bin_off = bin_len - 1; 138 for (i = hex_len - 1; i >= 0; i--) { 139 nibble = chap_hex2int(hex[i]); 140 if (nibble < 0) { 141 log_warnx("malformed variable, invalid char \"%c\"", 142 hex[i]); 143 free(bin); 144 return (-1); 145 } 146 147 assert(bin_off < bin_len); 148 if (lo) { 149 bin[bin_off] = nibble; 150 lo = false; 151 } else { 152 bin[bin_off] |= nibble << 4; 153 bin_off--; 154 lo = true; 155 } 156 } 157 158 *binp = bin; 159 *bin_lenp = bin_len; 160 return (0); 161} 162 163static char * 164chap_bin2hex(const char *bin, size_t bin_len) 165{ 166 unsigned char *hex, *tmp, ch; 167 size_t hex_len; 168 size_t i; 169 170 hex_len = bin_len * 2 + 3; /* +2 for "0x", +1 for '\0'. */ 171 hex = malloc(hex_len); 172 if (hex == NULL) 173 log_err(1, "malloc"); 174 175 tmp = hex; 176 tmp += sprintf(tmp, "0x"); 177 for (i = 0; i < bin_len; i++) { 178 ch = bin[i]; 179 tmp += sprintf(tmp, "%02x", ch); 180 } 181 182 return (hex); 183} 184 185struct chap * 186chap_new(void) 187{ 188 struct chap *chap; 189 int rv; 190 191 chap = calloc(sizeof(*chap), 1); 192 if (chap == NULL) 193 log_err(1, "calloc"); 194 195 /* 196 * Generate the challenge. 197 */ 198 rv = RAND_bytes(chap->chap_challenge, sizeof(chap->chap_challenge)); 199 if (rv != 1) { 200 log_errx(1, "RAND_bytes failed: %s", 201 ERR_error_string(ERR_get_error(), NULL)); 202 } 203 rv = RAND_bytes(&chap->chap_id, sizeof(chap->chap_id)); 204 if (rv != 1) { 205 log_errx(1, "RAND_bytes failed: %s", 206 ERR_error_string(ERR_get_error(), NULL)); 207 } 208 209 return (chap); 210} 211 212char * 213chap_get_id(const struct chap *chap) 214{ 215 char *chap_i; 216 int ret; 217 218 ret = asprintf(&chap_i, "%d", chap->chap_id); 219 if (ret < 0) 220 log_err(1, "asprintf"); 221 222 return (chap_i); 223} 224 225char * 226chap_get_challenge(const struct chap *chap) 227{ 228 char *chap_c; 229 230 chap_c = chap_bin2hex(chap->chap_challenge, 231 sizeof(chap->chap_challenge)); 232 233 return (chap_c); 234} 235 236static int 237chap_receive_bin(struct chap *chap, void *response, size_t response_len) 238{ 239 240 if (response_len != sizeof(chap->chap_response)) { 241 log_debugx("got CHAP response with invalid length; " 242 "got %zd, should be %zd", 243 response_len, sizeof(chap->chap_response)); 244 return (1); 245 } 246 247 memcpy(chap->chap_response, response, response_len); 248 return (0); 249} 250 251int 252chap_receive(struct chap *chap, const char *response) 253{ 254 void *response_bin; 255 size_t response_bin_len; 256 int error; 257 258 error = chap_hex2bin(response, &response_bin, &response_bin_len); 259 if (error != 0) { 260 log_debugx("got incorrectly encoded CHAP response \"%s\"", 261 response); 262 return (1); 263 } 264 265 error = chap_receive_bin(chap, response_bin, response_bin_len); 266 free(response_bin); 267 268 return (error); 269} 270 271int 272chap_authenticate(struct chap *chap, const char *secret) 273{ 274 char expected_response[MD5_DIGEST_LENGTH]; 275 276 chap_compute_md5(chap->chap_id, secret, 277 chap->chap_challenge, sizeof(chap->chap_challenge), 278 expected_response, sizeof(expected_response)); 279 280 if (memcmp(chap->chap_response, 281 expected_response, sizeof(expected_response)) != 0) { 282 return (-1); 283 } 284 285 return (0); 286} 287 288void 289chap_delete(struct chap *chap) 290{ 291 292 free(chap); 293} 294 295struct rchap * 296rchap_new(const char *secret) 297{ 298 struct rchap *rchap; 299 300 rchap = calloc(sizeof(*rchap), 1); 301 if (rchap == NULL) 302 log_err(1, "calloc"); 303 304 rchap->rchap_secret = checked_strdup(secret); 305 306 return (rchap); 307} 308 309static void 310rchap_receive_bin(struct rchap *rchap, const unsigned char id, 311 const void *challenge, size_t challenge_len) 312{ 313 314 rchap->rchap_id = id; 315 rchap->rchap_challenge = calloc(challenge_len, 1); 316 if (rchap->rchap_challenge == NULL) 317 log_err(1, "calloc"); 318 memcpy(rchap->rchap_challenge, challenge, challenge_len); 319 rchap->rchap_challenge_len = challenge_len; 320} 321 322int 323rchap_receive(struct rchap *rchap, const char *id, const char *challenge) 324{ 325 unsigned char id_bin; 326 void *challenge_bin; 327 size_t challenge_bin_len; 328 329 int error; 330 331 id_bin = strtoul(id, NULL, 10); 332 333 error = chap_hex2bin(challenge, &challenge_bin, &challenge_bin_len); 334 if (error != 0) { 335 log_debugx("got incorrectly encoded CHAP challenge \"%s\"", 336 challenge); 337 return (1); 338 } 339 340 rchap_receive_bin(rchap, id_bin, challenge_bin, challenge_bin_len); 341 free(challenge_bin); 342 343 return (0); 344} 345 346static void 347rchap_get_response_bin(struct rchap *rchap, 348 void **responsep, size_t *response_lenp) 349{ 350 void *response_bin; 351 size_t response_bin_len = MD5_DIGEST_LENGTH; 352 353 response_bin = calloc(response_bin_len, 1); 354 if (response_bin == NULL) 355 log_err(1, "calloc"); 356 357 chap_compute_md5(rchap->rchap_id, rchap->rchap_secret, 358 rchap->rchap_challenge, rchap->rchap_challenge_len, 359 response_bin, response_bin_len); 360 361 *responsep = response_bin; 362 *response_lenp = response_bin_len; 363} 364 365char * 366rchap_get_response(struct rchap *rchap) 367{ 368 void *response; 369 size_t response_len; 370 char *chap_r; 371 372 rchap_get_response_bin(rchap, &response, &response_len); 373 chap_r = chap_bin2hex(response, response_len); 374 free(response); 375 376 return (chap_r); 377} 378 379void 380rchap_delete(struct rchap *rchap) 381{ 382 383 free(rchap->rchap_secret); 384 free(rchap->rchap_challenge); 385 free(rchap); 386} 387