Coverage Report

Created: 2024-04-23 06:19

/src/unrar/extinfo.cpp
Line
Count
Source (jump to first uncovered line)
1
#include "rar.hpp"
2
3
#include "hardlinks.cpp"
4
#include "win32stm.cpp"
5
6
#ifdef _WIN_ALL
7
#include "win32acl.cpp"
8
#include "win32lnk.cpp"
9
#endif
10
11
#ifdef _UNIX
12
#include "uowners.cpp"
13
#ifdef SAVE_LINKS
14
#include "ulinks.cpp"
15
#endif
16
#endif
17
18
19
20
// RAR2 service header extra records.
21
#ifndef SFX_MODULE
22
void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name)
23
0
{
24
0
  if (Cmd->Test)
25
0
    return;
26
0
  switch(Arc.SubBlockHead.SubType)
27
0
  {
28
0
#ifdef _UNIX
29
0
    case UO_HEAD:
30
0
      if (Cmd->ProcessOwners)
31
0
        ExtractUnixOwner20(Arc,Name);
32
0
      break;
33
0
#endif
34
#ifdef _WIN_ALL
35
    case NTACL_HEAD:
36
      if (Cmd->ProcessOwners)
37
        ExtractACL20(Arc,Name);
38
      break;
39
    case STREAM_HEAD:
40
      ExtractStreams20(Arc,Name);
41
      break;
42
#endif
43
0
  }
44
0
}
45
#endif
46
47
48
// RAR3 and RAR5 service header extra records.
49
void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name)
50
197
{
51
197
#ifdef _UNIX
52
197
  if (!Cmd->Test && Cmd->ProcessOwners && Arc.Format==RARFMT15 &&
53
197
      Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER))
54
0
    ExtractUnixOwner30(Arc,Name);
55
197
#endif
56
#ifdef _WIN_ALL
57
  if (!Cmd->Test && Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_ACL))
58
    ExtractACL(Arc,Name);
59
  if (Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM))
60
    ExtractStreams(Arc,Name,Cmd->Test);
61
#endif
62
197
}
63
64
65
// Extra data stored directly in file header.
66
void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name)
67
1.64k
{
68
1.64k
#ifdef _UNIX
69
1.64k
   if (Cmd->ProcessOwners && Arc.Format==RARFMT50 && Arc.FileHead.UnixOwnerSet)
70
0
     SetUnixOwner(Arc,Name);
71
1.64k
#endif
72
1.64k
}
73
74
75
76
77
// Calculate a number of path components except \. and \..
78
static int CalcAllowedDepth(const wchar *Name)
79
8
{
80
8
  int AllowedDepth=0;
81
36
  while (*Name!=0)
82
28
  {
83
28
    if (IsPathDiv(Name[0]) && Name[1]!=0 && !IsPathDiv(Name[1]))
84
0
    {
85
0
      bool Dot=Name[1]=='.' && (IsPathDiv(Name[2]) || Name[2]==0);
86
0
      bool Dot2=Name[1]=='.' && Name[2]=='.' && (IsPathDiv(Name[3]) || Name[3]==0);
87
0
      if (!Dot && !Dot2)
88
0
        AllowedDepth++;
89
0
    }
90
28
    Name++;
91
28
  }
92
8
  return AllowedDepth;
93
8
}
94
95
96
// Check if all existing path components are directories and not links.
97
static bool LinkInPath(const wchar *Name)
98
1
{
99
1
  wchar Path[NM];
100
1
  if (wcslen(Name)>=ASIZE(Path))
101
0
    return true;  // It should not be that long, skip.
102
1
  wcsncpyz(Path,Name,ASIZE(Path));
103
3
  for (wchar *s=Path+wcslen(Path)-1;s>Path;s--)
104
2
    if (IsPathDiv(*s))
105
0
    {
106
0
      *s=0;
107
0
      FindData FD;
108
0
      if (FindFile::FastFind(Path,&FD,true) && (FD.IsLink || !FD.IsDir))
109
0
        return true;
110
0
    }
111
1
  return false;
112
1
}
113
114
115
bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName)
116
6
{
117
  // Catch root dir based /path/file paths also as stuff like \\?\.
118
  // Do not check PrepSrcName here, it can be root based if destination path
119
  // is a root based.
120
6
  if (IsFullRootPath(SrcName) || IsFullRootPath(TargetName))
121
2
    return false;
122
123
  // Number of ".." in link target.
124
4
  int UpLevels=0;
125
38
  for (int Pos=0;*TargetName!=0;Pos++)
126
34
  {
127
34
    bool Dot2=TargetName[0]=='.' && TargetName[1]=='.' && 
128
34
              (IsPathDiv(TargetName[2]) || TargetName[2]==0) &&
129
34
              (Pos==0 || IsPathDiv(*(TargetName-1)));
130
34
    if (Dot2)
131
1
      UpLevels++;
132
34
    TargetName++;
133
34
  }
134
  // If link target includes "..", it must not have another links
135
  // in the path, because they can bypass our safety check. For example,
136
  // suppose we extracted "lnk1" -> "." first and "lnk1/lnk2" -> ".." next
137
  // or "dir/lnk1" -> ".." first and "dir/lnk1/lnk2" -> ".." next.
138
4
  if (UpLevels>0 && LinkInPath(PrepSrcName))
139
0
    return false;
140
    
141
  // We could check just prepared src name, but for extra safety
142
  // we check both original (as from archive header) and prepared
143
  // (after applying the destination path and -ep switches) names.
144
145
4
  int AllowedDepth=CalcAllowedDepth(SrcName); // Original name depth.
146
147
  // Remove the destination path from prepared name if any. We should not
148
  // count the destination path depth, because the link target must point
149
  // inside of this path, not outside of it.
150
4
  size_t ExtrPathLength=wcslen(Cmd->ExtrPath);
151
4
  if (ExtrPathLength>0 && wcsncmp(PrepSrcName,Cmd->ExtrPath,ExtrPathLength)==0)
152
0
  {
153
0
    PrepSrcName+=ExtrPathLength;
154
0
    while (IsPathDiv(*PrepSrcName))
155
0
      PrepSrcName++;
156
0
  }
157
4
  int PrepAllowedDepth=CalcAllowedDepth(PrepSrcName);
158
159
4
  return AllowedDepth>=UpLevels && PrepAllowedDepth>=UpLevels;
160
4
}
161
162
163
bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName)
164
11
{
165
11
#if defined(SAVE_LINKS) && defined(_UNIX)
166
  // For RAR 3.x archives we process links even in test mode to skip link data.
167
11
  if (Arc.Format==RARFMT15)
168
7
    return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName);
169
4
  if (Arc.Format==RARFMT50)
170
4
    return ExtractUnixLink50(Cmd,LinkName,&Arc.FileHead);
171
#elif defined _WIN_ALL
172
  // RAR 5.0 archives store link information in file header, so there is
173
  // no need to additionally test it if we do not create a file.
174
  if (Arc.Format==RARFMT50)
175
    return CreateReparsePoint(Cmd,LinkName,&Arc.FileHead);
176
#endif
177
0
  return false;
178
4
}