From a5232cfecd69e1f912c10e0025d3aa86c7baa50c Mon Sep 17 00:00:00 2001 From: Shpoike Date: Mon, 20 Feb 2023 07:47:16 +0000 Subject: [PATCH] Allow manifests to provide signatures for packages. Restart the menus when a package with a menu.dat is enabled. --- engine/client/m_download.c | 35 +++++++++++++++++--- engine/common/common.h | 3 ++ engine/common/fs.c | 68 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 99 insertions(+), 7 deletions(-) diff --git a/engine/client/m_download.c b/engine/client/m_download.c index 68ebb4edf..f9a6f9e39 100644 --- a/engine/client/m_download.c +++ b/engine/client/m_download.c @@ -2093,26 +2093,26 @@ void PM_LoadPackages(searchpath_t **oldpaths, const char *parent_pure, const cha pri = maxpri; for (p = availablepackages; p; p = p->next) { - if ((p->flags & DPF_ENABLED) && p->qhash && p->priority>=minpri&&p->prioritygamedir)) + if ((p->flags & (DPF_ENABLED|DPF_MANIMARKED)) && p->qhash && p->priority>=minpri&&p->prioritygamedir)) pri = p->priority; } minpri = pri+1; for (p = availablepackages; p; p = p->next) { - if ((p->flags & DPF_ENABLED) && p->qhash && p->priority==pri && !Q_strcasecmp(parent_pure, p->gamedir)) + if ((p->flags & (DPF_ENABLED|DPF_MANIMARKED)) && p->qhash && p->priority==pri && !Q_strcasecmp(parent_pure, p->gamedir)) { for (d = p->deps; d; d = d->next) { if (d->dtype == DEP_FILE) { Q_snprintfz(temp, sizeof(temp), "%s/%s", p->gamedir, d->name); - FS_AddHashedPackage(oldpaths, parent_pure, parent_logical, search, loadstuff, temp, *p->qhash?p->qhash:NULL, p->packprefix, SPF_COPYPROTECTED|SPF_UNTRUSTED); + FS_AddHashedPackage(oldpaths, parent_pure, parent_logical, search, loadstuff, temp, *p->qhash?p->qhash:NULL, p->packprefix, SPF_COPYPROTECTED|((p->flags & DPF_SIGNATUREACCEPTED)?0:SPF_UNTRUSTED)); } else if (d->dtype == DEP_CACHEFILE) { Q_snprintfz(temp, sizeof(temp), "downloads/%s", d->name); - FS_AddHashedPackage(oldpaths, parent_pure, parent_logical, NULL, loadstuff, temp, *p->qhash?p->qhash:NULL, p->packprefix, SPF_COPYPROTECTED|SPF_UNTRUSTED); + FS_AddHashedPackage(oldpaths, parent_pure, parent_logical, NULL, loadstuff, temp, *p->qhash?p->qhash:NULL, p->packprefix, SPF_COPYPROTECTED|((p->flags & DPF_SIGNATUREACCEPTED)?0:SPF_UNTRUSTED)); } } } @@ -3270,6 +3270,7 @@ static void PM_PackageEnabled(package_t *p) if (dep->dtype != DEP_FILE && dep->dtype != DEP_CACHEFILE) continue; COM_FileExtension(dep->name, ext, sizeof(ext)); + if (!pm_packagesinstalled) if (!stricmp(ext, "pak") || !stricmp(ext, "pk3") || !stricmp(ext, "zip")) { if (pm_packagesinstalled) @@ -3420,6 +3421,10 @@ static qboolean PM_Download_Got_Extract(package_t *p, searchpathfuncs_t *archive archive->ReadFile(archive, &loc, f); if (FS_WriteFile(destname, f, loc.len, p->fsroot)) { + if (!FS_NativePath(destname, p->fsroot, native, sizeof(native))) + Q_strncpyz(native, destname, sizeof(native)); + Con_Printf("Extracted %s (to %s)\n", p->name, native); + p->flags = nfl; success = true; continue; @@ -4672,6 +4677,17 @@ void PM_Command_f(void) Con_Printf(" ^[[Add]\\type\\pkg add %s;pkg apply^]", COM_QuotedString(p->name, quoted, sizeof(quoted), false)); if ((p->flags&DPF_MARKED) && p == PM_MarkedPackage(p->name, DPF_MARKED)) Con_Printf(" ^[[Remove]\\type\\pkg rem %s;pkg apply^]", COM_QuotedString(p->name, quoted, sizeof(quoted), false)); + + + if (p->flags & DPF_SIGNATUREACCEPTED) + Con_Printf(" ^&02Trusted"); + else if (p->flags & DPF_SIGNATUREREJECTED) + Con_Printf(" ^&04Untrusted"); + else if (p->flags & DPF_SIGNATUREUNKNOWN) + Con_Printf(" ^&0EUnverified"); + else + Con_Printf(" ^&0EUnsigned"); + Con_Printf("\n"); } Z_Free(sorted); @@ -5041,6 +5057,15 @@ void PM_AddManifestPackages(ftemanifest_t *man) p->flags = DPF_FORGETONUNINSTALL|DPF_MANIFEST|DPF_GUESSED; p->qhash = pack->crcknown?Z_StrDupf("%#x", pack->crc):NULL; + //note that this signs the hash(validated with size) with an separately trusted authority and is thus not dependant upon trusting the manifest itself... + //that said, we can't necessarily trust any overrides the manifest might include - those parts do not form part of the signature. + if (!pack->prefix && pack->crcknown && strchr(p->name, '/')) + { + p->signature = pack->signature?Z_StrDup(pack->signature):NULL; + p->filesha512 = pack->sha512?Z_StrDup(pack->sha512):NULL; + p->filesize = pack->filesize; + } + { char *c = p->name; for (c=p->name; *c; c++) //don't get confused. @@ -5106,6 +5131,8 @@ void PM_AddManifestPackages(ftemanifest_t *man) } PM_AddDep(p, DEP_FILE, path); + PM_ValidateAuthenticity(p, VH_UNSUPPORTED); + m = PM_InsertPackage(p); if (!m) continue; diff --git a/engine/common/common.h b/engine/common/common.h index 99ad87262..fb87e55f4 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -797,6 +797,9 @@ typedef struct unsigned int crc; //the public crc char *mirrors[8]; //a randomized (prioritized-on-load) list of mirrors to use. (may be 'prompt:game,package', 'unzip:file,url', 'xz:url', 'gz:url' char *condition; //only downloaded if this cvar is set | delimited allows multiple cvars. + char *sha512; //package must match this hash, if specified + char *signature; //signs the hash + qofs_t filesize; int mirrornum; //the index we last tried to download from, so we still work even if mirrors are down. } package[64]; } ftemanifest_t; diff --git a/engine/common/fs.c b/engine/common/fs.c index c605bb632..7ac1405c8 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -189,7 +189,7 @@ qboolean Sys_ResolveFileURL(const char *inurl, int inlen, char *out, int outlen) { //has an authority field... i+=2; //except we don't support authorities other than ourself... - if (i < inend || *i != '/') + if (i >= inend || *i != '/') return false; //must be an absolute path... #ifdef _WIN32 i++; //on windows, (full)absolute paths start with a drive name... @@ -320,6 +320,8 @@ void FS_Manifest_Free(ftemanifest_t *man) Z_Free(man->package[i].path); Z_Free(man->package[i].prefix); Z_Free(man->package[i].condition); + Z_Free(man->package[i].sha512); + Z_Free(man->package[i].signature); for (j = 0; j < sizeof(man->package[i].mirrors) / sizeof(man->package[i].mirrors[0]); j++) Z_Free(man->package[i].mirrors[j]); } @@ -372,12 +374,20 @@ static ftemanifest_t *FS_Manifest_Clone(ftemanifest_t *oldm) } for (i = 0; i < sizeof(newm->package) / sizeof(newm->package[0]); i++) { + newm->package[i].type = oldm->package[i].type; + newm->package[i].crc = oldm->package[i].crc; + newm->package[i].crcknown = oldm->package[i].crcknown; if (oldm->package[i].path) newm->package[i].path = Z_StrDup(oldm->package[i].path); if (oldm->package[i].prefix) newm->package[i].prefix = Z_StrDup(oldm->package[i].prefix); if (oldm->package[i].condition) newm->package[i].condition = Z_StrDup(oldm->package[i].condition); + if (oldm->package[i].sha512) + newm->package[i].sha512 = Z_StrDup(oldm->package[i].sha512); + if (oldm->package[i].signature) + newm->package[i].signature = Z_StrDup(oldm->package[i].signature); + newm->package[i].filesize = oldm->package[i].filesize; for (j = 0; j < sizeof(newm->package[i].mirrors) / sizeof(newm->package[i].mirrors[0]); j++) if (oldm->package[i].mirrors[j]) newm->package[i].mirrors[j] = Z_StrDup(oldm->package[i].mirrors[j]); @@ -483,10 +493,16 @@ static void FS_Manifest_Print(ftemanifest_t *man) else Con_Printf("package "); Con_Printf("%s", COM_QuotedString(man->package[i].path, buffer, sizeof(buffer), false)); - if (man->package[i].condition) - Con_Printf(" prefix %s", COM_QuotedString(man->package[i].condition, buffer, sizeof(buffer), false)); + if (man->package[i].prefix) + Con_Printf(" prefix %s", COM_QuotedString(man->package[i].prefix, buffer, sizeof(buffer), false)); if (man->package[i].condition) Con_Printf(" condition %s", COM_QuotedString(man->package[i].condition, buffer, sizeof(buffer), false)); + if (man->package[i].filesize) + Con_Printf(" filesize %"PRIuQOFS, man->package[i].filesize); + if (man->package[i].sha512) + Con_Printf(" sha512 %s", COM_QuotedString(man->package[i].sha512, buffer, sizeof(buffer), false)); + if (man->package[i].signature) + Con_Printf(" signature %s", COM_QuotedString(man->package[i].signature, buffer, sizeof(buffer), false)); if (man->package[i].crcknown) Con_Printf(" crc 0x%x", man->package[i].crc); for (j = 0; j < sizeof(man->package[i].mirrors) / sizeof(man->package[i].mirrors[0]); j++) @@ -552,6 +568,9 @@ static qboolean FS_Manifest_ParsePackage(ftemanifest_t *man, int packagetype) char *condition = NULL; char *prefix = NULL; char *arch = NULL; + char *signature = NULL; + char *sha512 = NULL; + qofs_t filesize = 0; unsigned int arg = 1; unsigned int mirrors = 0; char *mirror[countof(man->package[0].mirrors)]; @@ -598,6 +617,12 @@ static qboolean FS_Manifest_ParsePackage(ftemanifest_t *man, int packagetype) prefix = Cmd_Argv(arg++); else if (!strcmp(a, "arch")) arch = Cmd_Argv(arg++); + else if (!strcmp(a, "signature")) + signature = Cmd_Argv(arg++); + else if (!strcmp(a, "sha512")) + sha512 = Cmd_Argv(arg++); + else if (!strcmp(a, "filesize")||!strcmp(a, "size")) + filesize = strtoull(Cmd_Argv(arg++), NULL, 0); else if (!strcmp(a, "mirror")) { a = Cmd_Argv(arg++); @@ -642,6 +667,9 @@ mirror: man->package[i].path = Z_StrDup(path); man->package[i].prefix = prefix?Z_StrDup(prefix):NULL; man->package[i].condition = condition?Z_StrDup(condition):NULL; + man->package[i].sha512 = sha512?Z_StrDup(sha512):NULL; + man->package[i].signature = signature?Z_StrDup(signature):NULL; + man->package[i].filesize = filesize; man->package[i].crcknown = crcknown; man->package[i].crc = crc; for (j = 0; j < mirrors; j++) @@ -3529,6 +3557,7 @@ void FS_AddHashedPackage(searchpath_t **oldpaths, const char *parentpath, const static void FS_AddManifestPackages(searchpath_t **oldpaths, const char *purepath, const char *logicalpaths, searchpath_t *search, unsigned int loadstuff) { +#ifndef PACKAGEMANAGER int i; int ptlen, palen; @@ -3552,6 +3581,7 @@ static void FS_AddManifestPackages(searchpath_t **oldpaths, const char *purepath ); } } +#endif } static void FS_AddDownloadManifestPackages(searchpath_t **oldpaths, unsigned int loadstuff)//, const char *purepath, searchpath_t *search, const char *extension, searchpathfuncs_t *(QDECL *OpenNew)(vfsfile_t *file, const char *desc)) @@ -6141,6 +6171,8 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean char *vidfile[] = {"gfx.wad", "gfx/conback.lmp", //misc stuff "gfx/palette.lmp", "pics/colormap.pcx", "gfx/conchars.png"}; //palettes searchpathfuncs_t *vidpath[countof(vidfile)]; + char *menufile[] = {"menu.dat"/*mods*/, "gfx/ttl_main.lmp"/*q1*/, "pics/m_main_quit.pcx"/*q2*/, "gfx/menu/title0.lmp"/*h2*/}; + searchpathfuncs_t *menupath[countof(menufile)]; #endif //if any of these files change location, the configs will be re-execed. @@ -6159,6 +6191,16 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean else vidpath[i] = NULL; } + for (i = 0; i < countof(menufile); i++) + { + if (allowreloadconfigs) + { + FS_FLocateFile(menufile[i], FSLF_IFFOUND|FSLF_SECUREONLY, &loc); + menupath[i] = loc.search?loc.search->handle:NULL; + } + else + menupath[i] = NULL; + } #endif if (allowreloadconfigs && fs_noreexec.ival) @@ -6439,6 +6481,12 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean } #endif + //our basic filesystem should be okay, but no packages loaded yet. +#ifdef MANIFESTDOWNLOADS + //make sure the package manager knows what its meant to know... + PM_AddManifestPackages(man); +#endif + if (Sys_LockMutex(fs_thread_mutex)) { #ifdef HAVE_CLIENT @@ -6537,6 +6585,20 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean Cbuf_AddText ("vid_reload\n", RESTRICT_LOCAL); vidrestart = false; } + + + if (qrenderer != QR_NONE && allowreloadconfigs) + { + for (i = 0; i < countof(menufile); i++) + { + FS_FLocateFile(menufile[i], FSLF_IFFOUND, &loc); + if (menupath[i] != (loc.search?loc.search->handle:NULL)) + { + Cbuf_AddText ("menu_restart\n", RESTRICT_LOCAL); + break; + } + } + } #endif //rebuild the cache now, should be safe to waste some cycles on it