Skip to content

File authenticate_password.cpp

File List > cubrid > src > object > authenticate_password.cpp

Go to the documentation of this file

/*
 *
 * Copyright 2016 CUBRID Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

/*
 * authenticate_password.cpp -
 */

#include "authenticate_password.hpp"

#include "authenticate.h"
#include "dbtype.h"
#include "encryption.h" /* crypt_seed () */
#include "crypt_opfunc.h" /* crypt_sha_two () */
#include "object_accessor.h"
#include "schema_manager.h" /* sm_find_class */

/*
 * Password Encoding
 */

/*
 * Password encoding is a bit kludgey to support older databases where the
 * password was stored in an unencoded format.  Since we don't want
 * to invalidate existing databases unless absolutely necessary, we
 * need a way to recognize if the password in the database is encoded or not.
 *
 * The kludge is to store encoded passwords with a special prefix character
 * that could not normally be typed as part of a password.  This will
 * be the binary char \001 or Control-A.  The prefix could be used
 * in the future to identify other encoding schemes in case we find
 * a better way to store passwords.
 *
 * If the password string has this prefix character, we can assume that it
 * has been encoded, otherwise it is assumed to be an older unencoded password.
 *
 */

/*
 * encrypt_password -  Encrypts a password string using DES
 *   return: none
 *   pass(in): string to encrypt
 *   add_prefix(in): non-zero to add the prefix char
 *   dest(out): destination buffer
 */
void
encrypt_password (const char *pass, int add_prefix, char *dest)
{
  if (pass == NULL)
    {
      strcpy (dest, "");
    }
  else
    {
      crypt_seed (PASSWORD_ENCRYPTION_SEED);
      if (!add_prefix)
    {
      crypt_encrypt_printable (pass, dest, AU_MAX_PASSWORD_BUF);
    }
      else
    {
      crypt_encrypt_printable (pass, dest + 1, AU_MAX_PASSWORD_BUF);
      dest[0] = ENCODE_PREFIX_DES;
    }
    }
}

/*
 * encrypt_password_sha1 -  hashing a password string using SHA1
 *   return: none
 *   pass(in): string to encrypt
 *   add_prefix(in): non-zero to add the prefix char
 *   dest(out): destination buffer
 */
void
encrypt_password_sha1 (const char *pass, int add_prefix, char *dest)
{
  if (pass == NULL)
    {
      strcpy (dest, "");
    }
  else
    {
      if (!add_prefix)
    {
      crypt_encrypt_sha1_printable (pass, dest, AU_MAX_PASSWORD_BUF);
    }
      else
    {
      crypt_encrypt_sha1_printable (pass, dest + 1, AU_MAX_PASSWORD_BUF);
      dest[0] = ENCODE_PREFIX_SHA1;
    }
    }
}

/*
 * encrypt_password_sha2_512 -  hashing a password string using SHA2 512
 *   return: none
 *   pass(in): string to encrypt
 *   dest(out): destination buffer
 */
void
encrypt_password_sha2_512 (const char *pass, char *dest)
{
  int error_status = NO_ERROR;
  char *result_strp = NULL;
  int result_len = 0;

  if (pass == NULL)
    {
      strcpy (dest, "");
    }
  else
    {
      error_status = crypt_sha_two (NULL, pass, strlen (pass), 512, &result_strp, &result_len);
      if (error_status == NO_ERROR)
    {
      assert (result_strp != NULL);

      memcpy (dest + 1, result_strp, result_len);
      dest[result_len + 1] = '\0';  /* null termination for match_password () */
      dest[0] = ENCODE_PREFIX_SHA2_512;

      db_private_free_and_init (NULL, result_strp);
    }
      else
    {
      strcpy (dest, "");
    }
    }
}


/*
 * match_password -  This compares two passwords to see if they match.
 *   return: non-zero if the passwords match
 *   user(in): user supplied password
 *   database(in): stored database password
 *
 * Note: Either the user or database password can be encrypted or unencrypted.
 *       The database password will only be unencrypted if this is a very
 *       old database.  The user password will be unencrypted if we're logging
 *       in to an active session.
 */
