chap.c revision 274866
167555Smsmith/*- 267555Smsmith * Copyright (c) 2014 The FreeBSD Foundation 3123103Sps * All rights reserved. 4123103Sps * 567555Smsmith * This software was developed by Edward Tomasz Napierala under sponsorship 667555Smsmith * from the FreeBSD Foundation. 767555Smsmith * 867555Smsmith * Redistribution and use in source and binary forms, with or without 967555Smsmith * modification, are permitted provided that the following conditions 1067555Smsmith * are met: 1167555Smsmith * 1. Redistributions of source code must retain the above copyright 1267555Smsmith * notice, this list of conditions and the following disclaimer. 1367555Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1467555Smsmith * notice, this list of conditions and the following disclaimer in the 1567555Smsmith * documentation and/or other materials provided with the distribution. 1667555Smsmith * 1767555Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1867555Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1967555Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2067555Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2167555Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2267555Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2367555Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2467555Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2567555Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2667555Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2767555Smsmith * SUCH DAMAGE. 2867555Smsmith * 2967555Smsmith */ 3067555Smsmith 3167555Smsmith#include <sys/cdefs.h> 3267555Smsmith__FBSDID("$FreeBSD: stable/10/usr.sbin/iscsid/chap.c 274866 2014-11-22 17:39:39Z trasz $"); 3367555Smsmith 3467555Smsmith#include <assert.h> 3567555Smsmith#include <string.h> 3667555Smsmith#include <openssl/err.h> 3767555Smsmith#include <openssl/md5.h> 3867555Smsmith#include <openssl/rand.h> 3967555Smsmith 4067555Smsmith#include "iscsid.h" 4167555Smsmith 4267555Smsmithstatic void 4367555Smsmithchap_compute_md5(const char id, const char *secret, 4467555Smsmith const void *challenge, size_t challenge_len, void *response, 4567555Smsmith size_t response_len) 4667555Smsmith{ 4767555Smsmith MD5_CTX ctx; 4867555Smsmith int rv; 4967555Smsmith 5067555Smsmith assert(response_len == MD5_DIGEST_LENGTH); 5167555Smsmith 5267555Smsmith MD5_Init(&ctx); 5367555Smsmith MD5_Update(&ctx, &id, sizeof(id)); 5467555Smsmith MD5_Update(&ctx, secret, strlen(secret)); 5567555Smsmith MD5_Update(&ctx, challenge, challenge_len); 5667555Smsmith rv = MD5_Final(response, &ctx); 5767555Smsmith if (rv != 1) 5867555Smsmith log_errx(1, "MD5_Final"); 5967555Smsmith} 6067555Smsmith 6167555Smsmithstatic int 6267555Smsmithchap_hex2int(const char hex) 6367555Smsmith{ 6467555Smsmith switch (hex) { 6567555Smsmith case '0': 6667555Smsmith return (0x00); 6767555Smsmith case '1': 6867555Smsmith return (0x01); 6967555Smsmith case '2': 7067555Smsmith return (0x02); 7167555Smsmith case '3': 7267555Smsmith return (0x03); 7367555Smsmith case '4': 7467555Smsmith return (0x04); 7567555Smsmith case '5': 7667555Smsmith return (0x05); 7767555Smsmith case '6': 7867555Smsmith return (0x06); 7967555Smsmith case '7': 8067555Smsmith return (0x07); 8167555Smsmith case '8': 8267555Smsmith return (0x08); 8367555Smsmith case '9': 8467555Smsmith return (0x09); 8567555Smsmith case 'a': 8667555Smsmith case 'A': 8767555Smsmith return (0x0a); 8867555Smsmith case 'b': 8967555Smsmith case 'B': 9067555Smsmith return (0x0b); 9167555Smsmith case 'c': 9267555Smsmith case 'C': 9367555Smsmith return (0x0c); 9467555Smsmith case 'd': 9567555Smsmith case 'D': 9667555Smsmith return (0x0d); 9767555Smsmith case 'e': 9867555Smsmith case 'E': 9967555Smsmith return (0x0e); 10067555Smsmith case 'f': 10167555Smsmith case 'F': 10267555Smsmith return (0x0f); 10367555Smsmith default: 10467555Smsmith return (-1); 10567555Smsmith } 10667555Smsmith} 107123103Sps 10891790Smsmith/* 10967555Smsmith * XXX: Review this _carefully_. 11067555Smsmith */ 11167555Smsmithstatic int 11267555Smsmithchap_hex2bin(const char *hex, void **binp, size_t *bin_lenp) 11367555Smsmith{ 11467555Smsmith int i, hex_len, nibble; 11567555Smsmith bool lo = true; /* As opposed to 'hi'. */ 11667555Smsmith char *bin; 11791790Smsmith size_t bin_off, bin_len; 11891790Smsmith 11991790Smsmith if (strncasecmp(hex, "0x", strlen("0x")) != 0) { 12091790Smsmith log_warnx("malformed variable, should start with \"0x\""); 12191790Smsmith return (-1); 12291790Smsmith } 12391790Smsmith 12491790Smsmith hex += strlen("0x"); 12591790Smsmith hex_len = strlen(hex); 12691790Smsmith if (hex_len < 1) { 12791790Smsmith log_warnx("malformed variable; doesn't contain anything " 12891790Smsmith "but \"0x\""); 12991790Smsmith return (-1); 13091790Smsmith } 13191790Smsmith 13291790Smsmith bin_len = hex_len / 2 + hex_len % 2; 133118508Sps bin = calloc(bin_len, 1); 134118508Sps if (bin == NULL) 135118508Sps log_err(1, "calloc"); 13691790Smsmith 13767555Smsmith bin_off = bin_len - 1; 13867555Smsmith for (i = hex_len - 1; i >= 0; i--) { 13967555Smsmith nibble = chap_hex2int(hex[i]); 14067555Smsmith if (nibble < 0) { 14167555Smsmith log_warnx("malformed variable, invalid char \"%c\"", 14267555Smsmith hex[i]); 14367555Smsmith free(bin); 14467555Smsmith return (-1); 14567555Smsmith } 14667555Smsmith 14767555Smsmith assert(bin_off < bin_len); 14867555Smsmith if (lo) { 14967555Smsmith bin[bin_off] = nibble; 15067555Smsmith lo = false; 15167555Smsmith } else { 15267555Smsmith bin[bin_off] |= nibble << 4; 15367555Smsmith bin_off--; 15467555Smsmith lo = true; 15567555Smsmith } 15667555Smsmith } 15767555Smsmith 15867555Smsmith *binp = bin; 15967555Smsmith *bin_lenp = bin_len; 16067555Smsmith return (0); 16167555Smsmith} 16267555Smsmith 16367555Smsmithstatic char * 16467555Smsmithchap_bin2hex(const char *bin, size_t bin_len) 16567555Smsmith{ 16667555Smsmith unsigned char *hex, *tmp, ch; 16767555Smsmith 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