Date: Wed, 22 Oct 2014 08:59:24 +0000 (UTC) From: Edward Tomasz Napierala <trasz@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r273459 - in head/usr.sbin: ctld iscsid Message-ID: <201410220859.s9M8xOBO029895@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: trasz Date: Wed Oct 22 08:59:23 2014 New Revision: 273459 URL: https://svnweb.freebsd.org/changeset/base/273459 Log: Untangle iSCSI authentication code by splitting off the CHAP implementation. Reviewed by: mav@ MFC after: 1 month Sponsored by: The FreeBSD Foundation Added: head/usr.sbin/ctld/chap.c (contents, props changed) head/usr.sbin/iscsid/chap.c (contents, props changed) Modified: head/usr.sbin/ctld/Makefile head/usr.sbin/ctld/ctld.h head/usr.sbin/ctld/login.c head/usr.sbin/iscsid/Makefile head/usr.sbin/iscsid/iscsid.h head/usr.sbin/iscsid/login.c Modified: head/usr.sbin/ctld/Makefile ============================================================================== --- head/usr.sbin/ctld/Makefile Wed Oct 22 08:56:57 2014 (r273458) +++ head/usr.sbin/ctld/Makefile Wed Oct 22 08:59:23 2014 (r273459) @@ -1,7 +1,7 @@ # $FreeBSD$ PROG= ctld -SRCS= ctld.c discovery.c kernel.c keys.c log.c login.c parse.y pdu.c token.l y.tab.h +SRCS= chap.c ctld.c discovery.c kernel.c keys.c log.c login.c parse.y pdu.c token.l y.tab.h CFLAGS+= -I${.CURDIR} CFLAGS+= -I${.CURDIR}/../../sys CFLAGS+= -I${.CURDIR}/../../sys/cam/ctl Added: head/usr.sbin/ctld/chap.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/usr.sbin/ctld/chap.c Wed Oct 22 08:59:23 2014 (r273459) @@ -0,0 +1,386 @@ +/*- + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <assert.h> +#include <string.h> +#include <openssl/err.h> +#include <openssl/md5.h> +#include <openssl/rand.h> + +#include "ctld.h" + +static void +chap_compute_md5(const char id, const char *secret, + const void *challenge, size_t challenge_len, void *response, + size_t response_len) +{ + MD5_CTX ctx; + int rv; + + assert(response_len == MD5_DIGEST_LENGTH); + + MD5_Init(&ctx); + MD5_Update(&ctx, &id, sizeof(id)); + MD5_Update(&ctx, secret, strlen(secret)); + MD5_Update(&ctx, challenge, challenge_len); + rv = MD5_Final(response, &ctx); + if (rv != 1) + log_errx(1, "MD5_Final"); +} + +static int +chap_hex2int(const char hex) +{ + switch (hex) { + case '0': + return (0x00); + case '1': + return (0x01); + case '2': + return (0x02); + case '3': + return (0x03); + case '4': + return (0x04); + case '5': + return (0x05); + case '6': + return (0x06); + case '7': + return (0x07); + case '8': + return (0x08); + case '9': + return (0x09); + case 'a': + case 'A': + return (0x0a); + case 'b': + case 'B': + return (0x0b); + case 'c': + case 'C': + return (0x0c); + case 'd': + case 'D': + return (0x0d); + case 'e': + case 'E': + return (0x0e); + case 'f': + case 'F': + return (0x0f); + default: + return (-1); + } +} + +/* + * XXX: Review this _carefully_. + */ +static int +chap_hex2bin(const char *hex, void **binp, size_t *bin_lenp) +{ + int i, hex_len, nibble; + bool lo = true; /* As opposed to 'hi'. */ + char *bin; + size_t bin_off, bin_len; + + if (strncasecmp(hex, "0x", strlen("0x")) != 0) { + log_warnx("malformed variable, should start with \"0x\""); + return (-1); + } + + hex += strlen("0x"); + hex_len = strlen(hex); + if (hex_len < 1) { + log_warnx("malformed variable; doesn't contain anything " + "but \"0x\""); + return (-1); + } + + bin_len = hex_len / 2 + hex_len % 2; + bin = calloc(bin_len, 1); + if (bin == NULL) + log_err(1, "calloc"); + + bin_off = bin_len - 1; + for (i = hex_len - 1; i >= 0; i--) { + nibble = chap_hex2int(hex[i]); + if (nibble < 0) { + log_warnx("malformed variable, invalid char \"%c\"", + hex[i]); + free(bin); + return (-1); + } + + assert(bin_off < bin_len); + if (lo) { + bin[bin_off] = nibble; + lo = false; + } else { + bin[bin_off] |= nibble << 4; + bin_off--; + lo = true; + } + } + + *binp = bin; + *bin_lenp = bin_len; + return (0); +} + +static char * +chap_bin2hex(const char *bin, size_t bin_len) +{ + unsigned char *hex, *tmp, ch; + size_t hex_len; + size_t i; + + hex_len = bin_len * 2 + 3; /* +2 for "0x", +1 for '\0'. */ + hex = malloc(hex_len); + if (hex == NULL) + log_err(1, "malloc"); + + tmp = hex; + tmp += sprintf(tmp, "0x"); + for (i = 0; i < bin_len; i++) { + ch = bin[i]; + tmp += sprintf(tmp, "%02x", ch); + } + + return (hex); +} + +struct chap * +chap_new(void) +{ + struct chap *chap; + int rv; + + chap = calloc(sizeof(*chap), 1); + if (chap == NULL) + log_err(1, "calloc"); + + /* + * Generate the challenge. + */ + rv = RAND_bytes(chap->chap_challenge, sizeof(chap->chap_challenge)); + if (rv != 1) { + log_errx(1, "RAND_bytes failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + rv = RAND_bytes(&chap->chap_id, sizeof(chap->chap_id)); + if (rv != 1) { + log_errx(1, "RAND_bytes failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + + return (chap); +} + +char * +chap_get_id(const struct chap *chap) +{ + char *chap_i; + int ret; + + ret = asprintf(&chap_i, "%d", chap->chap_id); + if (ret < 0) + log_err(1, "asprintf"); + + return (chap_i); +} + +char * +chap_get_challenge(const struct chap *chap) +{ + char *chap_c; + + chap_c = chap_bin2hex(chap->chap_challenge, + sizeof(chap->chap_challenge)); + + return (chap_c); +} + +static int +chap_receive_bin(struct chap *chap, void *response, size_t response_len) +{ + + if (response_len != sizeof(chap->chap_response)) { + log_debugx("got CHAP response with invalid length; " + "got %zd, should be %zd", + response_len, sizeof(chap->chap_response)); + return (1); + } + + memcpy(chap->chap_response, response, response_len); + return (0); +} + +int +chap_receive(struct chap *chap, const char *response) +{ + void *response_bin; + size_t response_bin_len; + int error; + + error = chap_hex2bin(response, &response_bin, &response_bin_len); + if (error != 0) { + log_debugx("got incorrectly encoded CHAP response \"%s\"", + response); + return (1); + } + + error = chap_receive_bin(chap, response_bin, response_bin_len); + free(response_bin); + + return (error); +} + +int +chap_authenticate(struct chap *chap, const char *secret) +{ + char expected_response[MD5_DIGEST_LENGTH]; + + chap_compute_md5(chap->chap_id, secret, + chap->chap_challenge, sizeof(chap->chap_challenge), + expected_response, sizeof(expected_response)); + + if (memcmp(chap->chap_response, + expected_response, sizeof(expected_response)) != 0) { + return (-1); + } + + return (0); +} + +void +chap_delete(struct chap *chap) +{ + + free(chap); +} + +struct rchap * +rchap_new(const char *secret) +{ + struct rchap *rchap; + + rchap = calloc(sizeof(*rchap), 1); + if (rchap == NULL) + log_err(1, "calloc"); + + rchap->rchap_secret = checked_strdup(secret); + + return (rchap); +} + +static void +rchap_receive_bin(struct rchap *rchap, const unsigned char id, + const void *challenge, size_t challenge_len) +{ + + rchap->rchap_id = id; + rchap->rchap_challenge = calloc(challenge_len, 1); + if (rchap->rchap_challenge == NULL) + log_err(1, "calloc"); + memcpy(rchap->rchap_challenge, challenge, challenge_len); + rchap->rchap_challenge_len = challenge_len; +} + +int +rchap_receive(struct rchap *rchap, const char *id, const char *challenge) +{ + unsigned char id_bin; + void *challenge_bin; + size_t challenge_bin_len; + + int error; + + id_bin = strtoul(id, NULL, 10); + + error = chap_hex2bin(challenge, &challenge_bin, &challenge_bin_len); + if (error != 0) { + log_debugx("got incorrectly encoded CHAP challenge \"%s\"", + challenge); + return (1); + } + + rchap_receive_bin(rchap, id_bin, challenge_bin, challenge_bin_len); + free(challenge_bin); + + return (0); +} + +static void +rchap_get_response_bin(struct rchap *rchap, + void **responsep, size_t *response_lenp) +{ + void *response_bin; + size_t response_bin_len = MD5_DIGEST_LENGTH; + + response_bin = calloc(response_bin_len, 1); + if (response_bin == NULL) + log_err(1, "calloc"); + + chap_compute_md5(rchap->rchap_id, rchap->rchap_secret, + rchap->rchap_challenge, rchap->rchap_challenge_len, + response_bin, response_bin_len); + + *responsep = response_bin; + *response_lenp = response_bin_len; +} + +char * +rchap_get_response(struct rchap *rchap) +{ + void *response; + size_t response_len; + char *chap_r; + + rchap_get_response_bin(rchap, &response, &response_len); + chap_r = chap_bin2hex(response, response_len); + free(response); + + return (chap_r); +} + +void +rchap_delete(struct rchap *rchap) +{ + + free(rchap->rchap_secret); + free(rchap->rchap_challenge); + free(rchap); +} Modified: head/usr.sbin/ctld/ctld.h ============================================================================== --- head/usr.sbin/ctld/ctld.h Wed Oct 22 08:56:57 2014 (r273458) +++ head/usr.sbin/ctld/ctld.h Wed Oct 22 08:59:23 2014 (r273459) @@ -39,6 +39,7 @@ #include <sys/socket.h> #include <stdbool.h> #include <libutil.h> +#include <openssl/md5.h> #define DEFAULT_CONFIG_PATH "/etc/ctl.conf" #define DEFAULT_PIDFILE "/var/run/ctld.pid" @@ -207,6 +208,35 @@ struct keys { size_t keys_data_len; }; +#define CHAP_CHALLENGE_LEN 1024 + +struct chap { + unsigned char chap_id; + char chap_challenge[CHAP_CHALLENGE_LEN]; + char chap_response[MD5_DIGEST_LENGTH]; +}; + +struct rchap { + char *rchap_secret; + unsigned char rchap_id; + void *rchap_challenge; + size_t rchap_challenge_len; +}; + +struct chap *chap_new(void); +char *chap_get_id(const struct chap *chap); +char *chap_get_challenge(const struct chap *chap); +int chap_receive(struct chap *chap, const char *response); +int chap_authenticate(struct chap *chap, + const char *secret); +void chap_delete(struct chap *chap); + +struct rchap *rchap_new(const char *secret); +int rchap_receive(struct rchap *rchap, + const char *id, const char *challenge); +char *rchap_get_response(struct rchap *rchap); +void rchap_delete(struct rchap *rchap); + struct conf *conf_new(void); struct conf *conf_new_from_file(const char *path); struct conf *conf_new_from_kernel(void); Modified: head/usr.sbin/ctld/login.c ============================================================================== --- head/usr.sbin/ctld/login.c Wed Oct 22 08:56:57 2014 (r273458) +++ head/usr.sbin/ctld/login.c Wed Oct 22 08:59:23 2014 (r273459) @@ -39,9 +39,6 @@ __FBSDID("$FreeBSD$"); #include <string.h> #include <unistd.h> #include <netinet/in.h> -#include <openssl/err.h> -#include <openssl/md5.h> -#include <openssl/rand.h> #include "ctld.h" #include "iscsi_proto.h" @@ -229,150 +226,6 @@ login_list_prefers(const char *list, return (-1); } -static int -login_hex2int(const char hex) -{ - switch (hex) { - case '0': - return (0x00); - case '1': - return (0x01); - case '2': - return (0x02); - case '3': - return (0x03); - case '4': - return (0x04); - case '5': - return (0x05); - case '6': - return (0x06); - case '7': - return (0x07); - case '8': - return (0x08); - case '9': - return (0x09); - case 'a': - case 'A': - return (0x0a); - case 'b': - case 'B': - return (0x0b); - case 'c': - case 'C': - return (0x0c); - case 'd': - case 'D': - return (0x0d); - case 'e': - case 'E': - return (0x0e); - case 'f': - case 'F': - return (0x0f); - default: - return (-1); - } -} - -/* - * XXX: Review this _carefully_. - */ -static int -login_hex2bin(const char *hex, char **binp, size_t *bin_lenp) -{ - int i, hex_len, nibble; - bool lo = true; /* As opposed to 'hi'. */ - char *bin; - size_t bin_off, bin_len; - - if (strncasecmp(hex, "0x", strlen("0x")) != 0) { - log_warnx("malformed variable, should start with \"0x\""); - return (-1); - } - - hex += strlen("0x"); - hex_len = strlen(hex); - if (hex_len < 1) { - log_warnx("malformed variable; doesn't contain anything " - "but \"0x\""); - return (-1); - } - - bin_len = hex_len / 2 + hex_len % 2; - bin = calloc(bin_len, 1); - if (bin == NULL) - log_err(1, "calloc"); - - bin_off = bin_len - 1; - for (i = hex_len - 1; i >= 0; i--) { - nibble = login_hex2int(hex[i]); - if (nibble < 0) { - log_warnx("malformed variable, invalid char \"%c\"", - hex[i]); - return (-1); - } - - assert(bin_off < bin_len); - if (lo) { - bin[bin_off] = nibble; - lo = false; - } else { - bin[bin_off] |= nibble << 4; - bin_off--; - lo = true; - } - } - - *binp = bin; - *bin_lenp = bin_len; - return (0); -} - -static char * -login_bin2hex(const char *bin, size_t bin_len) -{ - unsigned char *hex, *tmp, ch; - size_t hex_len; - size_t i; - - hex_len = bin_len * 2 + 3; /* +2 for "0x", +1 for '\0'. */ - hex = malloc(hex_len); - if (hex == NULL) - log_err(1, "malloc"); - - tmp = hex; - tmp += sprintf(tmp, "0x"); - for (i = 0; i < bin_len; i++) { - ch = bin[i]; - tmp += sprintf(tmp, "%02x", ch); - } - - return (hex); -} - -static void -login_compute_md5(const char id, const char *secret, - const void *challenge, size_t challenge_len, void *response, - size_t response_len) -{ - MD5_CTX ctx; - int rv; - - assert(response_len == MD5_DIGEST_LENGTH); - - MD5_Init(&ctx); - MD5_Update(&ctx, &id, sizeof(id)); - MD5_Update(&ctx, secret, strlen(secret)); - MD5_Update(&ctx, challenge, challenge_len); - rv = MD5_Final(response, &ctx); - if (rv != 1) - log_errx(1, "MD5_Final"); -} - -#define LOGIN_CHALLENGE_LEN 1024 - static struct pdu * login_receive_chap_a(struct connection *conn) { @@ -400,21 +253,21 @@ login_receive_chap_a(struct connection * } static void -login_send_chap_c(struct pdu *request, const unsigned char id, - const void *challenge, const size_t challenge_len) +login_send_chap_c(struct pdu *request, struct chap *chap) { struct pdu *response; struct keys *response_keys; - char *chap_c, chap_i[4]; + char *chap_c, *chap_i; - chap_c = login_bin2hex(challenge, challenge_len); - snprintf(chap_i, sizeof(chap_i), "%d", id); + chap_c = chap_get_challenge(chap); + chap_i = chap_get_id(chap); response = login_new_response(request); response_keys = keys_new(); keys_add(response_keys, "CHAP_A", "5"); keys_add(response_keys, "CHAP_I", chap_i); keys_add(response_keys, "CHAP_C", chap_c); + free(chap_i); free(chap_c); keys_save(response_keys, response); pdu_send(response); @@ -423,15 +276,12 @@ login_send_chap_c(struct pdu *request, c } static struct pdu * -login_receive_chap_r(struct connection *conn, - struct auth_group *ag, const unsigned char id, const void *challenge, - const size_t challenge_len, const struct auth **cap) +login_receive_chap_r(struct connection *conn, struct auth_group *ag, + struct chap *chap, const struct auth **authp) { struct pdu *request; struct keys *request_keys; const char *chap_n, *chap_r; - char *response_bin, expected_response_bin[MD5_DIGEST_LENGTH]; - size_t response_bin_len; const struct auth *auth; int error; @@ -449,7 +299,7 @@ login_receive_chap_r(struct connection * login_send_error(request, 0x02, 0x07); log_errx(1, "received CHAP Login PDU without CHAP_R"); } - error = login_hex2bin(chap_r, &response_bin, &response_bin_len); + error = chap_receive(chap, chap_r); if (error != 0) { login_send_error(request, 0x02, 0x07); log_errx(1, "received CHAP Login PDU with malformed CHAP_R"); @@ -469,21 +319,17 @@ login_receive_chap_r(struct connection * assert(auth->a_secret != NULL); assert(strlen(auth->a_secret) > 0); - login_compute_md5(id, auth->a_secret, challenge, - challenge_len, expected_response_bin, - sizeof(expected_response_bin)); - if (memcmp(response_bin, expected_response_bin, - sizeof(expected_response_bin)) != 0) { + error = chap_authenticate(chap, auth->a_secret); + if (error != 0) { login_send_error(request, 0x02, 0x01); log_errx(1, "CHAP authentication failed for user \"%s\"", auth->a_user); } keys_delete(request_keys); - free(response_bin); - *cap = auth; + *authp = auth; return (request); } @@ -494,10 +340,9 @@ login_send_chap_success(struct pdu *requ struct pdu *response; struct keys *request_keys, *response_keys; struct iscsi_bhs_login_response *bhslr2; + struct rchap *rchap; const char *chap_i, *chap_c; - char *chap_r, *challenge, response_bin[MD5_DIGEST_LENGTH]; - size_t challenge_len; - unsigned char id; + char *chap_r; int error; response = login_new_response(request); @@ -530,21 +375,18 @@ login_send_chap_success(struct pdu *requ "is not set", auth->a_user); } - id = strtoul(chap_i, NULL, 10); - error = login_hex2bin(chap_c, &challenge, &challenge_len); + log_debugx("performing mutual authentication as user \"%s\"", + auth->a_mutual_user); + + rchap = rchap_new(auth->a_mutual_secret); + error = rchap_receive(rchap, chap_i, chap_c); if (error != 0) { login_send_error(request, 0x02, 0x07); log_errx(1, "received CHAP Login PDU with malformed " - "CHAP_C"); + "CHAP_I or CHAP_C"); } - - log_debugx("performing mutual authentication as user \"%s\"", - auth->a_mutual_user); - login_compute_md5(id, auth->a_mutual_secret, challenge, - challenge_len, response_bin, sizeof(response_bin)); - - chap_r = login_bin2hex(response_bin, - sizeof(response_bin)); + chap_r = rchap_get_response(rchap); + rchap_delete(rchap); response_keys = keys_new(); keys_add(response_keys, "CHAP_N", auth->a_mutual_user); keys_add(response_keys, "CHAP_R", chap_r); @@ -564,10 +406,8 @@ static void login_chap(struct connection *conn, struct auth_group *ag) { const struct auth *auth; + struct chap *chap; struct pdu *request; - char challenge_bin[LOGIN_CHALLENGE_LEN]; - unsigned char id; - int rv; /* * Receive CHAP_A PDU. @@ -578,34 +418,21 @@ login_chap(struct connection *conn, stru /* * Generate the challenge. */ - rv = RAND_bytes(challenge_bin, sizeof(challenge_bin)); - if (rv != 1) { - login_send_error(request, 0x03, 0x02); - log_errx(1, "RAND_bytes failed: %s", - ERR_error_string(ERR_get_error(), NULL)); - } - rv = RAND_bytes(&id, sizeof(id)); - if (rv != 1) { - login_send_error(request, 0x03, 0x02); - log_errx(1, "RAND_bytes failed: %s", - ERR_error_string(ERR_get_error(), NULL)); - } + chap = chap_new(); /* * Send the challenge. */ log_debugx("sending CHAP_C, binary challenge size is %zd bytes", - sizeof(challenge_bin)); - login_send_chap_c(request, id, challenge_bin, - sizeof(challenge_bin)); + sizeof(chap->chap_challenge)); + login_send_chap_c(request, chap); pdu_delete(request); /* * Receive CHAP_N/CHAP_R PDU and authenticate. */ log_debugx("waiting for CHAP_N/CHAP_R"); - request = login_receive_chap_r(conn, ag, id, challenge_bin, - sizeof(challenge_bin), &auth); + request = login_receive_chap_r(conn, ag, chap, &auth); /* * Yay, authentication succeeded! @@ -614,6 +441,7 @@ login_chap(struct connection *conn, stru "transitioning to Negotiation Phase", auth->a_user); login_send_chap_success(request, auth); pdu_delete(request); + chap_delete(chap); } static void Modified: head/usr.sbin/iscsid/Makefile ============================================================================== --- head/usr.sbin/iscsid/Makefile Wed Oct 22 08:56:57 2014 (r273458) +++ head/usr.sbin/iscsid/Makefile Wed Oct 22 08:59:23 2014 (r273459) @@ -1,7 +1,7 @@ # $FreeBSD$ PROG= iscsid -SRCS= discovery.c iscsid.c keys.c log.c login.c pdu.c +SRCS= chap.c discovery.c iscsid.c keys.c log.c login.c pdu.c CFLAGS+= -I${.CURDIR} CFLAGS+= -I${.CURDIR}/../../sys/cam CFLAGS+= -I${.CURDIR}/../../sys/dev/iscsi Added: head/usr.sbin/iscsid/chap.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/usr.sbin/iscsid/chap.c Wed Oct 22 08:59:23 2014 (r273459) @@ -0,0 +1,386 @@ +/*- + * Copyright (c) 2014 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <assert.h> +#include <string.h> +#include <openssl/err.h> +#include <openssl/md5.h> +#include <openssl/rand.h> + +#include "iscsid.h" + +static void +chap_compute_md5(const char id, const char *secret, + const void *challenge, size_t challenge_len, void *response, + size_t response_len) +{ + MD5_CTX ctx; + int rv; + + assert(response_len == MD5_DIGEST_LENGTH); + + MD5_Init(&ctx); + MD5_Update(&ctx, &id, sizeof(id)); + MD5_Update(&ctx, secret, strlen(secret)); + MD5_Update(&ctx, challenge, challenge_len); + rv = MD5_Final(response, &ctx); + if (rv != 1) + log_errx(1, "MD5_Final"); +} + +static int +chap_hex2int(const char hex) +{ + switch (hex) { + case '0': + return (0x00); + case '1': + return (0x01); + case '2': + return (0x02); + case '3': + return (0x03); + case '4': + return (0x04); + case '5': + return (0x05); + case '6': + return (0x06); + case '7': + return (0x07); + case '8': + return (0x08); + case '9': + return (0x09); + case 'a': + case 'A': + return (0x0a); + case 'b': + case 'B': + return (0x0b); + case 'c': + case 'C': + return (0x0c); + case 'd': + case 'D': + return (0x0d); + case 'e': + case 'E': + return (0x0e); + case 'f': + case 'F': + return (0x0f); + default: + return (-1); + } +} + +/* + * XXX: Review this _carefully_. + */ +static int +chap_hex2bin(const char *hex, void **binp, size_t *bin_lenp) +{ + int i, hex_len, nibble; + bool lo = true; /* As opposed to 'hi'. */ + char *bin; + size_t bin_off, bin_len; + + if (strncasecmp(hex, "0x", strlen("0x")) != 0) { + log_warnx("malformed variable, should start with \"0x\""); + return (-1); + } + + hex += strlen("0x"); + hex_len = strlen(hex); + if (hex_len < 1) { + log_warnx("malformed variable; doesn't contain anything " + "but \"0x\""); + return (-1); + } + + bin_len = hex_len / 2 + hex_len % 2; + bin = calloc(bin_len, 1); + if (bin == NULL) + log_err(1, "calloc"); + + bin_off = bin_len - 1; + for (i = hex_len - 1; i >= 0; i--) { + nibble = chap_hex2int(hex[i]); + if (nibble < 0) { + log_warnx("malformed variable, invalid char \"%c\"", + hex[i]); + free(bin); + return (-1); + } + + assert(bin_off < bin_len); + if (lo) { + bin[bin_off] = nibble; + lo = false; + } else { + bin[bin_off] |= nibble << 4; + bin_off--; + lo = true; + } + } + + *binp = bin; + *bin_lenp = bin_len; + return (0); +} + +static char * +chap_bin2hex(const char *bin, size_t bin_len) +{ + unsigned char *hex, *tmp, ch; + size_t hex_len; + size_t i; + + hex_len = bin_len * 2 + 3; /* +2 for "0x", +1 for '\0'. */ + hex = malloc(hex_len); + if (hex == NULL) + log_err(1, "malloc"); + + tmp = hex; + tmp += sprintf(tmp, "0x"); + for (i = 0; i < bin_len; i++) { + ch = bin[i]; + tmp += sprintf(tmp, "%02x", ch); + } + + return (hex); +} + +struct chap * +chap_new(void) +{ + struct chap *chap; + int rv; + + chap = calloc(sizeof(*chap), 1); + if (chap == NULL) + log_err(1, "calloc"); + + /* + * Generate the challenge. *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201410220859.s9M8xOBO029895>