VirtualBox

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

Last change on this file since 74901 was 73505, checked in by vboxsync, 6 years ago

Main,Config.kmk: GCC 8.2.0 fixes

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