← Back to team overview

maria-developers team mailing list archive

Re: PLEASE REVIEW: (MDEV-7574) Security definer views don't work with CONNECT ODBC tables

 

Hi, Alexander!

On Feb 24, Alexander Barkov wrote:
> 
> There is only one problem with that. In case of embedded server
> table->grant.privilege is always 0, because the embedded version
> of check_table_access() is just an empty function.
> 
> This change in sql/handler.cc, in handler::ha_external_lock() helps:
> 
> +#ifdef NO_EMBEDDED_ACCESS_CHECKS
> +  table->grant.privilege= ~NO_ACCESS;
> +#endif

May be, it'd be better to do it in check_table_access() ?

With a comment "plugins (e.g. CONNECT engine) should not depend on
whether embedded is built with NO_EMBEDDED_ACCESS_CHECKS or without".

> === modified file 'sql/handler.cc'
> --- sql/handler.cc	2015-01-21 11:03:02 +0000
> +++ sql/handler.cc	2015-02-24 11:47:05 +0000
> @@ -5873,6 +5873,9 @@ int handler::ha_external_lock(THD *thd,
>  
>    ha_statistic_increment(&SSV::ha_external_lock_count);
>  
> +#ifdef NO_EMBEDDED_ACCESS_CHECKS
> +  table->grant.privilege= ~NO_ACCESS;
> +#endif

See above.
If you could't put this in check_table_access(), then, at least, add
this comment here.

>    /*
>      We cache the table flags if the locking succeeded. Otherwise, we
>      keep them as they were when they were fetched in ha_open().
> 
> === modified file 'storage/connect/ha_connect.cc'
> --- storage/connect/ha_connect.cc	2015-02-11 20:39:41 +0000
> +++ storage/connect/ha_connect.cc	2015-02-24 12:03:25 +0000
> @@ -3922,7 +3922,21 @@ int ha_connect::delete_all_rows()
>  } // end of delete_all_rows
>  
>  
> -bool ha_connect::check_privileges(THD *thd, PTOS options, char *dbn)
> +/**
> +  Check privileges.
> +  @param THD                   - Current thread
> +  @param options               - Connect table options
> +  @param dbn                   - database name
> +  @param using_table_privilege - whether check table->grant.privilege,
> +                                 or execute check_access(FILE_ACL).
> +
> +  Using table->grant.privilege is important in cases when we need to take into
> +  account privileges of the VIEW definer when accessing to a view created with
> +    "CREATE VIEW v1 SQL SECURITY DEFINER".
> +  See ha_connect::check_privileges_external_lock() for details.
> +*/
> +bool ha_connect::check_privileges(THD *thd, PTOS options,
> +                                  char *dbn, bool using_table_privilege)
>  {
>    const char *db= (dbn && *dbn) ? dbn : NULL;
>    TABTYPE     type=GetRealType(options);
> @@ -4143,6 +4180,67 @@ MODE ha_connect::CheckMode(PGLOBAL g, TH
>    return newmode;
>  } // end of check_mode
>  
> +
> +/**
> +  A check_privilege() wrapper for external_lock().
> +  Decides if check_privilege():
> +  - should test table->grant.privilege for FILE_ACL
> +  - or should call check_access(FILE_ACL)
> +  depending on the current SQL command and lock type.
> +*/
> +bool ha_connect::check_privileges_external_lock(PGLOBAL g, THD *thd,
> +                                                PTOS options, int lock_type)
> +{
> +  bool use_table_priv;
> +  switch (thd_sql_command(thd))
> +  {
> +  case SQLCOM_SELECT:
> +  case SQLCOM_UPDATE:
> +  case SQLCOM_INSERT:
> +  case SQLCOM_DELETE:
> +  case SQLCOM_REPLACE:
> +  case SQLCOM_LOAD:
> +    use_table_priv= true;              // use table->grant.privilege
> +    break;
> +
> +  case SQLCOM_CREATE_TABLE:
> +  case SQLCOM_INSERT_SELECT:
> +  case SQLCOM_REPLACE_SELECT:
> +  case SQLCOM_UPDATE_MULTI:
> +  case SQLCOM_DELETE_MULTI:
> +    /*
> +      CREATE TABLE target_table AS SELECT * FROM source_table;
> +      INSERT INTO  target_table SELECT * FROM source_table;
> +      REPLACE INTO target_table SELECT * FROM source_table;    
> +      UPDATE target_table,source_table SET target_table.column=xxx WHERE ...;
> +      DELETE target_table FROM target_table,source_table WHERE ...;
> +
> +      If we're working with "source_table", use table->grant.privilege.
> +      If we're working with "target_table", use check_access().
> +    */
> +    use_table_priv= lock_type != F_WRLCK;

I don't quite understand that. Why use_table_priv is FALSE for these
commands? Like, why it's true for SQLCOM_INSERT, but false for
SQLCOM_INSERT_SELECT? True for SQLCOM_UPDATE, false for
SQLCOM_UPDATE_MULTI?

Other cases below aren't clear either.
Could you explain the rule - when one should use table->grant.privilege
and when check_access()? I mean, not a list of cases, but a general
underlying rule.

> +    break;
> +
> +  case SQLCOM_TRUNCATE:
> +  case SQLCOM_LOCK_TABLES:
> +  case SQLCOM_DROP_TABLE:
> +  case SQLCOM_RENAME_TABLE:
> +  case SQLCOM_CREATE_VIEW:
> +  case SQLCOM_DROP_VIEW:
> +  case SQLCOM_ALTER_TABLE:
> +  case SQLCOM_DROP_INDEX:
> +  case SQLCOM_CREATE_INDEX:
> +  case SQLCOM_OPTIMIZE:
> +    use_table_priv= false;             // use check_access()
> +    break;
> +  default:
> +    report_unsupported_sql_command(g, thd);
> +    return true;                       // Something went wrong, deny access.
> +  }
> +  return check_privileges(thd, options, table->s->db.str, use_table_priv);
> +}
> +
> +
>  int ha_connect::start_stmt(THD *thd, thr_lock_type lock_type)
>  {
>    int     rc= 0;
> @@ -4614,7 +4712,7 @@ int ha_connect::delete_or_rename_table(c
>      if (!open_table_def(thd, share)) {
>        // Now we can work
>        if ((pos= share->option_struct)) {
> -        if (check_privileges(thd, pos, db))
> +        if (check_privileges(thd, pos, db, false))
>            rc= HA_ERR_INTERNAL_ERROR;         // ???
>          else
>            if (IsFileType(GetRealType(pos)) && !pos->filename)
> @@ -5592,7 +5690,7 @@ int ha_connect::create(const char *name,
>      DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
>    } // endif ttp
>  
> -  if (check_privileges(thd, options, GetDBfromName(name)))
> +  if (check_privileges(thd, options, GetDBfromName(name), false))
>      DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
>  
>    inward= IsFileType(type) && !options->filename;

Regards,
Sergei


Follow ups

References