From 354bb40e75d94466e91fe6960523612c9d17ccfb Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Thu, 2 Nov 2017 23:11:29 +0300 Subject: Add implementation --- mysql/mysys_ssl/crypt_genhash_impl.cpp | 465 +++++++++ mysql/mysys_ssl/mf_tempdir.cpp | 100 ++ mysql/mysys_ssl/my_aes.cpp | 59 ++ mysql/mysys_ssl/my_aes_impl.h | 31 + mysql/mysys_ssl/my_aes_yassl.cpp | 244 +++++ mysql/mysys_ssl/my_default.cpp | 1523 ++++++++++++++++++++++++++++++ mysql/mysys_ssl/my_default_priv.h | 43 + mysql/mysys_ssl/my_getopt.cpp | 1615 ++++++++++++++++++++++++++++++++ mysql/mysys_ssl/my_md5.cpp | 68 ++ mysql/mysys_ssl/my_murmur3.cpp | 134 +++ mysql/mysys_ssl/my_rnd.cpp | 115 +++ mysql/mysys_ssl/my_sha1.cpp | 141 +++ mysql/mysys_ssl/my_sha2.cpp | 68 ++ 13 files changed, 4606 insertions(+) create mode 100644 mysql/mysys_ssl/crypt_genhash_impl.cpp create mode 100644 mysql/mysys_ssl/mf_tempdir.cpp create mode 100644 mysql/mysys_ssl/my_aes.cpp create mode 100644 mysql/mysys_ssl/my_aes_impl.h create mode 100644 mysql/mysys_ssl/my_aes_yassl.cpp create mode 100644 mysql/mysys_ssl/my_default.cpp create mode 100644 mysql/mysys_ssl/my_default_priv.h create mode 100644 mysql/mysys_ssl/my_getopt.cpp create mode 100644 mysql/mysys_ssl/my_md5.cpp create mode 100644 mysql/mysys_ssl/my_murmur3.cpp create mode 100644 mysql/mysys_ssl/my_rnd.cpp create mode 100644 mysql/mysys_ssl/my_sha1.cpp create mode 100644 mysql/mysys_ssl/my_sha2.cpp (limited to 'mysql/mysys_ssl') diff --git a/mysql/mysys_ssl/crypt_genhash_impl.cpp b/mysql/mysys_ssl/crypt_genhash_impl.cpp new file mode 100644 index 0000000..80b61dc --- /dev/null +++ b/mysql/mysys_ssl/crypt_genhash_impl.cpp @@ -0,0 +1,465 @@ +/* Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +// First include (the generated) my_config.h, to get correct platform defines. +#include "my_config.h" + +#ifdef HAVE_OPENSSL + +#ifdef HAVE_YASSL +#include +#include +#else +#include +#include +#endif + +#include "crypt_genhash_impl.h" + +#include "m_string.h" + +#include +#include +#include + +#ifdef HAVE_ALLOCA_H +#include +#endif + +#ifndef HAVE_YASSL +#define DIGEST_CTX SHA256_CTX +#define DIGESTInit SHA256_Init +#define DIGESTUpdate SHA256_Update +#define DIGESTFinal SHA256_Final +#define DIGEST_LEN SHA256_DIGEST_LENGTH +#else +#define DIGEST_CTX TaoCrypt::SHA256 +#define DIGEST_LEN 32 +void DIGESTInit(DIGEST_CTX *ctx) +{ + ctx->Init(); +} + +void DIGESTUpdate(DIGEST_CTX *ctx, const void *plaintext, int len) +{ + ctx->Update((const TaoCrypt::byte *)plaintext, len); +} + +void DIGESTFinal(void *txt, DIGEST_CTX *ctx) +{ + ctx->Final((TaoCrypt::byte *)txt); +} + +#endif // HAVE_YASSL + +static const char crypt_alg_magic[] = "$5"; + +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + + +/** + Size-bounded string copying and concatenation + This is a replacement for STRLCPY(3) +*/ + +size_t +strlcat(char *dst, const char *src, size_t siz) +{ + char *d= dst; + const char *s= src; + size_t n= siz; + size_t dlen; + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen= d - dst; + n= siz - dlen; + if (n == 0) + return(dlen + siz); + while (*s != '\0') + { + if (n != 1) + { + *d++= *s; + n--; + } + s++; + } + *d= '\0'; + return(dlen + (s - src)); /* count does not include NUL */ +} + +static const int crypt_alg_magic_len = sizeof (crypt_alg_magic) - 1; + +static unsigned char b64t[] = /* 0 ... 63 => ascii - 64 */ + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +#define b64_from_24bit(B2, B1, B0, N) \ +{ \ + uint32_t w = ((B2) << 16) | ((B1) << 8) | (B0); \ + int n = (N); \ + while (--n >= 0 && ctbufflen > 0) { \ + *p++ = b64t[w & 0x3f]; \ + w >>= 6; \ + ctbufflen--; \ +} \ +} + +#define ROUNDS "rounds=" +#define ROUNDSLEN (sizeof (ROUNDS) - 1) + +/** + Get the integer value after rounds= where ever it occurs in the string. + if the last char after the int is a , or $ that is fine anything else is an + error. +*/ +static uint getrounds(const char *s) +{ + const char *r; + const char *p; + char *e; + long val; + + if (s == NULL) + return (0); + + if ((r = strstr(s, ROUNDS)) == NULL) + { + return (0); + } + + if (strncmp(r, ROUNDS, ROUNDSLEN) != 0) + { + return (0); + } + + p= r + ROUNDSLEN; + errno= 0; + val= strtol(p, &e, 10); + /* + An error occurred or there is non-numeric stuff at the end + which isn't one of the crypt(3c) special chars ',' or '$' + */ + if (errno != 0 || val < 0 || !(*e == '\0' || *e == ',' || *e == '$')) + { + return (0); + } + + return ((uint32_t) val); +} + +/** + Finds the interval which envelopes the user salt in a crypt password + The crypt format is assumed to be $a$bbbb$cccccc\0 and the salt is found + by counting the delimiters and marking begin and end. + + @param salt_being[in] Pointer to start of crypt passwd + @param salt_being[out] Pointer to first byte of the salt + @param salt_end[in] Pointer to the last byte in passwd + @param salt_end[out] Pointer to the byte immediatly following the salt ($) + + @return The size of the salt identified +*/ + +int extract_user_salt(char **salt_begin, + char **salt_end) +{ + char *it= *salt_begin; + int delimiter_count= 0; + while(it != *salt_end) + { + if (*it == '$') + { + ++delimiter_count; + if (delimiter_count == 2) + { + *salt_begin= it + 1; + } + if (delimiter_count == 3) + break; + } + ++it; + } + *salt_end= it; + return *salt_end - *salt_begin; +} + +const char *sha256_find_digest(char *pass) +{ + int sz= strlen(pass); + return pass + sz - SHA256_HASH_LENGTH; +} + +/* + * Portions of the below code come from crypt_bsdmd5.so (bsdmd5.c) : + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * ---------------------------------------------------------------------------- + * + * $FreeBSD: crypt.c,v 1.5 1996/10/14 08:34:02 phk Exp $ + * + */ + +/* + * The below code implements the specification from: + * + * From http://people.redhat.com/drepper/SHA-crypt.txt + * + * Portions of the code taken from inspired by or verified against the + * source in the above document which is licensed as: + * + * "Released into the Public Domain by Ulrich Drepper ." + */ + +/* + Due to a Solaris namespace bug DS is a reserved word. To work around this + DS is undefined. +*/ +#undef DS + +/* ARGSUSED4 */ +extern "C" +char * +my_crypt_genhash(char *ctbuffer, + size_t ctbufflen, + const char *plaintext, + size_t plaintext_len, + const char *switchsalt, + const char **params) +{ + int salt_len; + size_t i; + char *salt; + unsigned char A[DIGEST_LEN]; + unsigned char B[DIGEST_LEN]; + unsigned char DP[DIGEST_LEN]; + unsigned char DS[DIGEST_LEN]; + DIGEST_CTX ctxA, ctxB, ctxC, ctxDP, ctxDS; + uint rounds = ROUNDS_DEFAULT; + int srounds = 0; + bool custom_rounds= false; + char *p; + char *P, *Pp; + char *S, *Sp; + + /* Refine the salt */ + salt = (char *)switchsalt; + + /* skip our magic string */ + if (strncmp(salt, crypt_alg_magic, crypt_alg_magic_len) == 0) + { + salt += crypt_alg_magic_len + 1; + } + + srounds = getrounds(salt); + if (srounds != 0) { + rounds = MAX(ROUNDS_MIN, MIN(srounds, ROUNDS_MAX)); + custom_rounds= true; + p = strchr(salt, '$'); + if (p != NULL) + salt = p + 1; + } + + salt_len = MIN(strcspn(salt, "$"), CRYPT_SALT_LENGTH); + //plaintext_len = strlen(plaintext); + + /* 1. */ + DIGESTInit(&ctxA); + + /* 2. The password first, since that is what is most unknown */ + DIGESTUpdate(&ctxA, plaintext, plaintext_len); + + /* 3. Then the raw salt */ + DIGESTUpdate(&ctxA, salt, salt_len); + + /* 4. - 8. */ + DIGESTInit(&ctxB); + DIGESTUpdate(&ctxB, plaintext, plaintext_len); + DIGESTUpdate(&ctxB, salt, salt_len); + DIGESTUpdate(&ctxB, plaintext, plaintext_len); + DIGESTFinal(B, &ctxB); + + /* 9. - 10. */ + for (i= plaintext_len; i > MIXCHARS; i -= MIXCHARS) + DIGESTUpdate(&ctxA, B, MIXCHARS); + DIGESTUpdate(&ctxA, B, i); + + /* 11. */ + for (i= plaintext_len; i > 0; i >>= 1) { + if ((i & 1) != 0) + { + DIGESTUpdate(&ctxA, B, MIXCHARS); + } + else + { + DIGESTUpdate(&ctxA, plaintext, plaintext_len); + } + } + + /* 12. */ + DIGESTFinal(A, &ctxA); + + /* 13. - 15. */ + DIGESTInit(&ctxDP); + for (i= 0; i < plaintext_len; i++) + DIGESTUpdate(&ctxDP, plaintext, plaintext_len); + DIGESTFinal(DP, &ctxDP); + + /* 16. */ + Pp= P= (char *)alloca(plaintext_len); + for (i= plaintext_len; i >= MIXCHARS; i -= MIXCHARS) + { + Pp= (char *)(memcpy(Pp, DP, MIXCHARS)) + MIXCHARS; + } + (void) memcpy(Pp, DP, i); + + /* 17. - 19. */ + DIGESTInit(&ctxDS); + for (i= 0; i < 16U + (uint8_t)A[0]; i++) + DIGESTUpdate(&ctxDS, salt, salt_len); + DIGESTFinal(DS, &ctxDS); + + /* 20. */ + Sp= S= (char *)alloca(salt_len); + for (i= salt_len; i >= MIXCHARS; i -= MIXCHARS) + { + Sp= (char *)(memcpy(Sp, DS, MIXCHARS)) + MIXCHARS; + } + (void) memcpy(Sp, DS, i); + + /* 21. */ + for (i= 0; i < rounds; i++) + { + DIGESTInit(&ctxC); + + if ((i & 1) != 0) + { + DIGESTUpdate(&ctxC, P, plaintext_len); + } + else + { + if (i == 0) + DIGESTUpdate(&ctxC, A, MIXCHARS); + else + DIGESTUpdate(&ctxC, DP, MIXCHARS); + } + + if (i % 3 != 0) { + DIGESTUpdate(&ctxC, S, salt_len); + } + + if (i % 7 != 0) { + DIGESTUpdate(&ctxC, P, plaintext_len); + } + + if ((i & 1) != 0) + { + if (i == 0) + DIGESTUpdate(&ctxC, A, MIXCHARS); + else + DIGESTUpdate(&ctxC, DP, MIXCHARS); + } + else + { + DIGESTUpdate(&ctxC, P, plaintext_len); + } + DIGESTFinal(DP, &ctxC); + } + + /* 22. Now make the output string */ + if (custom_rounds) + { + (void) my_snprintf(ctbuffer, ctbufflen, + "%s$rounds=%zu$", crypt_alg_magic, (size_t)rounds); + } + else + { + (void) my_snprintf(ctbuffer, ctbufflen, + "%s$", crypt_alg_magic); + } + (void) strncat(ctbuffer, (const char *)salt, salt_len); + (void) strlcat(ctbuffer, "$", ctbufflen); + + p= ctbuffer + strlen(ctbuffer); + ctbufflen -= strlen(ctbuffer); + + b64_from_24bit(DP[ 0], DP[10], DP[20], 4); + b64_from_24bit(DP[21], DP[ 1], DP[11], 4); + b64_from_24bit(DP[12], DP[22], DP[ 2], 4); + b64_from_24bit(DP[ 3], DP[13], DP[23], 4); + b64_from_24bit(DP[24], DP[ 4], DP[14], 4); + b64_from_24bit(DP[15], DP[25], DP[ 5], 4); + b64_from_24bit(DP[ 6], DP[16], DP[26], 4); + b64_from_24bit(DP[27], DP[ 7], DP[17], 4); + b64_from_24bit(DP[18], DP[28], DP[ 8], 4); + b64_from_24bit(DP[ 9], DP[19], DP[29], 4); + b64_from_24bit(0, DP[31], DP[30], 3); + *p= '\0'; + + (void) memset(A, 0, sizeof (A)); + (void) memset(B, 0, sizeof (B)); + (void) memset(DP, 0, sizeof (DP)); + (void) memset(DS, 0, sizeof (DS)); + + return (ctbuffer); +} + + +/** + Generate a random string using ASCII characters but avoid seperator character. + Stdlib rand and srand are used to produce pseudo random numbers between + with about 7 bit worth of entropty between 1-127. +*/ +extern "C" +void generate_user_salt(char *buffer, int buffer_len) +{ + char *end= buffer + buffer_len - 1; +#ifdef HAVE_YASSL + yaSSL::RAND_bytes((unsigned char *) buffer, buffer_len); +#else + RAND_bytes((unsigned char *) buffer, buffer_len); +#endif + + /* Sequence must be a legal UTF8 string */ + for (; buffer < end; buffer++) + { + *buffer &= 0x7f; + if (*buffer == '\0' || *buffer == '$') + *buffer= *buffer + 1; + } + /* Make sure the buffer is terminated properly */ + *end= '\0'; +} + +void xor_string(char *to, int to_len, char *pattern, int pattern_len) +{ + int loop= 0; + while(loop <= to_len) + { + *(to + loop) ^= *(pattern + loop % pattern_len); + ++loop; + } +} + +#endif // HAVE_OPENSSL diff --git a/mysql/mysys_ssl/mf_tempdir.cpp b/mysql/mysys_ssl/mf_tempdir.cpp new file mode 100644 index 0000000..2935509 --- /dev/null +++ b/mysql/mysys_ssl/mf_tempdir.cpp @@ -0,0 +1,100 @@ +/* Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "mysys_priv.h" +#include +#include "prealloced_array.h" +#include "mutex_lock.h" + +#if defined(_WIN32) +#define DELIM ';' +#else +#define DELIM ':' +#endif + +my_bool init_tmpdir(MY_TMPDIR *tmpdir, const char *pathlist) +{ + char *end, *copy; + char buff[FN_REFLEN]; + DBUG_ENTER("init_tmpdir"); + DBUG_PRINT("enter", ("pathlist: %s", pathlist ? pathlist : "NULL")); + + Prealloced_array full_list(key_memory_MY_TMPDIR_full_list); + + memset(tmpdir, 0, sizeof(*tmpdir)); + if (!pathlist || !pathlist[0]) + { + /* Get default temporary directory */ + pathlist=getenv("TMPDIR"); /* Use this if possible */ +#if defined(_WIN32) + if (!pathlist) + pathlist=getenv("TEMP"); + if (!pathlist) + pathlist=getenv("TMP"); +#endif + if (!pathlist || !pathlist[0]) + pathlist= DEFAULT_TMPDIR; + } + do + { + size_t length; + end=strcend(pathlist, DELIM); + strmake(buff, pathlist, (uint) (end-pathlist)); + length= cleanup_dirname(buff, buff); + if (!(copy= my_strndup(key_memory_MY_TMPDIR_full_list, + buff, length, MYF(MY_WME))) || + full_list.push_back(copy)) + DBUG_RETURN(TRUE); + pathlist=end+1; + } + while (*end); + + tmpdir->list= + static_cast(my_malloc(key_memory_MY_TMPDIR_full_list, + sizeof(char*) * full_list.size(), + MYF(MY_WME))); + if (tmpdir->list == NULL) + DBUG_RETURN(TRUE); + + mysql_mutex_init(key_TMPDIR_mutex, &tmpdir->mutex, MY_MUTEX_INIT_FAST); + memcpy(tmpdir->list, &full_list[0], sizeof(char*) * full_list.size()); + tmpdir->max= full_list.size() - 1; + tmpdir->cur= 0; + DBUG_RETURN(FALSE); +} + + +char *my_tmpdir(MY_TMPDIR *tmpdir) +{ + if (0 == tmpdir->max) + return tmpdir->list[0]; + + Mutex_lock(&tmpdir->mutex); + char *dir= tmpdir->list[tmpdir->cur]; + tmpdir->cur= (tmpdir->cur == tmpdir->max) ? 0 : tmpdir->cur + 1; + + return dir; +} + + +void free_tmpdir(MY_TMPDIR *tmpdir) +{ + if (tmpdir->list == NULL) + return; + for (uint i= 0; i <= tmpdir->max; i++) + my_free(tmpdir->list[i]); + my_free(tmpdir->list); + mysql_mutex_destroy(&tmpdir->mutex); +} diff --git a/mysql/mysys_ssl/my_aes.cpp b/mysql/mysys_ssl/my_aes.cpp new file mode 100644 index 0000000..f5ebe8b --- /dev/null +++ b/mysql/mysys_ssl/my_aes.cpp @@ -0,0 +1,59 @@ +/* Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +#include +#include +#include +#include "my_aes_impl.h" + + +/** + Transforms an arbitrary long key into a fixed length AES key + + AES keys are of fixed length. This routine takes an arbitrary long key + iterates over it in AES key length increment and XORs the bytes with the + AES key buffer being prepared. + The bytes from the last incomplete iteration are XORed to the start + of the key until their depletion. + Needed since crypto function routines expect a fixed length key. + + @param key [in] Key to use for real key creation + @param key_length [in] Length of the key + @param rkey [out] Real key (used by OpenSSL/YaSSL) + @param opmode [out] encryption mode +*/ + +void my_aes_create_key(const unsigned char *key, uint key_length, + uint8 *rkey, enum my_aes_opmode opmode) +{ + const uint key_size= my_aes_opmode_key_sizes[opmode] / 8; + uint8 *rkey_end; /* Real key boundary */ + uint8 *ptr; /* Start of the real key*/ + uint8 *sptr; /* Start of the working key */ + uint8 *key_end= ((uint8 *)key) + key_length; /* Working key boundary*/ + + rkey_end= rkey + key_size; + + memset(rkey, 0, key_size); /* Set initial key */ + + for (ptr= rkey, sptr= (uint8 *)key; sptr < key_end; ptr++, sptr++) + { + if (ptr == rkey_end) + /* Just loop over tmp_key until we used all key */ + ptr= rkey; + *ptr^= *sptr; + } +} diff --git a/mysql/mysys_ssl/my_aes_impl.h b/mysql/mysys_ssl/my_aes_impl.h new file mode 100644 index 0000000..2e18b2e --- /dev/null +++ b/mysql/mysys_ssl/my_aes_impl.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + +/** Maximum supported key kength */ +#define MAX_AES_KEY_LENGTH 256 + +/* TODO: remove in a future version */ +/* Guard against using an old export control restriction #define */ +#ifdef AES_USE_KEY_BITS +#error AES_USE_KEY_BITS not supported +#endif + +extern uint *my_aes_opmode_key_sizes; + + +void my_aes_create_key(const unsigned char *key, uint key_length, + uint8 *rkey, enum my_aes_opmode opmode); + diff --git a/mysql/mysys_ssl/my_aes_yassl.cpp b/mysql/mysys_ssl/my_aes_yassl.cpp new file mode 100644 index 0000000..f387e34 --- /dev/null +++ b/mysql/mysys_ssl/my_aes_yassl.cpp @@ -0,0 +1,244 @@ +/* Copyright (c) 2015, 2016 Oracle and/or its affiliates. All rights reserved. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include +#include +#include +#include "my_aes_impl.h" + +#include "aes.hpp" +#include "openssl/ssl.h" + +/* keep in sync with enum my_aes_opmode in my_aes.h */ +const char *my_aes_opmode_names[]= +{ + "aes-128-ecb", + "aes-192-ecb", + "aes-256-ecb", + "aes-128-cbc", + "aes-192-cbc", + "aes-256-cbc", + NULL /* needed for the type enumeration */ +}; + + +/* keep in sync with enum my_aes_opmode in my_aes.h */ +static uint my_aes_opmode_key_sizes_impl[]= +{ + 128 /* aes-128-ecb */, + 192 /* aes-192-ecb */, + 256 /* aes-256-ecb */, + 128 /* aes-128-cbc */, + 192 /* aes-192-cbc */, + 256 /* aes-256-cbc */, +}; + +uint *my_aes_opmode_key_sizes= my_aes_opmode_key_sizes_impl; + + +template +class MyCipherCtx +{ +public: + MyCipherCtx(enum my_aes_opmode mode) : m_mode(mode) + { + switch (m_mode) + { + case my_aes_128_ecb: + case my_aes_192_ecb: + case my_aes_256_ecb: + m_need_iv= false; + break; + default: + m_need_iv= true; + break; + } + } + + bool SetKey(const unsigned char *key, uint block_size, + const unsigned char *iv) + { + if (m_need_iv) + { + if (!iv) + return TRUE; + cbc.SetKey(key, block_size, iv); + } + else + ecb.SetKey(key, block_size); + return false; + } + + void Process(unsigned char *dest, const unsigned char * source, + uint block_size) + { + if (m_need_iv) + cbc.Process(dest, source, block_size); + else + ecb.Process(dest, source, block_size); + } + + bool needs_iv() const + { + return m_need_iv; + } + +private: + /* we initialize the two classes to avoid dynamic allocation */ + TaoCrypt::BlockCipher ecb; + TaoCrypt::BlockCipher cbc; + enum my_aes_opmode m_mode; + bool m_need_iv; +}; + + +int my_aes_encrypt(const unsigned char *source, uint32 source_length, + unsigned char *dest, + const unsigned char *key, uint32 key_length, + enum my_aes_opmode mode, const unsigned char *iv, + bool padding) +{ + MyCipherCtx enc(mode); + + /* 128 bit block used for padding */ + unsigned char block[MY_AES_BLOCK_SIZE]; + uint num_blocks; /* number of complete blocks */ + uint i; + /* predicted real key size */ + const uint key_size= my_aes_opmode_key_sizes[mode] / 8; + /* The real key to be used for encryption */ + unsigned char rkey[MAX_AES_KEY_LENGTH / 8]; + + my_aes_create_key(key, key_length, rkey, mode); + + if (enc.SetKey(rkey, key_size, iv)) + return MY_AES_BAD_DATA; + + num_blocks= source_length / MY_AES_BLOCK_SIZE; + + /* Encode all complete blocks */ + for (i = num_blocks; i > 0; + i--, source+= MY_AES_BLOCK_SIZE, dest+= MY_AES_BLOCK_SIZE) + enc.Process(dest, source, MY_AES_BLOCK_SIZE); + + /* If no padding, return here */ + if (!padding) + return (int) (MY_AES_BLOCK_SIZE * num_blocks); + /* + Re-implement standard PKCS padding for the last block. + Pad the last incomplete data block (even if empty) with bytes + equal to the size of extra padding stored into that last packet. + This also means that there will always be one more block, + even if the source data size is dividable by the AES block size. + */ + unsigned char pad_len= + MY_AES_BLOCK_SIZE - (source_length - MY_AES_BLOCK_SIZE * num_blocks); + memcpy(block, source, MY_AES_BLOCK_SIZE - pad_len); + memset(block + MY_AES_BLOCK_SIZE - pad_len, pad_len, pad_len); + + enc.Process(dest, block, MY_AES_BLOCK_SIZE); + + /* we've added a block */ + num_blocks+= 1; + + return (int) (MY_AES_BLOCK_SIZE * num_blocks); +} + +int my_aes_decrypt(const unsigned char *source, uint32 source_length, + unsigned char *dest, + const unsigned char *key, uint32 key_length, + enum my_aes_opmode mode, const unsigned char *iv, + bool padding) +{ + MyCipherCtx dec(mode); + /* 128 bit block used for padding */ + uint8 block[MY_AES_BLOCK_SIZE]; + uint32 num_blocks; /* Number of complete blocks */ + int i; + /* predicted real key size */ + const uint key_size= my_aes_opmode_key_sizes[mode] / 8; + /* The real key to be used for decryption */ + unsigned char rkey[MAX_AES_KEY_LENGTH / 8]; + + my_aes_create_key(key, key_length, rkey, mode); + dec.SetKey(rkey, key_size, iv); + + num_blocks= source_length / MY_AES_BLOCK_SIZE; + + /* + Input size has to be a multiple of the AES block size. + And, due to the standard PKCS padding, at least one block long. + */ + if ((source_length != num_blocks * MY_AES_BLOCK_SIZE) || num_blocks == 0) + return MY_AES_BAD_DATA; + + /* Decode all but the last block */ + for (i= padding? num_blocks - 1: num_blocks; i > 0; + i--, source+= MY_AES_BLOCK_SIZE, dest+= MY_AES_BLOCK_SIZE) + dec.Process(dest, source, MY_AES_BLOCK_SIZE); + + /* If no padding, return here. */ + if (!padding) + return MY_AES_BLOCK_SIZE * num_blocks; + + /* unwarp the standard PKCS padding */ + dec.Process(block, source, MY_AES_BLOCK_SIZE); + + /* Use last char in the block as size */ + uint8 pad_len = block[MY_AES_BLOCK_SIZE - 1]; + + if (pad_len > MY_AES_BLOCK_SIZE) + return MY_AES_BAD_DATA; + /* We could also check whole padding but we do not really need this */ + + memcpy(dest, block, MY_AES_BLOCK_SIZE - pad_len); + return MY_AES_BLOCK_SIZE * num_blocks - pad_len; +} + +/** + Get size of buffer which will be large enough for encrypted data + + SYNOPSIS + my_aes_get_size() + @param source_length [in] Length of data to be encrypted + @param mode encryption mode + + @return Size of buffer required to store encrypted data +*/ + +int my_aes_get_size(uint32 source_length, my_aes_opmode opmode) +{ + return MY_AES_BLOCK_SIZE * (source_length / MY_AES_BLOCK_SIZE) + + MY_AES_BLOCK_SIZE; +} + +/** + Return true if the AES cipher and block mode requires an IV + + SYNOPSIS + my_aes_needs_iv() + @param mode encryption mode + + @retval TRUE IV needed + @retval FALSE IV not needed +*/ + +my_bool my_aes_needs_iv(my_aes_opmode opmode) +{ + MyCipherCtx enc(opmode); + + return enc.needs_iv() ? TRUE : FALSE; +} + diff --git a/mysql/mysys_ssl/my_default.cpp b/mysql/mysys_ssl/my_default.cpp new file mode 100644 index 0000000..5a45200 --- /dev/null +++ b/mysql/mysys_ssl/my_default.cpp @@ -0,0 +1,1523 @@ +/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ + +/**************************************************************************** + Add all options from files named "group".cnf from the default_directories + before the command line arguments. + On Windows defaults will also search in the Windows directory for a file + called 'group'.ini + As long as the program uses the last argument for conflicting + options one only have to add a call to "load_defaults" to enable + use of default values. + pre- and end 'blank space' are removed from options and values. The + following escape sequences are recognized in values: \b \t \n \r \\ + + The following arguments are handled automatically; If used, they must be + first argument on the command line! + --no-defaults ; no options are read, except for the ones provided in the + login file. + --defaults-file=full-path-to-default-file ; Only this file will be read. + --defaults-extra-file=full-path-to-default-file ; Read this file before ~/ + --defaults-group-suffix ; Also read groups with concat(group, suffix) + --print-defaults ; Print the modified command line and exit + --login-path=login-path-name ; Read options under login-path-name from + the login file. +****************************************************************************/ + +#include "../mysys/mysys_priv.h" +#include "my_default.h" +#include "my_default_priv.h" +#include "m_string.h" +#include "m_ctype.h" +#include +#include +#include "mysql/psi/mysql_file.h" +#ifdef _WIN32 +#include +#endif + +#include "prealloced_array.h" + +C_MODE_START +#ifdef HAVE_PSI_INTERFACE +extern PSI_file_key key_file_cnf; +#endif +PSI_memory_key key_memory_defaults; +C_MODE_END + +/** + arguments separator + + load_defaults() loads arguments from config file and put them + before the arguments from command line, this separator is used to + separate the arguments loaded from config file and arguments user + provided on command line. + + Options with value loaded from config file are always in the form + '--option=value', while for command line options, the value can be + given as the next argument. Thus we used a separator so that + handle_options() can distinguish them. + + Note: any other places that does not need to distinguish them + should skip the separator. + + The content of arguments separator does not matter, one should only + check the pointer, use "----args-separator----" here to ease debug + if someone misused it. + + The args seprator will only be added when + my_getopt_use_args_seprator is set to TRUE before calling + load_defaults(); + + See BUG#25192 +*/ +static const char *args_separator= "----args-separator----"; +inline static void set_args_separator(char** arg) +{ + DBUG_ASSERT(my_getopt_use_args_separator); + *arg= (char*)args_separator; +} + +/* + This flag indicates that the argument separator string + (args_separator) should be added to the list of arguments, + in order to separate arguments received from config file + and command line. +*/ +my_bool my_getopt_use_args_separator= FALSE; +my_bool my_getopt_is_args_separator(const char* arg) +{ + return (arg == args_separator); +} +const char *my_defaults_file=0; +const char *my_defaults_group_suffix=0; +const char *my_defaults_extra_file=0; + +static const char *my_login_path= 0; + +static char my_defaults_file_buffer[FN_REFLEN]; +static char my_defaults_extra_file_buffer[FN_REFLEN]; + +static my_bool defaults_already_read= FALSE; + +/* Which directories are searched for options (and in which order) */ + +#define MAX_DEFAULT_DIRS 6 +#define DEFAULT_DIRS_SIZE (MAX_DEFAULT_DIRS + 1) /* Terminate with NULL */ +static const char **default_directories = NULL; + +#ifdef _WIN32 +static const char *f_extensions[]= { ".ini", ".cnf", 0 }; +#define NEWLINE "\r\n" +#else +static const char *f_extensions[]= { ".cnf", 0 }; +#define NEWLINE "\n" +#endif + +extern "C" { +static int handle_default_option(void *in_ctx, const char *group_name, + const char *option); +} + +/* + This structure defines the context that we pass to callback + function 'handle_default_option' used in search_default_file + to process each option. This context is used if search_default_file + was called from load_defaults. +*/ + +typedef Prealloced_array My_args; +struct handle_option_ctx +{ + MEM_ROOT *alloc; + My_args *m_args; + TYPELIB *group; +}; + +static int search_default_file(Process_option_func func, void *func_ctx, + const char *dir, const char *config_file, + my_bool is_login_file); +static int search_default_file_with_ext(Process_option_func func, + void *func_ctx, + const char *dir, const char *ext, + const char *config_file, + int recursion_level, + my_bool is_login_file); +static my_bool mysql_file_getline(char *str, int size, MYSQL_FILE *file, + my_bool is_login_file); + +/** + Create the list of default directories. + + @param alloc MEM_ROOT where the list of directories is stored + + @details + The directories searched, in order, are: + - Windows: GetSystemWindowsDirectory() + - Windows: GetWindowsDirectory() + - Windows: C:/ + - Windows: Directory above where the executable is located + - Unix: /etc/ + - Unix: /etc/mysql/ + - Unix: --sysconfdir= (compile-time option) + - ALL: getenv("MYSQL_HOME") + - ALL: --defaults-extra-file= (run-time option) + - Unix: ~/ + + On all systems, if a directory is already in the list, it will be moved + to the end of the list. This avoids reading defaults files multiple times, + while ensuring the correct precedence. + + @retval NULL Failure (out of memory, probably) + @retval other Pointer to NULL-terminated array of default directories +*/ + +static const char **init_default_directories(MEM_ROOT *alloc); + + +static char *remove_end_comment(char *ptr); + + +/* + Expand a file name so that the current working directory is added if + the name is relative. + + RETURNS + 0 All OK + 2 Out of memory or path too long + 3 Not able to get working directory + */ + +static int +fn_expand(const char *filename, char *result_buf) +{ + char dir[FN_REFLEN]; + const int flags= MY_UNPACK_FILENAME | MY_SAFE_PATH | MY_RELATIVE_PATH; + DBUG_ENTER("fn_expand"); + DBUG_PRINT("enter", ("filename: %s, result_buf: 0x%lx", + filename, (unsigned long) result_buf)); + if (my_getwd(dir, sizeof(dir), MYF(0))) + DBUG_RETURN(3); + DBUG_PRINT("debug", ("dir: %s", dir)); + if (fn_format(result_buf, filename, dir, "", flags) == NULL) + DBUG_RETURN(2); + DBUG_PRINT("return", ("result: %s", result_buf)); + DBUG_RETURN(0); +} + +/* + Process config files in default directories. + + SYNOPSIS + my_search_option_files() + conf_file Basename for configuration file to search for. + If this is a path, then only this file is read. + argc Pointer to argc of original program + argv Pointer to argv of original program + args_used Pointer to variable for storing the number of + arguments used. + func Pointer to the function to process options + func_ctx It's context. Usually it is the structure to + store additional options. + default_directories List of default directories. + found_no_defaults TRUE, if --no-defaults is specified. + + DESCRIPTION + Process the default options from argc & argv + Read through each found config file looks and calls 'func' to process + each option. This function also reads options from login file. + + NOTES + --defaults-group-suffix is only processed if we are called from + load_defaults(). + + + RETURN + 0 ok + 1 given cinf_file doesn't exist + 2 out of memory + 3 Can't get current working directory + + The global variable 'my_defaults_group_suffix' is updated with value for + --defaults_group_suffix +*/ + +int my_search_option_files(const char *conf_file, int *argc, char ***argv, + uint *args_used, Process_option_func func, + void *func_ctx, const char **default_directories, + my_bool is_login_file, my_bool found_no_defaults) +{ + const char **dirs, *forced_default_file, *forced_extra_defaults; + int error= 0; + DBUG_ENTER("my_search_option_files"); + + /* Skip for login file. */ + if (! is_login_file) + { + /* Check if we want to force the use a specific default file */ + *args_used+= get_defaults_options(*argc - *args_used, *argv + *args_used, + (char **) &forced_default_file, + (char **) &forced_extra_defaults, + (char **) &my_defaults_group_suffix, + (char **) &my_login_path, found_no_defaults); + + if (! my_defaults_group_suffix) + my_defaults_group_suffix= getenv(STRINGIFY_ARG(DEFAULT_GROUP_SUFFIX_ENV)); + + if (forced_extra_defaults && !defaults_already_read) + { + int error= fn_expand(forced_extra_defaults, + my_defaults_extra_file_buffer); + if (error) + DBUG_RETURN(error); + + my_defaults_extra_file= my_defaults_extra_file_buffer; + } + + if (forced_default_file && !defaults_already_read) + { + int error= fn_expand(forced_default_file, my_defaults_file_buffer); + if (error) + DBUG_RETURN(error); + my_defaults_file= my_defaults_file_buffer; + } + + defaults_already_read= TRUE; + + /* + We can only handle 'defaults-group-suffix' if we are called from + load_defaults() as otherwise we can't know the type of 'func_ctx' + */ + + if (my_defaults_group_suffix && func == handle_default_option) + { + /* Handle --defaults-group-suffix= */ + uint i; + const char **extra_groups; + const size_t instance_len= strlen(my_defaults_group_suffix); + struct handle_option_ctx *ctx= (struct handle_option_ctx*) func_ctx; + char *ptr; + TYPELIB *group= ctx->group; + + if (!(extra_groups= + (const char**)alloc_root(ctx->alloc, + (2*group->count+1)*sizeof(char*)))) + DBUG_RETURN(2); + + for (i= 0; i < group->count; i++) + { + size_t len; + extra_groups[i]= group->type_names[i]; /** copy group */ + + len= strlen(extra_groups[i]); + if (!(ptr= (char *) alloc_root(ctx->alloc, + (uint) (len + instance_len + 1)))) + DBUG_RETURN(2); + + extra_groups[i+group->count]= ptr; + + /** Construct new group */ + memcpy(ptr, extra_groups[i], len); + memcpy(ptr+len, my_defaults_group_suffix, instance_len+1); + } + + group->count*= 2; + group->type_names= extra_groups; + group->type_names[group->count]= 0; + } + } + else if (my_login_path && func == handle_default_option) + { + /* Handle --login_path= */ + uint i; + size_t len; + const char **extra_groups; + size_t instance_len= 0; + struct handle_option_ctx *ctx= (struct handle_option_ctx*) func_ctx; + char *ptr; + TYPELIB *group= ctx->group; + + if (!(extra_groups= (const char**)alloc_root(ctx->alloc, + (group->count + 3) + * sizeof(char *)))) + DBUG_RETURN(2); + + for (i= 0; i < group->count; i++) + { + extra_groups[i]= group->type_names[i]; /** copy group */ + } + + extra_groups[i]= my_login_path; + + if (my_defaults_group_suffix && func == handle_default_option) + { + instance_len= strlen(my_defaults_group_suffix); + len= strlen(extra_groups[i]); + + if (!(ptr= (char *) alloc_root(ctx->alloc, + (uint) (len + instance_len + 1)))) + DBUG_RETURN(2); + + extra_groups[i + 1]= ptr; + + /** Construct new group */ + memcpy(ptr, extra_groups[i], len); + memcpy(ptr+len, my_defaults_group_suffix, instance_len + 1); + group->count += 1; + } + + group->count += 1; + group->type_names= extra_groups; + group->type_names[group->count]= 0; + } + + // If conf_file is an absolute path, we only read it + if (dirname_length(conf_file)) + { + if ((error= search_default_file(func, func_ctx, NullS, conf_file, + is_login_file)) < 0) + goto err; + } + // If my defaults file is set (from a previous run), we read it + else if (my_defaults_file) + { + if ((error= search_default_file_with_ext(func, func_ctx, "", "", + my_defaults_file, 0, + is_login_file)) < 0) + goto err; + if (error > 0) + { + my_message_local(ERROR_LEVEL, + "Could not open required defaults file: %s", + my_defaults_file); + goto err; + } + } + else if (! found_no_defaults) + { + for (dirs= default_directories ; *dirs; dirs++) + { + if (**dirs) + { + if (search_default_file(func, func_ctx, *dirs, conf_file, + is_login_file) < 0) + goto err; + } + else if (my_defaults_extra_file) + { + if ((error= search_default_file_with_ext(func, func_ctx, "", "", + my_defaults_extra_file, 0, + is_login_file)) < 0) + goto err; /* Fatal error */ + if (error > 0) + { + my_message_local(ERROR_LEVEL, + "Could not open required defaults file: %s", + my_defaults_extra_file); + goto err; + } + } + } + } + + DBUG_RETURN(0); + +err: + my_message_local(ERROR_LEVEL, + "Fatal error in defaults handling. Program aborted!"); + DBUG_RETURN(1); +} + + +/* + The option handler for load_defaults. + + SYNOPSIS + handle_deault_option() + in_ctx Handler context. In this case it is a + handle_option_ctx structure. + group_name The name of the group the option belongs to. + option The very option to be processed. It is already + prepared to be used in argv (has -- prefix). If it + is NULL, we are handling a new group (section). + + DESCRIPTION + This handler checks whether a group is one of the listed and adds an option + to the array if yes. Some other handler can record, for instance, all + groups and their options, not knowing in advance the names and amount of + groups. + + RETURN + 0 - ok + 1 - error occured +*/ + +static int handle_default_option(void *in_ctx, const char *group_name, + const char *option) +{ + char *tmp; + struct handle_option_ctx *ctx= (struct handle_option_ctx *) in_ctx; + + if (!option) + return 0; + + if (find_type((char *)group_name, ctx->group, FIND_TYPE_NO_PREFIX)) + { + if (!(tmp= (char *) alloc_root(ctx->alloc, strlen(option) + 1))) + return 1; + if (ctx->m_args->push_back(tmp)) + return 1; + my_stpcpy(tmp, option); + } + + return 0; +} + + +/* + Gets options from the command line, however if --no-defaults + option is used, --defaults-file & --defaults-extra-file options + would be ignored. + + SYNOPSIS + get_defaults_options() + argc Pointer to argc of original program + argv Pointer to argv of original program + defaults --defaults-file option + extra_defaults --defaults-extra-file option + group_suffix --defaults-group-suffix option + login_path --login-path option + + RETURN + # Number of arguments used from *argv + defaults and extra_defaults will be set to option of the appropriate + items of argv array, or to NULL if there are no such options +*/ + +int get_defaults_options(int argc, char **argv, + char **defaults, + char **extra_defaults, + char **group_suffix, + char **login_path, + my_bool found_no_defaults) +{ + int org_argc= argc, prev_argc= 0, default_option_count= 0; + *defaults= *extra_defaults= *group_suffix= *login_path= 0; + + while (argc >= 2 && argc != prev_argc) + { + /* Skip program name or previously handled argument */ + argv++; + prev_argc= argc; /* To check if we found */ + /* --no-defaults is always the first option. */ + if (is_prefix(*argv,"--no-defaults") && ! default_option_count) + { + argc--; + default_option_count ++; + continue; + } + if (!*defaults && is_prefix(*argv, "--defaults-file=") && ! found_no_defaults) + { + *defaults= *argv + sizeof("--defaults-file=")-1; + argc--; + default_option_count ++; + continue; + } + if (!*extra_defaults && is_prefix(*argv, "--defaults-extra-file=") + && ! found_no_defaults) + { + *extra_defaults= *argv + sizeof("--defaults-extra-file=")-1; + argc--; + default_option_count ++; + continue; + } + if (!*group_suffix && is_prefix(*argv, "--defaults-group-suffix=")) + { + *group_suffix= *argv + sizeof("--defaults-group-suffix=")-1; + argc--; + default_option_count ++; + continue; + } + if (!*login_path && is_prefix(*argv, "--login-path=")) + { + *login_path= *argv + sizeof("--login-path=")-1; + argc--; + default_option_count ++; + continue; + } + } + return org_argc - argc; +} + +/* + Wrapper around my_load_defaults() for interface compatibility. + + SYNOPSIS + load_defaults() + conf_file Basename for configuration file to search for. + If this is a path, then only this file is read. + groups Which [group] entrys to read. + Points to an null terminated array of pointers + argc Pointer to argc of original program + argv Pointer to argv of original program + + NOTES + + This function is NOT thread-safe as it uses a global pointer internally. + See also notes for my_load_defaults(). + + RETURN + 0 ok + 1 The given conf_file didn't exists +*/ +int load_defaults(const char *conf_file, const char **groups, + int *argc, char ***argv) +{ + return my_load_defaults(conf_file, groups, argc, argv, &default_directories); +} + +/** A global to turn off or on reading the mylogin file. On by default */ +my_bool my_defaults_read_login_file= TRUE; +/* + Read options from configurations files + + SYNOPSIS + my_load_defaults() + conf_file Basename for configuration file to search for. + If this is a path, then only this file is read. + groups Which [group] entrys to read. + Points to an null terminated array of pointers + argc Pointer to argc of original program + argv Pointer to argv of original program + default_directories Pointer to a location where a pointer to the list + of default directories will be stored + + IMPLEMENTATION + + Read options from configuration files and put them BEFORE the arguments + that are already in argc and argv. This way the calling program can + easily command line options override options in configuration files + + NOTES + In case of fatal error, the function will print a warning and do + exit(1) + + To free used memory one should call free_defaults() with the argument + that was put in *argv + + RETURN + - If successful, 0 is returned. If 'default_directories' is not NULL, + a pointer to the array of default directory paths is stored to a location + it points to. That stored value must be passed to my_search_option_files() + later. + + - 1 is returned if the given conf_file didn't exist. In this case, the + value pointed to by default_directories is undefined. +*/ + + +int my_load_defaults(const char *conf_file, const char **groups, + int *argc, char ***argv, const char ***default_directories) +{ + My_args my_args(key_memory_defaults); + TYPELIB group; + my_bool found_print_defaults= 0; + uint args_used= 0; + int error= 0; + MEM_ROOT alloc; + char *ptr,**res; + struct handle_option_ctx ctx; + const char **dirs; + char my_login_file[FN_REFLEN]; + my_bool found_no_defaults= false; + uint args_sep= my_getopt_use_args_separator ? 1 : 0; + DBUG_ENTER("load_defaults"); + + init_alloc_root(key_memory_defaults, &alloc,512,0); + if ((dirs= init_default_directories(&alloc)) == NULL) + goto err; + /* + Check if the user doesn't want any default option processing + --no-defaults is always the first option + */ + if (*argc >= 2 && !strcmp(argv[0][1], "--no-defaults")) + found_no_defaults= TRUE; + + group.count=0; + group.name= "defaults"; + group.type_names= groups; + + for (; *groups ; groups++) + group.count++; + + ctx.alloc= &alloc; + ctx.m_args= &my_args; + ctx.group= &group; + + if ((error= my_search_option_files(conf_file, argc, argv, + &args_used, handle_default_option, + (void *) &ctx, dirs, false, found_no_defaults))) + { + free_root(&alloc,MYF(0)); + DBUG_RETURN(error); + } + + if (my_defaults_read_login_file) + { + /* Read options from login group. */ + if (my_default_get_login_file(my_login_file, sizeof(my_login_file)) && + (error= my_search_option_files(my_login_file, argc, argv, &args_used, + handle_default_option, (void *) &ctx, + dirs, true, found_no_defaults))) + { + free_root(&alloc, MYF(0)); + DBUG_RETURN(error); + } + } + /* + Here error contains <> 0 only if we have a fully specified conf_file + or a forced default file + */ + if (!(ptr=(char*) + alloc_root(&alloc,sizeof(alloc)+ + (my_args.size() + *argc + 1 + args_sep) *sizeof(char*)))) + goto err; + res= (char**) (ptr+sizeof(alloc)); + + /* copy name + found arguments + command line arguments to new array */ + res[0]= argv[0][0]; /* Name MUST be set, even by embedded library */ + if (!my_args.empty()) + memcpy((res+1), &my_args[0], my_args.size() * sizeof(char*)); + /* Skip --defaults-xxx options */ + (*argc)-= args_used; + (*argv)+= args_used; + + /* + Check if we wan't to see the new argument list + This options must always be the last of the default options + */ + if (*argc >= 2 && !strcmp(argv[0][1],"--print-defaults")) + { + found_print_defaults=1; + --*argc; ++*argv; /* skip argument */ + } + + if (my_getopt_use_args_separator) + { + /* set arguments separator for arguments from config file and + command line */ + set_args_separator(&res[my_args.size() + 1]); + } + + if (*argc) + memcpy((uchar*) (res + 1 + my_args.size() + args_sep), + (char*) ((*argv)+1), + (*argc-1)*sizeof(char*)); + res[my_args.size() + *argc + args_sep]= 0; /* last null */ + + (*argc)+= my_args.size() + args_sep; + *argv= res; + *(MEM_ROOT*) ptr= alloc; /* Save alloc root for free */ + + if (default_directories) + *default_directories= dirs; + + if (found_no_defaults) + DBUG_RETURN(0); + + if (found_print_defaults) + { + int i; + printf("%s would have been started with the following arguments:\n", + **argv); + for (i=1 ; i < *argc ; i++) + if (!my_getopt_is_args_separator((*argv)[i])) /* skip arguments separator */ + { + if(strncmp((*argv)[i], "--password", 10) == 0) + printf("%s ", "--password=*****"); + else + printf("%s ", (*argv)[i]); + } + puts(""); + exit(0); + } + + DBUG_RETURN(0); + + err: + my_message_local(ERROR_LEVEL, + "Fatal error in defaults handling. Program aborted!"); + exit(1); + return 0; /* Keep compiler happy */ +} + + +void free_defaults(char **argv) +{ + MEM_ROOT ptr; + memcpy(&ptr, ((char *) argv) - sizeof(ptr), sizeof(ptr)); + free_root(&ptr,MYF(0)); +} + + +static int search_default_file(Process_option_func opt_handler, + void *handler_ctx, + const char *dir, + const char *config_file, + my_bool is_login_file) +{ + char **ext; + const char *empty_list[]= { "", 0 }; + my_bool have_ext= fn_ext(config_file)[0] != 0; + const char **exts_to_use= have_ext ? empty_list : f_extensions; + + for (ext= (char**) exts_to_use; *ext; ext++) + { + int error; + if ((error= search_default_file_with_ext(opt_handler, handler_ctx, + dir, *ext, + config_file, 0, is_login_file)) < 0) + return error; + } + return 0; +} + + +/* + Skip over keyword and get argument after keyword + + SYNOPSIS + get_argument() + keyword Include directive keyword + kwlen Length of keyword + ptr Pointer to the keword in the line under process + line line number + + RETURN + 0 error + # Returns pointer to the argument after the keyword. +*/ + +static char *get_argument(const char *keyword, size_t kwlen, + char *ptr, char *name, uint line) +{ + char *end; + + /* Skip over "include / includedir keyword" and following whitespace */ + + for (ptr+= kwlen - 1; + my_isspace(&my_charset_latin1, ptr[0]); + ptr++) + {} + + /* + Trim trailing whitespace from directory name + The -1 below is for the newline added by fgets() + Note that my_isspace() is true for \r and \n + */ + for (end= ptr + strlen(ptr) - 1; + my_isspace(&my_charset_latin1, *(end - 1)); + end--) + {} + end[0]= 0; /* Cut off end space */ + + /* Print error msg if there is nothing after !include* directive */ + if (end <= ptr) + { + my_message_local(ERROR_LEVEL, + "Wrong '!%s' directive in config file %s at line %d!", + keyword, name, line); + return 0; + } + return ptr; +} + + +/* + Open a configuration file (if exists) and read given options from it + + SYNOPSIS + search_default_file_with_ext() + opt_handler Option handler function. It is used to process + every separate option. + handler_ctx Pointer to the structure to store actual + parameters of the function. + dir directory to read + ext Extension for configuration file + config_file Name of configuration file + group groups to read + recursion_level the level of recursion, got while processing + "!include" or "!includedir" + is_login_file TRUE, when login file is being processed. + + RETURN + 0 Success + -1 Fatal error, abort + 1 File not found (Warning) +*/ + +static int search_default_file_with_ext(Process_option_func opt_handler, + void *handler_ctx, + const char *dir, + const char *ext, + const char *config_file, + int recursion_level, + my_bool is_login_file) +{ + char name[FN_REFLEN + 10], buff[4096], curr_gr[4096], *ptr, *end, **tmp_ext; + char *value, option[4096+2], tmp[FN_REFLEN]; + static const char includedir_keyword[]= "includedir"; + static const char include_keyword[]= "include"; + const int max_recursion_level= 10; + MYSQL_FILE *fp; + uint line=0; + my_bool found_group=0; + uint i, rc; + MY_DIR *search_dir; + FILEINFO *search_file; + + if ((dir ? strlen(dir) : 0 )+strlen(config_file) >= FN_REFLEN-3) + return 0; /* Ignore wrong paths */ + if (dir) + { + end=convert_dirname(name, dir, NullS); + if (dir[0] == FN_HOMELIB) /* Add . to filenames in home */ + *end++='.'; + strxmov(end,config_file,ext,NullS); + } + else + { + my_stpcpy(name,config_file); + } + fn_format(name,name,"","",4); + + if ((rc= check_file_permissions(name, is_login_file)) < 2) + return (int) rc; + + if (is_login_file) + { + if ( !(fp = mysql_file_fopen(key_file_cnf, name, (O_RDONLY | O_BINARY), + MYF(0)))) + return 1; /* Ignore wrong files. */ + } + else + { + if ( !(fp = mysql_file_fopen(key_file_cnf, name, O_RDONLY, MYF(0)))) + return 1; /* Ignore wrong files */ + } + + while (mysql_file_getline(buff, sizeof(buff) - 1, fp, is_login_file)) + { + line++; + /* Ignore comment and empty lines */ + for (ptr= buff; my_isspace(&my_charset_latin1, *ptr); ptr++) + {} + + if (*ptr == '#' || *ptr == ';' || !*ptr) + continue; + + /* Configuration File Directives */ + if (*ptr == '!') + { + if (recursion_level >= max_recursion_level) + { + for (end= ptr + strlen(ptr) - 1; + my_isspace(&my_charset_latin1, *(end - 1)); + end--) + {} + end[0]= 0; + my_message_local(WARNING_LEVEL, + "skipping '%s' directive as maximum include" + "recursion level was reached in file %s at line %d!", + ptr, name, line); + continue; + } + + /* skip over `!' and following whitespace */ + for (++ptr; my_isspace(&my_charset_latin1, ptr[0]); ptr++) + {} + + if ((!strncmp(ptr, includedir_keyword, + sizeof(includedir_keyword) - 1)) && + my_isspace(&my_charset_latin1, ptr[sizeof(includedir_keyword) - 1])) + { + if (!(ptr= get_argument(includedir_keyword, + sizeof(includedir_keyword), + ptr, name, line))) + goto err; + + if (!(search_dir= my_dir(ptr, MYF(MY_WME)))) + goto err; + + for (i= 0; i < search_dir->number_off_files; i++) + { + search_file= search_dir->dir_entry + i; + ext= fn_ext(search_file->name); + + /* check extension */ + for (tmp_ext= (char**) f_extensions; *tmp_ext; tmp_ext++) + { + if (!strcmp(ext, *tmp_ext)) + break; + } + + if (*tmp_ext) + { + fn_format(tmp, search_file->name, ptr, "", + MY_UNPACK_FILENAME | MY_SAFE_PATH); + + search_default_file_with_ext(opt_handler, handler_ctx, "", "", tmp, + recursion_level + 1, is_login_file); + } + } + + my_dirend(search_dir); + } + else if ((!strncmp(ptr, include_keyword, sizeof(include_keyword) - 1)) && + my_isspace(&my_charset_latin1, ptr[sizeof(include_keyword)-1])) + { + if (!(ptr= get_argument(include_keyword, + sizeof(include_keyword), ptr, + name, line))) + goto err; + + search_default_file_with_ext(opt_handler, handler_ctx, "", "", ptr, + recursion_level + 1, is_login_file); + } + + continue; + } + + if (*ptr == '[') /* Group name */ + { + found_group=1; + if (!(end= strchr(++ptr,']'))) + { + my_message_local(ERROR_LEVEL, + "Wrong group definition in config file %s at line %d!", + name,line); + goto err; + } + /* Remove end space */ + for ( ; my_isspace(&my_charset_latin1, end[-1]); end --) + {} + + end[0]=0; + + strmake(curr_gr, ptr, MY_MIN((size_t) (end-ptr)+1, sizeof(curr_gr)-1)); + + /* signal that a new group is found */ + opt_handler(handler_ctx, curr_gr, NULL); + + continue; + } + if (!found_group) + { + my_message_local(ERROR_LEVEL, + "Found option without preceding group in config file" + " %s at line %d!", name,line); + goto err; + } + + + end= remove_end_comment(ptr); + if ((value= strchr(ptr, '='))) + end= value; /* Option without argument */ + for ( ; my_isspace(&my_charset_latin1, end[-1]) ; end--) + {} + + if (!value) + { + strmake(my_stpcpy(option,"--"),ptr, (size_t) (end-ptr)); + if (opt_handler(handler_ctx, curr_gr, option)) + goto err; + } + else + { + /* Remove pre- and end space */ + char *value_end; + for (value++ ; my_isspace(&my_charset_latin1, *value); value ++) + {} + + value_end=strend(value); + /* + We don't have to test for value_end >= value as we know there is + an '=' before + */ + for ( ; my_isspace(&my_charset_latin1, value_end[-1]); value_end --) + {} + + if (value_end < value) /* Empty string */ + value_end=value; + + /* remove quotes around argument */ + if ((*value == '\"' || *value == '\'') && /* First char is quote */ + (value + 1 < value_end ) && /* String is longer than 1 */ + *value == value_end[-1] ) /* First char is equal to last char */ + { + value++; + value_end--; + } + ptr=my_stpnmov(my_stpcpy(option,"--"),ptr,(size_t) (end-ptr)); + *ptr++= '='; + + for ( ; value != value_end; value++) + { + if (*value == '\\' && value != value_end-1) + { + switch(*++value) { + case 'n': + *ptr++='\n'; + break; + case 't': + *ptr++= '\t'; + break; + case 'r': + *ptr++ = '\r'; + break; + case 'b': + *ptr++ = '\b'; + break; + case 's': + *ptr++= ' '; /* space */ + break; + case '\"': + *ptr++= '\"'; + break; + case '\'': + *ptr++= '\''; + break; + case '\\': + *ptr++= '\\'; + break; + default: /* Unknown; Keep '\' */ + *ptr++= '\\'; + *ptr++= *value; + break; + } + } + else + *ptr++= *value; + } + *ptr=0; + if (opt_handler(handler_ctx, curr_gr, option)) + goto err; + } + } + mysql_file_fclose(fp, MYF(0)); + return(0); + + err: + mysql_file_fclose(fp, MYF(0)); + return -1; /* Fatal error */ +} + + +static char *remove_end_comment(char *ptr) +{ + char quote= 0; /* we are inside quote marks */ + char escape= 0; /* symbol is protected by escape chagacter */ + + for (; *ptr; ptr++) + { + if ((*ptr == '\'' || *ptr == '\"') && !escape) + { + if (!quote) + quote= *ptr; + else if (quote == *ptr) + quote= 0; + } + /* We are not inside a string */ + if (!quote && *ptr == '#') + { + *ptr= 0; + return ptr; + } + escape= (quote && *ptr == '\\' && !escape); + } + return ptr; +} + + +/** + Read one line from the specified file. In case + of scrambled login file, the line read is first + decrypted and then returned. + + @param str [out] Buffer to store the read text. + @param size [in] At max, size-1 bytes to be read. + @param file [in] Source file. + @param is_login_file [in] TRUE, when login file is being processed. + + @return 1 Success + 0 Error +*/ + +static my_bool mysql_file_getline(char *str, int size, MYSQL_FILE *file, + my_bool is_login_file) +{ + uchar cipher[4096], len_buf[MAX_CIPHER_STORE_LEN]; + static unsigned char my_key[LOGIN_KEY_LEN]; + int length= 0, cipher_len= 0; + + if (is_login_file) + { + if (mysql_file_ftell(file, MYF(MY_WME)) == 0) + { + /* Move past unused bytes. */ + mysql_file_fseek(file, 4, SEEK_SET, MYF(MY_WME)); + if (mysql_file_fread(file, my_key, LOGIN_KEY_LEN, + MYF(MY_WME)) != LOGIN_KEY_LEN) + return 0; + } + + if (mysql_file_fread(file, len_buf, MAX_CIPHER_STORE_LEN, + MYF(MY_WME)) == MAX_CIPHER_STORE_LEN) + { + cipher_len= sint4korr(len_buf); + if (cipher_len > size) + return 0; + } + else + return 0; + + mysql_file_fread(file, cipher, cipher_len, MYF(MY_WME)); + if ((length= my_aes_decrypt(cipher, cipher_len, (unsigned char *) str, + my_key, LOGIN_KEY_LEN, my_aes_128_ecb, NULL)) < 0) + { + /* Attempt to decrypt failed. */ + return 0; + } + str[length]= 0; + return 1; + } + else + { + if (mysql_file_fgets(str, size, file)) + return 1; + else + return 0; + } +} + + +void my_print_default_files(const char *conf_file) +{ + const char *empty_list[]= { "", 0 }; + my_bool have_ext= fn_ext(conf_file)[0] != 0; + const char **exts_to_use= have_ext ? empty_list : f_extensions; + char name[FN_REFLEN], **ext; + + puts("\nDefault options are read from the following files in the given order:"); + + if (dirname_length(conf_file)) + fputs(conf_file,stdout); + else + { + const char **dirs; + MEM_ROOT alloc; + init_alloc_root(key_memory_defaults, &alloc, 512, 0); + + if ((dirs= init_default_directories(&alloc)) == NULL) + { + fputs("Internal error initializing default directories list", stdout); + } + else + { + for ( ; *dirs; dirs++) + { + for (ext= (char**) exts_to_use; *ext; ext++) + { + const char *pos; + char *end; + if (**dirs) + pos= *dirs; + else if (my_defaults_extra_file) + pos= my_defaults_extra_file; + else + continue; + end= convert_dirname(name, pos, NullS); + if (name[0] == FN_HOMELIB) /* Add . to filenames in home */ + *end++= '.'; + + if (my_defaults_extra_file == pos) + end[(strlen(end)-1)] = ' '; + else + strxmov(end, conf_file, *ext , " ", NullS); + fputs(name, stdout); + } + } + } + + free_root(&alloc, MYF(0)); + } + puts(""); +} + +void print_defaults(const char *conf_file, const char **groups) +{ + const char **groups_save= groups; + my_print_default_files(conf_file); + + fputs("The following groups are read:",stdout); + for ( ; *groups ; groups++) + { + fputc(' ',stdout); + fputs(*groups,stdout); + } + + if (my_defaults_group_suffix) + { + groups= groups_save; + for ( ; *groups ; groups++) + { + fputc(' ',stdout); + fputs(*groups,stdout); + fputs(my_defaults_group_suffix,stdout); + } + } + puts("\nThe following options may be given as the first argument:\n\ +--print-defaults Print the program argument list and exit.\n\ +--no-defaults Don't read default options from any option file,\n\ + except for login file.\n\ +--defaults-file=# Only read default options from the given file #.\n\ +--defaults-extra-file=# Read this file after the global files are read.\n\ +--defaults-group-suffix=#\n\ + Also read groups with concat(group, suffix)\n\ +--login-path=# Read this path from the login file."); +} + + +static int add_directory(MEM_ROOT *alloc, const char *dir, const char **dirs) +{ + char buf[FN_REFLEN]; + size_t len; + char *p; + my_bool err MY_ATTRIBUTE((unused)); + + len= normalize_dirname(buf, dir); + if (!(p= strmake_root(alloc, buf, len))) + return 1; /* Failure */ + /* Should never fail if DEFAULT_DIRS_SIZE is correct size */ + err= array_append_string_unique(p, dirs, DEFAULT_DIRS_SIZE); + DBUG_ASSERT(err == FALSE); + + return 0; +} + + +#ifdef _WIN32 +/* + This wrapper for GetSystemWindowsDirectory() will dynamically bind to the + function if it is available, emulate it on NT4 Terminal Server by stripping + the \SYSTEM32 from the end of the results of GetSystemDirectory(), or just + return GetSystemDirectory(). + */ + +typedef UINT (WINAPI *GET_SYSTEM_WINDOWS_DIRECTORY)(LPSTR, UINT); + +static size_t my_get_system_windows_directory(char *buffer, size_t size) +{ + size_t count; + GET_SYSTEM_WINDOWS_DIRECTORY + func_ptr= (GET_SYSTEM_WINDOWS_DIRECTORY) + GetProcAddress(GetModuleHandle("kernel32.dll"), + "GetSystemWindowsDirectoryA"); + + if (func_ptr) + return func_ptr(buffer, (uint) size); + + /* + Windows NT 4.0 Terminal Server Edition: + To retrieve the shared Windows directory, call GetSystemDirectory and + trim the "System32" element from the end of the returned path. + */ + count= GetSystemDirectory(buffer, (uint) size); + if (count > 8 && stricmp(buffer+(count-8), "\\System32") == 0) + { + count-= 8; + buffer[count] = '\0'; + } + return count; +} + + +static const char *my_get_module_parent(char *buf, size_t size) +{ + char *last= NULL; + char *end; + if (!GetModuleFileName(NULL, buf, (DWORD) size)) + return NULL; + end= strend(buf); + + /* + Look for the second-to-last \ in the filename, but hang on + to a pointer after the last \ in case we're in the root of + a drive. + */ + for ( ; end > buf; end--) + { + if (*end == FN_LIBCHAR) + { + if (last) + { + /* Keep the last '\' as this works both with D:\ and a directory */ + end[1]= 0; + break; + } + last= end; + } + } + + return buf; +} +#endif /* _WIN32 */ + + +static const char **init_default_directories(MEM_ROOT *alloc) +{ + const char **dirs; + char *env; + int errors= 0; + + dirs= (const char **)alloc_root(alloc, DEFAULT_DIRS_SIZE * sizeof(char *)); + if (dirs == NULL) + return NULL; + memset(dirs, 0, DEFAULT_DIRS_SIZE * sizeof(char *)); + +#ifdef _WIN32 + + { + char fname_buffer[FN_REFLEN]; + if (my_get_system_windows_directory(fname_buffer, sizeof(fname_buffer))) + errors += add_directory(alloc, fname_buffer, dirs); + + if (GetWindowsDirectory(fname_buffer, sizeof(fname_buffer))) + errors += add_directory(alloc, fname_buffer, dirs); + + errors += add_directory(alloc, "C:/", dirs); + + if (my_get_module_parent(fname_buffer, sizeof(fname_buffer)) != NULL) + errors += add_directory(alloc, fname_buffer, dirs); + } + +#else + + errors += add_directory(alloc, "/etc/", dirs); + errors += add_directory(alloc, "/etc/mysql/", dirs); + +#if defined(DEFAULT_SYSCONFDIR) + if (DEFAULT_SYSCONFDIR[0]) + errors += add_directory(alloc, DEFAULT_SYSCONFDIR, dirs); +#endif /* DEFAULT_SYSCONFDIR */ + +#endif + + if ((env= getenv("MYSQL_HOME"))) + errors += add_directory(alloc, env, dirs); + + /* Placeholder for --defaults-extra-file= */ + errors += add_directory(alloc, "", dirs); + +#if !defined(_WIN32) + errors += add_directory(alloc, "~/", dirs); +#endif + + return (errors > 0 ? NULL : dirs); +} + +/** + Place the login file name in the specified buffer. + + @param file_name [out] Buffer to hold login file name + @param file_name_size [in] Length of the buffer + + @return 1 - Success + 0 - Failure +*/ + +int my_default_get_login_file(char *file_name, size_t file_name_size) +{ + size_t rc; + + if (getenv("MYSQL_TEST_LOGIN_FILE")) + rc= my_snprintf(file_name, file_name_size, "%s", + getenv("MYSQL_TEST_LOGIN_FILE")); +#ifdef _WIN32 + else if (getenv("APPDATA")) + rc= my_snprintf(file_name, file_name_size, "%s\\MySQL\\.mylogin.cnf", + getenv("APPDATA")); +#else + else if (getenv("HOME")) + rc= my_snprintf(file_name, file_name_size, "%s/.mylogin.cnf", + getenv("HOME")); +#endif + else + { + memset(file_name, 0, file_name_size); + return 0; + } + /* Anything <= 0 will be treated as error. */ + if (rc <= 0) + return 0; + + return 1; +} + +/** + Check file permissions of the option file. + + @param file_name [in] Name of the option file. + @param is_login_file [in] TRUE, when login file is being processed. + + @return 0 - Non-allowable file permissions. + 1 - Failed to stat. + 2 - Success. +*/ +int check_file_permissions(const char *file_name, my_bool is_login_file) +{ +#if !defined(_WIN32) + MY_STAT stat_info; + + if (!my_stat(file_name,&stat_info,MYF(0))) + return 1; + /* + Ignore .mylogin.cnf file if not exclusively readable/writable + by current user. + */ + if (is_login_file && (stat_info.st_mode & (S_IXUSR | S_IRWXG | S_IRWXO)) + && (stat_info.st_mode & S_IFMT) == S_IFREG) + { + my_message_local(WARNING_LEVEL, "%s should be readable/writable only by " + "current user.", file_name); + return 0; + } + /* + Ignore world-writable regular files. + This is mainly done to protect us to not read a file created by + the mysqld server, but the check is still valid in most context. + */ + else if ((stat_info.st_mode & S_IWOTH) && + (stat_info.st_mode & S_IFMT) == S_IFREG) + + { + my_message_local(WARNING_LEVEL, + "World-writable config file '%s' is ignored.", file_name); + return 0; + } +#endif + return 2; /* Success */ +} + diff --git a/mysql/mysys_ssl/my_default_priv.h b/mysql/mysys_ssl/my_default_priv.h new file mode 100644 index 0000000..9c55d47 --- /dev/null +++ b/mysql/mysys_ssl/my_default_priv.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ + +#ifndef MY_DEFAULT_PRIV_INCLUDED +#define MY_DEFAULT_PRIV_INCLUDED + +#include "my_global.h" /* C_MODE_START, C_MODE_END */ + +/* + Number of byte used to store the length of + cipher that follows. +*/ +#define MAX_CIPHER_STORE_LEN 4U +#define LOGIN_KEY_LEN 20U + +C_MODE_START + +/** + Place the login file name in the specified buffer. + + @param file_name [out] Buffer to hold login file name + @param file_name_size [in] Length of the buffer + + @return 1 - Success + 0 - Failure +*/ +int my_default_get_login_file(char *file_name, size_t file_name_size); + +C_MODE_END + +#endif /* MY_DEFAULT_PRIV_INCLUDED */ diff --git a/mysql/mysys_ssl/my_getopt.cpp b/mysql/mysys_ssl/my_getopt.cpp new file mode 100644 index 0000000..40ae171 --- /dev/null +++ b/mysql/mysys_ssl/my_getopt.cpp @@ -0,0 +1,1615 @@ +/* Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ + +#include +#include +#include +#include +#include +#include +#include +#include "my_default.h" +#include +#include "../mysys/mysys_priv.h" + +typedef void (*init_func_p)(const struct my_option *option, void *variable, + longlong value); + +my_error_reporter my_getopt_error_reporter= &my_message_local; + +static bool findopt(char *, uint, const struct my_option **); +my_bool getopt_compare_strings(const char *, const char *, uint); +static longlong getopt_ll(char *arg, const struct my_option *optp, int *err); +static ulonglong getopt_ull(char *, const struct my_option *, int *); +static double getopt_double(char *arg, const struct my_option *optp, int *err); +static void init_variables(const struct my_option *, init_func_p); +static void init_one_value(const struct my_option *, void *, longlong); +static void fini_one_value(const struct my_option *, void *, longlong); +static int setval(const struct my_option *, void *, char *, my_bool); +static char *check_struct_option(char *cur_arg, char *key_name); +static my_bool get_bool_argument(const struct my_option *opts, + const char *argument, + bool *error); + +/* + The following three variables belong to same group and the number and + order of their arguments must correspond to each other. +*/ +static const char *special_opt_prefix[]= +{"skip", "disable", "enable", "maximum", "loose", 0}; +static const uint special_opt_prefix_lengths[]= +{ 4, 7, 6, 7, 5, 0}; +enum enum_special_opt +{ OPT_SKIP, OPT_DISABLE, OPT_ENABLE, OPT_MAXIMUM, OPT_LOOSE}; + +char *disabled_my_option= (char*) "0"; +char *enabled_my_option= (char*) "1"; + +/* + This is a flag that can be set in client programs. 0 means that + my_getopt will not print error messages, but the client should do + it by itself +*/ + +my_bool my_getopt_print_errors= 1; + +/* + This is a flag that can be set in client programs. 1 means that + my_getopt will skip over options it does not know how to handle. +*/ + +my_bool my_getopt_skip_unknown= 0; + +static my_getopt_value getopt_get_addr; + +void my_getopt_register_get_addr(my_getopt_value func_addr) +{ + getopt_get_addr= func_addr; +} + + +/** + Wrapper around my_handle_options() for interface compatibility. + + @param argc [in, out] Command line options (count) + @param argv [in, out] Command line options (values) + @param longopts [in] Descriptor of all valid options + @param get_one_option [in] Optional callback function to process each option, + can be NULL. + + @return Error in case of ambiguous or unknown options, + 0 on success. +*/ +int handle_options(int *argc, char ***argv, + const struct my_option *longopts, + my_get_one_option get_one_option) +{ + return my_handle_options(argc, argv, longopts, get_one_option, NULL, FALSE); +} + +union ull_dbl +{ + ulonglong ull; + double dbl; +}; + +/** + Returns an ulonglong value containing a raw + representation of the given double value. +*/ +ulonglong getopt_double2ulonglong(double v) +{ + union ull_dbl u; + u.dbl= v; + compile_time_assert(sizeof(ulonglong) >= sizeof(double)); + return u.ull; +} + +/** + Returns the double value which corresponds to + the given raw representation. +*/ +double getopt_ulonglong2double(ulonglong v) +{ + union ull_dbl u; + u.ull= v; + return u.dbl; +} + +/** + Handle command line options. + Sort options. + Put options first, until special end of options (--), + or until the end of argv. Parse options, check that the given option + matches with one of the options in struct 'my_option'. + Check that option was given an argument if it requires one + Call the optional 'get_one_option()' function once for each option. + + Note that handle_options() can be invoked multiple times to + parse a command line in several steps. + In this case, use the global flag @c my_getopt_skip_unknown to indicate + that options unknown in the current step should be preserved in the + command line for later parsing in subsequent steps. + + For 'long' options (--a_long_option), @c my_getopt_skip_unknown is + fully supported. Command line parameters such as: + - "--a_long_option" + - "--a_long_option=value" + - "--a_long_option value" + will be preserved as is when the option is not known. + + For 'short' options (-S), support for @c my_getopt_skip_unknown + comes with some limitation, because several short options + can also be specified together in the same command line argument, + as in "-XYZ". + + The first use case supported is: all short options are declared. + handle_options() will be able to interpret "-XYZ" as one of: + - an unknown X option + - "-X -Y -Z", three short options with no arguments + - "-X -YZ", where Y is a short option with argument Z + - "-XYZ", where X is a short option with argument YZ + based on the full short options specifications. + + The second use case supported is: no short option is declared. + handle_options() will reject "-XYZ" as unknown, to be parsed later. + + The use case that is explicitly not supported is to provide + only a partial list of short options to handle_options(). + This function can not be expected to extract some option Y + in the middle of the string "-XYZ" in these conditions, + without knowing if X will be declared an option later. + + Note that this limitation only impacts parsing of several + short options from the same command line argument, + as in "mysqld -anW5". + When each short option is properly separated out in the command line + argument, for example in "mysqld -a -n -w5", the code would actually + work even with partial options specs given at each stage. + + @param [in, out] argc command line options (count) + @param [in, out] argv command line options (values) + @param [in] longopts descriptor of all valid options + @param [in] get_one_option optional callback function to process each option, + can be NULL. + @param [in] command_list NULL-terminated list of strings (commands) which + (if set) is looked up for all non-option strings + found while parsing the command line parameters. + The parsing terminates if a match is found. At + exit, argv [out] would contain all the remaining + unparsed options along with the matched command. + + @param [in] ignore_unknown_option When set to TRUE, options are continued to + be read even when unknown options are + encountered. + + @return error in case of ambiguous or unknown options, + 0 on success. +*/ +int my_handle_options(int *argc, char ***argv, + const struct my_option *longopts, + my_get_one_option get_one_option, + const char **command_list, my_bool ignore_unknown_option) +{ + uint argvpos= 0, length; + my_bool end_of_options= 0, must_be_var, set_maximum_value, + option_is_loose; + char **pos, **pos_end, *optend, *opt_str, key_name[FN_REFLEN]; + const struct my_option *optp; + void *value; + int error, i; + my_bool is_cmdline_arg= 1; + bool opt_found; + + /* handle_options() assumes arg0 (program name) always exists */ + DBUG_ASSERT(argc && *argc >= 1); + DBUG_ASSERT(argv && *argv); + (*argc)--; /* Skip the program name */ + (*argv)++; /* --- || ---- */ + init_variables(longopts, init_one_value); + + /* + Search for args_separator, if found, then the first part of the + arguments are loaded from configs + */ + for (pos= *argv, pos_end=pos+ *argc; pos != pos_end ; pos++) + { + if (my_getopt_is_args_separator(*pos)) + { + is_cmdline_arg= 0; + break; + } + } + + for (pos= *argv, pos_end=pos+ *argc; pos != pos_end ; pos++) + { + char **first= pos; + char *cur_arg= *pos; + opt_found= false; + if (!is_cmdline_arg && (my_getopt_is_args_separator(cur_arg))) + { + is_cmdline_arg= 1; + + /* save the separator too if skip unkown options */ + if (my_getopt_skip_unknown) + (*argv)[argvpos++]= cur_arg; + else + (*argc)--; + continue; + } + if (cur_arg[0] == '-' && cur_arg[1] && !end_of_options) /* must be opt */ + { + char *argument= 0; + must_be_var= 0; + set_maximum_value= 0; + option_is_loose= 0; + + cur_arg++; /* skip '-' */ + if (*cur_arg == '-') /* check for long option, */ + { + if (!*++cur_arg) /* skip the double dash */ + { + /* '--' means end of options, look no further */ + end_of_options= 1; + (*argc)--; + continue; + } + opt_str= check_struct_option(cur_arg, key_name); + optend= strcend(opt_str, '='); + length= (uint) (optend - opt_str); + if (*optend == '=') + optend++; + else + optend= 0; + + /* + Find first the right option. Return error in case of an ambiguous, + or unknown option + */ + optp= longopts; + if (!(opt_found= findopt(opt_str, length, &optp))) + { + /* + Didn't find any matching option. Let's see if someone called + option with a special option prefix + */ + if (!must_be_var) + { + if (optend) + must_be_var= 1; /* option is followed by an argument */ + for (i= 0; special_opt_prefix[i]; i++) + { + if (!getopt_compare_strings(special_opt_prefix[i], opt_str, + special_opt_prefix_lengths[i]) && + (opt_str[special_opt_prefix_lengths[i]] == '-' || + opt_str[special_opt_prefix_lengths[i]] == '_')) + { + /* + We were called with a special prefix, we can reuse opt_found + */ + opt_str+= special_opt_prefix_lengths[i] + 1; + length-= special_opt_prefix_lengths[i] + 1; + if (i == OPT_LOOSE) + option_is_loose= 1; + if ((opt_found= findopt(opt_str, length, &optp))) + { + switch (i) { + case OPT_SKIP: + case OPT_DISABLE: /* fall through */ + /* + double negation is actually enable again, + for example: --skip-option=0 -> option = TRUE + */ + optend= (optend && *optend == '0' && !(*(optend + 1))) ? + enabled_my_option : disabled_my_option; + break; + case OPT_ENABLE: + optend= (optend && *optend == '0' && !(*(optend + 1))) ? + disabled_my_option : enabled_my_option; + break; + case OPT_MAXIMUM: + set_maximum_value= 1; + must_be_var= 1; + break; + } + break; /* break from the inner loop, main loop continues */ + } + i= -1; /* restart the loop */ + } + } + } + if (!opt_found) + { + if (my_getopt_skip_unknown) + { + /* Preserve all the components of this unknown option. */ + do { + (*argv)[argvpos++]= *first++; + } while (first <= pos); + continue; + } + if (must_be_var) + { + if (my_getopt_print_errors) + my_getopt_error_reporter(option_is_loose ? + WARNING_LEVEL : ERROR_LEVEL, + "unknown variable '%s'", cur_arg); + if (!option_is_loose) + return EXIT_UNKNOWN_VARIABLE; + } + else + { + if (my_getopt_print_errors) + my_getopt_error_reporter(option_is_loose ? + WARNING_LEVEL : ERROR_LEVEL, + "unknown option '--%s'", cur_arg); + if (!(option_is_loose || ignore_unknown_option)) + return EXIT_UNKNOWN_OPTION; + } + if (option_is_loose || ignore_unknown_option) + { + (*argc)--; + continue; + } + } + } + if ((optp->var_type & GET_TYPE_MASK) == GET_DISABLED) + { + if (my_getopt_print_errors) + my_message_local(option_is_loose ? WARNING_LEVEL : ERROR_LEVEL, + "%s: Option '%s' used, but is disabled", + my_progname, opt_str); + if (option_is_loose) + { + (*argc)--; + continue; + } + return EXIT_OPTION_DISABLED; + } + error= 0; + value= optp->var_type & GET_ASK_ADDR ? + (*getopt_get_addr)(key_name, strlen(key_name), optp, &error) : + optp->value; + if (error) + return error; + + if (optp->arg_type == NO_ARG) + { + /* + Due to historical reasons GET_BOOL var_types still accepts arguments + despite the NO_ARG arg_type attribute. This can seems a bit unintuitive + and care should be taken when refactoring this code. + */ + if (optend && (optp->var_type & GET_TYPE_MASK) != GET_BOOL) + { + if (my_getopt_print_errors) + my_getopt_error_reporter(ERROR_LEVEL, + "%s: option '--%s' cannot take an argument", + my_progname, optp->name); + return EXIT_NO_ARGUMENT_ALLOWED; + } + if ((optp->var_type & GET_TYPE_MASK) == GET_BOOL) + { + /* + Set bool to 1 if no argument or if the user has used + --enable-'option-name'. + *optend was set to '0' if one used --disable-option + */ + (*argc)--; + if(!optend) + *((my_bool*) value)= (my_bool) 1; + else + { + my_bool ret= 0; + bool error= 0; + ret= get_bool_argument(optp, optend, &error); + if(error) + { + my_getopt_error_reporter(WARNING_LEVEL, + "%s: ignoring option '--%s' " + "due to invalid value '%s'", + my_progname, optp->name, optend); + continue; + } + else + *((my_bool*) value)= ret; + } + if (get_one_option && get_one_option(optp->id, optp, + *((my_bool*) value) ? + enabled_my_option : disabled_my_option)) + return EXIT_ARGUMENT_INVALID; + continue; + } + argument= optend; + } + else if (optp->arg_type == REQUIRED_ARG && !optend) + { + /* Check if there are more arguments after this one, + Note: options loaded from config file that requires value + should always be in the form '--option=value'. + */ + if (!is_cmdline_arg || !*++pos) + { + if (my_getopt_print_errors) + my_getopt_error_reporter(ERROR_LEVEL, + "%s: option '--%s' requires an argument", + my_progname, optp->name); + return EXIT_ARGUMENT_REQUIRED; + } + argument= *pos; + (*argc)--; + } + else + argument= optend; + + if (optp->var_type == GET_PASSWORD && is_cmdline_arg && argument) + print_cmdline_password_warning(); + } + else /* must be short option */ + { + for (optend= cur_arg; *optend; optend++) + { + opt_found= false; + for (optp= longopts; optp->name; optp++) + { + if (optp->id && optp->id == (int) (uchar) *optend) + { + /* Option recognized. Find next what to do with it */ + opt_found= true; + if ((optp->var_type & GET_TYPE_MASK) == GET_DISABLED) + { + if (my_getopt_print_errors) + my_message_local(ERROR_LEVEL, + "%s: Option '-%c' used, but is disabled", + my_progname, optp->id); + return EXIT_OPTION_DISABLED; + } + if ((optp->var_type & GET_TYPE_MASK) == GET_BOOL && + optp->arg_type == NO_ARG) + { + *((my_bool*) optp->value)= (my_bool) 1; + if (get_one_option && get_one_option(optp->id, optp, argument)) + return EXIT_UNSPECIFIED_ERROR; + continue; + } + else if (optp->arg_type == REQUIRED_ARG || + optp->arg_type == OPT_ARG) + { + if (*(optend + 1)) + { + /* The rest of the option is option argument */ + argument= optend + 1; + /* This is in effect a jump out of the outer loop */ + optend= (char*) " "; + if (optp->var_type == GET_PASSWORD && is_cmdline_arg) + print_cmdline_password_warning(); + } + else + { + if (optp->arg_type == OPT_ARG) + { + if (optp->var_type == GET_BOOL) + *((my_bool*) optp->value)= (my_bool) 1; + if (get_one_option && get_one_option(optp->id, optp, argument)) + return EXIT_UNSPECIFIED_ERROR; + continue; + } + /* Check if there are more arguments after this one */ + if (!pos[1]) + { + if (my_getopt_print_errors) + my_getopt_error_reporter(ERROR_LEVEL, + "%s: option '-%c' requires an argument", + my_progname, optp->id); + return EXIT_ARGUMENT_REQUIRED; + } + argument= *++pos; + (*argc)--; + /* the other loop will break, because *optend + 1 == 0 */ + } + } + if ((error= setval(optp, optp->value, argument, + set_maximum_value))) + return error; + if (get_one_option && get_one_option(optp->id, optp, argument)) + return EXIT_UNSPECIFIED_ERROR; + break; + } + } + if (!opt_found) + { + if (my_getopt_skip_unknown) + { + /* + We are currently parsing a single argv[] argument + of the form "-XYZ". + One or the argument found (say Y) is not an option. + Hack the string "-XYZ" to make a "-YZ" substring in it, + and push that to the output as an unrecognized parameter. + */ + DBUG_ASSERT(optend > *pos); + DBUG_ASSERT(optend >= cur_arg); + DBUG_ASSERT(optend <= *pos + strlen(*pos)); + DBUG_ASSERT(*optend); + optend--; + optend[0]= '-'; /* replace 'X' or '-' by '-' */ + (*argv)[argvpos++]= optend; + /* + Do not continue to parse at the current "-XYZ" argument, + skip to the next argv[] argument instead. + */ + optend= (char*) " "; + } + else + { + if (my_getopt_print_errors) + my_getopt_error_reporter(ERROR_LEVEL, + "%s: unknown option '-%c'", + my_progname, *optend); + return EXIT_UNKNOWN_OPTION; + } + } + } + if (opt_found) + (*argc)--; /* option handled (short), decrease argument count */ + continue; + } + if ((error= setval(optp, value, argument, set_maximum_value))) + return error; + if (get_one_option && get_one_option(optp->id, optp, argument)) + return EXIT_UNSPECIFIED_ERROR; + + (*argc)--; /* option handled (long), decrease argument count */ + } + else /* non-option found */ + { + if (command_list) + { + while (* command_list) + { + if (!strcmp(*command_list, cur_arg)) + { + /* Match found. */ + (*argv)[argvpos ++]= cur_arg; + + /* Copy rest of the un-parsed elements & return. */ + while ((++ pos) != pos_end) + (*argv)[argvpos ++]= *pos; + goto done; + } + command_list ++; + } + } + (*argv)[argvpos ++]= cur_arg; + } + } + +done: + /* + Destroy the first, already handled option, so that programs that look + for arguments in 'argv', without checking 'argc', know when to stop. + Items in argv, before the destroyed one, are all non-option -arguments + to the program, yet to be (possibly) handled. + */ + (*argv)[argvpos]= 0; + return 0; +} + + +/** + * This function should be called to print a warning message + * if password string is specified on the command line. + */ + +void print_cmdline_password_warning() +{ + static my_bool password_warning_announced= FALSE; + + if (!password_warning_announced) + { + my_message_local(WARNING_LEVEL, "Using a password on the command line " + "interface can be insecure."); + password_warning_announced= TRUE; + } +} + + +/** + @brief Check for struct options + + @param[in] cur_arg Current argument under processing from argv + @param[in] key_name variable where to store the possible key name + + @details + In case option is a struct option, returns a pointer to the current + argument at the position where the struct option (key_name) ends, the + next character after the dot. In case argument is not a struct option, + returns a pointer to the argument. + key_name will hold the name of the key, or 0 if not found. + + @return char* + If struct option Pointer to next character after dot. + If no struct option Pointer to the argument +*/ + +static char *check_struct_option(char *cur_arg, char *key_name) +{ + char *dot_pos, *equal_pos, *space_pos; + + dot_pos= strcend(cur_arg + 1, '.'); /* Skip the first character */ + equal_pos= strcend(cur_arg, '='); + space_pos= strcend(cur_arg, ' '); + + /* + If the first dot is after an equal sign, then it is part + of a variable value and the option is not a struct option. + Also, if the last character in the string before the ending + NULL, or the character right before equal sign is the first + dot found, the option is not a struct option. + */ + if ((equal_pos > dot_pos) && (space_pos > dot_pos)) + { + size_t len= (uint) (dot_pos - cur_arg); + set_if_smaller(len, FN_REFLEN-1); + strmake(key_name, cur_arg, len); + return ++dot_pos; + } + else + { + key_name[0]= 0; + return cur_arg; + } +} + + +/** + Parse a boolean command line argument + + "ON", "TRUE" and "1" will return true, + other values will return false. + + @param[in] argument The value argument + @return boolean value +*/ +static my_bool get_bool_argument(const struct my_option *opts, + const char *argument, + bool *error) +{ + if (!my_strcasecmp(&my_charset_latin1, argument, "true") || + !my_strcasecmp(&my_charset_latin1, argument, "on") || + !my_strcasecmp(&my_charset_latin1, argument, "1")) + return 1; + else if (!my_strcasecmp(&my_charset_latin1, argument, "false") || + !my_strcasecmp(&my_charset_latin1, argument, "off") || + !my_strcasecmp(&my_charset_latin1, argument, "0")) + return 0; + else + *error= 1; + return 0; +} + +/* + function: setval + + Arguments: opts, argument + Will set the option value to given value +*/ + +static int setval(const struct my_option *opts, void *value, char *argument, + my_bool set_maximum_value) +{ + int err= 0, res= 0; + bool error= 0; + ulong var_type= opts->var_type & GET_TYPE_MASK; + + if (!argument) + argument= enabled_my_option; + + /* + Thus check applies only to options that have a defined value + storage pointer. + We do it for numeric types only, as empty value is a valid + option for strings (the only way to reset back to default value). + Note: it does not relate to OPT_ARG/REQUIRED_ARG/NO_ARG, since + --param="" is not generally the same as --param. + TODO: Add an option definition flag to signify whether empty value + (i.e. --param="") is an acceptable value or an error and extend + the check to all options. + */ + if (!*argument && + ( + var_type == GET_INT || + var_type == GET_UINT || + var_type == GET_LONG || + var_type == GET_ULONG || + var_type == GET_LL || + var_type == GET_ULL || + var_type == GET_DOUBLE || + var_type == GET_ENUM + ) + ) + { + if (strncmp(opts->name, "port", 10) == 0) + { + my_getopt_error_reporter(WARNING_LEVEL, + "%s: Empty value for '%s' specified. Will throw an error in future versions", + my_progname, opts->name); + } + else + { + my_getopt_error_reporter(ERROR_LEVEL, + "%s: Empty value for '%s' specified", + my_progname, opts->name); + return EXIT_ARGUMENT_REQUIRED; + } + } + + if (value) + { + if (set_maximum_value && !(value= opts->u_max_value)) + { + my_getopt_error_reporter(ERROR_LEVEL, + "%s: Maximum value of '%s' cannot be set", + my_progname, opts->name); + return EXIT_NO_PTR_TO_VARIABLE; + } + + switch (var_type) { + case GET_BOOL: /* If argument differs from 0, enable option, else disable */ + *((my_bool*) value)= get_bool_argument(opts, argument, &error); + if(error) + my_getopt_error_reporter(WARNING_LEVEL, + "option '%s': boolean value '%s' wasn't recognized. Set to OFF.", + opts->name, argument); + break; + case GET_INT: + *((int*) value)= (int) getopt_ll(argument, opts, &err); + break; + case GET_UINT: + *((uint*) value)= (uint) getopt_ull(argument, opts, &err); + break; + case GET_LONG: + *((long*) value)= (long) getopt_ll(argument, opts, &err); + break; + case GET_ULONG: + *((long*) value)= (long) getopt_ull(argument, opts, &err); + break; + case GET_LL: + *((longlong*) value)= getopt_ll(argument, opts, &err); + break; + case GET_ULL: + *((ulonglong*) value)= getopt_ull(argument, opts, &err); + break; + case GET_DOUBLE: + *((double*) value)= getopt_double(argument, opts, &err); + break; + case GET_STR: + case GET_PASSWORD: + if (argument == enabled_my_option) + break; /* string options don't use this default of "1" */ + *((char**) value)= argument; + break; + case GET_STR_ALLOC: + if (argument == enabled_my_option) + break; /* string options don't use this default of "1" */ + my_free(*((char**) value)); + if (!(*((char**) value)= my_strdup(key_memory_defaults, + argument, MYF(MY_WME)))) + { + res= EXIT_OUT_OF_MEMORY; + goto ret; + }; + break; + case GET_ENUM: + { + int type= find_type(argument, opts->typelib, FIND_TYPE_BASIC); + if (type == 0) + { + /* + Accept an integer representation of the enumerated item. + */ + char *endptr; + ulong arg= strtoul(argument, &endptr, 10); + if (*endptr || arg >= opts->typelib->count) + { + res= EXIT_ARGUMENT_INVALID; + goto ret; + } + *(ulong*)value= arg; + } + else if (type < 0) + { + res= EXIT_AMBIGUOUS_OPTION; + goto ret; + } + else + *(ulong*)value= type - 1; + } + break; + case GET_SET: + *((ulonglong*)value)= find_typeset(argument, opts->typelib, &err); + if (err) + { + /* Accept an integer representation of the set */ + char *endptr; + ulonglong arg= (ulonglong) strtol(argument, &endptr, 10); + if (*endptr || (arg >> 1) >= (1ULL << (opts->typelib->count-1))) + { + res= EXIT_ARGUMENT_INVALID; + goto ret; + }; + *(ulonglong*)value= arg; + err= 0; + } + break; + case GET_FLAGSET: + { + char *error; + uint error_len; + + *((ulonglong*)value)= + find_set_from_flags(opts->typelib, opts->typelib->count, + *(ulonglong *)value, opts->def_value, + argument, (uint)strlen(argument), + &error, &error_len); + if (error) + { + res= EXIT_ARGUMENT_INVALID; + goto ret; + }; + } + break; + case GET_NO_ARG: /* get_one_option has taken care of the value already */ + default: /* dummy default to avoid compiler warnings */ + break; + } + if (err) + { + res= EXIT_UNKNOWN_SUFFIX; + goto ret; + }; + } + return 0; + +ret: + my_getopt_error_reporter(ERROR_LEVEL, + "%s: Error while setting value '%s' to '%s'", + my_progname, argument, opts->name); + return res; +} + + +/** + Find option + + IMPLEMENTATION + Go through all options in the my_option struct. Return true + if an option is found. sets opt_res to the option found, if any. + + @param optpat name of option to find (with - or _) + @param length Length of optpat + @param[in,out] opt_res Options + + @retval false No matching options + @retval true Found an option +*/ + +static bool findopt(char *optpat, uint length, + const struct my_option **opt_res) +{ + for (const struct my_option *opt= *opt_res; opt->name; opt++) + if (!getopt_compare_strings(opt->name, optpat, length) && + !opt->name[length]) + { + (*opt_res)= opt; + return true; + } + return false; +} + + +/* + function: compare_strings + + Works like strncmp, other than 1.) considers '-' and '_' the same. + 2.) Returns -1 if strings differ, 0 if they are equal +*/ + +my_bool getopt_compare_strings(const char *s, const char *t, + uint length) +{ + char const *end= s + length; + for (;s != end ; s++, t++) + { + if ((*s != '-' ? *s : '_') != (*t != '-' ? *t : '_')) + return 1; + } + return 0; +} + +/* + function: eval_num_suffix + + Transforms a number with a suffix to real number. Suffix can + be k|K for kilo, m|M for mega or g|G for giga. +*/ + +static longlong eval_num_suffix(char *argument, int *error, char *option_name) +{ + char *endchar; + longlong num; + + *error= 0; + errno= 0; + num= my_strtoll(argument, &endchar, 10); + if (errno == ERANGE) + { + my_getopt_error_reporter(ERROR_LEVEL, + "Incorrect integer value: '%s'", argument); + *error= 1; + return 0; + } + if (*endchar == 'k' || *endchar == 'K') + num*= 1024L; + else if (*endchar == 'm' || *endchar == 'M') + num*= 1024L * 1024L; + else if (*endchar == 'g' || *endchar == 'G') + num*= 1024L * 1024L * 1024L; + else if (*endchar) + { + my_message_local(ERROR_LEVEL, + "Unknown suffix '%c' used for variable '%s' (value '%s')", + *endchar, option_name, argument); + *error= 1; + return 0; + } + return num; +} + +/** + function: eval_num_suffix_ull + This is the same as eval_num_suffix, but is meant for unsigned long long + values. Transforms an unsigned number with a suffix to real number. Suffix can + be k|K for kilo, m|M for mega or g|G for giga. + @param [IN] argument argument value for option_name + @param [IN, OUT] error error no. + @param [IN] option_name name of option +*/ + +static ulonglong eval_num_suffix_ull(char *argument, int *error, char *option_name) +{ + char *endchar; + ulonglong num; + + *error= 0; + errno= 0; + num= my_strtoull(argument, &endchar, 10); + if (errno == ERANGE) + { + my_getopt_error_reporter(ERROR_LEVEL, + "Incorrect unsigned integer value: '%s'", argument); + *error= 1; + return 0; + } + if (*endchar == 'k' || *endchar == 'K') + num*= 1024L; + else if (*endchar == 'm' || *endchar == 'M') + num*= 1024L * 1024L; + else if (*endchar == 'g' || *endchar == 'G') + num*= 1024L * 1024L * 1024L; + else if (*endchar) + { + my_message_local(ERROR_LEVEL, + "Unknown suffix '%c' used for variable '%s' (value '%s')", + *endchar, option_name, argument); + *error= 1; + return 0; + } + return num; +} + +/* + function: getopt_ll + + Evaluates and returns the value that user gave as an argument + to a variable. Recognizes (case insensitive) K as KILO, M as MEGA + and G as GIGA bytes. Some values must be in certain blocks, as + defined in the given my_option struct, this function will check + that those values are honored. + In case of an error, set error value in *err. +*/ + +static longlong getopt_ll(char *arg, const struct my_option *optp, int *err) +{ + longlong num=eval_num_suffix(arg, err, (char*) optp->name); + return getopt_ll_limit_value(num, optp, NULL); +} + + +/** + Maximum possible value for an integer GET_* variable type + @param var_type type of integer variable (GET_*) + @returns maximum possible value for this type + */ +ulonglong max_of_int_range(int var_type) +{ + switch (var_type) + { + case GET_INT: + return INT_MAX; + case GET_LONG: + return LONG_MAX; + case GET_LL: + return LLONG_MAX; + case GET_UINT: + return UINT_MAX; + case GET_ULONG: + return ULONG_MAX; + case GET_ULL: + return ULLONG_MAX; + default: + DBUG_ASSERT(0); + return 0; + } +} + + +/* + function: getopt_ll_limit_value + + Applies min/max/block_size to a numeric value of an option. + Returns "fixed" value. +*/ + +longlong getopt_ll_limit_value(longlong num, const struct my_option *optp, + my_bool *fix) +{ + longlong old= num; + my_bool adjusted= FALSE; + char buf1[255], buf2[255]; + ulonglong block_size= (optp->block_size ? (ulonglong) optp->block_size : 1L); + const longlong max_of_type= + (longlong)max_of_int_range(optp->var_type & GET_TYPE_MASK); + + if (num > 0 && ((ulonglong) num > (ulonglong) optp->max_value) && + optp->max_value) /* if max value is not set -> no upper limit */ + { + num= (ulonglong) optp->max_value; + adjusted= TRUE; + } + + if (num > max_of_type) + { + num= max_of_type; + adjusted= TRUE; + } + + num= (num / block_size); + num= (longlong) (num * block_size); + + if (num < optp->min_value) + { + num= optp->min_value; + if (old < optp->min_value) + adjusted= TRUE; + } + + if (fix) + *fix= old != num; + else if (adjusted) + my_getopt_error_reporter(WARNING_LEVEL, + "option '%s': signed value %s adjusted to %s", + optp->name, llstr(old, buf1), llstr(num, buf2)); + return num; +} + +static inline my_bool is_negative_num(char* num) +{ + while (my_isspace(&my_charset_latin1, *num)) + num++; + + return (*num == '-'); +} + +/* + function: getopt_ull + + This is the same as getopt_ll, but is meant for unsigned long long + values. +*/ + +static ulonglong getopt_ull(char *arg, const struct my_option *optp, int *err) +{ + char buf[255]; + ulonglong num; + + /* If a negative number is specified as a value for the option. */ + if (arg == NULL || is_negative_num(arg) == TRUE) + { + num= (ulonglong) optp->min_value; + my_getopt_error_reporter(WARNING_LEVEL, + "option '%s': value %s adjusted to %s", + optp->name, arg, ullstr(num, buf)); + } + else + num= eval_num_suffix_ull(arg, err, (char*) optp->name); + + return getopt_ull_limit_value(num, optp, NULL); +} + + +ulonglong getopt_ull_limit_value(ulonglong num, const struct my_option *optp, + my_bool *fix) +{ + my_bool adjusted= FALSE; + ulonglong old= num; + char buf1[255], buf2[255]; + const ulonglong max_of_type= + max_of_int_range(optp->var_type & GET_TYPE_MASK); + + if (num > (ulonglong) optp->max_value && + optp->max_value) /* if max value is not set -> no upper limit */ + { + num= (ulonglong) optp->max_value; + adjusted= TRUE; + } + + if (num > max_of_type) + { + num= max_of_type; + adjusted= TRUE; + } + + if (optp->block_size > 1) + { + num/= (ulonglong) optp->block_size; + num*= (ulonglong) optp->block_size; + } + + if (num < (ulonglong) optp->min_value) + { + num= (ulonglong) optp->min_value; + if (old < (ulonglong) optp->min_value) + adjusted= TRUE; + } + + if (fix) + *fix= old != num; + else if (adjusted) + my_getopt_error_reporter(WARNING_LEVEL, + "option '%s': unsigned value %s adjusted to %s", + optp->name, ullstr(old, buf1), ullstr(num, buf2)); + + return num; +} + +double getopt_double_limit_value(double num, const struct my_option *optp, + my_bool *fix) +{ + my_bool adjusted= FALSE; + double old= num; + double min, max; + + max= getopt_ulonglong2double(optp->max_value); + min= getopt_ulonglong2double(optp->min_value); + if (max && num > max) + { + num= max; + adjusted= TRUE; + } + if (num < min) + { + num= min; + adjusted= TRUE; + } + if (fix) + *fix= adjusted; + else if (adjusted) + my_getopt_error_reporter(WARNING_LEVEL, + "option '%s': value %g adjusted to %g", + optp->name, old, num); + return num; +} + +/* + Get double value withing ranges + + Evaluates and returns the value that user gave as an argument to a variable. + + RETURN + decimal value of arg + + In case of an error, prints an error message and sets *err to + EXIT_ARGUMENT_INVALID. Otherwise err is not touched +*/ + +static double getopt_double(char *arg, const struct my_option *optp, int *err) +{ + double num; + int error; + char *end= arg + 1000; /* Big enough as *arg is \0 terminated */ + num= my_strtod(arg, &end, &error); + if (end[0] != 0 || error) + { + my_getopt_error_reporter(ERROR_LEVEL, + "Invalid decimal value for option '%s'\n", optp->name); + *err= EXIT_ARGUMENT_INVALID; + return 0.0; + } + return getopt_double_limit_value(num, optp, NULL); +} + +/* + Init one value to it's default values + + SYNOPSIS + init_one_value() + option Option to initialize + value Pointer to variable +*/ + +static void init_one_value(const struct my_option *option, void *variable, + longlong value) +{ + DBUG_ENTER("init_one_value"); + switch ((option->var_type & GET_TYPE_MASK)) { + case GET_BOOL: + *((my_bool*) variable)= (my_bool) value; + break; + case GET_INT: + *((int*) variable)= (int) getopt_ll_limit_value((int) value, option, NULL); + break; + case GET_ENUM: + *((ulong*) variable)= (ulong) value; + break; + case GET_UINT: + *((uint*) variable)= (uint) getopt_ull_limit_value((uint) value, option, NULL); + break; + case GET_LONG: + *((long*) variable)= (long) getopt_ll_limit_value((long) value, option, NULL); + break; + case GET_ULONG: + *((ulong*) variable)= (ulong) getopt_ull_limit_value((ulong) value, option, NULL); + break; + case GET_LL: + *((longlong*) variable)= getopt_ll_limit_value(value, option, NULL); + break; + case GET_ULL: + *((ulonglong*) variable)= getopt_ull_limit_value((ulonglong) value, option, NULL); + break; + case GET_SET: + case GET_FLAGSET: + *((ulonglong*) variable)= (ulonglong) value; + break; + case GET_DOUBLE: + *((double*) variable)= getopt_ulonglong2double(value); + break; + case GET_STR: + case GET_PASSWORD: + /* + Do not clear variable value if it has no default value. + The default value may already be set. + NOTE: To avoid compiler warnings, we first cast longlong to intptr, + so that the value has the same size as a pointer. + */ + if ((char*) (intptr) value) + *((char**) variable)= (char*) (intptr) value; + break; + case GET_STR_ALLOC: + /* + Do not clear variable value if it has no default value. + The default value may already be set. + NOTE: To avoid compiler warnings, we first cast longlong to intptr, + so that the value has the same size as a pointer. + */ + if ((char*) (intptr) value) + { + char **pstr= (char **) variable; + my_free(*pstr); + *pstr= my_strdup(key_memory_defaults, + (char*) (intptr) value, MYF(MY_WME)); + } + break; + default: /* dummy default to avoid compiler warnings */ + break; + } + DBUG_VOID_RETURN; +} + + +/* + Init one value to it's default values + + SYNOPSIS + init_one_value() + option Option to initialize + value Pointer to variable +*/ + +static void fini_one_value(const struct my_option *option, void *variable, + longlong value MY_ATTRIBUTE ((unused))) +{ + DBUG_ENTER("fini_one_value"); + switch ((option->var_type & GET_TYPE_MASK)) { + case GET_STR_ALLOC: + my_free(*((char**) variable)); + *((char**) variable)= NULL; + break; + default: /* dummy default to avoid compiler warnings */ + break; + } + DBUG_VOID_RETURN; +} + + +void my_cleanup_options(const struct my_option *options) +{ + init_variables(options, fini_one_value); +} + + +/* + initialize all variables to their default values + + SYNOPSIS + init_variables() + options Array of options + + NOTES + We will initialize the value that is pointed to by options->value. + If the value is of type GET_ASK_ADDR, we will ask for the address + for a value and initialize. +*/ + +static void init_variables(const struct my_option *options, + init_func_p init_one_value) +{ + DBUG_ENTER("init_variables"); + for (; options->name; options++) + { + void *value; + DBUG_PRINT("options", ("name: '%s'", options->name)); + /* + We must set u_max_value first as for some variables + options->u_max_value == options->value and in this case we want to + set the value to default value. + */ + if (options->u_max_value) + init_one_value(options, options->u_max_value, options->max_value); + value= (options->var_type & GET_ASK_ADDR ? + (*getopt_get_addr)("", 0, options, 0) : options->value); + if (value) + init_one_value(options, value, options->def_value); + } + DBUG_VOID_RETURN; +} + +/** + Prints variable or option name, replacing _ with - to given file stream + parameter (by default to stdout). + @param [IN] optp my_option parameter + @param [IN] file stream where the output of optp parameter name + goes (by default to stdout). +*/ +static uint print_name(const struct my_option *optp, FILE* file = stdout) +{ + const char *s= optp->name; + for (;*s;s++) + putc(*s == '_' ? '-' : *s, file); + return s - optp->name; +} + +/* + function: my_print_options + + Print help for all options and variables. +*/ + +void my_print_help(const struct my_option *options) +{ + uint col, name_space= 22, comment_space= 57; + const char *line_end; + const struct my_option *optp; + + for (optp= options; optp->name; optp++) + { + if (optp->id && optp->id < 256) + { + printf(" -%c%s", optp->id, strlen(optp->name) ? ", " : " "); + col= 6; + } + else + { + printf(" "); + col= 2; + } + if (strlen(optp->name)) + { + printf("--"); + col+= 2 + print_name(optp); + if (optp->arg_type == NO_ARG || + (optp->var_type & GET_TYPE_MASK) == GET_BOOL) + { + putchar(' '); + col++; + } + else if ((optp->var_type & GET_TYPE_MASK) == GET_STR || + (optp->var_type & GET_TYPE_MASK) == GET_PASSWORD || + (optp->var_type & GET_TYPE_MASK) == GET_STR_ALLOC || + (optp->var_type & GET_TYPE_MASK) == GET_ENUM || + (optp->var_type & GET_TYPE_MASK) == GET_SET || + (optp->var_type & GET_TYPE_MASK) == GET_FLAGSET ) + { + printf("%s=name%s ", optp->arg_type == OPT_ARG ? "[" : "", + optp->arg_type == OPT_ARG ? "]" : ""); + col+= (optp->arg_type == OPT_ARG) ? 8 : 6; + } + else + { + printf("%s=#%s ", optp->arg_type == OPT_ARG ? "[" : "", + optp->arg_type == OPT_ARG ? "]" : ""); + col+= (optp->arg_type == OPT_ARG) ? 5 : 3; + } + if (col > name_space && optp->comment && *optp->comment) + { + putchar('\n'); + col= 0; + } + } + for (; col < name_space; col++) + putchar(' '); + if (optp->comment && *optp->comment) + { + const char *comment= optp->comment, *end= strend(comment); + + while ((uint) (end - comment) > comment_space) + { + for (line_end= comment + comment_space; *line_end != ' '; line_end--) + {} + for (; comment != line_end; comment++) + putchar(*comment); + comment++; /* skip the space, as a newline will take it's place now */ + putchar('\n'); + for (col= 0; col < name_space; col++) + putchar(' '); + } + printf("%s", comment); + } + putchar('\n'); + if ((optp->var_type & GET_TYPE_MASK) == GET_BOOL) + { + if (optp->def_value != 0) + { + printf("%*s(Defaults to on; use --skip-", name_space, ""); + print_name(optp); + printf(" to disable.)\n"); + } + } + } +} + +/** + function: my_print_variables + Print variables. + @param [IN] options my_option list +*/ +void my_print_variables(const struct my_option *options) +{ + my_print_variables_ex(options, stdout); +} + +/** + function: my_print_variables_ex + Print variables to given file parameter stream (by default to stdout). + @param [IN] options my_options list + @param [IN] file stream where the output goes. +*/ + +void my_print_variables_ex(const struct my_option *options, FILE* file) +{ + uint name_space= 34, nr; + size_t length; + ulonglong llvalue; + char buff[255]; + const struct my_option *optp; + + for (optp= options; optp->name; optp++) + { + length= strlen(optp->name)+1; + if (length > name_space) + name_space= length; + } + + fprintf(file, "\nVariables (--variable-name=value)\n"); + fprintf(file, "%-*s%s", name_space, "and boolean options {FALSE|TRUE}", + "Value (after reading options)\n"); + for (length=1; length < 75; length++) + putc(length == name_space ? ' ' : '-', file); + putc('\n', file); + + for (optp= options; optp->name; optp++) + { + void *value= (optp->var_type & GET_ASK_ADDR ? + (*getopt_get_addr)("", 0, optp, 0) : optp->value); + if (value) + { + length= print_name(optp, file); + for (; length < name_space; length++) + putc(' ', file); + switch ((optp->var_type & GET_TYPE_MASK)) { + case GET_SET: + if (!(llvalue= *(ulonglong*) value)) + fprintf(file, "%s\n", ""); + else + for (nr= 0; llvalue && nr < optp->typelib->count; nr++, llvalue >>=1) + { + if (llvalue & 1) + fprintf(file, llvalue > 1 ? "%s," : "%s\n", + get_type(optp->typelib, nr)); + } + break; + case GET_FLAGSET: + llvalue= *(ulonglong*) value; + for (nr= 0; llvalue && nr < optp->typelib->count; nr++, llvalue >>=1) + { + fprintf(file, "%s%s=", (nr ? "," : ""), get_type(optp->typelib, nr)); + fprintf(file, llvalue & 1 ? "on" : "off"); + } + fprintf(file, "\n"); + break; + case GET_ENUM: + fprintf(file, "%s\n", get_type(optp->typelib, *(ulong*) value)); + break; + case GET_STR: + case GET_PASSWORD: + case GET_STR_ALLOC: /* fall through */ + fprintf(file, "%s\n", *((char**) value) ? *((char**) value) : + "(No default value)"); + break; + case GET_BOOL: + fprintf(file, "%s\n", *((my_bool*) value) ? "TRUE" : "FALSE"); + break; + case GET_INT: + fprintf(file, "%d\n", *((int*) value)); + break; + case GET_UINT: + fprintf(file, "%d\n", *((uint*) value)); + break; + case GET_LONG: + fprintf(file, "%ld\n", *((long*) value)); + break; + case GET_ULONG: + fprintf(file, "%lu\n", *((ulong*) value)); + break; + case GET_LL: + fprintf(file, "%s\n", llstr(*((longlong*) value), buff)); + break; + case GET_ULL: + longlong2str(*((ulonglong*) value), buff, 10); + fprintf(file, "%s\n", buff); + break; + case GET_DOUBLE: + fprintf(file, "%g\n", *(double*) value); + break; + case GET_NO_ARG: + fprintf(file, "(No default value)\n"); + break; + default: + fprintf(file, "(Disabled)\n"); + break; + } + } + } +} diff --git a/mysql/mysys_ssl/my_md5.cpp b/mysql/mysys_ssl/my_md5.cpp new file mode 100644 index 0000000..4c14366 --- /dev/null +++ b/mysql/mysys_ssl/my_md5.cpp @@ -0,0 +1,68 @@ +/* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ + + +/** + @file + + @brief + Wrapper functions for OpenSSL and YaSSL. Also provides a Compatibility layer + to make available YaSSL's MD5 implementation. +*/ + +#include +#include + +#if defined(HAVE_YASSL) +#include "my_config.h" +#include "md5.hpp" + +static void my_md5_hash(char *digest, const char *buf, int len) +{ + TaoCrypt::MD5 hasher; + hasher.Update((TaoCrypt::byte *) buf, len); + hasher.Final((TaoCrypt::byte *) digest); +} + +#elif defined(HAVE_OPENSSL) +#include + +static void my_md5_hash(unsigned char* digest, unsigned const char *buf, int len) +{ + MD5_CTX ctx; + MD5_Init (&ctx); + MD5_Update (&ctx, buf, len); + MD5_Final (digest, &ctx); +} + +#endif /* HAVE_YASSL */ + +/** + Wrapper function to compute MD5 message digest. + + @param digest [out] Computed MD5 digest + @param buf [in] Message to be computed + @param len [in] Length of the message + + @return void +*/ +void compute_md5_hash(char *digest, const char *buf, int len) +{ +#if defined(HAVE_YASSL) + my_md5_hash(digest, buf, len); +#elif defined(HAVE_OPENSSL) + my_md5_hash((unsigned char*)digest, (unsigned const char*)buf, len); +#endif /* HAVE_YASSL */ +} diff --git a/mysql/mysys_ssl/my_murmur3.cpp b/mysql/mysys_ssl/my_murmur3.cpp new file mode 100644 index 0000000..82dccb6 --- /dev/null +++ b/mysql/mysys_ssl/my_murmur3.cpp @@ -0,0 +1,134 @@ +/* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ + +/* + Implementation of 32-bit version of MurmurHash3 - fast non-cryptographic + hash function with good statistical properties, which is based on public + domain code by Austin Appleby. +*/ + +#include + + +/* + Platform-specific implementations of ROTL32 helper. +*/ + +#if defined(_MSC_VER) + +/* Microsoft Visual Studio supports intrinsic for rotate left. */ + +#include + +#define ROTL32(x, y) _rotl(x, y) + +/* + Force inlining of intrinsic even though /Oi option is turned off + in release builds. +*/ +#pragma intrinsic(_rotl) + +#else // !defined(_MSC_VER) + +/* + Many other compilers should be able to optimize the below + function to single instruction. +*/ + +inline uint32 rotl32 (uint32 x, char r) +{ + return (x << r) | (x >> (32 - r)); +} + +#define ROTL32(x,y) rotl32(x,y) + +#endif // !defined(_MSC_VER) + + +/** + Compute 32-bit version of MurmurHash3 hash for the key. + + @param key Key for which hash value to be computed. + @param len Key length. + @param seed Seed for hash computation. + + @note WARNING! Since MurmurHash3 is known to be susceptible to "hash DoS" + attack it should not be used in any situation where attacker has + control over key being hashed and thus can cause performance problems + due to degradation of hash lookup to linear list search. + + @returns Hash value for the key. +*/ + +uint32 murmur3_32(const uchar *key, size_t len, uint32 seed) +{ + const uchar *tail= key + (len - len % 4); + + uint32 h1= seed; + + /* Constants for magic numbers that are used more than once. */ + const uint32 c1= 0xcc9e2d51; + const uint32 c2= 0x1b873593; + + /* Body: process all 32-bit blocks in the key. */ + + for (const uchar *data= key; data != tail; data+= 4) + { + uint32 k1= uint4korr(data); + + k1*= c1; + k1= ROTL32(k1, 15); + k1*= c2; + + h1^= k1; + h1= ROTL32(h1, 13); + h1= h1 * 5 + 0xe6546b64; + } + + /* Tail: handle remaining len % 4 bytes. */ + + uint32 k1= 0; + + switch(len % 4) + { + case 3: + k1^= static_cast(tail[2]) << 16; + /* Fall through. */ + case 2: + k1^= static_cast(tail[1]) << 8; + /* Fall through. */ + case 1: + k1^= tail[0]; + k1*= c1; + k1= ROTL32(k1, 15); + k1*= c2; + h1^= k1; + }; + + /* + Finalization mix: + Add length and force all bits of a hash block to avalanche. + */ + + h1^= len; + + h1^= h1 >> 16; + h1*= 0x85ebca6b; + h1^= h1 >> 13; + h1*= 0xc2b2ae35; + h1^= h1 >> 16; + + return h1; +} diff --git a/mysql/mysys_ssl/my_rnd.cpp b/mysql/mysys_ssl/my_rnd.cpp new file mode 100644 index 0000000..19fd37e --- /dev/null +++ b/mysql/mysys_ssl/my_rnd.cpp @@ -0,0 +1,115 @@ +/* + Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include + +#if defined(HAVE_YASSL) + +#if defined(YASSL_PREFIX) +#define RAND_bytes yaRAND_bytes +#endif /* YASSL_PREFIX */ + +#include + +#elif defined(HAVE_OPENSSL) +#include +#include +#endif /* HAVE_YASSL */ + + +/* + A wrapper to use OpenSSL/yaSSL PRNGs. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Generate random number. + + @param rand_st [INOUT] Structure used for number generation. + + @retval Generated pseudo random number. +*/ + +double my_rnd(struct rand_struct *rand_st) +{ + rand_st->seed1= (rand_st->seed1*3+rand_st->seed2) % rand_st->max_value; + rand_st->seed2= (rand_st->seed1+rand_st->seed2+33) % rand_st->max_value; + return (((double) rand_st->seed1) / rand_st->max_value_dbl); +} + + + +/** +Fill a buffer with random bytes using the SSL library routines + +@param buffer [OUT] Buffer to receive the random data +@param buffer_size [IN] sizeof the the buffer + +@retval 1 error ocurred. +@retval 0 OK +*/ +int +my_rand_buffer(unsigned char *buffer, size_t buffer_size) +{ + int rc; +#if defined(HAVE_YASSL) /* YaSSL */ + rc= yaSSL::RAND_bytes(buffer, buffer_size); + + if (!rc) + return 1; +#elif defined(HAVE_OPENSSL) + rc= RAND_bytes(buffer, buffer_size); + + if (!rc) + { + ERR_clear_error(); + return 1; + } +#else /* no SSL */ +#error not using an SSL library not supported +#endif + return 0; +} + + +/** + Generate a random number using the OpenSSL/yaSSL supplied + random number generator if available. + + @param rand_st [INOUT] Structure used for number generation + only if none of the SSL libraries are + available. + + @retval Generated random number. +*/ + +double my_rnd_ssl(struct rand_struct *rand_st) +{ + unsigned int res; + + if (my_rand_buffer((unsigned char *) &res, sizeof(res))) + return my_rnd(rand_st); + + return (double)res / (double)UINT_MAX; +} + + +#ifdef __cplusplus +} +#endif diff --git a/mysql/mysys_ssl/my_sha1.cpp b/mysql/mysys_ssl/my_sha1.cpp new file mode 100644 index 0000000..5d9d480 --- /dev/null +++ b/mysql/mysys_ssl/my_sha1.cpp @@ -0,0 +1,141 @@ +/* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ + + +/** + @file + + @brief + Wrapper functions for OpenSSL, YaSSL implementations. Also provides a + Compatibility layer to make available YaSSL's SHA1 implementation. +*/ + +#include +#include + +#if defined(HAVE_YASSL) +#include "sha.hpp" + +/** + Compute SHA1 message digest using YaSSL. + + @param digest [out] Computed SHA1 digest + @param buf [in] Message to be computed + @param len [in] Length of the message + + @return void +*/ +void mysql_sha1_yassl(uint8 *digest, const char *buf, size_t len) +{ + TaoCrypt::SHA hasher; + hasher.Update((const TaoCrypt::byte *) buf, (TaoCrypt::word32)len); + hasher.Final ((TaoCrypt::byte *) digest); +} + +/** + Compute SHA1 message digest for two messages in order to + emulate sha1(msg1, msg2) using YaSSL. + + @param digest [out] Computed SHA1 digest + @param buf1 [in] First message + @param len1 [in] Length of first message + @param buf2 [in] Second message + @param len2 [in] Length of second message + + @return void +*/ +void mysql_sha1_multi_yassl(uint8 *digest, const char *buf1, int len1, + const char *buf2, int len2) +{ + TaoCrypt::SHA hasher; + hasher.Update((const TaoCrypt::byte *) buf1, len1); + hasher.Update((const TaoCrypt::byte *) buf2, len2); + hasher.Final((TaoCrypt::byte *) digest); +} + +#elif defined(HAVE_OPENSSL) +#include + +int mysql_sha1_reset(SHA_CTX *context) +{ + return SHA1_Init(context); +} + + +int mysql_sha1_input(SHA_CTX *context, const uint8 *message_array, + unsigned length) +{ + return SHA1_Update(context, message_array, length); +} + + +int mysql_sha1_result(SHA_CTX *context, + uint8 Message_Digest[SHA1_HASH_SIZE]) +{ + return SHA1_Final(Message_Digest, context); +} + +#endif /* HAVE_YASSL */ + +/** + Wrapper function to compute SHA1 message digest. + + @param digest [out] Computed SHA1 digest + @param buf [in] Message to be computed + @param len [in] Length of the message + + @return void +*/ +void compute_sha1_hash(uint8 *digest, const char *buf, size_t len) +{ +#if defined(HAVE_YASSL) + mysql_sha1_yassl(digest, buf, len); +#elif defined(HAVE_OPENSSL) + SHA_CTX sha1_context; + + mysql_sha1_reset(&sha1_context); + mysql_sha1_input(&sha1_context, (const uint8 *) buf, len); + mysql_sha1_result(&sha1_context, digest); +#endif /* HAVE_YASSL */ +} + + +/** + Wrapper function to compute SHA1 message digest for + two messages in order to emulate sha1(msg1, msg2). + + @param digest [out] Computed SHA1 digest + @param buf1 [in] First message + @param len1 [in] Length of first message + @param buf2 [in] Second message + @param len2 [in] Length of second message + + @return void +*/ +void compute_sha1_hash_multi(uint8 *digest, const char *buf1, int len1, + const char *buf2, int len2) +{ +#if defined(HAVE_YASSL) + mysql_sha1_multi_yassl(digest, buf1, len1, buf2, len2); +#elif defined(HAVE_OPENSSL) + SHA_CTX sha1_context; + + mysql_sha1_reset(&sha1_context); + mysql_sha1_input(&sha1_context, (const uint8 *) buf1, len1); + mysql_sha1_input(&sha1_context, (const uint8 *) buf2, len2); + mysql_sha1_result(&sha1_context, digest); +#endif /* HAVE_YASSL */ +} + diff --git a/mysql/mysys_ssl/my_sha2.cpp b/mysql/mysys_ssl/my_sha2.cpp new file mode 100644 index 0000000..0020033 --- /dev/null +++ b/mysql/mysys_ssl/my_sha2.cpp @@ -0,0 +1,68 @@ +/* Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */ + + +/** + @file + A compatibility layer to our built-in SSL implementation, to mimic the + oft-used external library, OpenSSL. +*/ + +#include +#include + +#ifdef HAVE_YASSL + +/* + If TaoCrypt::SHA512 or ::SHA384 are not defined (but ::SHA256 is), it's + probably that neither of config.h's SIZEOF_LONG or SIZEOF_LONG_LONG are + 64 bits long. At present, both OpenSSL and YaSSL require 64-bit integers + for SHA-512. (The SIZEOF_* definitions come from autoconf's config.h .) +*/ + +# define GEN_YASSL_SHA2_BRIDGE(size) \ +unsigned char* SHA##size(const unsigned char *input_ptr, size_t input_length, \ + char unsigned *output_ptr) { \ + TaoCrypt::SHA##size hasher; \ + \ + hasher.Update(input_ptr, input_length); \ + hasher.Final(output_ptr); \ + return(output_ptr); \ +} + + +/** + @fn SHA512 + @fn SHA384 + @fn SHA256 + @fn SHA224 + + Instantiate an hash object, fill in the cleartext value, compute the digest, + and extract the result from the object. + + (Generate the functions. See similar .h code for the prototypes.) +*/ +# ifndef OPENSSL_NO_SHA512 +GEN_YASSL_SHA2_BRIDGE(512); +GEN_YASSL_SHA2_BRIDGE(384); +# else +# warning Some SHA2 functionality is missing. See OPENSSL_NO_SHA512. +# endif +GEN_YASSL_SHA2_BRIDGE(256); +GEN_YASSL_SHA2_BRIDGE(224); + +# undef GEN_YASSL_SHA2_BRIDGE + +#endif /* HAVE_YASSL */ -- cgit v1.1