bool
match_password (const char *user, const char *database)
{
  char buf1[AU_MAX_PASSWORD_BUF + 4];
  char buf2[AU_MAX_PASSWORD_BUF + 4];

  if (user == NULL || database == NULL)
    {
      return false;
    }

  /* get both passwords into an encrypted format */
  /* if database's password was encrypted with DES, then, user's password should be encrypted with DES, */
  if (IS_ENCODED_DES (database))
    {
      /* DB: DES */
      strcpy (buf2, database);
      if (IS_ENCODED_ANY (user))
    {
      /* USER : DES */
      strcpy (buf1, Au_user_password_des_oldstyle);
    }
      else
    {
      /* USER : PLAINTEXT -> DES */
      encrypt_password (user, 1, buf1);
    }
    }
  else if (IS_ENCODED_SHA1 (database))
    {
      /* DB: SHA1 */
      strcpy (buf2, database);
      if (IS_ENCODED_ANY (user))
    {
      /* USER:SHA1 */
      strcpy (buf1, Au_user_password_sha1);
    }
      else
    {
      /* USER:PLAINTEXT -> SHA1 */
      encrypt_password_sha1 (user, 1, buf1);
    }
    }
  else if (IS_ENCODED_SHA2_512 (database))
    {
      /* DB: SHA2 */
      strcpy (buf2, database);
      if (IS_ENCODED_ANY (user))
    {
      /* USER:SHA2 */
      strcpy (buf1, Au_user_password_sha2_512);
    }
      else
    {
      /* USER:PLAINTEXT -> SHA2 */
      encrypt_password_sha2_512 (user, buf1);
    }
    }
  else
    {
      /* DB:PLAINTEXT -> SHA2 */
      encrypt_password_sha2_512 (database, buf2);
      if (IS_ENCODED_ANY (user))
    {
      /* USER : SHA1 */
      strcpy (buf1, Au_user_password_sha1);
    }
      else
    {
      /* USER : PLAINTEXT -> SHA1 */
      encrypt_password_sha1 (user, 1, buf1);
    }
    }

  return strcmp (buf1, buf2) == 0;
}

/*
 * au_set_password_internal -  Set the password string for a user.
 *                             This should be using encrypted strings.
 *   return:error code
 *   user(in):  user object
 *   password(in): new password
 *   encode(in): flag to enable encryption of the string in the database
 *   encrypt_prefix(in): If encode flag is 0, then we assume that the given password have been encrypted. So, All I have
 *                       to do is add prefix(SHA2) to given password.
 *                       If encode flag is 1, then we should encrypt password with sha2 and add prefix (SHA2) to it.
 *                       So, I don't care what encrypt_prefix value is.
 */
int
au_set_password_internal (MOP user, const char *password, int encode, char encrypt_prefix)
{
  int error = NO_ERROR;
  DB_VALUE value;
  MOP pass, pclass;
  int save, len;
  char pbuf[AU_MAX_PASSWORD_BUF + 4];

  AU_DISABLE (save);
  if (!ws_is_same_object (Au_user, user) && !au_is_dba_group_member (Au_user))
    {
      error = ER_AU_UPDATE_FAILURE;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }
  else
    {
      /* convert empty password strings to NULL passwords */
      if (password != NULL)
    {
      len = strlen (password);
      if (len == 0)
        {
          password = NULL;
        }
      /*
       * check for large passwords, only do this
       * if the encode flag is on !
       */
      else if (len > AU_MAX_PASSWORD_CHARS && encode)
        {
          error = ER_AU_PASSWORD_OVERFLOW;
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
        }
    }
      if (error == NO_ERROR)
    {
      if ((error = obj_get (user, "password", &value)) == NO_ERROR)
        {
          if (DB_IS_NULL (&value))
        {
          pass = NULL;
        }
          else
        {
          pass = db_get_object (&value);
        }

          if (pass == NULL)
        {
          pclass = sm_find_class (AU_PASSWORD_CLASS_NAME);
          if (pclass != NULL)
            {
              pass = obj_create (pclass);
              if (pass != NULL)
            {
              db_make_object (&value, pass);
              error = obj_set (user, "password", &value);
            }
              else
            {
              assert (er_errid () != NO_ERROR);
              error = er_errid ();
            }
            }
          else
            {
              assert (er_errid () != NO_ERROR);
              error = er_errid ();
            }
        }

          if (error == NO_ERROR && pass != NULL)
        {
          if (encode && password != NULL)
            {
              encrypt_password_sha2_512 (password, pbuf);
              db_make_string (&value, pbuf);
              error = obj_set (pass, "password", &value);
            }
          else
            {
              /*
               * always add the prefix, the unload process strips it out
               * so the password can be read by the csql interpreter
               */
              if (password == NULL)
            {
              db_make_null (&value);
            }
              else
            {
              strcpy (pbuf + 1, password);
              pbuf[0] = encrypt_prefix;
              db_make_string (&value, pbuf);
            }
              error = obj_set (pass, "password", &value);
            }
        }
        }
    }
    }

  if (error == NO_ERROR)
    {
      error = au_update_user_timestamp (user);
    }

  AU_ENABLE (save);
  return (error);
}