static bool acl_load()

in sql/auth/sql_auth_cache.cc [1651:2207]


static bool acl_load(THD *thd, TABLE_LIST *tables)
{
  TABLE *table;
  READ_RECORD read_record_info;
  bool return_val= TRUE;
  bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
  char tmp_name[NAME_LEN+1];
  sql_mode_t old_sql_mode= thd->variables.sql_mode;
  bool password_expired= false;
  bool super_users_with_empty_plugin= false;
  Acl_load_user_table_schema_factory user_table_schema_factory;
  Acl_load_user_table_schema *table_schema = NULL;
  bool is_old_db_layout= false;
  DBUG_ENTER("acl_load");

  DBUG_EXECUTE_IF("wl_9262_set_max_length_hostname",
                    thd->security_context()->assign_priv_host(
                      "oh_my_gosh_this_is_a_long_"
                      "hostname_look_at_it_it_has_60"
                      "_char", 60);
                    thd->security_context()->assign_host(
                      "oh_my_gosh_this_is_a_long_"
                      "hostname_look_at_it_it_has_60"
                      "_char", 60);
                    thd->security_context()->set_host_or_ip_ptr();
                    );

  thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH;

  grant_version++; /* Privileges updated */

  clear_and_init_db_cache();             // Clear locked hostname cache

  init_sql_alloc(key_memory_acl_mem,
                 &global_acl_memory, ACL_ALLOC_BLOCK_SIZE, 0);
  /*
    Prepare reading from the mysql.user table
  */
  if (init_read_record(&read_record_info, thd, table=tables[0].table,
                       NULL, 1, 1, FALSE))
    goto end;
  table->use_all_columns();
  acl_users->clear();
  /*
   We need to check whether we are working with old database layout. This
   might be the case for instance when we are running mysql_upgrade.
  */
  table_schema= user_table_schema_factory.get_user_table_schema(table);
  is_old_db_layout= user_table_schema_factory.is_old_user_table_schema(table);

  allow_all_hosts=0;
  int read_rec_errcode;
  while (!(read_rec_errcode= read_record_info.read_record(&read_record_info)))
  {
    password_expired= false;
    /* Reading record from mysql.user */
    ACL_USER user;
    memset(&user, 0, sizeof(user));

    /*
      All accounts can authenticate per default. This will change when
      we add a new field to the user table.

      Currently this flag is only set to false when authentication is attempted
      using an unknown user name.
    */
    user.can_authenticate= true;

    /*
      Account is unlocked by default.
    */
    user.account_locked= false;

    /*
      The authorization id isn't a part of the role-graph per default.
      This is true even if CREATE ROLE is used.
    */
    user.is_role= false;

    user.host.update_hostname(get_field(&global_acl_memory,
                                      table->field[table_schema->host_idx()]));
    user.user= get_field(&global_acl_memory,
                         table->field[table_schema->user_idx()]);
    if (check_no_resolve && hostname_requires_resolving(user.host.get_host()))
    {
      LogErr(WARNING_LEVEL, ER_AUTHCACHE_USER_SKIPPED_NEEDS_RESOLVE,
             user.user ? user.user : "",
             user.host.get_host() ? user.host.get_host() : "");
    }

    /* Read password from authentication_string field */
    if (table->s->fields > table_schema->authentication_string_idx())
      user.auth_string.str=
        get_field(&global_acl_memory,
                  table->field[table_schema->authentication_string_idx()]);
    else
    {
      LogErr(ERROR_LEVEL, ER_AUTHCACHE_USER_TABLE_DODGY);

      end_read_record(&read_record_info);
      goto end;
    }
    if(user.auth_string.str)
      user.auth_string.length= strlen(user.auth_string.str);
    else
      user.auth_string= EMPTY_STR;

    {
      uint next_field;
      user.access= get_access(table, table_schema->select_priv_idx(),
                              &next_field) & GLOBAL_ACLS;
      /*
        if it is pre 5.0.1 privilege table then map CREATE privilege on
        CREATE VIEW & SHOW VIEW privileges
      */
      if (table->s->fields <= 31 && (user.access & CREATE_ACL))
        user.access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL);

      /*
        if it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on
        CREATE PROCEDURE & ALTER PROCEDURE privileges
      */
      if (table->s->fields <= 33 && (user.access & CREATE_ACL))
        user.access|= CREATE_PROC_ACL;
      if (table->s->fields <= 33 && (user.access & ALTER_ACL))
        user.access|= ALTER_PROC_ACL;

      /*
        pre 5.0.3 did not have CREATE_USER_ACL
      */
      if (table->s->fields <= 36 && (user.access & GRANT_ACL))
        user.access|= CREATE_USER_ACL;


      /*
        if it is pre 5.1.6 privilege table then map CREATE privilege on
        CREATE|ALTER|DROP|EXECUTE EVENT
      */
      if (table->s->fields <= 37 && (user.access & SUPER_ACL))
        user.access|= EVENT_ACL;

      /*
        if it is pre 5.1.6 privilege then map TRIGGER privilege on CREATE.
      */
      if (table->s->fields <= 38 && (user.access & SUPER_ACL))
        user.access|= TRIGGER_ACL;

      user.sort= get_sort(2,user.host.get_host(),user.user);

      /* Starting from 4.0.2 we have more fields */
      if (table->s->fields >= 31)
      {
        char *ssl_type=
          get_field(thd->mem_root, table->field[table_schema->ssl_type_idx()]);
        if (!ssl_type)
          user.ssl_type=SSL_TYPE_NONE;
        else if (!strcmp(ssl_type, "ANY"))
          user.ssl_type=SSL_TYPE_ANY;
        else if (!strcmp(ssl_type, "X509"))
          user.ssl_type=SSL_TYPE_X509;
        else  /* !strcmp(ssl_type, "SPECIFIED") */
          user.ssl_type=SSL_TYPE_SPECIFIED;

        user.ssl_cipher=
          get_field(&global_acl_memory,
                    table->field[table_schema->ssl_cipher_idx()]);
        user.x509_issuer=
          get_field(&global_acl_memory,
                    table->field[table_schema->x509_issuer_idx()]);
        user.x509_subject=
          get_field(&global_acl_memory,
                    table->field[table_schema->x509_subject_idx()]);

        char *ptr= get_field(thd->mem_root,
                             table->field[table_schema->max_questions_idx()]);
        user.user_resource.questions=ptr ? atoi(ptr) : 0;
        ptr= get_field(thd->mem_root,
                       table->field[table_schema->max_updates_idx()]);
        user.user_resource.updates=ptr ? atoi(ptr) : 0;
        ptr= get_field(thd->mem_root,
                       table->field[table_schema->max_connections_idx()]);
        user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0;
        if (user.user_resource.questions || user.user_resource.updates ||
            user.user_resource.conn_per_hour)
          mqh_used=1;

        if (table->s->fields > table_schema->max_user_connections_idx())
        {
          /* Starting from 5.0.3 we have max_user_connections field */
          ptr= get_field(thd->mem_root,
                         table->field[table_schema->max_user_connections_idx()]);
          user.user_resource.user_conn= ptr ? atoi(ptr) : 0;
        }

        if (table->s->fields >= 41)
        {
          /* We may have plugin & auth_String fields */
          const char *tmpstr=
            get_field(&global_acl_memory,
                      table->field[table_schema->plugin_idx()]);

          /* In case we are working with 5.6 db layout we need to make server
             aware of Password field and that the plugin column can be null.
             In case when plugin column is null we use native password plugin
             if we can.
          */
          if (is_old_db_layout && (tmpstr == NULL || strlen(tmpstr) == 0 ||
              my_strcasecmp(system_charset_info, tmpstr,
                            native_password_plugin_name.str) == 0))
          {
            char *password= get_field(&global_acl_memory,
                                    table->field[table_schema->password_idx()]);

            //We only support native hash, we do not support pre 4.1 hashes
            plugin_ref native_plugin= NULL;
            native_plugin= my_plugin_lock_by_name(0,
                                       native_password_plugin_name,
                                       MYSQL_AUTHENTICATION_PLUGIN);
            if (native_plugin)
            {
              uint password_len= password ? strlen(password) : 0;
              st_mysql_auth *auth=
                (st_mysql_auth *) plugin_decl(native_plugin)->info;
              if (auth->validate_authentication_string(password,
                                                       password_len) == 0)
              {
                //auth_string takes precedence over password
                if (user.auth_string.length == 0)
                {
                  user.auth_string.str= password;
                  user.auth_string.length= password_len;
                }
                if (tmpstr == NULL || strlen(tmpstr) == 0)
                  tmpstr= native_password_plugin_name.str;
              }
              else
              {
                if ((user.access & SUPER_ACL) && !super_users_with_empty_plugin
                    && (tmpstr == NULL || strlen(tmpstr) == 0))
                  super_users_with_empty_plugin= true;

                LogErr(WARNING_LEVEL,
                       ER_AUTHCACHE_USER_IGNORED_DEPRECATED_PASSWORD,
                       user.user ? user.user : "",
                       user.host.get_host() ? user.host.get_host() : "");
                plugin_unlock(0, native_plugin);
                continue;
              }
              plugin_unlock(0, native_plugin);
            }
          }

          /*
            Check if the plugin string is blank or null.
            If it is, the user will be skipped.
          */
          if(tmpstr == NULL || strlen(tmpstr) == 0)
          {
            if ((user.access & SUPER_ACL) && !super_users_with_empty_plugin)
                      super_users_with_empty_plugin= true;
            LogErr(WARNING_LEVEL, ER_AUTHCACHE_USER_IGNORED_NEEDS_PLUGIN,
                   user.user ? user.user : "",
                   user.host.get_host() ? user.host.get_host() : "");
            continue;
          }
          /*
            By comparing the plugin with the built in plugins it is possible
            to optimize the string allocation and comparision.
          */
          if (my_strcasecmp(system_charset_info, tmpstr,
                            native_password_plugin_name.str) == 0)
            user.plugin= native_password_plugin_name;
#if defined(HAVE_OPENSSL)
          else
            if (my_strcasecmp(system_charset_info, tmpstr,
                              sha256_password_plugin_name.str) == 0)
              user.plugin= sha256_password_plugin_name;
#endif
          else
            {
              user.plugin.str= tmpstr;
              user.plugin.length= strlen(tmpstr);
            }
        }

        /* Validate the hash string. */
        plugin_ref plugin= NULL;
        plugin= my_plugin_lock_by_name(0, user.plugin,
                                       MYSQL_AUTHENTICATION_PLUGIN);
        if (plugin)
        {
          st_mysql_auth *auth= (st_mysql_auth *) plugin_decl(plugin)->info;
          if (auth->validate_authentication_string(user.auth_string.str,
                                                   user.auth_string.length))
          {
            LogErr(WARNING_LEVEL, ER_AUTHCACHE_USER_IGNORED_INVALID_PASSWORD,
                   user.user ? user.user : "",
                   user.host.get_host() ? user.host.get_host() : "");
            plugin_unlock(0, plugin);
            continue;
          }
          plugin_unlock(0, plugin);
        }

        if (table->s->fields > table_schema->password_expired_idx())
        {
          char *tmpstr= get_field(&global_acl_memory,
                           table->field[table_schema->password_expired_idx()]);
          if (tmpstr && (*tmpstr == 'Y' || *tmpstr == 'y'))
          {
            user.password_expired= true;

            if (!auth_plugin_supports_expiration(user.plugin.str))
            {
              LogErr(WARNING_LEVEL,
                     ER_AUTHCACHE_EXPIRED_PASSWORD_UNSUPPORTED,
                     user.user ? user.user : "",
                     user.host.get_host() ? user.host.get_host() : "");
              continue;
            }
            password_expired= true;
          }
        }

        if (table->s->fields > table_schema->account_locked_idx())
        {
          char *locked = get_field(&global_acl_memory,
                             table->field[table_schema->account_locked_idx()]);

          if (locked && (*locked == 'Y' || *locked == 'y'))
          {
            user.account_locked= true;
          }
        }

        if (table->s->fields > table_schema->drop_role_priv_idx())
        {
          char *priv=
            get_field(&global_acl_memory,
                      table->field[table_schema->create_role_priv_idx()]);

          if (priv && (*priv == 'Y' || *priv == 'y'))
          {
            user.access |= CREATE_ROLE_ACL;
          }

          priv= get_field(&global_acl_memory,
                          table->field[table_schema->drop_role_priv_idx()]);

          if (priv && (*priv == 'Y' || *priv == 'y'))
          {
            user.access |= DROP_ROLE_ACL;
          }
        }

	/*
	   Initalize the values of timestamp and expire after day
	   to error and true respectively.
	*/
	user.password_last_changed.time_type= MYSQL_TIMESTAMP_ERROR;
	user.use_default_password_lifetime= true;
	user.password_lifetime= 0;

	if (table->s->fields > table_schema->password_last_changed_idx())
        {
	  if (!table->field[table_schema->password_last_changed_idx()]->is_null())
	  {
            char *password_last_changed= get_field(&global_acl_memory,
	          table->field[table_schema->password_last_changed_idx()]);

	    if (password_last_changed &&
	        memcmp(password_last_changed, INVALID_DATE, sizeof(INVALID_DATE)))
	    {
	      String str(password_last_changed, &my_charset_bin);
              str_to_time_with_warn(&str,&(user.password_last_changed));
	    }
	  }
	}

        if (table->s->fields > table_schema->password_lifetime_idx())
        {
          if (!table->
              field[table_schema->password_lifetime_idx()]->is_null())
	  {
	    char *ptr= get_field(&global_acl_memory,
		table->field[table_schema->password_lifetime_idx()]);
	    user.password_lifetime= ptr ? atoi(ptr) : 0;
	    user.use_default_password_lifetime= false;
	  }
	}

      } // end if (table->s->fields >= 31)
      else
      {
        user.ssl_type=SSL_TYPE_NONE;
        if (table->s->fields <= 13)
        {                                               // Without grant
          if (user.access & CREATE_ACL)
            user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
        }
        /* Convert old privileges */
        user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL;
        if (user.access & FILE_ACL)
          user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL;
        if (user.access & PROCESS_ACL)
          user.access|= SUPER_ACL | EXECUTE_ACL;
      }

      set_user_salt(&user);
      user.password_expired= password_expired;

      acl_users->push_back(user);
      if (user.host.check_allow_all_hosts())
        allow_all_hosts=1;                      // Anyone can connect

    }
  } // END while reading records from the mysql.user table

  end_read_record(&read_record_info);
  if (read_rec_errcode > 0)
    goto end;

  std::sort(acl_users->begin(), acl_users->end(), ACL_compare());
  acl_users->shrink_to_fit();

  if (super_users_with_empty_plugin)
  {
    LogErr(WARNING_LEVEL, ER_NO_SUPER_WITHOUT_USER_PLUGIN);
  }

  /*
    Prepare reading from the mysql.db table
  */
  if (init_read_record(&read_record_info, thd, table=tables[1].table,
                       NULL, 1, 1, FALSE))
    goto end;
  table->use_all_columns();
  acl_dbs->clear();

  while (!(read_rec_errcode= read_record_info.read_record(&read_record_info)))
  {
    /* Reading record in mysql.db */
    ACL_DB db;
    db.host.update_hostname(get_field(&global_acl_memory,
                            table->field[MYSQL_DB_FIELD_HOST]));
    db.db=get_field(&global_acl_memory, table->field[MYSQL_DB_FIELD_DB]);
    if (!db.db)
    {
      LogErr(WARNING_LEVEL, ER_AUTHCACHE_DB_IGNORED_EMPTY_NAME);
      continue;
    }
    db.user=get_field(&global_acl_memory, table->field[MYSQL_DB_FIELD_USER]);
    if (check_no_resolve && hostname_requires_resolving(db.host.get_host()))
    {
      LogErr(WARNING_LEVEL, ER_AUTHCACHE_DB_SKIPPED_NEEDS_RESOLVE,
             db.db,
             db.user ? db.user : "",
             db.host.get_host() ? db.host.get_host() : "");
    }
    db.access=get_access(table,3,0);
    db.access=fix_rights_for_db(db.access);
    if (lower_case_table_names)
    {
      /*
        convert db to lower case and give a warning if the db wasn't
        already in lower case
      */
      (void)my_stpcpy(tmp_name, db.db);
      my_casedn_str(files_charset_info, db.db);
      if (strcmp(db.db, tmp_name) != 0)
      {
        LogErr(WARNING_LEVEL,
               ER_AUTHCACHE_DB_ENTRY_LOWERCASED_REVOKE_WILL_FAIL,
               db.db,
               db.user ? db.user : "",
               db.host.get_host() ? db.host.get_host() : "");
      }
    }
    db.sort=get_sort(3,db.host.get_host(),db.db,db.user);
    if (table->s->fields <=  9)
    {                                           // Without grant
      if (db.access & CREATE_ACL)
        db.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
    }
    acl_dbs->push_back(db);
  } // END reading records from mysql.db tables

  end_read_record(&read_record_info);
  if (read_rec_errcode > 0)
    goto end;

  std::sort(acl_dbs->begin(), acl_dbs->end(), ACL_compare());
  acl_dbs->shrink_to_fit();

  /* Prepare to read records from the mysql.proxies_priv table */
  acl_proxy_users->clear();

  if (tables[2].table)
  {
    if (init_read_record(&read_record_info, thd, table= tables[2].table,
                         NULL, 1, 1, FALSE))
      goto end;
    table->use_all_columns();
    while (!(read_rec_errcode= read_record_info.read_record(&read_record_info)))
    {
      /* Reading record in mysql.proxies_priv */
      ACL_PROXY_USER proxy;
      proxy.init(table, &global_acl_memory);
      if (proxy.check_validity(check_no_resolve))
        continue;
      if (acl_proxy_users->push_back(proxy))
      {
        end_read_record(&read_record_info);
        goto end;
      }
    } // END reading records from the mysql.proxies_priv table

    end_read_record(&read_record_info);
    if (read_rec_errcode > 0)
      goto end;

    std::sort(acl_proxy_users->begin(), acl_proxy_users->end(), ACL_compare());
  }
  else
  {
    LogErr(ERROR_LEVEL, ER_AUTHCACHE_TABLE_PROXIES_PRIV_MISSING);
  }
  acl_proxy_users->shrink_to_fit();
  validate_user_plugin_records();
  init_check_host();

  /* Load dynamic privileges */
  if (tables[3].table)
  {
    if (populate_dynamic_privilege_caches(thd, &tables[3]))
    {
      return_val= TRUE;
      goto end;
    }
  }
  else
  {
    sql_print_error("Missing system table mysql.global_grants; "
                    "please run mysql_upgrade to create it");
  }

  initialized=1;
  return_val= FALSE;

end:
  thd->variables.sql_mode= old_sql_mode;
  if (table_schema)
    delete table_schema;
  DBUG_EXECUTE_IF("induce_acl_load_failure",
                  return_val= TRUE;);
  DBUG_RETURN(return_val);
}