"Fossies" - the Fresh Open Source Software Archive

Member "mariadb-connector-c-3.0.9-src/plugins/auth/sha256_pw.c" (8 Feb 2019, 8849 Bytes) of package /linux/misc/mariadb-connector-c-3.0.9-src.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "sha256_pw.c": 3.0.8_vs_3.0.9.

    1 /************************************************************************************
    2   Copyright (C) 2017 MariaDB Corporation AB
    3 
    4   This library is free software; you can redistribute it and/or
    5   modify it under the terms of the GNU Library General Public
    6   License as published by the Free Software Foundation; either
    7   version 2 of the License, or (at your option) any later version.
    8 
    9   This library is distributed in the hope that it will be useful,
   10   but WITHOUT ANY WARRANTY; without even the implied warranty of
   11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   12   Library General Public License for more details.
   13 
   14   You should have received a copy of the GNU Library General Public
   15   License along with this library; if not see <http://www.gnu.org/licenses>
   16   or write to the Free Software Foundation, Inc.,
   17   51 Franklin St., Fifth Floor, Boston, MA 02110, USA
   18  *************************************************************************************/
   19 #ifndef _WIN32
   20 #define _GNU_SOURCE 1
   21 #endif
   22 
   23 #ifdef _WIN32
   24 #if !defined(HAVE_OPENSSL)
   25 #define HAVE_WINCRYPT
   26 #endif
   27 #endif
   28 
   29 #if defined(HAVE_OPENSSL) || defined(HAVE_WINCRYPT)
   30 
   31 #include <ma_global.h>
   32 #include <mysql.h>
   33 #include <mysql/client_plugin.h>
   34 #include <string.h>
   35 #include <memory.h>
   36 #include <errmsg.h>
   37 #include <ma_global.h>
   38 #include <ma_sys.h>
   39 #include <ma_common.h>
   40 
   41 #ifndef WIN32
   42 #include <dlfcn.h>
   43 #endif
   44 
   45 #if defined(HAVE_OPENSSL)
   46 #include <openssl/rsa.h>
   47 #include <openssl/pem.h>
   48 #include <openssl/err.h>
   49 #elif defined(HAVE_WINCRYPT)
   50 #include <wincrypt.h>
   51 #endif
   52 
   53 #define MAX_PW_LEN 1024
   54 
   55 /* function prototypes */
   56 static int auth_sha256_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql);
   57 static int auth_sha256_init(char *unused1,
   58     size_t unused2,
   59     int unused3,
   60     va_list);
   61 
   62 
   63 #ifndef PLUGIN_DYNAMIC
   64 struct st_mysql_client_plugin_AUTHENTICATION sha256_password_client_plugin=
   65 #else
   66 struct st_mysql_client_plugin_AUTHENTICATION _mysql_client_plugin_declaration_ =
   67 #endif
   68 {
   69   MYSQL_CLIENT_AUTHENTICATION_PLUGIN,
   70   MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION,
   71   "sha256_password",
   72   "Georg Richter",
   73   "SHA256 Authentication Plugin",
   74   {0,1,0},
   75   "LGPL",
   76   NULL,
   77   auth_sha256_init,
   78   NULL,
   79   NULL,
   80   auth_sha256_client
   81 };
   82 
   83 #ifdef HAVE_WINCRYPT
   84 static LPBYTE ma_load_pem(const char *buffer, DWORD *buffer_len)
   85 {
   86   LPBYTE der_buffer= NULL;
   87   DWORD der_buffer_length;
   88 
   89   if (buffer_len == NULL || *buffer_len == 0)
   90     return NULL;
   91   /* calculate the length of DER binary */
   92   if (!CryptStringToBinaryA(buffer, *buffer_len, CRYPT_STRING_BASE64HEADER,
   93         NULL, &der_buffer_length, NULL, NULL))
   94     goto end;
   95   /* allocate DER binary buffer */
   96   if (!(der_buffer= (LPBYTE)LocalAlloc(0, der_buffer_length)))
   97     goto end;
   98   /* convert to DER binary */
   99   if (!CryptStringToBinaryA(buffer, *buffer_len, CRYPT_STRING_BASE64HEADER,
  100         der_buffer, &der_buffer_length, NULL, NULL))
  101     goto end;
  102 
  103   *buffer_len= der_buffer_length;
  104 
  105   return der_buffer;
  106 
  107 end:
  108   if (der_buffer)
  109     LocalFree(der_buffer);
  110   *buffer_len= 0;
  111   return NULL;
  112 }
  113 #endif
  114 
  115 static char *load_pub_key_file(const char *filename, int *pub_key_size)
  116 {
  117   FILE *fp= NULL;
  118   char *buffer= NULL;
  119   unsigned char error= 1;
  120   size_t bytes_read= 0;
  121   long fsize= 0;
  122 
  123   if (!pub_key_size)
  124     return NULL;
  125 
  126   if (!(fp= fopen(filename, "r")))
  127     goto end;
  128 
  129   if (fseek(fp, 0, SEEK_END))
  130     goto end;
  131 
  132   fsize= ftell(fp);
  133   if (fsize < 0)
  134     goto end;
  135 
  136   rewind(fp);
  137 
  138   if (!(buffer= malloc(fsize + 1)))
  139     goto end;
  140 
  141   bytes_read= fread(buffer, 1, (size_t)fsize, fp);
  142   if (bytes_read < (size_t)fsize)
  143     goto end;
  144 
  145   *pub_key_size= (int)bytes_read;
  146 
  147   error= 0;
  148 
  149 end:
  150   if (fp)
  151     fclose(fp);
  152   if (error && buffer)
  153   {
  154     free(buffer);
  155     buffer= NULL;
  156   }
  157   return buffer;
  158 }
  159 
  160 
  161 static int auth_sha256_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
  162 {
  163   unsigned char *packet;
  164   int packet_length;
  165   int rc= CR_ERROR;
  166   char passwd[MAX_PW_LEN];
  167   unsigned char rsa_enc_pw[MAX_PW_LEN];
  168   unsigned int rsa_size;
  169   unsigned int pwlen, i;
  170 
  171 #if defined(HAVE_OPENSSL)
  172   RSA *pubkey= NULL;
  173   BIO *bio;
  174 #elif defined(HAVE_WINCRYPT)
  175   HCRYPTKEY pubkey= 0;
  176   HCRYPTPROV hProv= 0;
  177   LPBYTE der_buffer= NULL;
  178   DWORD der_buffer_len= 0;
  179   CERT_PUBLIC_KEY_INFO *publicKeyInfo= NULL;
  180   DWORD ParamSize= sizeof(DWORD);
  181   int publicKeyInfoLen;
  182 #endif
  183   char *filebuffer= NULL;
  184 
  185   /* read error */
  186   if ((packet_length= vio->read_packet(vio, &packet)) < 0)
  187     return CR_ERROR;
  188 
  189   if (packet_length != SCRAMBLE_LENGTH + 1)
  190     return CR_SERVER_HANDSHAKE_ERR;
  191 
  192   memmove(mysql->scramble_buff, packet, SCRAMBLE_LENGTH);
  193   mysql->scramble_buff[SCRAMBLE_LENGTH]= 0;
  194 
  195   /* if a tls session is active we need to send plain password */
  196   if (mysql->client_flag & CLIENT_SSL)
  197   {
  198     if (vio->write_packet(vio, (unsigned char *)mysql->passwd, (int)strlen(mysql->passwd) + 1))
  199       return CR_ERROR;
  200     return CR_OK;
  201   }
  202 
  203   /* send empty packet if no password was provided */
  204   if (!mysql->passwd || !mysql->passwd[0])
  205   {
  206     if (vio->write_packet(vio, 0, 0))
  207       return CR_ERROR;
  208     return CR_OK;
  209   }
  210 
  211   /* read public key file (if specified) */
  212   if (mysql->options.extension &&
  213       mysql->options.extension->server_public_key)
  214   {
  215     filebuffer= load_pub_key_file(mysql->options.extension->server_public_key,
  216                              &packet_length);
  217   }
  218 
  219   /* if no public key file was specified or if we couldn't read the file,
  220      we ask server to send public key */
  221   if (!filebuffer)
  222   {
  223     unsigned char buf= 1;
  224     if (vio->write_packet(vio, &buf, 1))
  225       return CR_ERROR;
  226     if ((packet_length=vio->read_packet(vio, &packet)) == -1)
  227       return CR_ERROR;
  228   }
  229 #if defined(HAVE_OPENSSL)
  230   bio= BIO_new_mem_buf(filebuffer ? (unsigned char *)filebuffer : packet,
  231                        packet_length);
  232   if ((pubkey= PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL)))
  233     rsa_size= RSA_size(pubkey);
  234   BIO_free(bio);
  235   ERR_clear_error();
  236 #elif defined(HAVE_WINCRYPT)
  237   der_buffer_len= packet_length;
  238   /* Load pem and convert it to binary object. New length will be returned
  239      in der_buffer_len */
  240   if (!(der_buffer= ma_load_pem(filebuffer ? filebuffer : (char *)packet, &der_buffer_len)))
  241     goto error;
  242 
  243   /* Create context and load public key */
  244   if (!CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO,
  245                            der_buffer, der_buffer_len,
  246                            CRYPT_DECODE_ALLOC_FLAG, NULL,
  247                            &publicKeyInfo, (DWORD *)&publicKeyInfoLen))
  248     goto error;
  249   LocalFree(der_buffer);
  250 
  251   if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 
  252                            CRYPT_VERIFYCONTEXT))
  253     goto error;
  254   if (!CryptImportPublicKeyInfo(hProv, X509_ASN_ENCODING,
  255                                 publicKeyInfo, &pubkey))
  256     goto error;
  257 
  258   /* Get rsa_size */
  259   CryptGetKeyParam(pubkey, KP_KEYLEN, (BYTE *)&rsa_size, &ParamSize, 0);
  260   rsa_size /= 8;
  261 #endif
  262   if (!pubkey)
  263     return CR_ERROR;
  264 
  265   pwlen= (unsigned int)strlen(mysql->passwd) + 1;  /* include terminating zero */
  266   if (pwlen > MAX_PW_LEN)
  267     goto error;
  268   memcpy(passwd, mysql->passwd, pwlen);
  269 
  270   /* xor password with scramble */
  271   for (i=0; i < pwlen; i++)
  272     passwd[i]^= *(mysql->scramble_buff + i % SCRAMBLE_LENGTH);
  273 
  274   /* encrypt scrambled password */
  275 #if defined(HAVE_OPENSSL)
  276   if (RSA_public_encrypt(pwlen, (unsigned char *)passwd, rsa_enc_pw, pubkey, RSA_PKCS1_OAEP_PADDING) < 0)
  277     goto error;
  278 #elif defined(HAVE_WINCRYPT)
  279   if (!CryptEncrypt(pubkey, 0, TRUE, CRYPT_OAEP, (BYTE *)passwd, (DWORD *)&pwlen, MAX_PW_LEN))
  280     goto error;
  281   /* Windows encrypts as little-endian, while server (openssl) expects
  282      big-endian, so we have to revert the string */
  283   for (i= 0; i < rsa_size / 2; i++)
  284   {
  285     rsa_enc_pw[i]= passwd[rsa_size - 1 - i];
  286     rsa_enc_pw[rsa_size - 1 - i]= passwd[i];
  287   }
  288 #endif
  289   if (vio->write_packet(vio, rsa_enc_pw, rsa_size))
  290     goto error;
  291 
  292   rc= CR_OK;
  293 error:
  294 #if defined(HAVE_OPENSSL)
  295   if (pubkey)
  296     RSA_free(pubkey);
  297 #elif defined(HAVE_WINCRYPT)
  298   CryptReleaseContext(hProv, 0);
  299   if (publicKeyInfo)
  300     LocalFree(publicKeyInfo);
  301 #endif
  302   free(filebuffer);
  303   return rc;
  304 }
  305 /* }}} */
  306 
  307 /* {{{ static int auth_sha256_init */
  308 /*
  309    Initialization routine
  310 
  311    SYNOPSIS
  312    auth_sha256_init
  313    unused1
  314    unused2
  315    unused3
  316    unused4
  317 
  318    DESCRIPTION
  319    Init function checks if the caller provides own dialog function.
  320    The function name must be mariadb_auth_dialog or
  321    mysql_authentication_dialog_ask. If the function cannot be found,
  322    we will use owr own simple command line input.
  323 
  324    RETURN
  325    0           success
  326  */
  327 static int auth_sha256_init(char *unused1 __attribute__((unused)), 
  328     size_t unused2  __attribute__((unused)), 
  329     int unused3     __attribute__((unused)), 
  330     va_list unused4 __attribute__((unused)))
  331 {
  332   return 0;
  333 }
  334 /* }}} */
  335 
  336 #endif  /* defined(HAVE_OPENSSL) || defined(HAVE_WINCRYPT) */