File: inspect_utils.ml

package info (click to toggle)
libguestfs 1%3A1.40.2-2
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 123,660 kB
  • sloc: ansic: 460,074; ml: 63,059; sh: 14,955; java: 9,512; makefile: 9,133; cs: 6,300; haskell: 5,652; python: 3,856; perl: 3,619; erlang: 2,435; xml: 1,683; ruby: 350; pascal: 255; lex: 135; yacc: 128; cpp: 10
file content (186 lines) | stat: -rw-r--r-- 5,972 bytes parent folder | download
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)