openpkg/license.lua

Thu, 04 Oct 2012 20:30:05 +0200

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 04 Oct 2012 20:30:05 +0200
changeset 715
c10fb90893b9
permissions
-rw-r--r--

Correct out of date build configuration, porting to Solaris 11 network
link infrastructure and new libpcap logic. This additionally allows for
device drivers in subdirectories of /dev. Correct packaged nmap
personalities and signatures to work out of the box. Finally, hack
arpd logic to properly close sockets and quit on TERM by repeating
signaling in the run command script. Sadly, all this fails to correct
the run time behaviour of honeyd which fails to bind to the IP layer.

michael@428 1 -----BEGIN PGP SIGNED MESSAGE-----
michael@428 2 Hash: SHA1
michael@428 3
michael@428 4 - --
michael@428 5 - -- OpenPKG Framework License Processor
michael@428 6 - -- Copyright (c) 2000-2012 OpenPKG GmbH <http://openpkg.com/>
michael@428 7 - --
michael@428 8 - -- This software is property of the OpenPKG GmbH, DE MUC HRB 160208.
michael@428 9 - -- All rights reserved. Licenses which grant limited permission to use,
michael@428 10 - -- copy, modify and distribute this software are available from the
michael@428 11 - -- OpenPKG GmbH.
michael@428 12 - --
michael@428 13 - -- THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
michael@428 14 - -- WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
michael@428 15 - -- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
michael@428 16 - -- IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
michael@428 17 - -- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
michael@428 18 - -- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
michael@428 19 - -- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
michael@428 20 - -- USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
michael@428 21 - -- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
michael@428 22 - -- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
michael@428 23 - -- OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
michael@428 24 - -- SUCH DAMAGE.
michael@428 25 - --
michael@428 26
michael@428 27 - -- This is the RPM run-time integrity processor of the OpenPKG
michael@428 28 - -- Framework. It currently checks the OpenPKG Framework run-time
michael@428 29 - -- license only. The following grammar specifies and documents all
michael@428 30 - -- currently supported license parameters.
michael@428 31 - --
michael@428 32 - -- license ::= "Assertion-MinProcVersion:" version
michael@428 33 - -- # require a minimum version of the license integrity processor
michael@428 34 - --
michael@428 35 - -- | "Assertion-ErrorToWarning:" yes-no
michael@428 36 - -- # allow all fatal integrity checking errors to be
michael@428 37 - -- # converted to non-fatal warnings
michael@428 38 - --
michael@428 39 - -- | "Assertion-OnlineApproval:" url
michael@428 40 - -- # require an online approval by receiving an "OK" from
michael@428 41 - -- # specified remote service
michael@428 42 - --
michael@428 43 - -- | "Assertion-OnlineReporting:" url
michael@428 44 - -- # perform an asynchronous online reporting to
michael@428 45 - -- # specified remote service
michael@428 46 - --
michael@428 47 - -- | "Assertion-Prefix:" path
michael@428 48 - -- # require %{l_prefix} to match specified path
michael@428 49 - --
michael@428 50 - -- | "Assertion-User:" user
michael@428 51 - -- # require %{l_musr} to match specified username
michael@428 52 - --
michael@428 53 - -- | "Assertion-Group:" group
michael@428 54 - -- # require %{l_mgrp} to match specified groupname
michael@428 55 - --
michael@428 56 - -- | "Assertion-Domain:" domain
michael@428 57 - -- # require domain of host to match specified domain name
michael@428 58 - --
michael@428 59 - -- | "Assertion-LifeTime:" iso-date.":".iso-date
michael@428 60 - -- # require current real-time to be within specified
michael@428 61 - -- # begin and end date
michael@428 62 - --
michael@428 63 - -- | "Assertion-GrantTime:" iso-date.":".iso-date
michael@428 64 - -- # require current OpenPKG Framework %{RELEASE}
michael@428 65 - -- # (release time) to be within specified begin and end
michael@428 66 - -- # date
michael@428 67 - --
michael@428 68 - -- | "Assertion-InstanceAge:" duration
michael@428 69 - -- # require current OpenPKG Framework %{ORIGINTIME}
michael@428 70 - -- # (first install time) to be within specified begin
michael@428 71 - -- # and end date
michael@428 72 - --
michael@428 73 - -- | "Assertion-FromSourceOnTarget:" yes-no
michael@428 74 - -- # require either (if "yes") that all package
michael@428 75 - -- # %{BUILDHOST} are equal the host name or (if "no")
michael@428 76 - -- # that all package %{BUILDHOST} are not equal the host
michael@428 77 - -- # name
michael@428 78 - --
michael@428 79 - -- | "Assertion-PackageNames:"
michael@428 80 - -- ("!"?.mode-regex.":"."!"?.package-regex)+
michael@428 81 - -- # require all package %{NAME} to (not) match the
michael@428 82 - -- # specified regex while the current RPM run-time mode
michael@428 83 - -- # has to (not) match the specified regex. RPM run-time
michael@428 84 - -- # modes are: query, verify, checksig, resign, install,
michael@428 85 - -- # erase, build rebuild, recompile, tarbuild, initdb,
michael@428 86 - -- # rebuilddb and verifydb.
michael@428 87 - --
michael@428 88 - -- | "Assertion-PackageReleaseAge:"
michael@428 89 - -- percent.":".duration.":".dist-regex ((package-name|"*").":".release)+
michael@428 90 - -- # require that for at least the specified amount (in
michael@428 91 - -- # percent) of packages, which %{DISTRIBUTION} matches
michael@428 92 - -- # the specified regex, the %{RELEASE} is at least as
michael@428 93 - -- # old as the specified release or at least not older
michael@428 94 - -- # than the specified duration.
michael@428 95 - --
michael@428 96 - -- | "Assertion-Expression:" expression
michael@428 97 - -- # evaluates the Lua boolean expression after expanding
michael@428 98 - -- # RPM macros %{VARNAME} and expanding the construct
michael@428 99 - -- # "<string>" ~~ /<regex>/ into the corresponding PCRE
michael@428 100 - -- # based regular expression match.
michael@428 101 - --
michael@428 102 - -- version ::= /^\d+\.\d+\.\d+$/
michael@428 103 - -- yes-no ::= /^yes|no$/
michael@428 104 - -- url ::= /^https?:\/\/.+$/
michael@428 105 - -- path ::= /^\/.+$/
michael@428 106 - -- user ::= /^[a-z][a-zA-Z0-9_]*$/
michael@428 107 - -- group ::= /^[a-z][a-zA-Z0-9_]*$/
michael@428 108 - -- domain ::= /^(?:[^.]+\.)+[^.]+$/
michael@428 109 - -- mode-regex ::= /^.+$/
michael@428 110 - -- package-regex ::= /^.+$/
michael@428 111 - -- package-name ::= /^[a-z][a-zA-Z0-9-]*$/
michael@428 112 - -- percent ::= /^\d+%$/
michael@428 113 - -- duration ::= /^\d+[smhdw]?$/
michael@428 114 - -- release ::= /^\d{8}$/
michael@428 115 - -- iso-date ::= /^\d{4}-\d{2}-\d{2}$/
michael@428 116 - -- expression ::= /^.+$/
michael@428 117
michael@428 118 - -- integrity processor version
michael@428 119 integrity.version = "1.0.0"
michael@428 120
michael@428 121 - -- integrity processor validation callback function
michael@428 122 function integrity.validate(ctx, cfg)
michael@428 123 integrity.util.debug(1, "OpenPKG run-time license integrity validation")
michael@428 124 integrity.util.debug(4, function (ctx) return "dump: ctx = " .. util.dump(ctx) end, ctx)
michael@428 125 integrity.util.debug(4, function (cfg) return "dump: cfg = " .. util.dump(cfg) end, cfg)
michael@428 126
michael@428 127 -- process "Assertion-OnlineApproval" constraint
michael@428 128 if os.getenv("OPENPKG_LICENSE_EXCEPTION") ~= nil then
michael@428 129 -- support explicitly requested license exception
michael@428 130 cfg["Assertion-OnlineApproval"] = "http://openpkg.com/go/framework-license-exception"
michael@428 131 end
michael@428 132 if cfg["Assertion-OnlineApproval"] ~= nil then
michael@428 133 integrity.util.debug(2, "checking: Assertion-OnlineApproval: \"%s\"", cfg["Assertion-OnlineApproval"])
michael@428 134 local uuids = integrity.util.uuids()
michael@428 135 if uuids["UUID_REGISTRY"] == "" then
michael@428 136 uuids["UUID_REGISTRY"] = "unknown"
michael@428 137 end
michael@428 138 if uuids["UUID_INSTANCE"] == "" then
michael@428 139 uuids["UUID_INSTANCE"] = "unknown"
michael@428 140 end
michael@428 141 if uuids["UUID_PLATFORM"] == "" then
michael@428 142 uuids["UUID_PLATFORM"] = "unknown"
michael@428 143 end
michael@428 144 local request = cfg["Assertion-OnlineApproval"]
michael@428 145 request = request .. "?UUID_REGISTRY=" .. uuids["UUID_REGISTRY"]
michael@428 146 request = request .. "&UUID_INSTANCE=" .. uuids["UUID_INSTANCE"]
michael@428 147 request = request .. "&UUID_PLATFORM=" .. uuids["UUID_PLATFORM"]
michael@428 148 integrity.util.debug(3, "info: remote request \"%s\"", request)
michael@428 149 local response = rpm.expand("%(%{l_prefix}/bin/openpkg curl -s -L -R '" .. request .. "')")
michael@428 150 integrity.util.debug(3, "info: remote response \"%s\"", response)
michael@428 151 if util.rmatch(response, "(?s)^\\s*OK\\s*$") then
michael@428 152 -- approved
michael@428 153 if os.getenv("OPENPKG_LICENSE_EXCEPTION") ~= nil then
michael@428 154 -- support explicitly requested license exception
michael@428 155 cfg["Assertion-ErrorToWarning"] = "yes"
michael@428 156 end
michael@428 157 else
michael@428 158 -- rejected
michael@428 159 cfg["Assertion-ErrorToWarning"] = "no"
michael@428 160 return integrity.util.error(ctx, cfg,
michael@428 161 "license requires online approval but we failed to get " ..
michael@428 162 "an \"OK\" response from the online service")
michael@428 163 end
michael@428 164 end
michael@428 165
michael@428 166 -- process "Assertion-MinProcVersion" constraint
michael@428 167 integrity.util.debug(2, "checking: Assertion-MinProcVersion: \"%s\"", cfg["Assertion-MinProcVersion"])
michael@428 168 if cfg["Assertion-MinProcVersion"] == nil then
michael@428 169 return integrity.util.error(ctx, cfg,
michael@428 170 "license configuration is missing required \"Assertion-MinProcVersion\" parameter")
michael@428 171 end
michael@428 172 integrity.util.debug(3, "require: %s <= %s", cfg["Assertion-MinProcVersion"], integrity.version)
michael@428 173 if rpm.vercmp(cfg["Assertion-MinProcVersion"], integrity.version) > 0 then
michael@428 174 return integrity.util.error(ctx, cfg,
michael@428 175 "license configuration requires a license processor of " ..
michael@428 176 "at least version \"" .. cfg["Assertion-MinProcVersion"] .. "\"")
michael@428 177 end
michael@428 178
michael@428 179 -- process "Assertion-OnlineReporting" constraint
michael@428 180 if cfg["Assertion-OnlineReporting"] ~= nil then
michael@428 181 integrity.util.debug(2, "checking: Assertion-OnlineReporting: \"%s\"", cfg["Assertion-OnlineReporting"])
michael@428 182 local uuids = integrity.util.uuids()
michael@428 183 if uuids["UUID_REGISTRY"] == "" then
michael@428 184 uuids["UUID_REGISTRY"] = "unknown"
michael@428 185 end
michael@428 186 if uuids["UUID_INSTANCE"] == "" then
michael@428 187 uuids["UUID_INSTANCE"] = "unknown"
michael@428 188 end
michael@428 189 if uuids["UUID_PLATFORM"] == "" then
michael@428 190 uuids["UUID_PLATFORM"] = "unknown"
michael@428 191 end
michael@428 192 local request = cfg["Assertion-OnlineReporting"]
michael@428 193 request = request .. "?UUID_REGISTRY=" .. uuids["UUID_REGISTRY"]
michael@428 194 request = request .. "&UUID_INSTANCE=" .. uuids["UUID_INSTANCE"]
michael@428 195 request = request .. "&UUID_PLATFORM=" .. uuids["UUID_PLATFORM"]
michael@428 196 integrity.util.debug(3, "info: remote request \"%s\"", request)
michael@428 197 rpm.expand("%(nohup %{l_prefix}/bin/openpkg curl -s -L -R '" .. request .. "' >/dev/null 2>&1 &)")
michael@428 198 integrity.util.debug(3, "response: (ignored, because asynchronous operation)")
michael@428 199 end
michael@428 200
michael@428 201 -- process "Assertion-Prefix" constraint
michael@428 202 if cfg["Assertion-Prefix"] ~= nil then
michael@428 203 integrity.util.debug(2, "checking: Assertion-Prefix: \"%s\"", cfg["Assertion-Prefix"])
michael@428 204 local prefix = rpm.expand("%{l_prefix}")
michael@428 205 integrity.util.debug(3, "require: \"%s\" == \"%s\"", cfg["Assertion-Prefix"], prefix)
michael@428 206 if cfg["Assertion-Prefix"] ~= prefix then
michael@428 207 return integrity.util.error(ctx, cfg,
michael@428 208 "instance prefix \"" .. prefix .. "\" " ..
michael@428 209 "does not match value \"" .. cfg["Assertion-Prefix"] .. "\" of " ..
michael@428 210 "license configuration parameter \"Assertion-Prefix\"")
michael@428 211 end
michael@428 212 end
michael@428 213
michael@428 214 -- process "Assertion-User" constraint
michael@428 215 if cfg["Assertion-User"] ~= nil then
michael@428 216 integrity.util.debug(2, "checking: Assertion-User: \"%s\"", cfg["Assertion-User"])
michael@428 217 local user = rpm.expand("%{l_musr}")
michael@428 218 integrity.util.debug(3, "require: \"%s\" == \"%s\"", cfg["Assertion-User"], user)
michael@428 219 if cfg["Assertion-User"] ~= user then
michael@428 220 return integrity.util.error(ctx, cfg,
michael@428 221 "instance management user \"" .. user .. "\" " ..
michael@428 222 "does not match value \"" .. cfg["Assertion-User"] .. "\" of " ..
michael@428 223 "license configuration parameter \"Assertion-User\"")
michael@428 224 end
michael@428 225 end
michael@428 226
michael@428 227 -- process "Assertion-Group" constraint
michael@428 228 if cfg["Assertion-Group"] ~= nil then
michael@428 229 integrity.util.debug(2, "checking: Assertion-Group: \"%s\"", cfg["Assertion-Group"])
michael@428 230 local group = rpm.expand("%{l_mgrp}")
michael@428 231 integrity.util.debug(3, "require: \"%s\" == \"%s\"", cfg["Assertion-Group"], group)
michael@428 232 if cfg["Assertion-Group"] ~= group then
michael@428 233 return integrity.util.error(ctx, cfg,
michael@428 234 "instance management group \"" .. group .. "\" " ..
michael@428 235 "does not match value \"" .. cfg["Assertion-Group"] .. "\" of " ..
michael@428 236 "license configuration parameter \"Assertion-Group\"")
michael@428 237 end
michael@428 238 end
michael@428 239
michael@428 240 -- process "Assertion-Domain" constraint
michael@428 241 if cfg["Assertion-Domain"] ~= nil then
michael@428 242 integrity.util.debug(2, "checking: Assertion-Domain: \"%s\"", cfg["Assertion-Domain"])
michael@428 243 local domain = rpm.expand("%(%{l_shtool} echo -n -e '%d')")
michael@428 244 integrity.util.debug(3, "require: \"%s\" ~~ /(?s)^.*%s$/", domain, cfg["Assertion-Domain"])
michael@428 245 local s, _, m = util.rmatch(domain, "(?s)^.*" .. cfg["Assertion-Domain"] .. "$")
michael@428 246 if s == nil then
michael@428 247 return integrity.util.error(ctx, cfg,
michael@428 248 "host domain \"" .. domain .. "\" " ..
michael@428 249 "does not end in pattern \"" .. cfg["Assertion-Domain"] .. "\") " ..
michael@428 250 "of license configuration parameter \"Assertion-Domain\"")
michael@428 251 end
michael@428 252 end
michael@428 253
michael@428 254 -- process "Assertion-LifeTime" constraint
michael@428 255 if cfg["Assertion-LifeTime"] ~= nil then
michael@428 256 integrity.util.debug(2, "checking: Assertion-LifeTime: \"%s\"", cfg["Assertion-LifeTime"])
michael@428 257
michael@428 258 -- determine lifetime begin and end
michael@428 259 local lifetime = cfg["Assertion-LifeTime"]
michael@428 260 local s, _, m = util.rmatch(lifetime, "^(?s)(\\d{4})-(\\d{2})-(\\d{2})\\s*:\\s*(\\d{4})-(\\d{2})-(\\d{2})$")
michael@428 261 if s == nil then
michael@428 262 return integrity.util.error(ctx, cfg,
michael@428 263 "failed to extract time information from " ..
michael@428 264 "license configuration parameter \"Assertion-LifeTime\"")
michael@428 265 end
michael@428 266 local lifetime_begin = os.time({
michael@428 267 year = tonumber(m[1]),
michael@428 268 month = tonumber(m[2]),
michael@428 269 day = tonumber(m[3]),
michael@428 270 hour = 0,
michael@428 271 min = 0,
michael@428 272 sec = 0
michael@428 273 })
michael@428 274 local lifetime_end = os.time({
michael@428 275 year = tonumber(m[4]),
michael@428 276 month = tonumber(m[5]),
michael@428 277 day = tonumber(m[6]),
michael@428 278 hour = 23,
michael@428 279 min = 59,
michael@428 280 sec = 59
michael@428 281 })
michael@428 282
michael@428 283 -- check whether current run-time is within lifetime
michael@428 284 local t_now = os.time()
michael@428 285 integrity.util.debug(3, "require: %d <= %d <= %d", lifetime_begin, t_now, lifetime_end)
michael@428 286 if not (lifetime_begin <= t_now and t_now <= lifetime_end) then
michael@428 287 return integrity.util.error(ctx, cfg,
michael@428 288 "current time \"" .. os.date("!%Y-%m-%d %H:%M:%S UTC", t_now) .. "\" " ..
michael@428 289 "is not within the timerange \"" .. cfg["Assertion-LifeTime"] .. "\" " ..
michael@428 290 "of license configuration parameter \"Assertion-LifeTime\"")
michael@428 291 end
michael@428 292 end
michael@428 293
michael@428 294 -- process "Assertion-GrantTime" constraint
michael@428 295 if cfg["Assertion-GrantTime"] ~= nil then
michael@428 296 integrity.util.debug(2, "checking: Assertion-GrantTime: \"%s\"", cfg["Assertion-GrantTime"])
michael@428 297
michael@428 298 -- determine granttime begin and end
michael@428 299 local granttime = cfg["Assertion-GrantTime"]
michael@428 300 local s, _, m = util.rmatch(granttime, "^(?s)(\\d{4})-(\\d{2})-(\\d{2})\\s*:\\s*(\\d{4})-(\\d{2})-(\\d{2})$")
michael@428 301 if s == nil then
michael@428 302 return integrity.util.error(ctx, cfg,
michael@428 303 "failed to extract time information from " ..
michael@428 304 "license configuration parameter \"Assertion-GrantTime\"")
michael@428 305 end
michael@428 306 local granttime_begin = os.time({
michael@428 307 year = tonumber(m[1]),
michael@428 308 month = tonumber(m[2]),
michael@428 309 day = tonumber(m[3]),
michael@428 310 hour = 0,
michael@428 311 min = 0,
michael@428 312 sec = 0
michael@428 313 })
michael@428 314 local granttime_end = os.time({
michael@428 315 year = tonumber(m[4]),
michael@428 316 month = tonumber(m[5]),
michael@428 317 day = tonumber(m[6]),
michael@428 318 hour = 23,
michael@428 319 min = 59,
michael@428 320 sec = 59
michael@428 321 })
michael@428 322
michael@428 323 -- determine OpenPKG Framework release time
michael@428 324 -- (allow openpkg.spec:%pre to override with a higher value for pre-checking)
michael@428 325 local t_release = 0
michael@428 326 local result = {}
michael@428 327 for _, line in ipairs(rpm.query("Q:%{RELEASE}", false, "openpkg")) do
michael@428 328 local s, _, m = util.rmatch(line, "(?s)^Q:(.+)$")
michael@428 329 if s ~= nil then
michael@428 330 table.insert(result, m[1])
michael@428 331 end
michael@428 332 end
michael@428 333 if result[1] ~= nil then
michael@428 334 local s, _, m = util.rmatch(result[1], "^(?s)(\\d{4})(\\d{2})(\\d{2})$")
michael@428 335 if s ~= nil then
michael@428 336 t_release = os.time({
michael@428 337 year = tonumber(m[1]),
michael@428 338 month = tonumber(m[2]),
michael@428 339 day = tonumber(m[3]),
michael@428 340 hour = 0,
michael@428 341 min = 0,
michael@428 342 sec = 0
michael@428 343 })
michael@428 344 end
michael@428 345 end
michael@428 346 if t_release == 0 then
michael@428 347 return integrity.util.error(ctx, cfg,
michael@428 348 "failed to determine OpenPKG Framework release time")
michael@428 349 end
michael@428 350 local override = os.getenv("OPENPKG_FRAMEWORK_RELEASE")
michael@428 351 if override ~= nil then
michael@428 352 local s, _, m = util.rmatch(override, "^(?s)(\\d{4})(\\d{2})(\\d{2})$")
michael@428 353 if s ~= nil then
michael@428 354 local t_override = os.time({
michael@428 355 year = tonumber(m[1]),
michael@428 356 month = tonumber(m[2]),
michael@428 357 day = tonumber(m[3]),
michael@428 358 hour = 0,
michael@428 359 min = 0,
michael@428 360 sec = 0
michael@428 361 })
michael@428 362 if t_release < t_override then
michael@428 363 t_release = t_override
michael@428 364 end
michael@428 365 end
michael@428 366 end
michael@428 367
michael@428 368 -- check whether current OpenPKG Framework release time is within granttime
michael@428 369 integrity.util.debug(3, "require: %d <= %d <= %d", granttime_begin, t_release, granttime_end)
michael@428 370 if not (granttime_begin <= t_release and t_release <= granttime_end) then
michael@428 371 return integrity.util.error(ctx, cfg,
michael@428 372 "current OpenPKG Framework release time \"" .. os.date("%Y-%m-%d", t_release) .. "\" " ..
michael@428 373 "is not within the timerange \"" .. cfg["Assertion-GrantTime"] .. "\" " ..
michael@428 374 "of license configuration parameter \"Assertion-GrantTime\"")
michael@428 375 end
michael@428 376 end
michael@428 377
michael@428 378 -- process "Assertion-InstanceAge" constraint
michael@428 379 if cfg["Assertion-InstanceAge"] ~= nil then
michael@428 380 integrity.util.debug(2, "checking: Assertion-InstanceAge: \"%s\"", cfg["Assertion-InstanceAge"])
michael@428 381
michael@428 382 -- determine maximum instance age in seconds
michael@428 383 local t_diff_max = cfg["Assertion-InstanceAge"]
michael@428 384 t_diff_max = 0 + util.rsubst(t_diff_max, "^(\\d+)([smhdw])$", function (t, unit)
michael@428 385 if unit == "s" then t = t * 1
michael@428 386 elseif unit == "m" then t = t * 60
michael@428 387 elseif unit == "h" then t = t * 60 * 60
michael@428 388 elseif unit == "d" then t = t * 60 * 60 * 24
michael@428 389 elseif unit == "w" then t = t * 60 * 60 * 24 * 7
michael@428 390 end
michael@428 391 return t
michael@428 392 end)
michael@428 393
michael@428 394 -- approach 1: determine install time via timestamp of UUID_REGISTRY
michael@428 395 local uuids = integrity.util.uuids()
michael@428 396 if uuids["UUID_REGISTRY"] == "" then
michael@428 397 return integrity.util.error(ctx, cfg,
michael@428 398 "failed to load UUID_REGISTRY")
michael@428 399 end
michael@428 400 txt = uuid.describe(uuids["UUID_REGISTRY"])
michael@428 401 if txt == nil then
michael@428 402 return integrity.util.error(ctx, cfg,
michael@428 403 "failed to parse extracted UUID_REGISTRY string \"" .. uuids["UUID_REGISTRY"] .. "\" as an UUID")
michael@428 404 end
michael@428 405 local s, _, m = util.rmatch(txt, "(?s)^.*time:\\s+(\\d{4})-(\\d{2})-(\\d{2})\\s+(\\d{2}):(\\d{2}):(\\d{2}).*$")
michael@428 406 if s == nil then
michael@428 407 return integrity.util.error(ctx, cfg,
michael@428 408 "failed to extract timestamp from UUID_REGISTRY \"" .. uuids["UUID_REGISTRY"] .. "\"")
michael@428 409 end
michael@428 410 local t_install = os.time({
michael@428 411 year = tonumber(m[1]),
michael@428 412 month = tonumber(m[2]),
michael@428 413 day = tonumber(m[3]),
michael@428 414 hour = tonumber(m[4]),
michael@428 415 min = tonumber(m[5]),
michael@428 416 sec = tonumber(m[6])
michael@428 417 })
michael@428 418
michael@428 419 -- approach 2: determine install time via first install time of "openpkg" package
michael@428 420 local result = {}
michael@428 421 for _, line in ipairs(rpm.query(
michael@428 422 "Q:%|ORIGINTIME?{" ..
michael@428 423 "%{ORIGINTIME}" .. -- regular case: RPM 5 installed/updated with RPM 5
michael@428 424 "}:{" ..
michael@428 425 "%|INSTALLTIME?{" ..
michael@428 426 "%{INSTALLTIME}" .. -- special case: RPM 5 installed initially with RPM 4
michael@428 427 "}:{" ..
michael@428 428 "}|" ..
michael@428 429 "}|", false, "openpkg"
michael@428 430 )) do
michael@428 431 local s, _, m = util.rmatch(line, "(?s)^Q:(.+)$")
michael@428 432 if s ~= nil then
michael@428 433 table.insert(result, m[1])
michael@428 434 end
michael@428 435 end
michael@428 436 if result[1] ~= nil then
michael@428 437 local n = tonumber(result[1])
michael@428 438 if n > 0 then
michael@428 439 t_install = n
michael@428 440 end
michael@428 441 end
michael@428 442
michael@428 443 -- check time difference
michael@428 444 local t_now = os.time()
michael@428 445 local t_diff = os.difftime(t_now, t_install)
michael@428 446 integrity.util.debug(3, "calc: %d - %d = %d", t_now, t_install, t_diff)
michael@428 447 if t_diff < 0 then
michael@428 448 return integrity.util.error(ctx, cfg,
michael@428 449 "current system time \"" .. t_now .. "\" is lower than " ..
michael@428 450 "instance installation time \"" .. t_install .. "\"")
michael@428 451 end
michael@428 452 integrity.util.debug(3, "require: %d <= %d", t_diff, t_diff_max)
michael@428 453 if t_diff > t_diff_max then
michael@428 454 return integrity.util.error(ctx, cfg,
michael@428 455 "instance age \"" .. t_diff .. "\" " ..
michael@428 456 "is greater than value \"" .. t_diff_max .. "\" (\"" .. cfg["Assertion-InstanceAge"] .. "\") " ..
michael@428 457 "of license configuration parameter \"Assertion-InstanceAge\"")
michael@428 458 end
michael@428 459 end
michael@428 460
michael@428 461 -- process "Assertion-FromSourceOnTarget" constraint
michael@428 462 if cfg["Assertion-FromSourceOnTarget"] ~= nil then
michael@428 463 integrity.util.debug(2, "checking: Assertion-FromSourceOnTarget: \"%s\"", cfg["Assertion-FromSourceOnTarget"])
michael@428 464 local hostname = rpm.hostname()
michael@428 465 for _, line in ipairs(rpm.query("Q:%{NAME}:%{BUILDHOST}", true, "*")) do
michael@428 466 local s, _, m = util.rmatch(line, "(?s)^Q:([^:]+):(.+)$")
michael@428 467 if s ~= nil then
michael@428 468 local name = m[1]
michael@428 469 local buildhost = m[2]
michael@428 470 integrity.util.debug(4, "info: name \"%s\", buildhost \"%s\"", name, buildhost)
michael@428 471 if not util.rmatch(name, "(?s)^gpg-.+$") and buildhost ~= "localhost" then
michael@428 472 if cfg["Assertion-FromSourceOnTarget"] == "yes" and buildhost ~= hostname then
michael@428 473 return integrity.util.error(ctx, cfg,
michael@428 474 "license-required \"build from source on target system only\" situation not met because " ..
michael@428 475 "package build host \"" .. buildhost .. "\" is not(!) equal to the package install host \"" .. hostname .. "\".")
michael@428 476 end
michael@428 477 if cfg["Assertion-FromSourceOnTarget"] == "no" and buildhost == hostname then
michael@428 478 return integrity.util.error(ctx, cfg,
michael@428 479 "license-required \"build binaries on separate build-host only\" situation not met because " ..
michael@428 480 "package build host \"" .. buildhost .. "\" is equal to the package install host \"" .. hostname .. "\".")
michael@428 481 end
michael@428 482 end
michael@428 483 end
michael@428 484 end
michael@428 485 end
michael@428 486
michael@428 487 -- process "Assertion-PackageNames" constraints
michael@428 488 if cfg["Assertion-PackageNames"] ~= nil then
michael@428 489 integrity.util.debug(2, "checking: Assertion-PackageNames: \"%s\"", cfg["Assertion-PackageNames"])
michael@428 490
michael@428 491 -- query RPMDB for names of all installed packages
michael@428 492 local packages = {}
michael@428 493 for _, line in ipairs(rpm.query("Q:%{NAME}", true, "*")) do
michael@428 494 local s, _, m = util.rmatch(line, "(?s)^Q:(.+)$")
michael@428 495 if s ~= nil then
michael@428 496 table.insert(packages, m[1])
michael@428 497 end
michael@428 498 end
michael@428 499
michael@428 500 -- iterate over all constraints
michael@428 501 for _, constraint in
michael@428 502 ipairs(
michael@428 503 util.rsplit(
michael@428 504 util.rsubst(
michael@428 505 cfg["Assertion-PackageNames"],
michael@428 506 "(?s)^\\s*(.+?)\\s*$", "%1"
michael@428 507 ),
michael@428 508 "(?s)\\s+"
michael@428 509 )
michael@428 510 ) do
michael@428 511 -- parse constraint
michael@428 512 local s, _, m = util.rmatch(constraint, "(?s)^(!?)([^:]+):(!?)(.+)$")
michael@428 513 if s == nil then
michael@428 514 return integrity.util.error(ctx, cfg,
michael@428 515 "invalid syntax in license configuration \"Assertion-PackageNames\" " ..
michael@428 516 "parameter: \"" .. constraint .. "\"")
michael@428 517 end
michael@428 518 local mode_negate = m[1] ~= ""
michael@428 519 local mode_regex = m[2]
michael@428 520 local package_negate = m[3] ~= ""
michael@428 521 local package_regex = m[4]
michael@428 522 -- apply the mode filter
michael@428 523 local mode_matches, _, _ = util.rmatch(ctx.rpm.mode, mode_regex);
michael@428 524 if (not mode_negate and mode_matches ~= nil)
michael@428 525 or ( mode_negate and mode_matches == nil) then
michael@428 526 -- apply the package filter to names of all installed packages
michael@428 527 for _, package in ipairs(packages) do
michael@428 528 if package_negate then
michael@428 529 integrity.util.debug(3, "require: \"%s\" !~ /%s/", package, package_regex)
michael@428 530 else
michael@428 531 integrity.util.debug(3, "require: \"%s\" ~~ /%s/", package, package_regex)
michael@428 532 end
michael@428 533 local package_matches, _, _ = util.rmatch(package, package_regex)
michael@428 534 if not ( (not package_negate and package_matches ~= nil)
michael@428 535 or ( package_negate and package_matches == nil)) then
michael@428 536 -- indicate integrity validation error
michael@428 537 return integrity.util.error(ctx, cfg,
michael@428 538 "installed package \"" .. package .. "\" " ..
michael@428 539 "under RPM run-time mode \"" .. ctx.rpm.mode .. "\" " ..
michael@428 540 "not covered by pattern \"" .. package_regex .. "\" " ..
michael@428 541 "of license configuration parameter \"Assertion-PackageNames\"")
michael@428 542 end
michael@428 543 end
michael@428 544 end
michael@428 545 end
michael@428 546 end
michael@428 547
michael@428 548 -- process "Assertion-PackageReleaseAge"
michael@428 549 if cfg["Assertion-PackageReleaseAge"] ~= nil then
michael@428 550 integrity.util.debug(2, "checking: Assertion-PackageReleaseAge: \"%s[...]\"", string.sub(cfg["Assertion-PackageReleaseAge"], 1, 20))
michael@428 551
michael@428 552 -- parse constraint
michael@428 553 local constraint = cfg["Assertion-PackageReleaseAge"]
michael@428 554 local s, _, m = util.rmatch(constraint, "(?s)^([^:]+)%:([^:]+):([^\\s]+)\\s+(.+)$")
michael@428 555 if s == nil then
michael@428 556 return integrity.util.error(ctx, cfg,
michael@428 557 "invalid syntax in license configuration \"Assertion-PackageReleaseAge\" parameter")
michael@428 558 end
michael@428 559 local percent = m[1] / 100
michael@428 560 local offset = m[2]
michael@428 561 local distregex = m[3]
michael@428 562 local spec = m[4]
michael@428 563
michael@428 564 -- determine maximum release time difference (in seconds)
michael@428 565 local t_diff_max = 0 + util.rsubst(offset, "^(\\d+)([smhdw])$", function (t, unit)
michael@428 566 if unit == "s" then t = t * 1
michael@428 567 elseif unit == "m" then t = t * 60
michael@428 568 elseif unit == "h" then t = t * 60 * 60
michael@428 569 elseif unit == "d" then t = t * 60 * 60 * 24
michael@428 570 elseif unit == "w" then t = t * 60 * 60 * 24 * 7
michael@428 571 end
michael@428 572 return t
michael@428 573 end)
michael@428 574
michael@428 575 -- iterate over all package specifications to build release map
michael@428 576 local releases = {}
michael@428 577 for _, constraint in
michael@428 578 ipairs(
michael@428 579 util.rsplit(
michael@428 580 util.rsubst(
michael@428 581 spec,
michael@428 582 "(?s)^\\s*(.+?)\\s*$", "%1"
michael@428 583 ),
michael@428 584 "(?s)\\s+"
michael@428 585 )
michael@428 586 ) do
michael@428 587
michael@428 588 -- parse specification into package name and release constraint
michael@428 589 local s, _, m = util.rmatch(constraint, "(?s)^([^:]+):(.+)$")
michael@428 590 if s == nil then
michael@428 591 return integrity.util.error(ctx, cfg,
michael@428 592 "invalid syntax in license configuration \"Assertion-PackageReleaseAge\" " ..
michael@428 593 "parameter: \"" .. constraint .. "\"")
michael@428 594 end
michael@428 595
michael@428 596 -- store result into release map
michael@428 597 releases[m[1]] = m[2]
michael@428 598 end
michael@428 599
michael@428 600 -- query RPMDB for releases of all installed packages and decide
michael@428 601 -- whether the release time is inside or outside our constraint window
michael@428 602 local release_window_inside = 0
michael@428 603 local release_window_outside = 0
michael@428 604 local release_window_foreign = 0
michael@428 605 local release_window_unknown = 0
michael@428 606 for _, line in ipairs(rpm.query("Q:%{NAME}:%{RELEASE}:%{DISTRIBUTION}", true, "*")) do
michael@428 607 local s, _, m = util.rmatch(line, "(?s)^Q:([^:]+):(\\d\\d\\d\\d)(\\d\\d)(\\d\\d):(.+)$")
michael@428 608 if s ~= nil then
michael@428 609 -- parse query results
michael@428 610 local name = m[1]
michael@428 611 local t_release = os.time({
michael@428 612 year = tonumber(m[2]),
michael@428 613 month = tonumber(m[3]),
michael@428 614 day = tonumber(m[4]),
michael@428 615 hour = 23,
michael@428 616 min = 59,
michael@428 617 sec = 59
michael@428 618 })
michael@428 619 local dist = m[5]
michael@428 620
michael@428 621 -- only check files of the constrained distribution(s)
michael@428 622 if util.rmatch(dist, "(?s)" .. distregex) then
michael@428 623
michael@428 624 -- determine minimum release constraint
michael@428 625 local t_release_min = releases[name]
michael@428 626 if t_release_min == nil then
michael@428 627 t_release_min = releases["*"]
michael@428 628 end
michael@428 629 if t_release_min == nil then
michael@428 630 t_release_min = os.time()
michael@428 631 else
michael@428 632 local s, _, m = util.rmatch(t_release_min, "^(?s)(\\d{4})(\\d{2})(\\d{2})$")
michael@428 633 t_release_min = os.time({
michael@428 634 year = tonumber(m[1]),
michael@428 635 month = tonumber(m[2]),
michael@428 636 day = tonumber(m[3]),
michael@428 637 hour = 0,
michael@428 638 min = 0,
michael@428 639 sec = 0
michael@428 640 })
michael@428 641 end
michael@428 642
michael@428 643 -- check time difference of package release
michael@428 644 local t_diff = os.difftime(t_release_min, t_release)
michael@428 645 integrity.util.debug(4, "calc: %d - %d = %d", t_release_min, t_release, t_diff)
michael@428 646 integrity.util.debug(4, "require: %d <= 0 or (%d > 0 and %d < %d)", t_diff, t_diff, t_diff, t_diff_max)
michael@428 647 if t_diff <= 0 or (t_diff > 0 and t_diff < t_diff_max) then
michael@428 648 release_window_inside = release_window_inside + 1
michael@428 649 else
michael@428 650 release_window_outside = release_window_outside + 1
michael@428 651 end
michael@428 652 else
michael@428 653 release_window_foreign = release_window_foreign + 1
michael@428 654 end
michael@428 655 else
michael@428 656 release_window_unknown = release_window_unknown + 1
michael@428 657 end
michael@428 658 end
michael@428 659 integrity.util.debug(3, "info: inside %d, outside %d, foreign %d, unknown %d",
michael@428 660 release_window_inside, release_window_outside, release_window_foreign, release_window_unknown)
michael@428 661
michael@428 662 -- check validity of overall constraint
michael@428 663 local percent_inside =
michael@428 664 (release_window_inside / (release_window_inside + release_window_outside))
michael@428 665 integrity.util.debug(3, "require: %d >= %d", percent_inside, percent)
michael@428 666 if percent_inside < percent then
michael@428 667 return integrity.util.error(ctx, cfg,
michael@428 668 "there are only " .. math.floor(percent_inside * 100) .. "% " ..
michael@428 669 "packages inside the release date constraint " ..
michael@428 670 "(expected a minimum of " .. math.floor(percent * 100) .. "%)")
michael@428 671 end
michael@428 672 end
michael@428 673
michael@428 674 -- process "Assertion-Expression" constraint
michael@428 675 if cfg["Assertion-Expression"] ~= nil then
michael@428 676 integrity.util.debug(2, "checking: Assertion-Expression: \"%s\"", cfg["Assertion-Expression"])
michael@428 677
michael@428 678 -- expand special consytructs in expression
michael@428 679 local expr = cfg["Assertion-Expression"]
michael@428 680 expr = util.rsubst(expr, "(%\{[a-zA-Z_][a-zA-Z0-9_]+\})", function (str)
michael@428 681 return rpm.expand(str)
michael@428 682 end)
michael@428 683 expr = util.rsubst(expr, "\"((?:\\\\.|[^\"])+)\"\\s*~~\\s*/((?:\\\\.|[^/])+)/", function (str, regex)
michael@428 684 if util.rmatch(str, "(?s)" .. regex) ~= nil then
michael@428 685 return "true"
michael@428 686 else
michael@428 687 return "false"
michael@428 688 end
michael@428 689 end)
michael@428 690
michael@428 691 -- evaluate expression
michael@428 692 integrity.util.debug(3, "evaluate: %s", expr)
michael@428 693 result = assert(loadstring(expr))()
michael@428 694 if type(result) ~= "boolean" then
michael@428 695 result = false
michael@428 696 end
michael@428 697 if not result then
michael@428 698 return integrity.util.error(ctx, cfg,
michael@428 699 "expression \"" .. cfg["Assertion-Expression"] .. "\" " ..
michael@428 700 "of license configuration parameter \"Assertion-Expression\"" ..
michael@428 701 "evaluated to false")
michael@428 702 end
michael@428 703 end
michael@428 704
michael@428 705 -- indicate license integrity validation success
michael@428 706 return "OK"
michael@428 707 end
michael@428 708
michael@428 709 - -- integrity processor utilities namespace
michael@428 710 integrity.util = {}
michael@428 711
michael@428 712 - -- write debug information to stderr
michael@428 713 function integrity.util.debug(level_this, msg, ...)
michael@428 714 local level_min = os.getenv("OPENPKG_LICENSE_DEBUG")
michael@428 715 if level_min ~= nil then
michael@428 716 if type(level_min) == "string" then
michael@428 717 level_min = tonumber(level_min)
michael@428 718 end
michael@428 719 if type(level_min) ~= "number" then
michael@428 720 level_min = 0
michael@428 721 end
michael@428 722 if level_this <= level_min then
michael@428 723 local output
michael@428 724 if type(msg) == "function" then
michael@428 725 output = msg(...)
michael@428 726 else
michael@428 727 output = string.format(msg, ...)
michael@428 728 end
michael@428 729 local prefix = ""
michael@428 730 local i = 1
michael@428 731 while (i < level_this) do
michael@428 732 prefix = prefix .. " "
michael@428 733 i = i + 1
michael@428 734 end
michael@428 735 io.stderr:write("rpm: DEBUG: " .. prefix .. output .. "\n")
michael@428 736 end
michael@428 737 end
michael@428 738 end
michael@428 739
michael@428 740 - -- load OpenPKG instance UUIDs
michael@428 741 function integrity.util.uuids()
michael@428 742 local uuids = {
michael@428 743 UUID_REGISTRY = "",
michael@428 744 UUID_INSTANCE = "",
michael@428 745 UUID_PLATFORM = ""
michael@428 746 }
michael@428 747 local filename = rpm.expand("%{l_prefix}/etc/openpkg/uuid")
michael@428 748 local txt = rpm.slurp(filename)
michael@428 749 if txt ~= nil then
michael@428 750 for name, _ in pairs(uuids) do
michael@428 751 local s, _, m = util.rmatch(txt, "(?s)^.*" .. name .. "=\"([^\"\"]+)\".*$")
michael@428 752 if s ~= nil then
michael@428 753 uuids[name] = m[1]
michael@428 754 end
michael@428 755 end
michael@428 756 end
michael@428 757 return uuids
michael@428 758 end
michael@428 759
michael@428 760 - -- report validation warning
michael@428 761 function integrity.util.warning(ctx, cfg, warning)
michael@428 762 -- return prominent warning message
michael@428 763 return
michael@428 764 "WARNING: OpenPKG run-time license check failed -- continue processing\n" ..
michael@428 765 "+-----------------------------------------------------------------------------+\n" ..
michael@428 766 "| Attention, the OpenPKG RPM run-time integrity checking facility encountered a\n" ..
michael@428 767 "| non-fatal problem during license checking, but allows processing to continue.\n" ..
michael@428 768 "| The particular warning reported by the OpenPKG license processor is:\n" ..
michael@428 769 "|\n" ..
michael@428 770 util.textwrap("| ", warning, 60, 70) ..
michael@428 771 "|\n" ..
michael@428 772 "| Notice: Operation of the OpenPKG Framework requires a valid license.\n" ..
michael@428 773 "| Go to http://openpkg.com/go/framework-license for more details, please.\n" ..
michael@428 774 "+-----------------------------------------------------------------------------+"
michael@428 775 end
michael@428 776
michael@428 777 - -- report validation error
michael@428 778 function integrity.util.error(ctx, cfg, error)
michael@428 779 -- support conversion of errors into warnings
michael@428 780 if cfg["Assertion-ErrorToWarning"] ~= nil then
michael@428 781 if cfg["Assertion-ErrorToWarning"] == "yes" then
michael@428 782 return integrity.util.warning(ctx, cfg, error)
michael@428 783 end
michael@428 784 end
michael@428 785
michael@428 786 -- return prominent error message
michael@428 787 return
michael@428 788 "ERROR: OpenPKG run-time license check failed -- stopping processing\n" ..
michael@428 789 "+-----------------------------------------------------------------------------+\n" ..
michael@428 790 "| Sorry, the OpenPKG RPM run-time integrity checking facility encountered a\n" ..
michael@428 791 "| fatal problem during license checking and stops processing immediately.\n" ..
michael@428 792 "| The particular error reported by the OpenPKG license processor is:\n" ..
michael@428 793 "|\n" ..
michael@428 794 util.textwrap("| ", error, 60, 70) ..
michael@428 795 "|\n" ..
michael@428 796 "| Notice: Operation of the OpenPKG Framework requires a valid license.\n" ..
michael@428 797 "| Go to http://openpkg.com/go/framework-license for more details, please.\n" ..
michael@428 798 "| Run \"openpkg man license\" for details about local license management.\n" ..
michael@428 799 "+-----------------------------------------------------------------------------+"
michael@428 800 end
michael@428 801
michael@428 802 -----BEGIN PGP SIGNATURE-----
michael@428 803 Comment: OpenPKG GmbH <openpkg@openpkg.com>
michael@428 804
michael@428 805 iEYEARECAAYFAk8BelcACgkQZwQuyWG3rjTL6QCeLTLVj4PTnd/E7mf+Sv4mgbZj
michael@428 806 5J0AoMXrO4EimPSSCZSJ1TLW8f8GP+B5
michael@428 807 =AVpf
michael@428 808 -----END PGP SIGNATURE-----

mercurial