VirtualBox

source: vbox/trunk/src/VBox/Main/src-all/ExtPackUtil.cpp@ 95140

Last change on this file since 95140 was 94912, checked in by vboxsync, 3 years ago

Main/src-all: Adjust to the new rules wrt. to rc -> hrc,vrc usage, ​bugref:10223

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette