From 95a0d98cfda242f49ba8f32d0b314f1961e40b36 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Fri, 3 Jan 2025 23:43:55 +1000 Subject: [PATCH] Add support for CHAP using SHA1 Signed-off-by: Ronnie Sahlberg --- README.md | 1 + include/iscsi-private.h | 7 +- include/iscsi.h | 15 ++ include/sha-private.h | 28 +++ include/sha.h | 334 ++++++++++++++++++++++++++++++ lib/Makefile.am | 2 +- lib/init.c | 27 +++ lib/libiscsi.def | 2 + lib/libiscsi.syms.in | 2 + lib/login.c | 143 +++++++++---- lib/sha1.c | 448 ++++++++++++++++++++++++++++++++++++++++ utils/iscsi-ls.c | 6 +- 12 files changed, 966 insertions(+), 49 deletions(-) create mode 100644 include/sha-private.h create mode 100644 include/sha.h create mode 100644 lib/sha1.c diff --git a/README.md b/README.md index 0886e58..1579f03 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ target_user= target_password= header_digest= data_digest= +auth= Transport: iser diff --git a/include/iscsi-private.h b/include/iscsi-private.h index 4a06658..065f243 100644 --- a/include/iscsi-private.h +++ b/include/iscsi-private.h @@ -72,7 +72,7 @@ struct iscsi_in_pdu { void iscsi_free_iscsi_in_pdu(struct iscsi_context *iscsi, struct iscsi_in_pdu *in); /* size of chap response field */ -#define CHAP_R_SIZE 16 +#define MAX_CHAP_R_SIZE 20 /* md5:16 sha1:20 */ /* max length of chap challange */ #define MAX_CHAP_C_LENGTH 2048 @@ -90,14 +90,15 @@ struct iscsi_context { char alias[MAX_STRING_SIZE+1]; char bind_interfaces[MAX_STRING_SIZE+1]; + enum iscsi_chap_auth chap_auth; char user[MAX_STRING_SIZE+1]; char passwd[MAX_STRING_SIZE+1]; char chap_c[MAX_CHAP_C_LENGTH+1]; char target_user[MAX_STRING_SIZE+1]; char target_passwd[MAX_STRING_SIZE+1]; - uint32_t target_chap_i; - unsigned char target_chap_r[CHAP_R_SIZE]; + int target_chap_i; + char target_chap_r[MAX_CHAP_R_SIZE]; char error_string[MAX_STRING_SIZE+1]; diff --git a/include/iscsi.h b/include/iscsi.h index e92a493..6c02fd2 100644 --- a/include/iscsi.h +++ b/include/iscsi.h @@ -183,6 +183,21 @@ EXTERN int iscsi_set_initial_r2t(struct iscsi_context *iscsi, enum iscsi_initial_r2t initial_r2t); +enum iscsi_chap_auth { + ISCSI_CHAP_MD5 = 5, + ISCSI_CHAP_SHA_1 = 6, +#if 0 + ISCSI_CHAP_SHA_256 = 7, + ISCSI_CHAP_SHA3_256 = 8, +#endif +}; + +EXTERN enum iscsi_chap_auth +iscsi_get_auth(struct iscsi_context *iscsi); + +EXTERN void +iscsi_set_auth(struct iscsi_context *iscsi, enum iscsi_chap_auth auth); + /* * This function is used to parse an iSCSI URL into a iscsi_url structure. * iSCSI URL format : diff --git a/include/sha-private.h b/include/sha-private.h new file mode 100644 index 0000000..5304bd0 --- /dev/null +++ b/include/sha-private.h @@ -0,0 +1,28 @@ +/*************************** sha-private.h ***************************/ +/********************** See RFC 4634 for details *********************/ +#ifndef _SHA_PRIVATE__H +#define _SHA_PRIVATE__H +/* + * These definitions are defined in FIPS-180-2, section 4.1. + * Ch() and Maj() are defined identically in sections 4.1.1, + * 4.1.2 and 4.1.3. + * + * The definitions used in FIPS-180-2 are as follows: + */ + +#ifndef USE_MODIFIED_MACROS +#define SHA_Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) +#define SHA_Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +#else /* USE_MODIFIED_MACROS */ +/* + * The following definitions are equivalent and potentially faster. + */ + +#define SHA_Ch(x, y, z) (((x) & ((y) ^ (z))) ^ (z)) +#define SHA_Maj(x, y, z) (((x) & ((y) | (z))) | ((y) & (z))) +#endif /* USE_MODIFIED_MACROS */ + +#define SHA_Parity(x, y, z) ((x) ^ (y) ^ (z)) + +#endif /* _SHA_PRIVATE__H */ diff --git a/include/sha.h b/include/sha.h new file mode 100644 index 0000000..bd309a8 --- /dev/null +++ b/include/sha.h @@ -0,0 +1,334 @@ +/**************************** sha.h ****************************/ +/******************* See RFC 4634 for details ******************/ +#ifndef _SHA_H_ +#define _SHA_H_ + +#ifndef USE_SHA1 + #define USE_SHA1 1 +#endif + +#ifndef USE_SHA224 + #define USE_SHA224 0 +#endif + +#ifndef USE_SHA384_SHA512 + #define USE_SHA384_SHA512 1 +#endif + +/* + * Description: + * This file implements the Secure Hash Signature Standard + * algorithms as defined in the National Institute of Standards + * and Technology Federal Information Processing Standards + * Publication (FIPS PUB) 180-1 published on April 17, 1995, 180-2 + * published on August 1, 2002, and the FIPS PUB 180-2 Change + * Notice published on February 28, 2004. + * + * A combined document showing all algorithms is available at + * http://csrc.nist.gov/publications/fips/ + * fips180-2/fips180-2withchangenotice.pdf + * + * The five hashes are defined in these sizes: + * SHA-1 20 byte / 160 bit + * SHA-224 28 byte / 224 bit + * SHA-256 32 byte / 256 bit + * SHA-384 48 byte / 384 bit + * SHA-512 64 byte / 512 bit + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDINT_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +/* + * If you do not have the ISO standard stdint.h header file, then you + * must typedef the following: + * name meaning + * uint64_t unsigned 64 bit integer + * uint32_t unsigned 32 bit integer + * uint8_t unsigned 8 bit integer (i.e., unsigned char) + * int_least16_t integer of >= 16 bits + * + */ + +#ifndef _SHA_enum_ +#define _SHA_enum_ +/* + * All SHA functions return one of these values. + */ +enum +{ + shaSuccess = 0, + shaNull, /* Null pointer parameter */ + shaInputTooLong, /* input data too long */ + shaStateError, /* called Input after FinalBits or Result */ + shaBadParam /* passed a bad parameter */ +}; +#endif /* _SHA_enum_ */ + +/* + * These constants hold size information for each of the SHA + * hashing operations + */ +enum +{ +#if defined(USE_SHA1) && USE_SHA1 + SHA1_Message_Block_Size = 64, + SHA1HashSize = 20, + SHA1HashSizeBits = 160, +#endif +#if defined(USE_SHA224) && USE_SHA224 + SHA224_Message_Block_Size = 64, + SHA224HashSize = 28, + SHA224HashSizeBits = 224, +#endif +#if defined(USE_SHA384_SHA512) && USE_SHA384_SHA512 + SHA384_Message_Block_Size = 128, + SHA384HashSize = 48, + SHA384HashSizeBits = 384, +#endif + SHA256_Message_Block_Size = 64, + SHA512_Message_Block_Size = 128, + USHA_Max_Message_Block_Size = SHA512_Message_Block_Size, + + SHA256HashSize = 32, + SHA512HashSize = 64, + USHAMaxHashSize = SHA512HashSize, + + SHA256HashSizeBits = 256, + SHA512HashSizeBits = 512, USHAMaxHashSizeBits = SHA512HashSizeBits +}; + +/* + * These constants are used in the USHA (unified sha) functions. + */ +typedef enum SHAversion +{ +#if defined(USE_SHA1) && USE_SHA1 + SHA1, +#endif +#if defined(USE_SHA224) && USE_SHA224 + SHA224, +#endif +#if defined(USE_SHA384_SHA512) && USE_SHA384_SHA512 + SHA384, + SHA512, +#endif + SHA256 +} SHAversion; + +#if defined(USE_SHA1) && USE_SHA1 +/* + * This structure will hold context information for the SHA-1 + * hashing operation. + */ +typedef struct SHA1Context +{ + uint32_t Intermediate_Hash[SHA1HashSize / 4]; /* Message Digest */ + + uint32_t Length_Low; /* Message length in bits */ + uint32_t Length_High; /* Message length in bits */ + + int_least16_t Message_Block_Index; /* Message_Block array index */ + /* 512-bit message blocks */ + uint8_t Message_Block[SHA1_Message_Block_Size]; + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the digest corrupted? */ +} SHA1Context; +#endif + +/* + * This structure will hold context information for the SHA-256 + * hashing operation. + */ +typedef struct SHA256Context +{ + uint32_t Intermediate_Hash[SHA256HashSize / 4]; /* Message Digest */ + + uint32_t Length_Low; /* Message length in bits */ + uint32_t Length_High; /* Message length in bits */ + + int_least16_t Message_Block_Index; /* Message_Block array index */ + /* 512-bit message blocks */ + uint8_t Message_Block[SHA256_Message_Block_Size]; + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the digest corrupted? */ +} SHA256Context; + +/* + * This structure will hold context information for the SHA-512 + * hashing operation. + */ +typedef struct SHA512Context +{ +#ifdef USE_32BIT_ONLY + uint32_t Intermediate_Hash[SHA512HashSize / 4]; /* Message Digest */ + uint32_t Length[4]; /* Message length in bits */ +#else /* !USE_32BIT_ONLY */ + uint64_t Intermediate_Hash[SHA512HashSize / 8]; /* Message Digest */ + uint64_t Length_Low, Length_High; /* Message length in bits */ +#endif /* USE_32BIT_ONLY */ + int_least16_t Message_Block_Index; /* Message_Block array index */ + /* 1024-bit message blocks */ + uint8_t Message_Block[SHA512_Message_Block_Size]; + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the digest corrupted? */ +} SHA512Context; + +#if defined(USE_SHA224) && USE_SHA224 +/* + * This structure will hold context information for the SHA-224 + * hashing operation. It uses the SHA-256 structure for computation. + */ +typedef struct SHA256Context SHA224Context; +#endif + +#if defined(USE_SHA384_SHA512) && USE_SHA384_SHA512 +/* + * This structure will hold context information for the SHA-384 + * hashing operation. It uses the SHA-512 structure for computation. + */ +typedef struct SHA512Context SHA384Context; +#endif + +/* + * This structure holds context information for all SHA + * hashing operations. + */ +typedef struct USHAContext +{ + SHAversion whichSha; /* which SHA is being used */ + union + { +#if defined(USE_SHA1) && USE_SHA1 + SHA1Context sha1Context; +#endif +#if defined(USE_SHA224) && USE_SHA224 + SHA224Context sha224Context; +#endif + SHA256Context sha256Context; +#if defined(USE_SHA384_SHA512) && USE_SHA384_SHA512 + SHA384Context sha384Context; + SHA512Context sha512Context; +#endif + } ctx; +} USHAContext; + +/* + * This structure will hold context information for the HMAC + * keyed hashing operation. + */ +typedef struct HMACContext +{ + SHAversion whichSha; /* which SHA is being used */ + int hashSize; /* hash size of SHA being used */ + int blockSize; /* block size of SHA being used */ + USHAContext shaContext; /* SHA context */ + unsigned char k_opad[USHA_Max_Message_Block_Size]; + /* outer padding - key XORd with opad */ +} HMACContext; + +/* + * Function Prototypes + */ + +#if defined(USE_SHA1) && USE_SHA1 +/* SHA-1 */ +extern int SHA1Reset (SHA1Context *); +extern int SHA1Input (SHA1Context *, const uint8_t * bytes, + size_t bytecount); +extern int SHA1FinalBits (SHA1Context *, const uint8_t bits, + size_t bitcount); +extern int SHA1Result (SHA1Context *, uint8_t Message_Digest[SHA1HashSize]); +#endif + +#if defined(USE_SHA224) && USE_SHA224 +/* SHA-224 */ +extern int SHA224Reset (SHA224Context *); +extern int SHA224Input (SHA224Context *, const uint8_t * bytes, + size_t bytecount); +extern int SHA224FinalBits (SHA224Context *, const uint8_t bits, + size_t bitcount); +extern int SHA224Result (SHA224Context *, + uint8_t Message_Digest[SHA224HashSize]); +#endif + +/* SHA-256 */ +extern int SHA256Reset (SHA256Context *); +extern int SHA256Input (SHA256Context *, const uint8_t * bytes, + size_t bytecount); +extern int SHA256FinalBits (SHA256Context *, const uint8_t bits, + size_t bitcount); +extern int SHA256Result (SHA256Context *, + uint8_t Message_Digest[SHA256HashSize]); + +#if defined(USE_SHA384_SHA512) && USE_SHA384_SHA512 +/* SHA-384 */ +extern int SHA384Reset (SHA384Context *); +extern int SHA384Input (SHA384Context *, const uint8_t * bytes, + size_t bytecount); +extern int SHA384FinalBits (SHA384Context *, const uint8_t bits, + size_t bitcount); +extern int SHA384Result (SHA384Context *, + uint8_t Message_Digest[SHA384HashSize]); + +/* SHA-512 */ +extern int SHA512Reset (SHA512Context *); +extern int SHA512Input (SHA512Context *, const uint8_t * bytes, + size_t bytecount); +extern int SHA512FinalBits (SHA512Context *, const uint8_t bits, + size_t bitcount); +extern int SHA512Result (SHA512Context *, + uint8_t Message_Digest[SHA512HashSize]); +#endif + +/* Unified SHA functions, chosen by whichSha */ +extern int USHAReset (USHAContext *, SHAversion whichSha); +extern int USHAInput (USHAContext *, + const uint8_t * bytes, size_t bytecount); +extern int USHAFinalBits (USHAContext *, + const uint8_t bits, size_t bitcount); +extern int USHAResult (USHAContext *, + uint8_t Message_Digest[USHAMaxHashSize]); +extern int USHABlockSize (enum SHAversion whichSha); +extern int USHAHashSize (enum SHAversion whichSha); +extern int USHAHashSizeBits (enum SHAversion whichSha); + +/* + * HMAC Keyed-Hashing for Message Authentication, RFC2104, + * for all SHAs. + * This interface allows a fixed-length text input to be used. + */ +extern int hmac (SHAversion whichSha, /* which SHA algorithm to use */ + const unsigned char *text, /* pointer to data stream */ + size_t text_len, /* length of data stream */ + const unsigned char *key, /* pointer to authentication key */ + size_t key_len, /* length of authentication key */ + uint8_t digest[USHAMaxHashSize]); /* caller digest to fill in */ + +/* + * HMAC Keyed-Hashing for Message Authentication, RFC2104, + * for all SHAs. + * This interface allows any length of text input to be used. + */ +extern int hmacReset (HMACContext * ctx, enum SHAversion whichSha, + const unsigned char *key, size_t key_len); +extern int hmacInput (HMACContext * ctx, const unsigned char *text, + size_t text_len); + +extern int hmacFinalBits (HMACContext * ctx, const uint8_t bits, + size_t bitcount); +extern int hmacResult (HMACContext * ctx, uint8_t *digest); + +#endif /* _SHA_H_ */ diff --git a/lib/Makefile.am b/lib/Makefile.am index ba6aaee..357bb9b 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -6,7 +6,7 @@ libiscsipriv_la_SOURCES = \ connect.c crc32c.c discovery.c init.c \ login.c nop.c pdu.c iscsi-command.c \ scsi-lowlevel.c socket.c sync.c task_mgmt.c \ - logging.c utils.c + logging.c utils.c sha1.c if TARGET_OS_IS_WIN32 libiscsipriv_la_SOURCES += ../win32/win32_compat.c diff --git a/lib/init.c b/lib/init.c index cb3baeb..f39b0d0 100644 --- a/lib/init.c +++ b/lib/init.c @@ -251,6 +251,7 @@ iscsi_create_context(const char *initiator_name) iscsi->tcp_keepidle=30; iscsi->reconnect_max_retries = -1; + iscsi->chap_auth = ISCSI_CHAP_MD5; if (getenv("LIBISCSI_DEBUG") != NULL) { iscsi_set_log_level(iscsi, atoi(getenv("LIBISCSI_DEBUG"))); @@ -622,6 +623,19 @@ iscsi_parse_url(struct iscsi_context *iscsi, const char *url, int full) if (value != NULL) { *value++ = 0; } + if (!strcmp(key, "auth")) { + if (!strcmp(value, "md5")) { + iscsi->chap_auth = ISCSI_CHAP_MD5; + } else if (!strcmp(value, "sha1")) { + iscsi->chap_auth = ISCSI_CHAP_SHA_1; +#if 0 + } else if (!strcmp(value, "sha-256")) { + iscsi->chap_auth = ISCSI_CHAP_SHA_256; + } else if (!strcmp(value, "sha3-256")) { + iscsi->chap_auth = ISCSI_CHAP_SHA3_256; +#endif + } + } if (!strcmp(key, "header_digest")) { if (!strcmp(value, "crc32c")) { iscsi_set_header_digest( @@ -873,3 +887,16 @@ iscsi_set_fd_dup_cb(struct iscsi_context *iscsi, iscsi->fd_dup_cb = cb; iscsi->fd_dup_opaque = opaque; } + +enum iscsi_chap_auth +iscsi_get_auth(struct iscsi_context *iscsi) +{ + return iscsi->chap_auth; +} + +void +iscsi_set_auth(struct iscsi_context *iscsi, enum iscsi_chap_auth auth) +{ + iscsi->chap_auth = auth; +} + diff --git a/lib/libiscsi.def b/lib/libiscsi.def index 7e9d5cd..65ce2ea 100644 --- a/lib/libiscsi.def +++ b/lib/libiscsi.def @@ -14,6 +14,7 @@ iscsi_free_discovery_data iscsi_force_reconnect iscsi_full_connect_async iscsi_full_connect_sync +iscsi_get_auth iscsi_get_error iscsi_get_fd iscsi_get_lba_status_sync @@ -111,6 +112,7 @@ iscsi_scsi_command_sync iscsi_scsi_cancel_task iscsi_service iscsi_set_alias +iscsi_set_auth iscsi_set_immediate_data iscsi_set_initial_r2t iscsi_set_log_level diff --git a/lib/libiscsi.syms.in b/lib/libiscsi.syms.in index 5fe7974..53fb955 100644 --- a/lib/libiscsi.syms.in +++ b/lib/libiscsi.syms.in @@ -17,6 +17,7 @@ iscsi_force_reconnect iscsi_force_reconnect_sync iscsi_full_connect_async iscsi_full_connect_sync +iscsi_get_auth iscsi_get_error iscsi_get_fd iscsi_get_lba_status_sync @@ -114,6 +115,7 @@ iscsi_scsi_command_sync iscsi_scsi_is_task_in_outqueue iscsi_service iscsi_set_alias +iscsi_set_auth iscsi_set_bind_interfaces iscsi_set_cache_allocations iscsi_set_header_digest diff --git a/lib/login.c b/lib/login.c index 268c409..c9017c4 100644 --- a/lib/login.c +++ b/lib/login.c @@ -44,6 +44,7 @@ #include "iscsi-private.h" #include "scsi-lowlevel.h" #include "md5.h" +#include "sha.h" #ifdef HAVE_LIBGNUTLS #include @@ -562,7 +563,10 @@ iscsi_login_add_authalgorithm(struct iscsi_context *iscsi, struct iscsi_pdu *pdu return 0; } - strncpy(str,"CHAP_A=5",MAX_STRING_SIZE); + if (snprintf(str, MAX_STRING_SIZE, "CHAP_A=%d", iscsi->chap_auth) == -1) { + iscsi_set_error(iscsi, "Out-of-memory: snprintf failed."); + return -1; + } if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) { iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed."); @@ -702,6 +706,7 @@ i2h(int i) return i + '0'; } + #if defined HAVE_LIBGNUTLS #define md5_context_t gnutls_hash_hd_t #define md5_open(hd) gnutls_hash_init(hd, GNUTLS_DIG_MD5) @@ -757,19 +762,74 @@ static inline void md5_putc(md5_context_t h, unsigned char c) md5_write(h, &c, 1); } -/* size of the challenge used for bidirectional chap */ -#define TARGET_CHAP_C_SIZE 32 +static void compute_chap_r_md5(struct iscsi_context *iscsi, int chap_i, + unsigned char *passwd, + unsigned char *chap_c, + unsigned char *digest) +{ + unsigned char *strp; + unsigned char c; + md5_context_t ctx; + + md5_open(&ctx); + md5_putc(ctx, chap_i); + md5_write(ctx, passwd, strlen((char *)passwd)); + + strp = chap_c; + while (*strp != 0) { + c = (h2i(strp[0]) << 4) | h2i(strp[1]); + strp += 2; + md5_putc(ctx, c); + } + md5_read(ctx, digest); + md5_close(ctx); +} + +static void compute_chap_r_sha1(struct iscsi_context *iscsi, int chap_i, + unsigned char *passwd, + unsigned char *chap_c, + unsigned char *digest) +{ + unsigned char *strp; + unsigned char c; + SHA1Context ctx; + + SHA1Reset(&ctx); + c = chap_i; + SHA1Input(&ctx, &c, 1); + SHA1Input(&ctx, passwd, strlen((char *)passwd)); + + strp = chap_c; + while (*strp != 0) { + c = (h2i(strp[0]) << 4) | h2i(strp[1]); + strp += 2; + SHA1Input(&ctx, &c, 1); + } + SHA1Result(&ctx, digest); +} + +static void compute_chap_r(struct iscsi_context *iscsi, int chap_i, + unsigned char *passwd, + unsigned char *chap_c, + unsigned char *digest) +{ + switch (iscsi->chap_auth) { + case ISCSI_CHAP_MD5: + return compute_chap_r_md5(iscsi, chap_i, passwd, chap_c, digest); + case ISCSI_CHAP_SHA_1: + return compute_chap_r_sha1(iscsi, chap_i, passwd, chap_c, digest); + } +} static int iscsi_login_add_chap_response(struct iscsi_context *iscsi, struct iscsi_pdu *pdu) { char str[MAX_STRING_SIZE+1]; - char * strp; unsigned char c, cc[2]; - unsigned char digest[CHAP_R_SIZE]; - md5_context_t ctx; + char digest[MAX_CHAP_R_SIZE]; int i; - + int chap_r_size = 0; + if (iscsi->current_phase != ISCSI_PDU_LOGIN_CSG_SECNEG || iscsi->secneg_phase != ISCSI_LOGIN_SECNEG_PHASE_SEND_RESPONSE) { return 0; @@ -780,22 +840,19 @@ iscsi_login_add_chap_response(struct iscsi_context *iscsi, struct iscsi_pdu *pdu return -1; } - md5_open(&ctx); - if (ctx == NULL) { - iscsi_set_error(iscsi, "Cannot create MD5 algorithm"); - return -1; - } - md5_putc(ctx, iscsi->chap_i); - md5_write(ctx, (unsigned char *)iscsi->passwd, strlen(iscsi->passwd)); - - strp = iscsi->chap_c; - while (*strp != 0) { - c = (h2i(strp[0]) << 4) | h2i(strp[1]); - strp += 2; - md5_putc(ctx, c); - } - md5_read(ctx, digest); - md5_close(ctx); + switch (iscsi->chap_auth) { + case ISCSI_CHAP_MD5: + chap_r_size = 16; + break; + case ISCSI_CHAP_SHA_1: + chap_r_size = 20; + break; + } + + compute_chap_r(iscsi, iscsi->chap_i, + (unsigned char *)iscsi->passwd, + (unsigned char *)iscsi->chap_c, + (unsigned char *)digest); strncpy(str,"CHAP_R=0x",MAX_STRING_SIZE); if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)) @@ -804,7 +861,7 @@ iscsi_login_add_chap_response(struct iscsi_context *iscsi, struct iscsi_pdu *pdu return -1; } - for (i = 0; i < CHAP_R_SIZE; i++) { + for (i = 0; i < chap_r_size; i++) { c = digest[i]; cc[0] = i2h((c >> 4)&0x0f); cc[1] = i2h((c )&0x0f); @@ -824,7 +881,7 @@ iscsi_login_add_chap_response(struct iscsi_context *iscsi, struct iscsi_pdu *pdu /* bidirectional chap */ if (iscsi->target_user[0]) { - unsigned char target_chap_c[TARGET_CHAP_C_SIZE]; + char target_chap_c[MAX_CHAP_R_SIZE * 2]; iscsi->target_chap_i++; snprintf(str, MAX_STRING_SIZE, "CHAP_I=%d", @@ -836,7 +893,7 @@ iscsi_login_add_chap_response(struct iscsi_context *iscsi, struct iscsi_pdu *pdu return -1; } - for (i = 0; i < TARGET_CHAP_C_SIZE; i++) { + for (i = 0; i < chap_r_size * 2; i++) { target_chap_c[i] = rand()&0xff; } strncpy(str, "CHAP_C=0x", MAX_STRING_SIZE); @@ -846,7 +903,7 @@ iscsi_login_add_chap_response(struct iscsi_context *iscsi, struct iscsi_pdu *pdu "failed."); return -1; } - for (i = 0; i < TARGET_CHAP_C_SIZE; i++) { + for (i = 0; i < chap_r_size * 2; i++) { c = target_chap_c[i]; cc[0] = i2h((c >> 4)&0x0f); cc[1] = i2h((c )&0x0f); @@ -863,19 +920,10 @@ iscsi_login_add_chap_response(struct iscsi_context *iscsi, struct iscsi_pdu *pdu return -1; } - md5_open(&ctx); - if (ctx == NULL) { - iscsi_set_error(iscsi, "Cannot create MD5 algorithm"); - return -1; - } - md5_putc(ctx, iscsi->target_chap_i); - md5_write(ctx, (unsigned char *)iscsi->target_passwd, - strlen(iscsi->target_passwd)); - md5_write(ctx, (unsigned char *)target_chap_c, - TARGET_CHAP_C_SIZE); - - md5_read(ctx, iscsi->target_chap_r); - md5_close(ctx); + compute_chap_r(iscsi, iscsi->target_chap_i, + (unsigned char *)iscsi->target_passwd, + (unsigned char *)target_chap_c, + (unsigned char *)iscsi->target_chap_r); } return 0; @@ -1338,9 +1386,18 @@ iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, } if (!strncmp(ptr, "CHAP_R=0x", 9)) { - int i; + int i, chap_r_size = 0; - if (len != 9 + 2 * CHAP_R_SIZE) { + switch (iscsi->chap_auth) { + case ISCSI_CHAP_MD5: + chap_r_size = 16; + break; + case ISCSI_CHAP_SHA_1: + chap_r_size = 20; + break; + } + + if (len != 9 + 2 * chap_r_size) { iscsi_set_error(iscsi, "Wrong size of CHAP_R" " received from target."); if (pdu->callback) { @@ -1349,7 +1406,7 @@ iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, } return 0; } - for (i = 0; i < CHAP_R_SIZE; i++) { + for (i = 0; i < chap_r_size; i++) { unsigned char c; c = ((h2i(ptr[9 + 2 * i]) << 4) | h2i(ptr[9 + 2 * i + 1])); if (c != iscsi->target_chap_r[i]) { diff --git a/lib/sha1.c b/lib/sha1.c new file mode 100644 index 0000000..9e541c1 --- /dev/null +++ b/lib/sha1.c @@ -0,0 +1,448 @@ +/**************************** sha1.c ****************************/ +/******************** See RFC 4634 for details ******************/ +/* + * Description: + * This file implements the Secure Hash Signature Standard + * algorithms as defined in the National Institute of Standards + * and Technology Federal Information Processing Standards + * Publication (FIPS PUB) 180-1 published on April 17, 1995, 180-2 + * published on August 1, 2002, and the FIPS PUB 180-2 Change + * Notice published on February 28, 2004. + * + * A combined document showing all algorithms is available at + * http://csrc.nist.gov/publications/fips/ + * fips180-2/fips180-2withchangenotice.pdf + * + * The SHA-1 algorithm produces a 160-bit message digest for a + * given data stream. It should take about 2**n steps to find a + * message with the same digest as a given message and + * 2**(n/2) to find any two messages with the same digest, + * when n is the digest size in bits. Therefore, this + * algorithm can serve as a means of providing a + * "fingerprint" for a message. + * + * Portability Issues: + * SHA-1 is defined in terms of 32-bit "words". This code + * uses (included via "sha.h") to define 32 and 8 + * bit unsigned integer types. If your C compiler does not + * support 32 bit unsigned integers, this code is not + * appropriate. + * + * Caveats: + * SHA-1 is designed to work with messages less than 2^64 bits + * long. This implementation uses SHA1Input() to hash the bits + * that are a multiple of the size of an 8-bit character, and then + * uses SHA1FinalBits() to hash the final few bits of the input. + */ + +#include "sha.h" +#include "sha-private.h" + +#if defined(USE_SHA1) && USE_SHA1 + +/* + * Define the SHA1 circular left shift macro + */ +#define SHA1_ROTL(bits,word) \ + (((word) << (bits)) | ((word) >> (32-(bits)))) + +/* + * add "length" to the length + */ +#define SHA1AddLength(context, length) \ + (addTemp = (context)->Length_Low, \ + (context)->Corrupted = \ + (((context)->Length_Low += (length)) < addTemp) && \ + (++(context)->Length_High == 0) ? 1 : 0) + +/* Local Function Prototypes */ +static void SHA1Finalize (SHA1Context * context, uint8_t Pad_Byte); +static void SHA1PadMessage (SHA1Context *, uint8_t Pad_Byte); +static void SHA1ProcessMessageBlock (SHA1Context *); + +/* + * SHA1Reset + * + * Description: + * This function will initialize the SHA1Context in preparation + * for computing a new SHA1 message digest. + * + * Parameters: + * context: [in/out] + * The context to reset. + * + * Returns: + * sha Error Code. + * + */ +int +SHA1Reset (SHA1Context * context) +{ + if (!context) + return shaNull; + + context->Length_Low = 0; + context->Length_High = 0; + context->Message_Block_Index = 0; + + /* Initial Hash Values: FIPS-180-2 section 5.3.1 */ + context->Intermediate_Hash[0] = 0x67452301; + context->Intermediate_Hash[1] = 0xEFCDAB89; + context->Intermediate_Hash[2] = 0x98BADCFE; + context->Intermediate_Hash[3] = 0x10325476; + context->Intermediate_Hash[4] = 0xC3D2E1F0; + + context->Computed = 0; + context->Corrupted = 0; + + return shaSuccess; +} + +/* + * SHA1Input + * + * Description: + * This function accepts an array of octets as the next portion + * of the message. + * + * Parameters: + * context: [in/out] + * The SHA context to update + * message_array: [in] + * An array of characters representing the next portion of + * the message. + * length: [in] + * The length of the message in message_array + * + * Returns: + * sha Error Code. + * + */ +int +SHA1Input (SHA1Context * context, + const uint8_t * message_array, size_t length) +{ + uint32_t addTemp; + + if (!length) + return shaSuccess; + + if (!context || !message_array) + return shaNull; + + if (context->Computed) + { + context->Corrupted = shaStateError; + return shaStateError; + } + + if (context->Corrupted) + return context->Corrupted; + + while (length-- && !context->Corrupted) + { + context->Message_Block[context->Message_Block_Index++] = + (*message_array & 0xFF); + + if (!SHA1AddLength (context, 8) && + (context->Message_Block_Index == SHA1_Message_Block_Size)) + SHA1ProcessMessageBlock (context); + + message_array++; + } + + return shaSuccess; +} + +/* + * SHA1FinalBits + * + * Description: + * This function will add in any final bits of the message. + * + * Parameters: + * context: [in/out] + * The SHA context to update + * message_bits: [in] + * The final bits of the message, in the upper portion of the + * byte. (Use 0b###00000 instead of 0b00000### to input the + * three bits ###.) + * length: [in] + * The number of bits in message_bits, between 1 and 7. + * + * Returns: + * sha Error Code. + */ +int +SHA1FinalBits (SHA1Context * context, const uint8_t message_bits, + size_t length) +{ + uint32_t addTemp; + uint8_t masks[8] = { + /* 0 0b00000000 */ 0x00, /* 1 0b10000000 */ 0x80, + /* 2 0b11000000 */ 0xC0, /* 3 0b11100000 */ 0xE0, + /* 4 0b11110000 */ 0xF0, /* 5 0b11111000 */ 0xF8, + /* 6 0b11111100 */ 0xFC, /* 7 0b11111110 */ 0xFE + }; + uint8_t markbit[8] = { + /* 0 0b10000000 */ 0x80, /* 1 0b01000000 */ 0x40, + /* 2 0b00100000 */ 0x20, /* 3 0b00010000 */ 0x10, + /* 4 0b00001000 */ 0x08, /* 5 0b00000100 */ 0x04, + /* 6 0b00000010 */ 0x02, /* 7 0b00000001 */ 0x01 + }; + + if (!length) + return shaSuccess; + + if (!context) + return shaNull; + + if (context->Computed || (length >= 8) || (length == 0)) + { + context->Corrupted = shaStateError; + return shaStateError; + } + + if (context->Corrupted) + return context->Corrupted; + + SHA1AddLength (context, length); + SHA1Finalize (context, + (uint8_t) ((message_bits & masks[length]) | markbit[length])); + + return shaSuccess; +} + +/* + * SHA1Result + * + * Description: + * This function will return the 160-bit message digest into the + * Message_Digest array provided by the caller. + * NOTE: The first octet of hash is stored in the 0th element, + * the last octet of hash in the 19th element. + * + * Parameters: + * context: [in/out] + * The context to use to calculate the SHA-1 hash. + * Message_Digest: [out] + * Where the digest is returned. + * + * Returns: + * sha Error Code. + * + */ +int +SHA1Result (SHA1Context * context, uint8_t Message_Digest[SHA1HashSize]) +{ + int i; + + if (!context || !Message_Digest) + return shaNull; + + if (context->Corrupted) + return context->Corrupted; + + if (!context->Computed) + SHA1Finalize (context, 0x80); + + for (i = 0; i < SHA1HashSize; ++i) + Message_Digest[i] = (uint8_t) (context->Intermediate_Hash[i >> 2] + >> 8 * (3 - (i & 0x03))); + + return shaSuccess; +} + +/* + * SHA1Finalize + * + * Description: + * This helper function finishes off the digest calculations. + * + * Parameters: + * context: [in/out] + * The SHA context to update + * Pad_Byte: [in] + * The last byte to add to the digest before the 0-padding + * and length. This will contain the last bits of the message + * followed by another single bit. If the message was an + * exact multiple of 8-bits long, Pad_Byte will be 0x80. + * + * Returns: + * sha Error Code. + * + */ +static void +SHA1Finalize (SHA1Context * context, uint8_t Pad_Byte) +{ + int i; + SHA1PadMessage (context, Pad_Byte); + /* message may be sensitive, clear it out */ + for (i = 0; i < SHA1_Message_Block_Size; ++i) + context->Message_Block[i] = 0; + context->Length_Low = 0; /* and clear length */ + context->Length_High = 0; + context->Computed = 1; +} + +/* + * SHA1PadMessage + * + * Description: + * According to the standard, the message must be padded to an + * even 512 bits. The first padding bit must be a '1'. The last + * 64 bits represent the length of the original message. All bits + * in between should be 0. This helper function will pad the + * message according to those rules by filling the Message_Block + * array accordingly. When it returns, it can be assumed that the + * message digest has been computed. + * + * Parameters: + * context: [in/out] + * The context to pad + * Pad_Byte: [in] + * The last byte to add to the digest before the 0-padding + * and length. This will contain the last bits of the message + * followed by another single bit. If the message was an + * exact multiple of 8-bits long, Pad_Byte will be 0x80. + * + * Returns: + * Nothing. + */ +static void +SHA1PadMessage (SHA1Context * context, uint8_t Pad_Byte) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index >= (SHA1_Message_Block_Size - 8)) + { + context->Message_Block[context->Message_Block_Index++] = Pad_Byte; + while (context->Message_Block_Index < SHA1_Message_Block_Size) + context->Message_Block[context->Message_Block_Index++] = 0; + + SHA1ProcessMessageBlock (context); + } + else + context->Message_Block[context->Message_Block_Index++] = Pad_Byte; + + while (context->Message_Block_Index < (SHA1_Message_Block_Size - 8)) + context->Message_Block[context->Message_Block_Index++] = 0; + + /* + * Store the message length as the last 8 octets + */ + context->Message_Block[56] = (uint8_t) (context->Length_High >> 24); + context->Message_Block[57] = (uint8_t) (context->Length_High >> 16); + context->Message_Block[58] = (uint8_t) (context->Length_High >> 8); + context->Message_Block[59] = (uint8_t) (context->Length_High); + context->Message_Block[60] = (uint8_t) (context->Length_Low >> 24); + context->Message_Block[61] = (uint8_t) (context->Length_Low >> 16); + context->Message_Block[62] = (uint8_t) (context->Length_Low >> 8); + context->Message_Block[63] = (uint8_t) (context->Length_Low); + + SHA1ProcessMessageBlock (context); +} + +/* + * SHA1ProcessMessageBlock + * + * Description: + * This helper function will process the next 512 bits of the + * message stored in the Message_Block array. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + * + * Comments: + * Many of the variable names in this code, especially the + * single character names, were used because those were the + * names used in the publication. + */ +static void +SHA1ProcessMessageBlock (SHA1Context * context) +{ + /* Constants defined in FIPS-180-2, section 4.2.1 */ + const uint32_t K[4] = { + 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 + }; + int t; /* Loop counter */ + uint32_t temp; /* Temporary word value */ + uint32_t W[80]; /* Word sequence */ + uint32_t A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for (t = 0; t < 16; t++) + { + W[t] = ((uint32_t) context->Message_Block[t * 4]) << 24; + W[t] |= ((uint32_t) context->Message_Block[t * 4 + 1]) << 16; + W[t] |= ((uint32_t) context->Message_Block[t * 4 + 2]) << 8; + W[t] |= ((uint32_t) context->Message_Block[t * 4 + 3]); + } + for (t = 16; t < 80; t++) + W[t] = SHA1_ROTL (1, W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]); + + A = context->Intermediate_Hash[0]; + B = context->Intermediate_Hash[1]; + C = context->Intermediate_Hash[2]; + D = context->Intermediate_Hash[3]; + E = context->Intermediate_Hash[4]; + + for (t = 0; t < 20; t++) + { + temp = SHA1_ROTL (5, A) + SHA_Ch (B, C, D) + E + W[t] + K[0]; + E = D; + D = C; + C = SHA1_ROTL (30, B); + B = A; + A = temp; + } + + for (t = 20; t < 40; t++) + { + temp = SHA1_ROTL (5, A) + SHA_Parity (B, C, D) + E + W[t] + K[1]; + E = D; + D = C; + C = SHA1_ROTL (30, B); + B = A; + A = temp; + } + + for (t = 40; t < 60; t++) + { + temp = SHA1_ROTL (5, A) + SHA_Maj (B, C, D) + E + W[t] + K[2]; + E = D; + D = C; + C = SHA1_ROTL (30, B); + B = A; + A = temp; + } + + for (t = 60; t < 80; t++) + { + temp = SHA1_ROTL (5, A) + SHA_Parity (B, C, D) + E + W[t] + K[3]; + E = D; + D = C; + C = SHA1_ROTL (30, B); + B = A; + A = temp; + } + + context->Intermediate_Hash[0] += A; + context->Intermediate_Hash[1] += B; + context->Intermediate_Hash[2] += C; + context->Intermediate_Hash[3] += D; + context->Intermediate_Hash[4] += E; + + context->Message_Block_Index = 0; +} + +#endif /* defined(USE_SHA1) && USE_SHA1 */ diff --git a/utils/iscsi-ls.c b/utils/iscsi-ls.c index a92ffde..3acd9bf 100644 --- a/utils/iscsi-ls.c +++ b/utils/iscsi-ls.c @@ -170,7 +170,8 @@ tur_try_again: printf("\n"); } -void list_luns(struct client_state *clnt, const char *target, const char *portal) +void list_luns(struct client_state *clnt, const char *target, const char *portal, + enum iscsi_chap_auth auth) { struct iscsi_context *iscsi; struct scsi_task *task; @@ -188,6 +189,7 @@ void list_luns(struct client_state *clnt, const char *target, const char *portal printf("Failed to create context\n"); exit(10); } + iscsi_set_auth(iscsi, auth); iscsi_set_initiator_username_pwd(iscsi, clnt->username, clnt->password); if (iscsi_set_targetname(iscsi, target)) { @@ -277,7 +279,7 @@ void discovery_cb(struct iscsi_context *iscsi, int status, void *command_data, v printf("Target:%s Portal:%s\n", addr->target_name, portal->portal); } if (showluns != 0) { - list_luns(private_data, addr->target_name, portal->portal); + list_luns(private_data, addr->target_name, portal->portal, iscsi_get_auth(iscsi)); } portal = portal->next; }