[33623] | 1 | /* $Id: ExtPackUtil.cpp 41783 2012-06-16 19:24:15Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
| 3 | * VirtualBox Main - Extension Pack Utilities and definitions, VBoxC, VBoxSVC, ++.
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[39878] | 7 | * Copyright (C) 2010-2012 Oracle Corporation
|
---|
[33623] | 8 | *
|
---|
[34307] | 9 | * This file is part of VirtualBox Open Source Edition (OSE), as
|
---|
| 10 | * available from http://www.virtualbox.org. This file is free software;
|
---|
| 11 | * you can redistribute it and/or modify it under the terms of the GNU
|
---|
| 12 | * General Public License (GPL) as published by the Free Software
|
---|
| 13 | * Foundation, in version 2 as it comes in the "COPYING" file of the
|
---|
| 14 | * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
|
---|
| 15 | * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
|
---|
[33623] | 16 | */
|
---|
| 17 |
|
---|
| 18 |
|
---|
| 19 | /*******************************************************************************
|
---|
| 20 | * Header Files *
|
---|
| 21 | *******************************************************************************/
|
---|
[35368] | 22 | #include "../include/ExtPackUtil.h"
|
---|
[33623] | 23 |
|
---|
| 24 | #include <iprt/ctype.h>
|
---|
| 25 | #include <iprt/dir.h>
|
---|
| 26 | #include <iprt/file.h>
|
---|
[34787] | 27 | #include <iprt/manifest.h>
|
---|
[33623] | 28 | #include <iprt/param.h>
|
---|
| 29 | #include <iprt/path.h>
|
---|
[39878] | 30 | #include <iprt/sha.h>
|
---|
[33623] | 31 | #include <iprt/string.h>
|
---|
[34787] | 32 | #include <iprt/vfs.h>
|
---|
| 33 | #include <iprt/tar.h>
|
---|
| 34 | #include <iprt/zip.h>
|
---|
[33623] | 35 | #include <iprt/cpp/xml.h>
|
---|
| 36 |
|
---|
| 37 | #include <VBox/log.h>
|
---|
| 38 |
|
---|
| 39 |
|
---|
| 40 | /**
|
---|
[34244] | 41 | * Worker for VBoxExtPackLoadDesc that loads the plug-in descriptors.
|
---|
| 42 | *
|
---|
| 43 | * @returns Same as VBoxExtPackLoadDesc.
|
---|
| 44 | * @param pVBoxExtPackElm
|
---|
| 45 | * @param pcPlugIns Where to return the number of plug-ins in the
|
---|
| 46 | * array.
|
---|
| 47 | * @param paPlugIns Where to return the plug-in descriptor array.
|
---|
| 48 | * (RTMemFree it even on failure)
|
---|
| 49 | */
|
---|
[36527] | 50 | static RTCString *
|
---|
[34244] | 51 | vboxExtPackLoadPlugInDescs(const xml::ElementNode *pVBoxExtPackElm,
|
---|
| 52 | uint32_t *pcPlugIns, PVBOXEXTPACKPLUGINDESC *paPlugIns)
|
---|
| 53 | {
|
---|
| 54 | *pcPlugIns = 0;
|
---|
| 55 | *paPlugIns = NULL;
|
---|
| 56 |
|
---|
| 57 | /** @todo plug-ins */
|
---|
| 58 | NOREF(pVBoxExtPackElm);
|
---|
| 59 |
|
---|
| 60 | return NULL;
|
---|
| 61 | }
|
---|
| 62 |
|
---|
| 63 | /**
|
---|
[34787] | 64 | * Clears the extension pack descriptor.
|
---|
[33623] | 65 | *
|
---|
[34787] | 66 | * @param a_pExtPackDesc The descriptor to clear.
|
---|
[33623] | 67 | */
|
---|
[34787] | 68 | static void vboxExtPackClearDesc(PVBOXEXTPACKDESC a_pExtPackDesc)
|
---|
[33623] | 69 | {
|
---|
| 70 | a_pExtPackDesc->strName.setNull();
|
---|
| 71 | a_pExtPackDesc->strDescription.setNull();
|
---|
| 72 | a_pExtPackDesc->strVersion.setNull();
|
---|
[39180] | 73 | a_pExtPackDesc->strEdition.setNull();
|
---|
[33623] | 74 | a_pExtPackDesc->uRevision = 0;
|
---|
| 75 | a_pExtPackDesc->strMainModule.setNull();
|
---|
[34244] | 76 | a_pExtPackDesc->strVrdeModule.setNull();
|
---|
[34073] | 77 | a_pExtPackDesc->cPlugIns = 0;
|
---|
| 78 | a_pExtPackDesc->paPlugIns = NULL;
|
---|
[34893] | 79 | a_pExtPackDesc->fShowLicense = false;
|
---|
[34787] | 80 | }
|
---|
[33623] | 81 |
|
---|
[34787] | 82 | /**
|
---|
[35523] | 83 | * Initializes an extension pack descriptor so that it's safe to call free on
|
---|
| 84 | * it whatever happens later on.
|
---|
| 85 | *
|
---|
| 86 | * @param a_pExtPackDesc The descirptor to initialize.
|
---|
| 87 | */
|
---|
| 88 | void VBoxExtPackInitDesc(PVBOXEXTPACKDESC a_pExtPackDesc)
|
---|
| 89 | {
|
---|
| 90 | vboxExtPackClearDesc(a_pExtPackDesc);
|
---|
| 91 | }
|
---|
| 92 |
|
---|
| 93 |
|
---|
| 94 | /**
|
---|
[34787] | 95 | * Load the extension pack descriptor from an XML document.
|
---|
| 96 | *
|
---|
| 97 | * @returns NULL on success, pointer to an error message on failure (caller
|
---|
| 98 | * deletes it).
|
---|
[41783] | 99 | * @param a_pDoc Pointer to the XML document.
|
---|
[34787] | 100 | * @param a_pExtPackDesc Where to store the extension pack descriptor.
|
---|
| 101 | */
|
---|
[36527] | 102 | static RTCString *vboxExtPackLoadDescFromDoc(xml::Document *a_pDoc, PVBOXEXTPACKDESC a_pExtPackDesc)
|
---|
[34787] | 103 | {
|
---|
[33623] | 104 | /*
|
---|
| 105 | * Get the main element and check its version.
|
---|
| 106 | */
|
---|
[34787] | 107 | const xml::ElementNode *pVBoxExtPackElm = a_pDoc->getRootElement();
|
---|
[34073] | 108 | if ( !pVBoxExtPackElm
|
---|
| 109 | || strcmp(pVBoxExtPackElm->getName(), "VirtualBoxExtensionPack") != 0)
|
---|
[36527] | 110 | return new RTCString("No VirtualBoxExtensionPack element");
|
---|
[33623] | 111 |
|
---|
[36527] | 112 | RTCString strFormatVersion;
|
---|
[33623] | 113 | if (!pVBoxExtPackElm->getAttributeValue("version", strFormatVersion))
|
---|
[36527] | 114 | return new RTCString("Missing format version");
|
---|
[33623] | 115 | if (!strFormatVersion.equals("1.0"))
|
---|
[36527] | 116 | return &(new RTCString("Unsupported format version: "))->append(strFormatVersion);
|
---|
[33623] | 117 |
|
---|
| 118 | /*
|
---|
[34244] | 119 | * Read and validate mandatory bits.
|
---|
[33623] | 120 | */
|
---|
| 121 | const xml::ElementNode *pNameElm = pVBoxExtPackElm->findChildElement("Name");
|
---|
| 122 | if (!pNameElm)
|
---|
[36527] | 123 | return new RTCString("The 'Name' element is missing");
|
---|
[33623] | 124 | const char *pszName = pNameElm->getValue();
|
---|
| 125 | if (!VBoxExtPackIsValidName(pszName))
|
---|
[36527] | 126 | return &(new RTCString("Invalid name: "))->append(pszName);
|
---|
[33623] | 127 |
|
---|
| 128 | const xml::ElementNode *pDescElm = pVBoxExtPackElm->findChildElement("Description");
|
---|
| 129 | if (!pDescElm)
|
---|
[36527] | 130 | return new RTCString("The 'Description' element is missing");
|
---|
[33623] | 131 | const char *pszDesc = pDescElm->getValue();
|
---|
| 132 | if (!pszDesc || *pszDesc == '\0')
|
---|
[36527] | 133 | return new RTCString("The 'Description' element is empty");
|
---|
[33623] | 134 | if (strpbrk(pszDesc, "\n\r\t\v\b") != NULL)
|
---|
[36527] | 135 | return new RTCString("The 'Description' must not contain control characters");
|
---|
[33623] | 136 |
|
---|
| 137 | const xml::ElementNode *pVersionElm = pVBoxExtPackElm->findChildElement("Version");
|
---|
| 138 | if (!pVersionElm)
|
---|
[36527] | 139 | return new RTCString("The 'Version' element is missing");
|
---|
[33623] | 140 | const char *pszVersion = pVersionElm->getValue();
|
---|
| 141 | if (!pszVersion || *pszVersion == '\0')
|
---|
[36527] | 142 | return new RTCString("The 'Version' element is empty");
|
---|
[33623] | 143 | if (!VBoxExtPackIsValidVersionString(pszVersion))
|
---|
[36527] | 144 | return &(new RTCString("Invalid version string: "))->append(pszVersion);
|
---|
[33623] | 145 |
|
---|
| 146 | uint32_t uRevision;
|
---|
| 147 | if (!pVersionElm->getAttributeValue("revision", uRevision))
|
---|
| 148 | uRevision = 0;
|
---|
| 149 |
|
---|
[39180] | 150 | const char *pszEdition;
|
---|
| 151 | if (!pVersionElm->getAttributeValue("edition", pszEdition))
|
---|
| 152 | pszEdition = "";
|
---|
| 153 | if (!VBoxExtPackIsValidEditionString(pszEdition))
|
---|
| 154 | return &(new RTCString("Invalid edition string: "))->append(pszEdition);
|
---|
| 155 |
|
---|
[33623] | 156 | const xml::ElementNode *pMainModuleElm = pVBoxExtPackElm->findChildElement("MainModule");
|
---|
| 157 | if (!pMainModuleElm)
|
---|
[36527] | 158 | return new RTCString("The 'MainModule' element is missing");
|
---|
[33623] | 159 | const char *pszMainModule = pMainModuleElm->getValue();
|
---|
| 160 | if (!pszMainModule || *pszMainModule == '\0')
|
---|
[36527] | 161 | return new RTCString("The 'MainModule' element is empty");
|
---|
[34244] | 162 | if (!VBoxExtPackIsValidModuleString(pszMainModule))
|
---|
[36527] | 163 | return &(new RTCString("Invalid main module string: "))->append(pszMainModule);
|
---|
[33623] | 164 |
|
---|
| 165 | /*
|
---|
[34244] | 166 | * The VRDE module, optional.
|
---|
| 167 | * Accept both none and empty as tokens of no VRDE module.
|
---|
| 168 | */
|
---|
| 169 | const char *pszVrdeModule = NULL;
|
---|
| 170 | const xml::ElementNode *pVrdeModuleElm = pVBoxExtPackElm->findChildElement("VRDEModule");
|
---|
| 171 | if (pVrdeModuleElm)
|
---|
| 172 | {
|
---|
| 173 | pszVrdeModule = pVrdeModuleElm->getValue();
|
---|
| 174 | if (!pszVrdeModule || *pszVrdeModule == '\0')
|
---|
| 175 | pszVrdeModule = NULL;
|
---|
| 176 | else if (!VBoxExtPackIsValidModuleString(pszVrdeModule))
|
---|
[36527] | 177 | return &(new RTCString("Invalid VRDE module string: "))->append(pszVrdeModule);
|
---|
[34244] | 178 | }
|
---|
| 179 |
|
---|
| 180 | /*
|
---|
[34893] | 181 | * Whether to show the license, optional. (presense is enough here)
|
---|
[34244] | 182 | */
|
---|
[34893] | 183 | const xml::ElementNode *pShowLicenseElm = pVBoxExtPackElm->findChildElement("ShowLicense");
|
---|
| 184 | bool fShowLicense = pShowLicenseElm != NULL;
|
---|
| 185 |
|
---|
| 186 | /*
|
---|
| 187 | * Parse plug-in descriptions (last because of the manual memory management).
|
---|
| 188 | */
|
---|
[34244] | 189 | uint32_t cPlugIns = 0;
|
---|
| 190 | PVBOXEXTPACKPLUGINDESC paPlugIns = NULL;
|
---|
[36527] | 191 | RTCString *pstrRet = vboxExtPackLoadPlugInDescs(pVBoxExtPackElm, &cPlugIns, &paPlugIns);
|
---|
[34244] | 192 | if (pstrRet)
|
---|
| 193 | {
|
---|
| 194 | RTMemFree(paPlugIns);
|
---|
| 195 | return pstrRet;
|
---|
| 196 | }
|
---|
| 197 |
|
---|
| 198 | /*
|
---|
[33623] | 199 | * Everything seems fine, fill in the return values and return successfully.
|
---|
| 200 | */
|
---|
| 201 | a_pExtPackDesc->strName = pszName;
|
---|
| 202 | a_pExtPackDesc->strDescription = pszDesc;
|
---|
| 203 | a_pExtPackDesc->strVersion = pszVersion;
|
---|
[39180] | 204 | a_pExtPackDesc->strEdition = pszEdition;
|
---|
[33623] | 205 | a_pExtPackDesc->uRevision = uRevision;
|
---|
| 206 | a_pExtPackDesc->strMainModule = pszMainModule;
|
---|
[34244] | 207 | a_pExtPackDesc->strVrdeModule = pszVrdeModule;
|
---|
| 208 | a_pExtPackDesc->cPlugIns = cPlugIns;
|
---|
| 209 | a_pExtPackDesc->paPlugIns = paPlugIns;
|
---|
[34893] | 210 | a_pExtPackDesc->fShowLicense = fShowLicense;
|
---|
[33623] | 211 |
|
---|
| 212 | return NULL;
|
---|
| 213 | }
|
---|
| 214 |
|
---|
[34787] | 215 | /**
|
---|
| 216 | * Reads the extension pack descriptor.
|
---|
| 217 | *
|
---|
| 218 | * @returns NULL on success, pointer to an error message on failure (caller
|
---|
| 219 | * deletes it).
|
---|
| 220 | * @param a_pszDir The directory containing the description file.
|
---|
| 221 | * @param a_pExtPackDesc Where to store the extension pack descriptor.
|
---|
| 222 | * @param a_pObjInfo Where to store the object info for the file (unix
|
---|
| 223 | * attribs). Optional.
|
---|
| 224 | */
|
---|
[36527] | 225 | RTCString *VBoxExtPackLoadDesc(const char *a_pszDir, PVBOXEXTPACKDESC a_pExtPackDesc, PRTFSOBJINFO a_pObjInfo)
|
---|
[34787] | 226 | {
|
---|
| 227 | vboxExtPackClearDesc(a_pExtPackDesc);
|
---|
[33623] | 228 |
|
---|
[34787] | 229 | /*
|
---|
| 230 | * Validate, open and parse the XML file.
|
---|
| 231 | */
|
---|
| 232 | char szFilePath[RTPATH_MAX];
|
---|
| 233 | int vrc = RTPathJoin(szFilePath, sizeof(szFilePath), a_pszDir, VBOX_EXTPACK_DESCRIPTION_NAME);
|
---|
| 234 | if (RT_FAILURE(vrc))
|
---|
[36527] | 235 | return new RTCString("RTPathJoin failed with %Rrc", vrc);
|
---|
[34787] | 236 |
|
---|
| 237 | RTFSOBJINFO ObjInfo;
|
---|
| 238 | vrc = RTPathQueryInfoEx(szFilePath, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
|
---|
| 239 | if (RT_FAILURE(vrc))
|
---|
[36527] | 240 | return &(new RTCString())->printf("RTPathQueryInfoEx failed with %Rrc", vrc);
|
---|
[34787] | 241 | if (a_pObjInfo)
|
---|
| 242 | *a_pObjInfo = ObjInfo;
|
---|
| 243 | if (!RTFS_IS_FILE(ObjInfo.Attr.fMode))
|
---|
| 244 | {
|
---|
| 245 | if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
|
---|
[36527] | 246 | return new RTCString("The XML file is symlinked, that is not allowed");
|
---|
| 247 | return &(new RTCString)->printf("The XML file is not a file (fMode=%#x)", ObjInfo.Attr.fMode);
|
---|
[34787] | 248 | }
|
---|
| 249 |
|
---|
| 250 | xml::Document Doc;
|
---|
| 251 | {
|
---|
| 252 | xml::XmlFileParser Parser;
|
---|
| 253 | try
|
---|
| 254 | {
|
---|
| 255 | Parser.read(szFilePath, Doc);
|
---|
| 256 | }
|
---|
| 257 | catch (xml::XmlError Err)
|
---|
| 258 | {
|
---|
[36527] | 259 | return new RTCString(Err.what());
|
---|
[34787] | 260 | }
|
---|
| 261 | }
|
---|
| 262 |
|
---|
| 263 | /*
|
---|
| 264 | * Hand the xml doc over to the common code.
|
---|
| 265 | */
|
---|
| 266 | return vboxExtPackLoadDescFromDoc(&Doc, a_pExtPackDesc);
|
---|
| 267 | }
|
---|
| 268 |
|
---|
[33623] | 269 | /**
|
---|
[34787] | 270 | * Reads the extension pack descriptor.
|
---|
| 271 | *
|
---|
| 272 | * @returns NULL on success, pointer to an error message on failure (caller
|
---|
| 273 | * deletes it).
|
---|
| 274 | * @param a_pszDir The directory containing the description file.
|
---|
| 275 | * @param a_pExtPackDesc Where to store the extension pack descriptor.
|
---|
| 276 | * @param a_pObjInfo Where to store the object info for the file (unix
|
---|
| 277 | * attribs). Optional.
|
---|
| 278 | */
|
---|
[36527] | 279 | RTCString *VBoxExtPackLoadDescFromVfsFile(RTVFSFILE hVfsFile, PVBOXEXTPACKDESC a_pExtPackDesc, PRTFSOBJINFO a_pObjInfo)
|
---|
[34787] | 280 | {
|
---|
| 281 | vboxExtPackClearDesc(a_pExtPackDesc);
|
---|
| 282 |
|
---|
| 283 | /*
|
---|
| 284 | * Query the object info.
|
---|
| 285 | */
|
---|
| 286 | RTFSOBJINFO ObjInfo;
|
---|
| 287 | int rc = RTVfsFileQueryInfo(hVfsFile, &ObjInfo, RTFSOBJATTRADD_UNIX);
|
---|
| 288 | if (RT_FAILURE(rc))
|
---|
[36527] | 289 | return &(new RTCString)->printf("RTVfsFileQueryInfo failed: %Rrc", rc);
|
---|
[34787] | 290 | if (a_pObjInfo)
|
---|
| 291 | *a_pObjInfo = ObjInfo;
|
---|
| 292 |
|
---|
| 293 | /*
|
---|
| 294 | * The simple approach, read the whole thing into memory and pass this to
|
---|
| 295 | * the XML parser.
|
---|
| 296 | */
|
---|
| 297 |
|
---|
| 298 | /* Check the file size. */
|
---|
| 299 | if (ObjInfo.cbObject > _1M || ObjInfo.cbObject < 0)
|
---|
[36527] | 300 | return &(new RTCString)->printf("The XML file is too large (%'RU64 bytes)", ObjInfo.cbObject);
|
---|
[34787] | 301 | size_t const cbFile = (size_t)ObjInfo.cbObject;
|
---|
| 302 |
|
---|
| 303 | /* Rewind to the start of the file. */
|
---|
| 304 | rc = RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL);
|
---|
| 305 | if (RT_FAILURE(rc))
|
---|
[36527] | 306 | return &(new RTCString)->printf("RTVfsFileSeek(,0,BEGIN) failed: %Rrc", rc);
|
---|
[34787] | 307 |
|
---|
| 308 | /* Allocate memory and read the file content into it. */
|
---|
| 309 | void *pvFile = RTMemTmpAlloc(cbFile);
|
---|
| 310 | if (!pvFile)
|
---|
[36527] | 311 | return &(new RTCString)->printf("RTMemTmpAlloc(%zu) failed", cbFile);
|
---|
[34787] | 312 |
|
---|
[36527] | 313 | RTCString *pstrErr = NULL;
|
---|
[34787] | 314 | rc = RTVfsFileRead(hVfsFile, pvFile, cbFile, NULL);
|
---|
| 315 | if (RT_FAILURE(rc))
|
---|
[36527] | 316 | pstrErr = &(new RTCString)->printf("RTVfsFileRead failed: %Rrc", rc);
|
---|
[34787] | 317 |
|
---|
| 318 | /*
|
---|
| 319 | * Parse the file.
|
---|
| 320 | */
|
---|
| 321 | xml::Document Doc;
|
---|
| 322 | if (RT_SUCCESS(rc))
|
---|
| 323 | {
|
---|
| 324 | xml::XmlMemParser Parser;
|
---|
[36527] | 325 | RTCString strFileName = VBOX_EXTPACK_DESCRIPTION_NAME;
|
---|
[34787] | 326 | try
|
---|
| 327 | {
|
---|
| 328 | Parser.read(pvFile, cbFile, strFileName, Doc);
|
---|
| 329 | }
|
---|
| 330 | catch (xml::XmlError Err)
|
---|
| 331 | {
|
---|
[36527] | 332 | pstrErr = new RTCString(Err.what());
|
---|
[34787] | 333 | rc = VERR_PARSE_ERROR;
|
---|
| 334 | }
|
---|
| 335 | }
|
---|
| 336 | RTMemTmpFree(pvFile);
|
---|
| 337 |
|
---|
| 338 | /*
|
---|
| 339 | * Hand the xml doc over to the common code.
|
---|
| 340 | */
|
---|
| 341 | if (RT_SUCCESS(rc))
|
---|
| 342 | pstrErr = vboxExtPackLoadDescFromDoc(&Doc, a_pExtPackDesc);
|
---|
| 343 |
|
---|
| 344 | return pstrErr;
|
---|
| 345 | }
|
---|
| 346 |
|
---|
| 347 | /**
|
---|
[34073] | 348 | * Frees all resources associated with a extension pack descriptor.
|
---|
| 349 | *
|
---|
[34244] | 350 | * @param a_pExtPackDesc The extension pack descriptor which members
|
---|
| 351 | * should be freed.
|
---|
[34073] | 352 | */
|
---|
| 353 | void VBoxExtPackFreeDesc(PVBOXEXTPACKDESC a_pExtPackDesc)
|
---|
| 354 | {
|
---|
| 355 | if (!a_pExtPackDesc)
|
---|
| 356 | return;
|
---|
| 357 |
|
---|
| 358 | a_pExtPackDesc->strName.setNull();
|
---|
| 359 | a_pExtPackDesc->strDescription.setNull();
|
---|
| 360 | a_pExtPackDesc->strVersion.setNull();
|
---|
[39180] | 361 | a_pExtPackDesc->strEdition.setNull();
|
---|
[34073] | 362 | a_pExtPackDesc->uRevision = 0;
|
---|
| 363 | a_pExtPackDesc->strMainModule.setNull();
|
---|
[34244] | 364 | a_pExtPackDesc->strVrdeModule.setNull();
|
---|
[34073] | 365 | a_pExtPackDesc->cPlugIns = 0;
|
---|
| 366 | RTMemFree(a_pExtPackDesc->paPlugIns);
|
---|
| 367 | a_pExtPackDesc->paPlugIns = NULL;
|
---|
[34893] | 368 | a_pExtPackDesc->fShowLicense = false;
|
---|
[34073] | 369 | }
|
---|
| 370 |
|
---|
| 371 | /**
|
---|
[33806] | 372 | * Extract the extension pack name from the tarball path.
|
---|
| 373 | *
|
---|
| 374 | * @returns String containing the name on success, the caller must delete it.
|
---|
| 375 | * NULL if no valid name was found or if we ran out of memory.
|
---|
| 376 | * @param pszTarball The path to the tarball.
|
---|
| 377 | */
|
---|
[36527] | 378 | RTCString *VBoxExtPackExtractNameFromTarballPath(const char *pszTarball)
|
---|
[33806] | 379 | {
|
---|
| 380 | /*
|
---|
| 381 | * Skip ahead to the filename part and count the number of characters
|
---|
[34579] | 382 | * that matches the criteria for a mangled extension pack name.
|
---|
[33806] | 383 | */
|
---|
| 384 | const char *pszSrc = RTPathFilename(pszTarball);
|
---|
| 385 | if (!pszSrc)
|
---|
| 386 | return NULL;
|
---|
| 387 |
|
---|
| 388 | size_t off = 0;
|
---|
[34579] | 389 | while (RT_C_IS_ALNUM(pszSrc[off]) || pszSrc[off] == '_')
|
---|
[33806] | 390 | off++;
|
---|
| 391 |
|
---|
| 392 | /*
|
---|
| 393 | * Check min and max name limits.
|
---|
| 394 | */
|
---|
[34570] | 395 | if ( off > VBOX_EXTPACK_NAME_MAX_LEN
|
---|
[33806] | 396 | || off < VBOX_EXTPACK_NAME_MIN_LEN)
|
---|
| 397 | return NULL;
|
---|
| 398 |
|
---|
| 399 | /*
|
---|
[34579] | 400 | * Return the unmangled name.
|
---|
[33806] | 401 | */
|
---|
[34579] | 402 | return VBoxExtPackUnmangleName(pszSrc, off);
|
---|
[33806] | 403 | }
|
---|
| 404 |
|
---|
| 405 | /**
|
---|
[33623] | 406 | * Validates the extension pack name.
|
---|
| 407 | *
|
---|
| 408 | * @returns true if valid, false if not.
|
---|
| 409 | * @param pszName The name to validate.
|
---|
[33806] | 410 | * @sa VBoxExtPackExtractNameFromTarballPath
|
---|
[33623] | 411 | */
|
---|
| 412 | bool VBoxExtPackIsValidName(const char *pszName)
|
---|
| 413 | {
|
---|
[33806] | 414 | if (!pszName)
|
---|
[33623] | 415 | return false;
|
---|
[33806] | 416 |
|
---|
| 417 | /*
|
---|
| 418 | * Check the characters making up the name, only english alphabet
|
---|
| 419 | * characters, decimal digits and spaces are allowed.
|
---|
| 420 | */
|
---|
| 421 | size_t off = 0;
|
---|
| 422 | while (pszName[off])
|
---|
| 423 | {
|
---|
[34075] | 424 | if (!RT_C_IS_ALNUM(pszName[off]) && pszName[off] != ' ')
|
---|
[33806] | 425 | return false;
|
---|
| 426 | off++;
|
---|
| 427 | }
|
---|
| 428 |
|
---|
| 429 | /*
|
---|
| 430 | * Check min and max name limits.
|
---|
| 431 | */
|
---|
[34073] | 432 | if ( off > VBOX_EXTPACK_NAME_MAX_LEN
|
---|
[33806] | 433 | || off < VBOX_EXTPACK_NAME_MIN_LEN)
|
---|
| 434 | return false;
|
---|
| 435 |
|
---|
| 436 | return true;
|
---|
[33623] | 437 | }
|
---|
| 438 |
|
---|
| 439 | /**
|
---|
[34579] | 440 | * Checks if an alledged manged extension pack name.
|
---|
| 441 | *
|
---|
| 442 | * @returns true if valid, false if not.
|
---|
| 443 | * @param pszMangledName The mangled name to validate.
|
---|
| 444 | * @param cchMax The max number of chars to test.
|
---|
| 445 | * @sa VBoxExtPackMangleName
|
---|
| 446 | */
|
---|
| 447 | bool VBoxExtPackIsValidMangledName(const char *pszMangledName, size_t cchMax /*= RTSTR_MAX*/)
|
---|
| 448 | {
|
---|
| 449 | if (!pszMangledName)
|
---|
| 450 | return false;
|
---|
| 451 |
|
---|
| 452 | /*
|
---|
| 453 | * Check the characters making up the name, only english alphabet
|
---|
| 454 | * characters, decimal digits and underscores (=space) are allowed.
|
---|
| 455 | */
|
---|
| 456 | size_t off = 0;
|
---|
| 457 | while (off < cchMax && pszMangledName[off])
|
---|
| 458 | {
|
---|
| 459 | if (!RT_C_IS_ALNUM(pszMangledName[off]) && pszMangledName[off] != '_')
|
---|
| 460 | return false;
|
---|
| 461 | off++;
|
---|
| 462 | }
|
---|
| 463 |
|
---|
| 464 | /*
|
---|
| 465 | * Check min and max name limits.
|
---|
| 466 | */
|
---|
| 467 | if ( off > VBOX_EXTPACK_NAME_MAX_LEN
|
---|
| 468 | || off < VBOX_EXTPACK_NAME_MIN_LEN)
|
---|
| 469 | return false;
|
---|
| 470 |
|
---|
| 471 | return true;
|
---|
| 472 | }
|
---|
| 473 |
|
---|
| 474 | /**
|
---|
| 475 | * Mangle an extension pack name so it can be used by a directory or file name.
|
---|
| 476 | *
|
---|
| 477 | * @returns String containing the mangled name on success, the caller must
|
---|
| 478 | * delete it. NULL on failure.
|
---|
| 479 | * @param pszName The unmangled name.
|
---|
| 480 | * @sa VBoxExtPackUnmangleName, VBoxExtPackIsValidMangledName
|
---|
| 481 | */
|
---|
[36527] | 482 | RTCString *VBoxExtPackMangleName(const char *pszName)
|
---|
[34579] | 483 | {
|
---|
| 484 | AssertReturn(VBoxExtPackIsValidName(pszName), NULL);
|
---|
| 485 |
|
---|
| 486 | char szTmp[VBOX_EXTPACK_NAME_MAX_LEN + 1];
|
---|
| 487 | size_t off = 0;
|
---|
| 488 | char ch;
|
---|
| 489 | while ((ch = pszName[off]) != '\0')
|
---|
| 490 | {
|
---|
| 491 | if (ch == ' ')
|
---|
| 492 | ch = '_';
|
---|
| 493 | szTmp[off++] = ch;
|
---|
| 494 | }
|
---|
| 495 | szTmp[off] = '\0';
|
---|
| 496 | Assert(VBoxExtPackIsValidMangledName(szTmp));
|
---|
| 497 |
|
---|
[36527] | 498 | return new RTCString(szTmp, off);
|
---|
[34579] | 499 | }
|
---|
| 500 |
|
---|
| 501 | /**
|
---|
| 502 | * Unmangle an extension pack name (reverses VBoxExtPackMangleName).
|
---|
| 503 | *
|
---|
| 504 | * @returns String containing the mangled name on success, the caller must
|
---|
| 505 | * delete it. NULL on failure.
|
---|
| 506 | * @param pszMangledName The mangled name.
|
---|
| 507 | * @param cchMax The max name length. RTSTR_MAX is fine.
|
---|
| 508 | * @sa VBoxExtPackMangleName, VBoxExtPackIsValidMangledName
|
---|
| 509 | */
|
---|
[36527] | 510 | RTCString *VBoxExtPackUnmangleName(const char *pszMangledName, size_t cchMax)
|
---|
[34579] | 511 | {
|
---|
| 512 | AssertReturn(VBoxExtPackIsValidMangledName(pszMangledName, cchMax), NULL);
|
---|
| 513 |
|
---|
| 514 | char szTmp[VBOX_EXTPACK_NAME_MAX_LEN + 1];
|
---|
| 515 | size_t off = 0;
|
---|
| 516 | char ch;
|
---|
| 517 | while ( off < cchMax
|
---|
| 518 | && (ch = pszMangledName[off]) != '\0')
|
---|
| 519 | {
|
---|
| 520 | if (ch == '_')
|
---|
| 521 | ch = ' ';
|
---|
| 522 | else
|
---|
| 523 | AssertReturn(RT_C_IS_ALNUM(ch) || ch == ' ', NULL);
|
---|
| 524 | szTmp[off++] = ch;
|
---|
| 525 | }
|
---|
| 526 | szTmp[off] = '\0';
|
---|
| 527 | AssertReturn(VBoxExtPackIsValidName(szTmp), NULL);
|
---|
| 528 |
|
---|
[36527] | 529 | return new RTCString(szTmp, off);
|
---|
[34579] | 530 | }
|
---|
| 531 |
|
---|
| 532 | /**
|
---|
| 533 | * Constructs the extension pack directory path.
|
---|
| 534 | *
|
---|
| 535 | * A combination of RTPathJoin and VBoxExtPackMangleName.
|
---|
| 536 | *
|
---|
| 537 | * @returns IPRT status code like RTPathJoin.
|
---|
| 538 | * @param pszExtPackDir Where to return the directory path.
|
---|
| 539 | * @param cbExtPackDir The size of the return buffer.
|
---|
| 540 | * @param pszParentDir The parent directory (".../Extensions").
|
---|
| 541 | * @param pszName The extension pack name, unmangled.
|
---|
| 542 | */
|
---|
| 543 | int VBoxExtPackCalcDir(char *pszExtPackDir, size_t cbExtPackDir, const char *pszParentDir, const char *pszName)
|
---|
| 544 | {
|
---|
| 545 | AssertReturn(VBoxExtPackIsValidName(pszName), VERR_INTERNAL_ERROR_5);
|
---|
| 546 |
|
---|
[36527] | 547 | RTCString *pstrMangledName = VBoxExtPackMangleName(pszName);
|
---|
[34579] | 548 | if (!pstrMangledName)
|
---|
| 549 | return VERR_INTERNAL_ERROR_4;
|
---|
| 550 |
|
---|
| 551 | int vrc = RTPathJoin(pszExtPackDir, cbExtPackDir, pszParentDir, pstrMangledName->c_str());
|
---|
| 552 | delete pstrMangledName;
|
---|
| 553 |
|
---|
| 554 | return vrc;
|
---|
| 555 | }
|
---|
| 556 |
|
---|
| 557 |
|
---|
| 558 | /**
|
---|
[33623] | 559 | * Validates the extension pack version string.
|
---|
| 560 | *
|
---|
| 561 | * @returns true if valid, false if not.
|
---|
| 562 | * @param pszVersion The version string to validate.
|
---|
| 563 | */
|
---|
| 564 | bool VBoxExtPackIsValidVersionString(const char *pszVersion)
|
---|
| 565 | {
|
---|
| 566 | if (!pszVersion || *pszVersion == '\0')
|
---|
| 567 | return false;
|
---|
| 568 |
|
---|
| 569 | /* 1.x.y.z... */
|
---|
| 570 | for (;;)
|
---|
| 571 | {
|
---|
[34073] | 572 | if (!RT_C_IS_DIGIT(*pszVersion))
|
---|
| 573 | return false;
|
---|
| 574 | do
|
---|
[33623] | 575 | pszVersion++;
|
---|
[34073] | 576 | while (RT_C_IS_DIGIT(*pszVersion));
|
---|
[33623] | 577 | if (*pszVersion != '.')
|
---|
| 578 | break;
|
---|
[34073] | 579 | pszVersion++;
|
---|
[33623] | 580 | }
|
---|
| 581 |
|
---|
| 582 | /* upper case string + numbers indicating the build type */
|
---|
| 583 | if (*pszVersion == '-' || *pszVersion == '_')
|
---|
| 584 | {
|
---|
[39180] | 585 | /** @todo Should probably restrict this to known build types (alpha,
|
---|
| 586 | * beta, rc, ++). */
|
---|
[33623] | 587 | do
|
---|
| 588 | pszVersion++;
|
---|
| 589 | while ( RT_C_IS_DIGIT(*pszVersion)
|
---|
| 590 | || RT_C_IS_UPPER(*pszVersion)
|
---|
| 591 | || *pszVersion == '-'
|
---|
| 592 | || *pszVersion == '_');
|
---|
| 593 | }
|
---|
| 594 |
|
---|
[39180] | 595 | return *pszVersion == '\0';
|
---|
| 596 | }
|
---|
| 597 |
|
---|
| 598 | /**
|
---|
| 599 | * Validates the extension pack edition string.
|
---|
| 600 | *
|
---|
| 601 | * @returns true if valid, false if not.
|
---|
| 602 | * @param pszEdition The edition string to validate.
|
---|
| 603 | */
|
---|
| 604 | bool VBoxExtPackIsValidEditionString(const char *pszEdition)
|
---|
| 605 | {
|
---|
| 606 | if (*pszEdition)
|
---|
[33623] | 607 | {
|
---|
[39180] | 608 | if (!RT_C_IS_UPPER(*pszEdition))
|
---|
[33623] | 609 | return false;
|
---|
| 610 |
|
---|
[39172] | 611 | do
|
---|
[39180] | 612 | pszEdition++;
|
---|
| 613 | while ( RT_C_IS_UPPER(*pszEdition)
|
---|
| 614 | || RT_C_IS_DIGIT(*pszEdition)
|
---|
| 615 | || *pszEdition == '-'
|
---|
| 616 | || *pszEdition == '_');
|
---|
[39172] | 617 | }
|
---|
[39180] | 618 | return *pszEdition == '\0';
|
---|
[33623] | 619 | }
|
---|
| 620 |
|
---|
| 621 | /**
|
---|
[34244] | 622 | * Validates an extension pack module string.
|
---|
[33623] | 623 | *
|
---|
| 624 | * @returns true if valid, false if not.
|
---|
[34244] | 625 | * @param pszModule The module string to validate.
|
---|
[33623] | 626 | */
|
---|
[34244] | 627 | bool VBoxExtPackIsValidModuleString(const char *pszModule)
|
---|
[33623] | 628 | {
|
---|
[34244] | 629 | if (!pszModule || *pszModule == '\0')
|
---|
[33623] | 630 | return false;
|
---|
| 631 |
|
---|
| 632 | /* Restricted charset, no extensions (dots). */
|
---|
[34244] | 633 | while ( RT_C_IS_ALNUM(*pszModule)
|
---|
| 634 | || *pszModule == '-'
|
---|
| 635 | || *pszModule == '_')
|
---|
| 636 | pszModule++;
|
---|
[33623] | 637 |
|
---|
[34244] | 638 | return *pszModule == '\0';
|
---|
[33623] | 639 | }
|
---|
| 640 |
|
---|
[34787] | 641 | /**
|
---|
| 642 | * RTStrPrintfv wrapper.
|
---|
| 643 | *
|
---|
| 644 | * @returns @a rc
|
---|
| 645 | * @param rc The status code to return.
|
---|
| 646 | * @param pszError The error buffer.
|
---|
| 647 | * @param cbError The size of the buffer.
|
---|
| 648 | * @param pszFormat The error message format string.
|
---|
| 649 | * @param ... Format arguments.
|
---|
| 650 | */
|
---|
| 651 | static int vboxExtPackReturnError(int rc, char *pszError, size_t cbError, const char *pszFormat, ...)
|
---|
| 652 | {
|
---|
| 653 | va_list va;
|
---|
| 654 | va_start(va, pszFormat);
|
---|
| 655 | RTStrPrintfV(pszError, cbError, pszFormat, va);
|
---|
| 656 | va_end(va);
|
---|
| 657 | return rc;
|
---|
| 658 | }
|
---|
| 659 |
|
---|
| 660 | /**
|
---|
| 661 | * RTStrPrintfv wrapper.
|
---|
| 662 | *
|
---|
| 663 | * @param pszError The error buffer.
|
---|
| 664 | * @param cbError The size of the buffer.
|
---|
| 665 | * @param pszFormat The error message format string.
|
---|
| 666 | * @param ... Format arguments.
|
---|
| 667 | */
|
---|
| 668 | static void vboxExtPackSetError(char *pszError, size_t cbError, const char *pszFormat, ...)
|
---|
| 669 | {
|
---|
| 670 | va_list va;
|
---|
| 671 | va_start(va, pszFormat);
|
---|
| 672 | RTStrPrintfV(pszError, cbError, pszFormat, va);
|
---|
| 673 | va_end(va);
|
---|
| 674 | }
|
---|
| 675 |
|
---|
| 676 | /**
|
---|
| 677 | * Verifies the manifest and its signature.
|
---|
| 678 | *
|
---|
| 679 | * @returns VBox status code, failures with message.
|
---|
| 680 | * @param hManifestFile The xml from the extension pack.
|
---|
| 681 | * @param pszExtPackName The expected extension pack name. This can be
|
---|
| 682 | * NULL, in which we don't have any expectations.
|
---|
| 683 | * @param pszError Where to store an error message on failure.
|
---|
| 684 | * @param cbError The size of the buffer @a pszError points to.
|
---|
| 685 | */
|
---|
| 686 | static int vboxExtPackVerifyXml(RTVFSFILE hXmlFile, const char *pszExtPackName, char *pszError, size_t cbError)
|
---|
| 687 | {
|
---|
| 688 | /*
|
---|
| 689 | * Load the XML.
|
---|
| 690 | */
|
---|
| 691 | VBOXEXTPACKDESC ExtPackDesc;
|
---|
[36527] | 692 | RTCString *pstrErr = VBoxExtPackLoadDescFromVfsFile(hXmlFile, &ExtPackDesc, NULL);
|
---|
[34787] | 693 | if (pstrErr)
|
---|
| 694 | {
|
---|
| 695 | RTStrCopy(pszError, cbError, pstrErr->c_str());
|
---|
| 696 | delete pstrErr;
|
---|
| 697 | return VERR_PARSE_ERROR;
|
---|
| 698 | }
|
---|
| 699 |
|
---|
| 700 | /*
|
---|
| 701 | * Check the name.
|
---|
| 702 | */
|
---|
| 703 | /** @todo drop this restriction after the old install interface is
|
---|
| 704 | * dropped. */
|
---|
| 705 | int rc = VINF_SUCCESS;
|
---|
| 706 | if ( pszExtPackName
|
---|
| 707 | && !ExtPackDesc.strName.equalsIgnoreCase(pszExtPackName))
|
---|
| 708 | rc = vboxExtPackReturnError(VERR_NOT_EQUAL, pszError, cbError,
|
---|
| 709 | "The name of the downloaded file and the name stored inside the extension pack does not match"
|
---|
| 710 | " (xml='%s' file='%s')", ExtPackDesc.strName.c_str(), pszExtPackName);
|
---|
| 711 | return rc;
|
---|
| 712 | }
|
---|
| 713 |
|
---|
| 714 | /**
|
---|
| 715 | * Verifies the manifest and its signature.
|
---|
| 716 | *
|
---|
| 717 | * @returns VBox status code, failures with message.
|
---|
| 718 | * @param hOurManifest The manifest we compiled.
|
---|
| 719 | * @param hManifestFile The manifest file in the extension pack.
|
---|
| 720 | * @param hSignatureFile The manifest signature file.
|
---|
| 721 | * @param pszError Where to store an error message on failure.
|
---|
| 722 | * @param cbError The size of the buffer @a pszError points to.
|
---|
| 723 | */
|
---|
| 724 | static int vboxExtPackVerifyManifestAndSignature(RTMANIFEST hOurManifest, RTVFSFILE hManifestFile, RTVFSFILE hSignatureFile,
|
---|
| 725 | char *pszError, size_t cbError)
|
---|
| 726 | {
|
---|
| 727 | /*
|
---|
| 728 | * Read the manifest from the extension pack.
|
---|
| 729 | */
|
---|
| 730 | int rc = RTVfsFileSeek(hManifestFile, 0, RTFILE_SEEK_BEGIN, NULL);
|
---|
| 731 | if (RT_FAILURE(rc))
|
---|
| 732 | return vboxExtPackReturnError(rc, pszError, cbError, "RTVfsFileSeek failed: %Rrc", rc);
|
---|
| 733 |
|
---|
| 734 | RTMANIFEST hTheirManifest;
|
---|
| 735 | rc = RTManifestCreate(0 /*fFlags*/, &hTheirManifest);
|
---|
| 736 | if (RT_FAILURE(rc))
|
---|
| 737 | return vboxExtPackReturnError(rc, pszError, cbError, "RTManifestCreate failed: %Rrc", rc);
|
---|
| 738 |
|
---|
| 739 | RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(hManifestFile);
|
---|
| 740 | rc = RTManifestReadStandard(hTheirManifest, hVfsIos);
|
---|
| 741 | RTVfsIoStrmRelease(hVfsIos);
|
---|
| 742 | if (RT_SUCCESS(rc))
|
---|
| 743 | {
|
---|
| 744 | /*
|
---|
| 745 | * Compare the manifests.
|
---|
| 746 | */
|
---|
| 747 | static const char *s_apszIgnoreEntries[] =
|
---|
| 748 | {
|
---|
| 749 | VBOX_EXTPACK_MANIFEST_NAME,
|
---|
| 750 | VBOX_EXTPACK_SIGNATURE_NAME,
|
---|
| 751 | "./" VBOX_EXTPACK_MANIFEST_NAME,
|
---|
| 752 | "./" VBOX_EXTPACK_SIGNATURE_NAME,
|
---|
| 753 | NULL
|
---|
| 754 | };
|
---|
| 755 | char szError[RTPATH_MAX];
|
---|
| 756 | rc = RTManifestEqualsEx(hOurManifest, hTheirManifest, &s_apszIgnoreEntries[0], NULL,
|
---|
| 757 | RTMANIFEST_EQUALS_IGN_MISSING_ATTRS /*fFlags*/,
|
---|
| 758 | szError, sizeof(szError));
|
---|
| 759 | if (RT_SUCCESS(rc))
|
---|
| 760 | {
|
---|
| 761 | /*
|
---|
| 762 | * Validate the manifest file signature.
|
---|
| 763 | */
|
---|
| 764 | /** @todo implement signature stuff */
|
---|
| 765 | NOREF(hSignatureFile);
|
---|
| 766 |
|
---|
| 767 | }
|
---|
| 768 | else if (rc == VERR_NOT_EQUAL && szError[0])
|
---|
| 769 | vboxExtPackSetError(pszError, cbError, "Manifest mismatch: %s", szError);
|
---|
| 770 | else
|
---|
| 771 | vboxExtPackSetError(pszError, cbError, "RTManifestEqualsEx failed: %Rrc", rc);
|
---|
| 772 | #if 0
|
---|
| 773 | RTVFSIOSTREAM hVfsIosStdOut = NIL_RTVFSIOSTREAM;
|
---|
| 774 | RTVfsIoStrmFromStdHandle(RTHANDLESTD_OUTPUT, RTFILE_O_WRITE, true, &hVfsIosStdOut);
|
---|
| 775 | RTVfsIoStrmWrite(hVfsIosStdOut, "Our:\n", sizeof("Our:\n") - 1, true, NULL);
|
---|
| 776 | RTManifestWriteStandard(hOurManifest, hVfsIosStdOut);
|
---|
| 777 | RTVfsIoStrmWrite(hVfsIosStdOut, "Their:\n", sizeof("Their:\n") - 1, true, NULL);
|
---|
| 778 | RTManifestWriteStandard(hTheirManifest, hVfsIosStdOut);
|
---|
| 779 | #endif
|
---|
| 780 | }
|
---|
| 781 | else
|
---|
| 782 | vboxExtPackSetError(pszError, cbError, "Error parsing '%s': %Rrc", VBOX_EXTPACK_MANIFEST_NAME, rc);
|
---|
| 783 |
|
---|
| 784 | RTManifestRelease(hTheirManifest);
|
---|
| 785 | return rc;
|
---|
| 786 | }
|
---|
| 787 |
|
---|
| 788 |
|
---|
| 789 | /**
|
---|
[39878] | 790 | * Verifies the file digest (if specified) and returns the SHA-256 of the file.
|
---|
| 791 | *
|
---|
| 792 | * @returns
|
---|
| 793 | * @param hFileManifest Manifest containing a SHA-256 digest of the file
|
---|
| 794 | * that was calculated as the file was processed.
|
---|
| 795 | * @param pszFileDigest SHA-256 digest of the file.
|
---|
| 796 | * @param pStrDigest Where to return the SHA-256 digest. Optional.
|
---|
| 797 | * @param pszError Where to write an error message on failure.
|
---|
| 798 | * @param cbError The size of the @a pszError buffer.
|
---|
| 799 | */
|
---|
| 800 | static int vboxExtPackVerifyFileDigest(RTMANIFEST hFileManifest, const char *pszFileDigest,
|
---|
| 801 | RTCString *pStrDigest, char *pszError, size_t cbError)
|
---|
| 802 | {
|
---|
| 803 | /*
|
---|
| 804 | * Extract the SHA-256 entry for the extpack file.
|
---|
| 805 | */
|
---|
| 806 | char szCalculatedDigest[RTSHA256_DIGEST_LEN + 1];
|
---|
| 807 | int rc = RTManifestEntryQueryAttr(hFileManifest, "extpack", NULL /*no name*/, RTMANIFEST_ATTR_SHA256,
|
---|
| 808 | szCalculatedDigest, sizeof(szCalculatedDigest), NULL);
|
---|
| 809 | if (RT_SUCCESS(rc))
|
---|
| 810 | {
|
---|
| 811 | /*
|
---|
| 812 | * Convert the two strings to binary form before comparing.
|
---|
| 813 | * We convert the calculated hash even if we don't have anything to
|
---|
| 814 | * compare with, just to validate it.
|
---|
| 815 | */
|
---|
| 816 | uint8_t abCalculatedHash[RTSHA256_HASH_SIZE];
|
---|
| 817 | rc = RTSha256FromString(szCalculatedDigest, abCalculatedHash);
|
---|
| 818 | if (RT_SUCCESS(rc))
|
---|
| 819 | {
|
---|
| 820 | if ( pszFileDigest
|
---|
| 821 | && *pszFileDigest != '\0')
|
---|
| 822 | {
|
---|
| 823 | uint8_t abFileHash[RTSHA256_HASH_SIZE];
|
---|
| 824 | rc = RTSha256FromString(pszFileDigest, abFileHash);
|
---|
| 825 | if (RT_SUCCESS(rc))
|
---|
| 826 | {
|
---|
| 827 | if (memcmp(abFileHash, abCalculatedHash, sizeof(abFileHash)))
|
---|
| 828 | {
|
---|
| 829 | vboxExtPackSetError(pszError, cbError, "The extension pack file has changed (SHA-256 mismatch)");
|
---|
| 830 | rc = VERR_NOT_EQUAL;
|
---|
| 831 | }
|
---|
| 832 | }
|
---|
| 833 | else
|
---|
| 834 | vboxExtPackSetError(pszError, cbError, "Bad SHA-256 '%s': %Rrc", szCalculatedDigest, rc);
|
---|
| 835 | }
|
---|
| 836 |
|
---|
| 837 | /*
|
---|
| 838 | * Set the output hash on success.
|
---|
| 839 | */
|
---|
| 840 | if (pStrDigest && RT_SUCCESS(rc))
|
---|
| 841 | {
|
---|
| 842 | try
|
---|
| 843 | {
|
---|
| 844 | *pStrDigest = szCalculatedDigest;
|
---|
| 845 | }
|
---|
| 846 | catch (std::bad_alloc)
|
---|
| 847 | {
|
---|
| 848 | rc = VERR_NO_MEMORY;
|
---|
| 849 | }
|
---|
| 850 | }
|
---|
| 851 | }
|
---|
| 852 | else
|
---|
| 853 | vboxExtPackSetError(pszError, cbError, "Bad SHA-256 '%s': %Rrc", szCalculatedDigest, rc);
|
---|
| 854 | }
|
---|
| 855 | else
|
---|
| 856 | vboxExtPackSetError(pszError, cbError, "RTManifestEntryGetAttr: %Rrc", rc);
|
---|
| 857 | return rc;
|
---|
| 858 | }
|
---|
| 859 |
|
---|
| 860 |
|
---|
| 861 |
|
---|
| 862 | /**
|
---|
[34965] | 863 | * Validates a standard file.
|
---|
| 864 | *
|
---|
| 865 | * Generally all files are
|
---|
| 866 | *
|
---|
| 867 | * @returns VBox status code, failure message in @a pszError.
|
---|
| 868 | * @param pszAdjName The adjusted member name.
|
---|
| 869 | * @param enmType The VFS object type.
|
---|
| 870 | * @param phVfsObj The pointer to the VFS object handle variable.
|
---|
| 871 | * This is both input and output.
|
---|
| 872 | * @param phVfsFile Where to store the handle to the memorized
|
---|
| 873 | * file. This is NULL for license files.
|
---|
| 874 | * @param pszError Where to write an error message on failure.
|
---|
| 875 | * @param cbError The size of the @a pszError buffer.
|
---|
| 876 | */
|
---|
| 877 | static int VBoxExtPackValidateStandardFile(const char *pszAdjName, RTVFSOBJTYPE enmType,
|
---|
| 878 | PRTVFSOBJ phVfsObj, PRTVFSFILE phVfsFile, char *pszError, size_t cbError)
|
---|
| 879 | {
|
---|
| 880 | int rc;
|
---|
| 881 |
|
---|
| 882 | /*
|
---|
| 883 | * Make sure it's a file and that it isn't too large.
|
---|
| 884 | */
|
---|
| 885 | if (phVfsFile && *phVfsFile != NIL_RTVFSFILE)
|
---|
| 886 | rc = vboxExtPackReturnError(VERR_DUPLICATE, pszError, cbError,
|
---|
| 887 | "There can only be one '%s'", pszAdjName);
|
---|
| 888 | else if (enmType != RTVFSOBJTYPE_IO_STREAM && enmType != RTVFSOBJTYPE_FILE)
|
---|
| 889 | rc = vboxExtPackReturnError(VERR_NOT_A_FILE, pszError, cbError,
|
---|
| 890 | "Standard member '%s' is not a file", pszAdjName);
|
---|
| 891 | else
|
---|
| 892 | {
|
---|
| 893 | RTFSOBJINFO ObjInfo;
|
---|
| 894 | rc = RTVfsObjQueryInfo(*phVfsObj, &ObjInfo, RTFSOBJATTRADD_NOTHING);
|
---|
| 895 | if (RT_SUCCESS(rc))
|
---|
| 896 | {
|
---|
| 897 | if (!RTFS_IS_FILE(ObjInfo.Attr.fMode))
|
---|
| 898 | rc = vboxExtPackReturnError(VERR_NOT_A_FILE, pszError, cbError,
|
---|
| 899 | "Standard member '%s' is not a file", pszAdjName);
|
---|
| 900 | else if (ObjInfo.cbObject >= _1M)
|
---|
| 901 | rc = vboxExtPackReturnError(VERR_OUT_OF_RANGE, pszError, cbError,
|
---|
| 902 | "Standard member '%s' is too large: %'RU64 bytes (max 1 MB)",
|
---|
| 903 | pszAdjName, (uint64_t)ObjInfo.cbObject);
|
---|
| 904 | else
|
---|
| 905 | {
|
---|
| 906 | /*
|
---|
| 907 | * Make an in memory copy of the stream and check that the file
|
---|
| 908 | * is UTF-8 clean.
|
---|
| 909 | */
|
---|
| 910 | RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(*phVfsObj);
|
---|
| 911 | RTVFSFILE hVfsFile;
|
---|
| 912 | rc = RTVfsMemorizeIoStreamAsFile(hVfsIos, RTFILE_O_READ, &hVfsFile);
|
---|
| 913 | if (RT_SUCCESS(rc))
|
---|
| 914 | {
|
---|
[34967] | 915 | rc = RTVfsIoStrmValidateUtf8Encoding(hVfsIos,
|
---|
| 916 | RTVFS_VALIDATE_UTF8_BY_RTC_3629 | RTVFS_VALIDATE_UTF8_NO_NULL,
|
---|
| 917 | NULL);
|
---|
[34965] | 918 | if (RT_SUCCESS(rc))
|
---|
| 919 | {
|
---|
| 920 | /*
|
---|
| 921 | * Replace *phVfsObj with the memorized file.
|
---|
| 922 | */
|
---|
| 923 | rc = RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL);
|
---|
| 924 | if (RT_SUCCESS(rc))
|
---|
| 925 | {
|
---|
| 926 | RTVfsObjRelease(*phVfsObj);
|
---|
| 927 | *phVfsObj = RTVfsObjFromFile(hVfsFile);
|
---|
| 928 | }
|
---|
| 929 | else
|
---|
| 930 | vboxExtPackSetError(pszError, cbError, "RTVfsFileSeek failed on '%s': %Rrc", pszAdjName, rc);
|
---|
| 931 | }
|
---|
| 932 |
|
---|
| 933 | if (phVfsFile && RT_SUCCESS(rc))
|
---|
| 934 | *phVfsFile = hVfsFile;
|
---|
| 935 | else
|
---|
| 936 | RTVfsFileRelease(hVfsFile);
|
---|
| 937 | }
|
---|
| 938 | else
|
---|
| 939 | vboxExtPackSetError(pszError, cbError, "RTVfsMemorizeIoStreamAsFile failed on '%s': %Rrc", pszAdjName, rc);
|
---|
| 940 | RTVfsIoStrmRelease(hVfsIos);
|
---|
| 941 | }
|
---|
| 942 | }
|
---|
| 943 | else
|
---|
| 944 | vboxExtPackSetError(pszError, cbError, "RTVfsObjQueryInfo failed on '%s': %Rrc", pszAdjName, rc);
|
---|
| 945 | }
|
---|
| 946 | return rc;
|
---|
| 947 | }
|
---|
| 948 |
|
---|
| 949 |
|
---|
| 950 | /**
|
---|
[34787] | 951 | * Validates a name in an extension pack.
|
---|
| 952 | *
|
---|
| 953 | * We restrict the charset to try make sure the extension pack can be unpacked
|
---|
| 954 | * on all file systems.
|
---|
| 955 | *
|
---|
| 956 | * @returns VBox status code, failures with message.
|
---|
| 957 | * @param pszName The name to validate.
|
---|
| 958 | * @param pszError Where to store an error message on failure.
|
---|
| 959 | * @param cbError The size of the buffer @a pszError points to.
|
---|
| 960 | */
|
---|
| 961 | static int vboxExtPackValidateMemberName(const char *pszName, char *pszError, size_t cbError)
|
---|
| 962 | {
|
---|
| 963 | if (RTPathStartsWithRoot(pszName))
|
---|
| 964 | return vboxExtPackReturnError(VERR_PATH_IS_NOT_RELATIVE, pszError, cbError, "'%s': starts with root spec", pszName);
|
---|
| 965 |
|
---|
| 966 | const char *pszErr = NULL;
|
---|
| 967 | const char *psz = pszName;
|
---|
| 968 | int ch;
|
---|
| 969 | while ((ch = *psz) != '\0')
|
---|
| 970 | {
|
---|
| 971 | /* Character set restrictions. */
|
---|
| 972 | if (ch < 0 || ch >= 128)
|
---|
| 973 | {
|
---|
| 974 | pszErr = "Only 7-bit ASCII allowed";
|
---|
| 975 | break;
|
---|
| 976 | }
|
---|
| 977 | if (ch <= 31 || ch == 127)
|
---|
| 978 | {
|
---|
| 979 | pszErr = "No control characters are not allowed";
|
---|
| 980 | break;
|
---|
| 981 | }
|
---|
| 982 | if (ch == '\\')
|
---|
| 983 | {
|
---|
| 984 | pszErr = "Only backward slashes are not allowed";
|
---|
| 985 | break;
|
---|
| 986 | }
|
---|
| 987 | if (strchr("'\":;*?|[]<>(){}", ch))
|
---|
| 988 | {
|
---|
| 989 | pszErr = "The characters ', \", :, ;, *, ?, |, [, ], <, >, (, ), { and } are not allowed";
|
---|
| 990 | break;
|
---|
| 991 | }
|
---|
| 992 |
|
---|
| 993 | /* Take the simple way out and ban all ".." sequences. */
|
---|
| 994 | if ( ch == '.'
|
---|
| 995 | && psz[1] == '.')
|
---|
| 996 | {
|
---|
| 997 | pszErr = "Double dot sequence are not allowed";
|
---|
| 998 | break;
|
---|
| 999 | }
|
---|
| 1000 |
|
---|
| 1001 | /* Keep the tree shallow or the hardening checks will fail. */
|
---|
| 1002 | if (psz - pszName > VBOX_EXTPACK_MAX_MEMBER_NAME_LENGTH)
|
---|
| 1003 | {
|
---|
| 1004 | pszErr = "Too long";
|
---|
| 1005 | break;
|
---|
| 1006 | }
|
---|
| 1007 |
|
---|
| 1008 | /* advance */
|
---|
| 1009 | psz++;
|
---|
| 1010 | }
|
---|
| 1011 |
|
---|
| 1012 | if (pszErr)
|
---|
| 1013 | return vboxExtPackReturnError(VERR_INVALID_NAME, pszError, cbError,
|
---|
| 1014 | "Bad member name '%s' (pos %zu): %s", pszName, (size_t)(psz - pszName), pszErr);
|
---|
| 1015 | return RTEXITCODE_SUCCESS;
|
---|
| 1016 | }
|
---|
| 1017 |
|
---|
| 1018 |
|
---|
| 1019 | /**
|
---|
| 1020 | * Validates a file in an extension pack.
|
---|
| 1021 | *
|
---|
| 1022 | * @returns VBox status code, failures with message.
|
---|
| 1023 | * @param pszName The name of the file.
|
---|
| 1024 | * @param hVfsObj The VFS object.
|
---|
| 1025 | * @param pszError Where to store an error message on failure.
|
---|
| 1026 | * @param cbError The size of the buffer @a pszError points to.
|
---|
| 1027 | */
|
---|
| 1028 | static int vboxExtPackValidateMemberFile(const char *pszName, RTVFSOBJ hVfsObj, char *pszError, size_t cbError)
|
---|
| 1029 | {
|
---|
| 1030 | int rc = vboxExtPackValidateMemberName(pszName, pszError, cbError);
|
---|
| 1031 | if (RT_SUCCESS(rc))
|
---|
| 1032 | {
|
---|
| 1033 | RTFSOBJINFO ObjInfo;
|
---|
| 1034 | rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_NOTHING);
|
---|
| 1035 | if (RT_SUCCESS(rc))
|
---|
| 1036 | {
|
---|
| 1037 | if (ObjInfo.cbObject >= 9*_1G64)
|
---|
| 1038 | rc = vboxExtPackReturnError(VERR_OUT_OF_RANGE, pszError, cbError,
|
---|
| 1039 | "'%s': too large (%'RU64 bytes)",
|
---|
| 1040 | pszName, (uint64_t)ObjInfo.cbObject);
|
---|
| 1041 | if (!RTFS_IS_FILE(ObjInfo.Attr.fMode))
|
---|
| 1042 | rc = vboxExtPackReturnError(VERR_NOT_A_FILE, pszError, cbError,
|
---|
| 1043 | "The alleged file '%s' has a mode mask stating otherwise (%RTfmode)",
|
---|
| 1044 | pszName, ObjInfo.Attr.fMode);
|
---|
| 1045 | }
|
---|
| 1046 | else
|
---|
| 1047 | vboxExtPackSetError(pszError, cbError, "RTVfsObjQueryInfo failed on '%s': %Rrc", pszName, rc);
|
---|
| 1048 | }
|
---|
| 1049 | return rc;
|
---|
| 1050 | }
|
---|
| 1051 |
|
---|
| 1052 |
|
---|
| 1053 | /**
|
---|
| 1054 | * Validates a directory in an extension pack.
|
---|
| 1055 | *
|
---|
| 1056 | * @returns VBox status code, failures with message.
|
---|
| 1057 | * @param pszName The name of the directory.
|
---|
| 1058 | * @param hVfsObj The VFS object.
|
---|
| 1059 | * @param pszError Where to store an error message on failure.
|
---|
| 1060 | * @param cbError The size of the buffer @a pszError points to.
|
---|
| 1061 | */
|
---|
| 1062 | static int vboxExtPackValidateMemberDir(const char *pszName, RTVFSOBJ hVfsObj, char *pszError, size_t cbError)
|
---|
| 1063 | {
|
---|
| 1064 | int rc = vboxExtPackValidateMemberName(pszName, pszError, cbError);
|
---|
| 1065 | if (RT_SUCCESS(rc))
|
---|
| 1066 | {
|
---|
| 1067 | RTFSOBJINFO ObjInfo;
|
---|
| 1068 | rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_NOTHING);
|
---|
| 1069 | if (RT_SUCCESS(rc))
|
---|
| 1070 | {
|
---|
| 1071 | if (!RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
|
---|
| 1072 | rc = vboxExtPackReturnError(VERR_NOT_A_DIRECTORY, pszError, cbError,
|
---|
| 1073 | "The alleged directory '%s' has a mode mask saying differently (%RTfmode)",
|
---|
| 1074 | pszName, ObjInfo.Attr.fMode);
|
---|
| 1075 | }
|
---|
| 1076 | else
|
---|
| 1077 | vboxExtPackSetError(pszError, cbError, "RTVfsObjQueryInfo failed on '%s': %Rrc", pszName, rc);
|
---|
| 1078 | }
|
---|
| 1079 | return rc;
|
---|
| 1080 | }
|
---|
| 1081 |
|
---|
| 1082 | /**
|
---|
| 1083 | * Validates a member of an extension pack.
|
---|
| 1084 | *
|
---|
| 1085 | * @returns VBox status code, failures with message.
|
---|
| 1086 | * @param pszName The name of the directory.
|
---|
| 1087 | * @param enmType The object type.
|
---|
| 1088 | * @param hVfsObj The VFS object.
|
---|
| 1089 | * @param pszError Where to store an error message on failure.
|
---|
| 1090 | * @param cbError The size of the buffer @a pszError points to.
|
---|
| 1091 | */
|
---|
| 1092 | int VBoxExtPackValidateMember(const char *pszName, RTVFSOBJTYPE enmType, RTVFSOBJ hVfsObj, char *pszError, size_t cbError)
|
---|
| 1093 | {
|
---|
| 1094 | Assert(cbError > 0);
|
---|
| 1095 | *pszError = '\0';
|
---|
| 1096 |
|
---|
| 1097 | int rc;
|
---|
| 1098 | if ( enmType == RTVFSOBJTYPE_FILE
|
---|
| 1099 | || enmType == RTVFSOBJTYPE_IO_STREAM)
|
---|
| 1100 | rc = vboxExtPackValidateMemberFile(pszName, hVfsObj, pszError, cbError);
|
---|
| 1101 | else if ( enmType == RTVFSOBJTYPE_DIR
|
---|
| 1102 | || enmType == RTVFSOBJTYPE_BASE)
|
---|
| 1103 | rc = vboxExtPackValidateMemberDir(pszName, hVfsObj, pszError, cbError);
|
---|
| 1104 | else
|
---|
| 1105 | rc = vboxExtPackReturnError(VERR_UNEXPECTED_FS_OBJ_TYPE, pszError, cbError,
|
---|
| 1106 | "'%s' is not a file or directory (enmType=%d)", pszName, enmType);
|
---|
| 1107 | return rc;
|
---|
| 1108 | }
|
---|
| 1109 |
|
---|
| 1110 |
|
---|
| 1111 | /**
|
---|
| 1112 | * Rewinds the tarball file handle and creates a gunzip | tar chain that
|
---|
| 1113 | * results in a filesystem stream.
|
---|
| 1114 | *
|
---|
| 1115 | * @returns VBox status code, failures with message.
|
---|
| 1116 | * @param hTarballFile The handle to the tarball file.
|
---|
| 1117 | * @param pszError Where to store an error message on failure.
|
---|
| 1118 | * @param cbError The size of the buffer @a pszError points to.
|
---|
| 1119 | * @param phTarFss Where to return the filesystem stream handle.
|
---|
[39878] | 1120 | * @param phFileManifest Where to return a manifest where the tarball is
|
---|
| 1121 | * gettting hashed. The entry will be called
|
---|
| 1122 | * "extpack" and be ready when the file system
|
---|
| 1123 | * stream is at an end. Optional.
|
---|
[34787] | 1124 | */
|
---|
[39878] | 1125 | int VBoxExtPackOpenTarFss(RTFILE hTarballFile, char *pszError, size_t cbError, PRTVFSFSSTREAM phTarFss, PRTMANIFEST phFileManifest)
|
---|
[34787] | 1126 | {
|
---|
| 1127 | Assert(cbError > 0);
|
---|
| 1128 | *pszError = '\0';
|
---|
| 1129 | *phTarFss = NIL_RTVFSFSSTREAM;
|
---|
| 1130 |
|
---|
| 1131 | /*
|
---|
| 1132 | * Rewind the file and set up a VFS chain for it.
|
---|
| 1133 | */
|
---|
| 1134 | int rc = RTFileSeek(hTarballFile, 0, RTFILE_SEEK_BEGIN, NULL);
|
---|
| 1135 | if (RT_FAILURE(rc))
|
---|
| 1136 | return vboxExtPackReturnError(rc, pszError, cbError, "Failed seeking to the start of the tarball: %Rrc", rc);
|
---|
| 1137 |
|
---|
| 1138 | RTVFSIOSTREAM hTarballIos;
|
---|
| 1139 | rc = RTVfsIoStrmFromRTFile(hTarballFile, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN, true /*fLeaveOpen*/,
|
---|
| 1140 | &hTarballIos);
|
---|
| 1141 | if (RT_FAILURE(rc))
|
---|
| 1142 | return vboxExtPackReturnError(rc, pszError, cbError, "RTVfsIoStrmFromRTFile failed: %Rrc", rc);
|
---|
| 1143 |
|
---|
[39878] | 1144 | RTMANIFEST hFileManifest = NIL_RTMANIFEST;
|
---|
| 1145 | rc = RTManifestCreate(0 /*fFlags*/, &hFileManifest);
|
---|
[34787] | 1146 | if (RT_SUCCESS(rc))
|
---|
| 1147 | {
|
---|
[39878] | 1148 | RTVFSIOSTREAM hPtIos;
|
---|
| 1149 | rc = RTManifestEntryAddPassthruIoStream(hFileManifest, hTarballIos, "extpack", RTMANIFEST_ATTR_SHA256, true /*read*/, &hPtIos);
|
---|
[34787] | 1150 | if (RT_SUCCESS(rc))
|
---|
| 1151 | {
|
---|
[39878] | 1152 | RTVFSIOSTREAM hGunzipIos;
|
---|
| 1153 | rc = RTZipGzipDecompressIoStream(hPtIos, 0 /*fFlags*/, &hGunzipIos);
|
---|
| 1154 | if (RT_SUCCESS(rc))
|
---|
| 1155 | {
|
---|
| 1156 | RTVFSFSSTREAM hTarFss;
|
---|
| 1157 | rc = RTZipTarFsStreamFromIoStream(hGunzipIos, 0 /*fFlags*/, &hTarFss);
|
---|
| 1158 | if (RT_SUCCESS(rc))
|
---|
| 1159 | {
|
---|
| 1160 | RTVfsIoStrmRelease(hPtIos);
|
---|
| 1161 | RTVfsIoStrmRelease(hGunzipIos);
|
---|
| 1162 | RTVfsIoStrmRelease(hTarballIos);
|
---|
| 1163 | *phTarFss = hTarFss;
|
---|
| 1164 | if (phFileManifest)
|
---|
| 1165 | *phFileManifest = hFileManifest;
|
---|
| 1166 | else
|
---|
| 1167 | RTManifestRelease(hFileManifest);
|
---|
| 1168 | return VINF_SUCCESS;
|
---|
| 1169 | }
|
---|
| 1170 |
|
---|
| 1171 | vboxExtPackSetError(pszError, cbError, "RTZipTarFsStreamFromIoStream failed: %Rrc", rc);
|
---|
| 1172 | RTVfsIoStrmRelease(hGunzipIos);
|
---|
| 1173 | }
|
---|
| 1174 | else
|
---|
| 1175 | vboxExtPackSetError(pszError, cbError, "RTZipGzipDecompressIoStream failed: %Rrc", rc);
|
---|
| 1176 | RTVfsIoStrmRelease(hPtIos);
|
---|
[34787] | 1177 | }
|
---|
[39878] | 1178 | else
|
---|
| 1179 | vboxExtPackSetError(pszError, cbError, "RTManifestEntryAddPassthruIoStream failed: %Rrc", rc);
|
---|
| 1180 | RTManifestRelease(hFileManifest);
|
---|
[34787] | 1181 | }
|
---|
| 1182 | else
|
---|
[39878] | 1183 | vboxExtPackSetError(pszError, cbError, "RTManifestCreate failed: %Rrc", rc);
|
---|
| 1184 |
|
---|
[34787] | 1185 | RTVfsIoStrmRelease(hTarballIos);
|
---|
| 1186 | return rc;
|
---|
| 1187 | }
|
---|
| 1188 |
|
---|
| 1189 |
|
---|
| 1190 | /**
|
---|
| 1191 | * Validates the extension pack tarball prior to unpacking.
|
---|
| 1192 | *
|
---|
| 1193 | * Operations performed:
|
---|
| 1194 | * - Mandatory files.
|
---|
| 1195 | * - Manifest check.
|
---|
| 1196 | * - Manifest seal check.
|
---|
| 1197 | * - XML check, match name.
|
---|
| 1198 | *
|
---|
| 1199 | * @returns VBox status code, failures with message.
|
---|
| 1200 | * @param hTarballFile The handle to open the @a pszTarball file.
|
---|
| 1201 | * @param pszExtPackName The name of the extension pack name. NULL if
|
---|
| 1202 | * the name is not fixed.
|
---|
| 1203 | * @param pszTarball The name of the tarball in case we have to
|
---|
| 1204 | * complain about something.
|
---|
[39878] | 1205 | * @param pszTarballDigest The SHA-256 digest of the tarball. Empty string
|
---|
| 1206 | * if no digest available.
|
---|
[34787] | 1207 | * @param pszError Where to store an error message on failure.
|
---|
| 1208 | * @param cbError The size of the buffer @a pszError points to.
|
---|
| 1209 | * @param phValidManifest Where to optionally return the handle to fully
|
---|
| 1210 | * validated the manifest for the extension pack.
|
---|
| 1211 | * This includes all files.
|
---|
| 1212 | * @param phXmlFile Where to optionally return the memorized XML
|
---|
| 1213 | * file.
|
---|
[39878] | 1214 | * @param pStrDigest Where to return the digest of the file.
|
---|
| 1215 | * Optional.
|
---|
[34787] | 1216 | */
|
---|
[39878] | 1217 | int VBoxExtPackValidateTarball(RTFILE hTarballFile, const char *pszExtPackName,
|
---|
| 1218 | const char *pszTarball, const char *pszTarballDigest,
|
---|
| 1219 | char *pszError, size_t cbError,
|
---|
| 1220 | PRTMANIFEST phValidManifest, PRTVFSFILE phXmlFile, RTCString *pStrDigest)
|
---|
[34787] | 1221 | {
|
---|
| 1222 | /*
|
---|
| 1223 | * Clear return values.
|
---|
| 1224 | */
|
---|
| 1225 | if (phValidManifest)
|
---|
| 1226 | *phValidManifest = NIL_RTMANIFEST;
|
---|
| 1227 | if (phXmlFile)
|
---|
| 1228 | *phXmlFile = NIL_RTVFSFILE;
|
---|
| 1229 | Assert(cbError > 1);
|
---|
| 1230 | *pszError = '\0';
|
---|
| 1231 | NOREF(pszTarball);
|
---|
| 1232 |
|
---|
| 1233 | /*
|
---|
| 1234 | * Open the tar.gz filesystem stream and set up an manifest in-memory file.
|
---|
| 1235 | */
|
---|
[39878] | 1236 | RTMANIFEST hFileManifest;
|
---|
| 1237 | RTVFSFSSTREAM hTarFss;
|
---|
| 1238 | int rc = VBoxExtPackOpenTarFss(hTarballFile, pszError, cbError, &hTarFss, &hFileManifest);
|
---|
[34787] | 1239 | if (RT_FAILURE(rc))
|
---|
| 1240 | return rc;
|
---|
| 1241 |
|
---|
| 1242 | RTMANIFEST hOurManifest;
|
---|
| 1243 | rc = RTManifestCreate(0 /*fFlags*/, &hOurManifest);
|
---|
| 1244 | if (RT_SUCCESS(rc))
|
---|
| 1245 | {
|
---|
| 1246 | /*
|
---|
| 1247 | * Process the tarball (would be nice to move this to a function).
|
---|
| 1248 | */
|
---|
[39878] | 1249 | RTVFSFILE hXmlFile = NIL_RTVFSFILE;
|
---|
| 1250 | RTVFSFILE hManifestFile = NIL_RTVFSFILE;
|
---|
| 1251 | RTVFSFILE hSignatureFile = NIL_RTVFSFILE;
|
---|
[34787] | 1252 | for (;;)
|
---|
| 1253 | {
|
---|
| 1254 | /*
|
---|
| 1255 | * Get the next stream object.
|
---|
| 1256 | */
|
---|
| 1257 | char *pszName;
|
---|
| 1258 | RTVFSOBJ hVfsObj;
|
---|
| 1259 | RTVFSOBJTYPE enmType;
|
---|
| 1260 | rc = RTVfsFsStrmNext(hTarFss, &pszName, &enmType, &hVfsObj);
|
---|
| 1261 | if (RT_FAILURE(rc))
|
---|
| 1262 | {
|
---|
| 1263 | if (rc != VERR_EOF)
|
---|
| 1264 | vboxExtPackSetError(pszError, cbError, "RTVfsFsStrmNext failed: %Rrc", rc);
|
---|
| 1265 | else
|
---|
| 1266 | rc = VINF_SUCCESS;
|
---|
| 1267 | break;
|
---|
| 1268 | }
|
---|
| 1269 | const char *pszAdjName = pszName[0] == '.' && pszName[1] == '/' ? &pszName[2] : pszName;
|
---|
| 1270 |
|
---|
| 1271 | /*
|
---|
[34965] | 1272 | * Check the type & name validity, performing special tests on
|
---|
| 1273 | * standard extension pack member files.
|
---|
[34787] | 1274 | *
|
---|
| 1275 | * N.B. We will always reach the end of the loop before breaking on
|
---|
| 1276 | * failure - cleanup reasons.
|
---|
| 1277 | */
|
---|
| 1278 | rc = VBoxExtPackValidateMember(pszName, enmType, hVfsObj, pszError, cbError);
|
---|
| 1279 | if (RT_SUCCESS(rc))
|
---|
| 1280 | {
|
---|
[35158] | 1281 | PRTVFSFILE phVfsFile = NULL;
|
---|
[34787] | 1282 | if (!strcmp(pszAdjName, VBOX_EXTPACK_DESCRIPTION_NAME))
|
---|
| 1283 | phVfsFile = &hXmlFile;
|
---|
| 1284 | else if (!strcmp(pszAdjName, VBOX_EXTPACK_MANIFEST_NAME))
|
---|
| 1285 | phVfsFile = &hManifestFile;
|
---|
| 1286 | else if (!strcmp(pszAdjName, VBOX_EXTPACK_SIGNATURE_NAME))
|
---|
| 1287 | phVfsFile = &hSignatureFile;
|
---|
[34965] | 1288 | else if (!strncmp(pszAdjName, VBOX_EXTPACK_LICENSE_NAME_PREFIX, sizeof(VBOX_EXTPACK_LICENSE_NAME_PREFIX) - 1))
|
---|
| 1289 | rc = VBoxExtPackValidateStandardFile(pszAdjName, enmType, &hVfsObj, NULL, pszError, cbError);
|
---|
[34787] | 1290 | if (phVfsFile)
|
---|
[34965] | 1291 | rc = VBoxExtPackValidateStandardFile(pszAdjName, enmType, &hVfsObj, phVfsFile, pszError, cbError);
|
---|
[34787] | 1292 | }
|
---|
| 1293 |
|
---|
| 1294 | /*
|
---|
| 1295 | * Add any I/O stream to the manifest
|
---|
| 1296 | */
|
---|
| 1297 | if ( RT_SUCCESS(rc)
|
---|
| 1298 | && ( enmType == RTVFSOBJTYPE_FILE
|
---|
| 1299 | || enmType == RTVFSOBJTYPE_IO_STREAM))
|
---|
| 1300 | {
|
---|
| 1301 | RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
|
---|
| 1302 | rc = RTManifestEntryAddIoStream(hOurManifest, hVfsIos, pszAdjName, RTMANIFEST_ATTR_SIZE | RTMANIFEST_ATTR_SHA256);
|
---|
| 1303 | if (RT_FAILURE(rc))
|
---|
| 1304 | vboxExtPackSetError(pszError, cbError, "RTManifestEntryAddIoStream failed on '%s': %Rrc", pszAdjName, rc);
|
---|
| 1305 | RTVfsIoStrmRelease(hVfsIos);
|
---|
| 1306 | }
|
---|
| 1307 |
|
---|
| 1308 | /*
|
---|
| 1309 | * Clean up and break out on failure.
|
---|
| 1310 | */
|
---|
| 1311 | RTVfsObjRelease(hVfsObj);
|
---|
| 1312 | RTStrFree(pszName);
|
---|
| 1313 | if (RT_FAILURE(rc))
|
---|
| 1314 | break;
|
---|
| 1315 | }
|
---|
| 1316 |
|
---|
| 1317 | /*
|
---|
[39878] | 1318 | * Check the integrity of the tarball file.
|
---|
| 1319 | */
|
---|
| 1320 | if (RT_SUCCESS(rc))
|
---|
| 1321 | {
|
---|
| 1322 | RTVfsFsStrmRelease(hTarFss);
|
---|
| 1323 | hTarFss = NIL_RTVFSFSSTREAM;
|
---|
| 1324 | rc = vboxExtPackVerifyFileDigest(hFileManifest, pszTarballDigest, pStrDigest, pszError, cbError);
|
---|
| 1325 | }
|
---|
| 1326 |
|
---|
| 1327 | /*
|
---|
[34787] | 1328 | * If we've successfully processed the tarball, verify that the
|
---|
| 1329 | * mandatory files are present.
|
---|
| 1330 | */
|
---|
| 1331 | if (RT_SUCCESS(rc))
|
---|
| 1332 | {
|
---|
| 1333 | if (hXmlFile == NIL_RTVFSFILE)
|
---|
| 1334 | rc = vboxExtPackReturnError(VERR_MISSING, pszError, cbError, "Mandator file '%s' is missing", VBOX_EXTPACK_DESCRIPTION_NAME);
|
---|
| 1335 | if (hManifestFile == NIL_RTVFSFILE)
|
---|
| 1336 | rc = vboxExtPackReturnError(VERR_MISSING, pszError, cbError, "Mandator file '%s' is missing", VBOX_EXTPACK_MANIFEST_NAME);
|
---|
| 1337 | if (hSignatureFile == NIL_RTVFSFILE)
|
---|
| 1338 | rc = vboxExtPackReturnError(VERR_MISSING, pszError, cbError, "Mandator file '%s' is missing", VBOX_EXTPACK_SIGNATURE_NAME);
|
---|
| 1339 | }
|
---|
| 1340 |
|
---|
| 1341 | /*
|
---|
| 1342 | * Check the manifest and it's signature.
|
---|
| 1343 | */
|
---|
| 1344 | if (RT_SUCCESS(rc))
|
---|
| 1345 | rc = vboxExtPackVerifyManifestAndSignature(hOurManifest, hManifestFile, hSignatureFile, pszError, cbError);
|
---|
| 1346 |
|
---|
| 1347 | /*
|
---|
| 1348 | * Check the XML.
|
---|
| 1349 | */
|
---|
| 1350 | if (RT_SUCCESS(rc))
|
---|
| 1351 | rc = vboxExtPackVerifyXml(hXmlFile, pszExtPackName, pszError, cbError);
|
---|
| 1352 |
|
---|
| 1353 | /*
|
---|
| 1354 | * Returns objects.
|
---|
| 1355 | */
|
---|
| 1356 | if (RT_SUCCESS(rc))
|
---|
| 1357 | {
|
---|
| 1358 | if (phValidManifest)
|
---|
| 1359 | {
|
---|
| 1360 | RTManifestRetain(hOurManifest);
|
---|
| 1361 | *phValidManifest = hOurManifest;
|
---|
| 1362 | }
|
---|
| 1363 | if (phXmlFile)
|
---|
| 1364 | {
|
---|
| 1365 | RTVfsFileRetain(hXmlFile);
|
---|
| 1366 | *phXmlFile = hXmlFile;
|
---|
| 1367 | }
|
---|
| 1368 | }
|
---|
| 1369 |
|
---|
| 1370 | /*
|
---|
| 1371 | * Release our object references.
|
---|
| 1372 | */
|
---|
| 1373 | RTManifestRelease(hOurManifest);
|
---|
| 1374 | RTVfsFileRelease(hXmlFile);
|
---|
| 1375 | RTVfsFileRelease(hManifestFile);
|
---|
| 1376 | RTVfsFileRelease(hSignatureFile);
|
---|
| 1377 | }
|
---|
| 1378 | else
|
---|
| 1379 | vboxExtPackSetError(pszError, cbError, "RTManifestCreate failed: %Rrc", rc);
|
---|
| 1380 | RTVfsFsStrmRelease(hTarFss);
|
---|
[39878] | 1381 | RTManifestRelease(hFileManifest);
|
---|
[34787] | 1382 |
|
---|
| 1383 | return rc;
|
---|
| 1384 | }
|
---|
| 1385 |
|
---|