1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
|
(* guestfs-inspection
* Copyright (C) 2009-2019 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*)
open Unix
open Printf
open Std_utils
open Utils
open Inspect_types
let max_augeas_file_size = 100 * 1000
let rec with_augeas ?name configfiles f =
let name =
match name with
| None -> sprintf "with_augeas: %s" (String.concat " " configfiles)
| Some name -> name in
let chroot = Chroot.create ~name () in
(* Security:
*
* The old C code had a few problems: It ignored non-regular-file
* objects (eg. devices), passing them to Augeas, so relying on
* Augeas to do the right thing. Also too-large regular files
* caused the whole inspection operation to fail.
*
* I have tried to improve this so that non-regular files and
* too large files are ignored (dropped from the configfiles list),
* so that Augeas won't touch them, but they also won't stop
* inspection.
*)
let safe_file file =
Is.is_file ~followsymlinks:true file && (
let size = (Chroot.f chroot Unix.stat file).Unix.st_size in
size <= max_augeas_file_size
)
in
let configfiles = List.filter safe_file configfiles in
let aug =
Augeas.create (Sysroot.sysroot ()) None
[Augeas.AugSaveNoop; Augeas.AugNoLoad] in
protect
~f:(fun () ->
(* Tell Augeas to only load configfiles and no other files. This
* prevents a rogue guest from performing a denial of service attack
* by having large, over-complicated configuration files which are
* unrelated to the task at hand. (Thanks Dominic Cleal).
* Note this requires Augeas >= 1.0.0 because of RHBZ#975412.
*)
let pathexpr = make_augeas_path_expression configfiles in
ignore (aug_rm_noerrors aug pathexpr);
Augeas.load aug;
(* Check that augeas did not get a parse error for any of the
* configfiles, otherwise we are silently missing information.
*)
let matches = aug_matches_noerrors aug "/augeas/files//error" in
List.iter (
fun match_ ->
List.iter (
fun file ->
let errorpath = sprintf "/augeas/files%s/error" file in
if match_ = errorpath then (
(* There's been an error - get the error details. *)
let get path =
match aug_get_noerrors aug (errorpath ^ path) with
| None -> "<missing>"
| Some v -> v
in
let message = get "message" in
let line = get "line" in
let charp = get "char" in
failwithf "%s:%s:%s: augeas parse failure: %s"
file line charp message
)
) configfiles
) matches;
f aug
)
~finally:(
fun () -> Augeas.close aug
)
(* Explained here: https://bugzilla.redhat.com/show_bug.cgi?id=975412#c0 *)
and make_augeas_path_expression files =
let subexprs =
List.map (
fun file ->
(* v NB trailing '/' after filename *)
sprintf "\"%s/\" !~ regexp('^') + glob(incl) + regexp('/.*')" file
) files in
let subexprs = String.concat " and " subexprs in
let ret = sprintf "/augeas/load/*[ %s ]" subexprs in
if verbose () then
eprintf "augeas pathexpr = %s\n%!" ret;
ret
and aug_get_noerrors aug path =
try Augeas.get aug path
with Augeas.Error _ -> None
and aug_matches_noerrors aug path =
try Augeas.matches aug path
with Augeas.Error _ -> []
and aug_rm_noerrors aug path =
try Augeas.rm aug path
with Augeas.Error _ -> 0
let is_file_nocase path =
let path =
try Some (Realpath.case_sensitive_path path)
with _ -> None in
match path with
| None -> false
| Some path -> Is.is_file path
and is_dir_nocase path =
let path =
try Some (Realpath.case_sensitive_path path)
with _ -> None in
match path with
| None -> false
| Some path -> Is.is_dir path
(* Rather hairy test for "is a partition", taken directly from
* the old C inspection code. XXX fix function and callers
*)
let is_partition partition =
try
let device = Devsparts.part_to_dev partition in
ignore (Devsparts.device_index device);
true
with _ -> false
let re_major_minor = PCRE.compile "(\\d+)\\.(\\d+)"
let re_major_no_minor = PCRE.compile "(\\d+)"
let parse_version_from_major_minor str data =
if verbose () then
eprintf "parse_version_from_major_minor: parsing '%s'\n%!" str;
if PCRE.matches re_major_minor str then (
let major = int_of_string (PCRE.sub 1) in
let minor = int_of_string (PCRE.sub 2) in
data.version <- Some (major, minor)
)
else if PCRE.matches re_major_no_minor str then (
let major = int_of_string (PCRE.sub 1) in
data.version <- Some (major, 0)
)
else (
eprintf "parse_version_from_major_minor: cannot parse version from '%s'\n"
str
)
let with_hive hive_filename f =
let flags = [] in
let flags =
match Daemon_config.hivex_flag_unsafe with
| None -> flags
| Some f -> f :: flags in
let flags = if verbose () then Hivex.OPEN_VERBOSE :: flags else flags in
let h = Hivex.open_file hive_filename flags in
protect ~f:(fun () -> f h (Hivex.root h)) ~finally:(fun () -> Hivex.close h)
|