1# ex:ts=8 sw=4: 2# $OpenBSD: ArcCheck.pm,v 1.35 2019/05/26 15:47:49 espie Exp $ 3# 4# Copyright (c) 2005-2006 Marc Espie <espie@openbsd.org> 5# 6# Permission to use, copy, modify, and distribute this software for any 7# purpose with or without fee is hereby granted, provided that the above 8# copyright notice and this permission notice appear in all copies. 9# 10# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 18# Supplementary code to handle archives in the package context. 19# Ustar allows about anything, but we want to forbid a lot of things. 20# this code is used during creation and extraction 21# specifically, during create time: 22# - prevent a lot of weird objects from entering the archives 23# - make sure all relevant users/modes are recorded in the PLIST item 24 25# during extraction: 26# - make sure complex objects have all their relevant properties recorded 27# - disallow extraction of non-files/links. 28# - guard against files much longer than they should be. 29 30use strict; 31use warnings; 32 33use OpenBSD::Ustar; 34 35package OpenBSD::Ustar::Object; 36use POSIX; 37 38sub is_allowed() { 0 } 39 40# match archive header link name against actual link name 41sub check_linkname 42{ 43 my ($self, $linkname) = @_; 44 my $c = $self->{linkname}; 45 if ($self->isHardLink && defined $self->{cwd}) { 46 $c = $self->{cwd}.'/'.$c; 47 } 48 return $c eq $linkname; 49} 50 51sub validate_meta 52{ 53 my ($o, $item) = @_; 54 55 $o->{cwd} = $item->cwd; 56 if (defined $item->{symlink} || $o->isSymLink) { 57 unless (defined $item->{symlink} && $o->isSymLink) { 58 $o->errsay("bogus symlink #1", $item->name); 59 return 0; 60 } 61 if (!$o->check_linkname($item->{symlink})) { 62 $o->errsay("archive symlink does not match #1 != #2", 63 $o->{linkname}, $item->{symlink}); 64 return 0; 65 } 66 } elsif (defined $item->{link} || $o->isHardLink) { 67 unless (defined $item->{link} && $o->isHardLink) { 68 $o->errsay("bogus hardlink #1", $item->name); 69 return 0; 70 } 71 if (!$o->check_linkname($item->{link})) { 72 $o->errsay("archive hardlink does not match #1 != #2", 73 $o->{linkname}, $item->{link}); 74 return 0; 75 } 76 } elsif ($o->isFile) { 77 if (!defined $item->{size}) { 78 $o->errsay("Error: file #1 does not have recorded size", 79 $item->fullname); 80 return 0; 81 } elsif ($item->{size} != $o->{size}) { 82 $o->errsay("Error: size does not match for #1", 83 $item->fullname); 84 return 0; 85 } 86 } else { 87 $o->errsay("archive content for #1 should be file", 88 $item->name); 89 return 0; 90 } 91 return $o->verify_modes($item); 92} 93 94sub strip_modes 95{ 96 my ($o, $item) = @_; 97 98 my $result = $o->{mode}; 99 100 # disallow writable files/dirs without explicit annotation 101 if (!defined $item->{mode}) { 102 # if there's an owner, we have to be explicit 103 if (defined $item->{owner}) { 104 $result &= ~(S_IWUSR|S_IWGRP|S_IWOTH); 105 } else { 106 $result &= ~(S_IWGRP|S_IWOTH); 107 } 108 # and make libraries non-executable 109 if ($item->is_a_library) { 110 $result &= ~(S_IXUSR|S_IXGRP|S_IXOTH); 111 } 112 $result |= S_IROTH | S_IRGRP; 113 } 114 # if we're going to set the group or owner, sguid bits won't 115 # survive the extraction 116 if (defined $item->{group} || defined $item->{owner}) { 117 $result &= ~(S_ISUID|S_ISGID); 118 } 119 return $result; 120} 121 122sub printable_mode 123{ 124 my $o = shift; 125 return sprintf("%4o", 126 $o->{mode} & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID)); 127} 128 129sub verify_modes 130{ 131 my ($o, $item) = @_; 132 my $result = 1; 133 134 if (!defined $item->{owner}) { 135 if ($o->{uname} ne 'root') { 136 $o->errsay("Error: no \@owner for #1 (#2)", 137 $item->fullname, $o->{uname}); 138 $result = 0; 139 } 140 } 141 if (!defined $item->{group}) { 142 if ($o->{gname} ne 'bin' && $o->{gname} ne 'wheel') { 143 $o->errsay("Error: no \@group for #1 (#2)", 144 $item->fullname, $o->{gname}); 145 $result = 0; 146 } 147 } 148 if ($o->{mode} != $o->strip_modes($o)) { 149 $o->errsay("Error: weird mode for #1: #2", $item->fullname, 150 $o->printable_mode); 151 $result = 0; 152 } 153 return $result; 154} 155 156package OpenBSD::Ustar::HardLink; 157sub is_allowed() { 1 } 158 159package OpenBSD::Ustar::SoftLink; 160sub is_allowed() { 1 } 161 162package OpenBSD::Ustar::File; 163sub is_allowed() { 1 } 164 165package OpenBSD::Ustar; 166use POSIX; 167 168# prepare item according to pkg_create's rules. 169sub prepare_long 170{ 171 my ($self, $item) = @_; 172 my $entry; 173 if (defined $item->{wtempname}) { 174 $entry = $self->prepare($item->{wtempname}, ''); 175 } else { 176 $entry = $self->prepare($item->name); 177 } 178 if (defined $item->{owner}) { 179 $entry->{uname} = $item->{owner}; 180 if (defined $item->{uid}) { 181 $entry->{uid} = $item->{uid}; 182 } else { 183 delete $entry->{uid}; 184 } 185 } else { 186 $entry->{uname} = "root"; 187 delete $entry->{uid}; 188 } 189 if (defined $item->{group}) { 190 $entry->{gname} = $item->{group}; 191 if (defined $item->{gid}) { 192 $entry->{gid} = $item->{gid}; 193 } else { 194 delete $entry->{gid}; 195 } 196 } else { 197 $entry->{gname} = "bin"; 198 delete $entry->{gid}; 199 } 200 # likewise, we skip links on extractions, so hey, don't even care 201 # about modes and stuff. 202 if ($entry->isSymLink) { 203 $entry->{mode} = 0777; 204 $entry->{uname} = 'root'; 205 $entry->{gname} = 'wheel'; 206 delete $entry->{uid}; 207 delete $entry->{gid}; 208 } 209 $entry->recheck_owner; 210 if (!defined $entry->{uname}) { 211 $self->fatal("No user name for #1 (uid #2)", 212 $item->name, $entry->{uid}); 213 } 214 if (!defined $entry->{gname}) { 215 $self->fatal("No group name for #1 (gid #2)", 216 $item->name, $entry->{gid}); 217 } 218 $entry->{mode} = $entry->strip_modes($item); 219 if (defined $item->{ts}) { 220 delete $entry->{mtime}; 221 } 222 223 $entry->set_name($item->name); 224 return $entry; 225} 226 2271; 228