@@ -519,6 +519,100 @@ static int make_iso8601_timestamp(char *buf, ulonglong utime= 0)
519519}
520520
521521
522+ bool is_valid_log_name (const char *name, size_t len)
523+ {
524+ if (len > 3 )
525+ {
526+ const char *tail= name + len - 4 ;
527+ if (my_strcasecmp (system_charset_info, tail, " .ini" ) == 0 ||
528+ my_strcasecmp (system_charset_info, tail, " .cnf" ) == 0 )
529+ {
530+ return false ;
531+ }
532+ }
533+ return true ;
534+ }
535+
536+
537+ /* *
538+ Get the real log file name, and possibly reopen file.
539+
540+ The implementation is platform dependent due to differences in how this is
541+ supported:
542+
543+ On Windows, we get the actual path based on the file descriptor. This path is
544+ copied into the supplied buffer. The 'file' parameter is returned without
545+ re-opening.
546+
547+ On other platforms, we use realpath() to get the path with symbolic links
548+ expanded. Then, we close the file, and reopen the real path using the
549+ O_NOFOLLOW flag. This will reject folowing symbolic links.
550+
551+ @param file File descriptor.
552+ @param log_file_key Key for P_S instrumentation.
553+ @param open_flags Flags to use for opening the file.
554+ @param opened_file_name Name of the open fd.
555+ @param [out] real_file_name Buffer for actual name of the fd.
556+
557+ @retval file descriptor to open file with 'real_file_name', or '-1'
558+ in case of errors.
559+ */
560+
561+ static File mysql_file_real_name_reopen (File file,
562+ #ifdef HAVE_PSI_INTERFACE
563+ PSI_file_key log_file_key,
564+ #endif
565+ int open_flags,
566+ const char *opened_file_name,
567+ char *real_file_name)
568+ {
569+ DBUG_ASSERT (file);
570+ DBUG_ASSERT (opened_file_name);
571+ DBUG_ASSERT (real_file_name);
572+
573+ #ifdef _WIN32
574+ /* On Windows, O_NOFOLLOW is not supported. Verify real path from fd. */
575+ DWORD real_length= GetFinalPathNameByHandle (my_get_osfhandle (file),
576+ real_file_name,
577+ FN_REFLEN,
578+ FILE_NAME_OPENED);
579+
580+ /* May ret 0 if e.g. on a ramdisk. Ignore - return open file and name. */
581+ if (real_length == 0 )
582+ {
583+ strcpy (real_file_name, opened_file_name);
584+ return file;
585+ }
586+
587+ if (real_length > FN_REFLEN)
588+ {
589+ mysql_file_close (file, MYF (0 ));
590+ return -1 ;
591+ }
592+
593+ return file;
594+ #else
595+ /* On *nix, get realpath, open realpath with O_NOFOLLOW. */
596+ if (realpath (opened_file_name, real_file_name) == NULL )
597+ {
598+ (void ) mysql_file_close (file, MYF (0 ));
599+ return -1 ;
600+ }
601+
602+ if (mysql_file_close (file, MYF (0 )))
603+ return -1 ;
604+
605+ /* Make sure the real path is not too long. */
606+ if (strlen (real_file_name) > FN_REFLEN)
607+ return -1 ;
608+
609+ return mysql_file_open (log_file_key, real_file_name,
610+ open_flags | O_NOFOLLOW,
611+ MYF (MY_WME));
612+ #endif // _WIN32
613+ }
614+
615+
522616bool File_query_log::open ()
523617{
524618 File file= -1 ;
@@ -552,12 +646,36 @@ bool File_query_log::open()
552646
553647 db[0 ]= 0 ;
554648
649+ /* First, open the file to make sure it exists. */
555650 if ((file= mysql_file_open (m_log_file_key,
556651 log_file_name,
557652 O_CREAT | O_BINARY | O_WRONLY | O_APPEND,
558653 MYF (MY_WME))) < 0 )
559654 goto err;
560655
656+ #ifdef _WIN32
657+ char real_log_file_name[FN_REFLEN];
658+ #else
659+ /* File name must have room for PATH_MAX. Checked against F_REFLEN later. */
660+ char real_log_file_name[PATH_MAX];
661+ #endif // _Win32
662+
663+ /* Reopen and get real path. */
664+ if ((file= mysql_file_real_name_reopen (file,
665+ #ifdef HAVE_PSI_INTERFACE
666+ m_log_file_key,
667+ #endif
668+ O_CREAT | O_BINARY | O_WRONLY | O_APPEND,
669+ log_file_name, real_log_file_name)) < 0 )
670+ goto err;
671+
672+ if (!is_valid_log_name (real_log_file_name, strlen (real_log_file_name)))
673+ {
674+ sql_print_error (" Invalid log file name after expanding symlinks: '%s'" ,
675+ real_log_file_name);
676+ goto err;
677+ }
678+
561679 if ((pos= mysql_file_tell (file, MYF (MY_WME))) == MY_FILEPOS_ERROR)
562680 {
563681 if (my_errno () == ESPIPE)
0 commit comments