Changeset 95675 in vbox for trunk/src/VBox/Runtime/tools/RTSignTool.cpp
- Timestamp:
- Jul 16, 2022 11:52:40 PM (2 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/tools/RTSignTool.cpp
r95674 r95675 42 42 #include <iprt/stream.h> 43 43 #include <iprt/string.h> 44 #ifdef RT_OS_WINDOWS 45 # include <iprt/utf16.h> 46 #endif 44 47 #include <iprt/uuid.h> 45 48 #include <iprt/zero.h> 49 #include <iprt/formats/asn1.h> 50 #include <iprt/formats/mach-o.h> 46 51 #ifndef RT_OS_WINDOWS 47 52 # include <iprt/formats/pecoff.h> 48 #else49 # include <iprt/utf16.h>50 53 #endif 51 54 #include <iprt/crypto/applecodesign.h> … … 67 70 # include <ncrypt.h> 68 71 #endif 72 #include "internal/ldr.h" /* for IMAGE_XX_SIGNATURE defines */ 69 73 70 74 … … 137 141 #define OPT_TIMESTAMP_OVERRIDE 1044 138 142 #define OPT_NO_SIGNING_TIME 1045 143 #define OPT_FILE_TYPE 1046 139 144 140 145 … … 148 153 RTSIGNTOOLHELP_FULL 149 154 } RTSIGNTOOLHELP; 155 156 157 /** Filetypes. */ 158 typedef enum RTSIGNTOOLFILETYPE 159 { 160 RTSIGNTOOLFILETYPE_INVALID = 0, 161 RTSIGNTOOLFILETYPE_DETECT, 162 RTSIGNTOOLFILETYPE_EXE, 163 RTSIGNTOOLFILETYPE_CAT, 164 RTSIGNTOOLFILETYPE_UNKNOWN, 165 RTSIGNTOOLFILETYPE_END 166 } RTSIGNTOOLFILETYPE; 150 167 151 168 … … 3187 3204 } 3188 3205 3206 static RTEXITCODE HandleOptFileType(RTSIGNTOOLFILETYPE *penmFileType, const char *pszType) 3207 { 3208 if (strcmp(pszType, "detect") == 0 || strcmp(pszType, "auto") == 0) 3209 *penmFileType = RTSIGNTOOLFILETYPE_DETECT; 3210 else if (strcmp(pszType, "exe") == 0) 3211 *penmFileType = RTSIGNTOOLFILETYPE_EXE; 3212 else if (strcmp(pszType, "cat") == 0) 3213 *penmFileType = RTSIGNTOOLFILETYPE_CAT; 3214 else 3215 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown forced file type: %s", pszType); 3216 return RTEXITCODE_SUCCESS; 3217 } 3218 3219 /** 3220 * Detects the type of files @a pszFile is (by reading from it). 3221 * 3222 * @returns The file type, or RTSIGNTOOLFILETYPE_UNKNOWN (error displayed). 3223 * @param enmForceFileType Usually set to RTSIGNTOOLFILETYPE_DETECT, but if 3224 * not we'll return this without probing the file. 3225 * @param pszFile The name of the file to detect the type of. 3226 */ 3227 static RTSIGNTOOLFILETYPE DetectFileType(RTSIGNTOOLFILETYPE enmForceFileType, const char *pszFile) 3228 { 3229 /* 3230 * Forced? 3231 */ 3232 if (enmForceFileType != RTSIGNTOOLFILETYPE_DETECT) 3233 return enmForceFileType; 3234 3235 /* 3236 * Read the start of the file. 3237 */ 3238 RTFILE hFile = NIL_RTFILE; 3239 int rc = RTFileOpen(&hFile, pszFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE); 3240 if (RT_FAILURE(rc)) 3241 { 3242 RTMsgError("Error opening '%s' for reading: %Rrc", pszFile, rc); 3243 return RTSIGNTOOLFILETYPE_UNKNOWN; 3244 } 3245 3246 union 3247 { 3248 uint8_t ab[256]; 3249 uint16_t au16[256/2]; 3250 uint32_t au32[256/4]; 3251 } uBuf; 3252 RT_ZERO(uBuf); 3253 3254 size_t cbRead = 0; 3255 rc = RTFileRead(hFile, &uBuf, sizeof(uBuf), &cbRead); 3256 if (RT_FAILURE(rc)) 3257 RTMsgError("Error reading from '%s': %Rrc", pszFile, rc); 3258 3259 uint64_t cbFile; 3260 int rcSize = RTFileQuerySize(hFile, &cbFile); 3261 if (RT_FAILURE(rcSize)) 3262 RTMsgError("Error querying size of '%s': %Rrc", pszFile, rc); 3263 3264 RTFileClose(hFile); 3265 if (RT_FAILURE(rc) || RT_FAILURE(rcSize)) 3266 return RTSIGNTOOLFILETYPE_UNKNOWN; 3267 3268 /* 3269 * Try guess the kind of file. 3270 */ 3271 /* All the executable magics we know: */ 3272 if ( uBuf.au16[0] == RT_H2LE_U16_C(IMAGE_DOS_SIGNATURE) 3273 || uBuf.au16[0] == RT_H2LE_U16_C(IMAGE_NE_SIGNATURE) 3274 || uBuf.au16[0] == RT_H2LE_U16_C(IMAGE_LX_SIGNATURE) 3275 || uBuf.au16[0] == RT_H2LE_U16_C(IMAGE_LE_SIGNATURE) 3276 || uBuf.au32[0] == RT_H2LE_U32_C(IMAGE_NT_SIGNATURE) 3277 || uBuf.au32[0] == RT_H2LE_U32_C(IMAGE_ELF_SIGNATURE) 3278 || uBuf.au32[0] == IMAGE_FAT_SIGNATURE 3279 || uBuf.au32[0] == IMAGE_FAT_SIGNATURE_OE 3280 || uBuf.au32[0] == IMAGE_MACHO32_SIGNATURE 3281 || uBuf.au32[0] == IMAGE_MACHO32_SIGNATURE_OE 3282 || uBuf.au32[0] == IMAGE_MACHO64_SIGNATURE 3283 || uBuf.au32[0] == IMAGE_MACHO64_SIGNATURE_OE) 3284 return RTSIGNTOOLFILETYPE_EXE; 3285 3286 /* 3287 * Catalog files are PKCS#7 SignedData and starts with a ContentInfo, i.e.: 3288 * SEQUENCE { 3289 * contentType OBJECT IDENTIFIER, 3290 * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL 3291 * } 3292 * 3293 * We ASSUME that it's DER encoded and doesn't use an indefinite length form 3294 * at the start and that contentType is signedData (1.2.840.113549.1.7.2). 3295 * 3296 * Example of a 10353 (0x2871) byte long file: 3297 * vv-------- contentType -------vv 3298 * 00000000 30 82 28 6D 06 09 2A 86 48 86 F7 0D 01 07 02 A0 3299 * 00000010 82 28 5E 30 82 28 5A 02 01 01 31 0B 30 09 06 05 3300 */ 3301 if ( uBuf.ab[0] == (ASN1_TAG_SEQUENCE | ASN1_TAGFLAG_CONSTRUCTED) 3302 && uBuf.ab[1] != 0x80 /* not indefinite form */ 3303 && uBuf.ab[1] > 0x30) 3304 { 3305 size_t off = 1; 3306 uint32_t cbRec = uBuf.ab[1]; 3307 if (cbRec & 0x80) 3308 { 3309 cbRec &= 0x7f; 3310 off += cbRec; 3311 switch (cbRec) 3312 { 3313 case 1: cbRec = uBuf.ab[2]; break; 3314 case 2: cbRec = RT_MAKE_U16( uBuf.ab[3], uBuf.ab[2]); break; 3315 case 3: cbRec = RT_MAKE_U32_FROM_U8(uBuf.ab[4], uBuf.ab[3], uBuf.ab[2], 0); break; 3316 case 4: cbRec = RT_MAKE_U32_FROM_U8(uBuf.ab[5], uBuf.ab[4], uBuf.ab[3], uBuf.ab[2]); break; 3317 default: cbRec = UINT32_MAX; break; 3318 } 3319 } 3320 if (off <= 5) 3321 { 3322 off++; 3323 if (off + cbRec == cbFile) 3324 { 3325 /* If the contentType is signedData we're going to treat it as a catalog file, 3326 we don't currently much care about the signed content of a cat file. */ 3327 static const uint8_t s_abSignedDataOid[] = 3328 { ASN1_TAG_OID, 9 /*length*/, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02 }; 3329 if (memcmp(&uBuf.ab[off], s_abSignedDataOid, sizeof(s_abSignedDataOid)) == 0) 3330 return RTSIGNTOOLFILETYPE_CAT; 3331 } 3332 } 3333 } 3334 3335 RTMsgError("Unable to detect type of '%s'", pszFile); 3336 return RTSIGNTOOLFILETYPE_UNKNOWN; 3337 } 3338 3189 3339 #endif /* !IPRT_IN_BUILD_TOOL */ 3190 3340 … … 3306 3456 #ifndef IPRT_IN_BUILD_TOOL 3307 3457 3308 static RTEXITCODE HelpSign Exe(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)3458 static RTEXITCODE HelpSign(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel) 3309 3459 { 3310 3460 RT_NOREF_PV(enmLevel); … … 3334 3484 3335 3485 3336 static RTEXITCODE HandleSign Exe(int cArgs, char **papszArgs)3486 static RTEXITCODE HandleSign(int cArgs, char **papszArgs) 3337 3487 { 3338 3488 /* … … 3357 3507 { "--timestamp-type", OPT_TIMESTAMP_TYPE, RTGETOPT_REQ_STRING }, 3358 3508 { "--timestamp-override", OPT_TIMESTAMP_OVERRIDE, RTGETOPT_REQ_STRING }, 3509 { "--file-type", OPT_FILE_TYPE, RTGETOPT_REQ_STRING }, 3359 3510 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }, 3360 3511 { "/v", 'v', RTGETOPT_REQ_NOTHING }, … … 3367 3518 bool fHashPages = false; 3368 3519 bool fNoSigningTime = false; 3520 RTSIGNTOOLFILETYPE enmForceFileType = RTSIGNTOOLFILETYPE_DETECT; 3369 3521 SignToolKeyPair SigningCertKey("signing", true); 3370 3522 RTCRSTORE hAddCerts = NIL_RTCRSTORE; /* leaked if returning directly (--help, --version) */ … … 3396 3548 case OPT_TIMESTAMP_TYPE: rcExit2 = HandleOptTimestampType(&fTimestampTypeOld, ValueUnion.psz); break; 3397 3549 case OPT_TIMESTAMP_OVERRIDE: rcExit2 = HandleOptTimestampOverride(&SigningTime, ValueUnion.psz); break; 3550 case OPT_FILE_TYPE: rcExit2 = HandleOptFileType(&enmForceFileType, ValueUnion.psz); break; 3398 3551 case 'v': cVerbosity++; break; 3399 3552 case 'V': return HandleVersion(cArgs, papszArgs); 3400 case 'h': return HelpSign Exe(g_pStdOut, RTSIGNTOOLHELP_FULL);3553 case 'h': return HelpSign(g_pStdOut, RTSIGNTOOLHELP_FULL); 3401 3554 3402 3555 case VINF_GETOPT_NOT_OPTION: 3403 /* Do final certificate and key option processing (first file only). */ 3556 /* 3557 * Do final certificate and key option processing (first file only). 3558 */ 3404 3559 rcExit2 = SigningCertKey.finalizeOptions(cVerbosity); 3405 3560 if (rcExit2 == RTEXITCODE_SUCCESS) … … 3407 3562 if (rcExit2 == RTEXITCODE_SUCCESS) 3408 3563 { 3409 /* Do the work: */3410 SIGNTOOLPKCS7EXE Exe;3411 rcExit2 = SignToolPkcs7Exe_InitFromFile(&Exe, ValueUnion.psz, cVerbosity,3412 RTLDRARCH_WHATEVER, true /*fAllowUnsigned*/);3413 if ( rcExit2 == RTEXITCODE_SUCCESS)3564 /* 3565 * Detect file type. 3566 */ 3567 RTSIGNTOOLFILETYPE enmFileType = DetectFileType(enmForceFileType, ValueUnion.psz); 3568 if (enmFileType == RTSIGNTOOLFILETYPE_EXE) 3414 3569 { 3415 rcExit2 = SignToolPkcs7_AddOrReplaceSignature(&Exe, cVerbosity, enmSigType, fReplaceExisting, fHashPages, 3416 fNoSigningTime, &SigningCertKey, hAddCerts, 3417 fTimestampTypeOld, SigningTime, &TimestampCertKey); 3570 /* 3571 * Sign executable image. 3572 */ 3573 SIGNTOOLPKCS7EXE Exe; 3574 rcExit2 = SignToolPkcs7Exe_InitFromFile(&Exe, ValueUnion.psz, cVerbosity, 3575 RTLDRARCH_WHATEVER, true /*fAllowUnsigned*/); 3418 3576 if (rcExit2 == RTEXITCODE_SUCCESS) 3419 rcExit2 = SignToolPkcs7_Encode(&Exe, cVerbosity); 3577 { 3578 rcExit2 = SignToolPkcs7_AddOrReplaceSignature(&Exe, cVerbosity, enmSigType, fReplaceExisting, 3579 fHashPages, fNoSigningTime, &SigningCertKey, hAddCerts, 3580 fTimestampTypeOld, SigningTime, &TimestampCertKey); 3581 if (rcExit2 == RTEXITCODE_SUCCESS) 3582 rcExit2 = SignToolPkcs7_Encode(&Exe, cVerbosity); 3583 if (rcExit2 == RTEXITCODE_SUCCESS) 3584 rcExit2 = SignToolPkcs7Exe_WriteSignatureToFile(&Exe, cVerbosity); 3585 SignToolPkcs7Exe_Delete(&Exe); 3586 } 3587 } 3588 else if (enmFileType == RTSIGNTOOLFILETYPE_CAT) 3589 { 3590 /* 3591 * Sign catalog file. 3592 */ 3593 SIGNTOOLPKCS7 Cat; 3594 rcExit2 = SignToolPkcs7_InitFromFile(&Cat, ValueUnion.psz, cVerbosity); 3420 3595 if (rcExit2 == RTEXITCODE_SUCCESS) 3421 rcExit2 = SignToolPkcs7Exe_WriteSignatureToFile(&Exe, cVerbosity); 3422 SignToolPkcs7Exe_Delete(&Exe); 3596 { 3597 rcExit2 = SignToolPkcs7_AddOrReplaceCatSignature(&Cat, cVerbosity, enmSigType, fReplaceExisting, 3598 fNoSigningTime, &SigningCertKey, hAddCerts, 3599 fTimestampTypeOld, SigningTime, &TimestampCertKey); 3600 if (rcExit2 == RTEXITCODE_SUCCESS) 3601 rcExit2 = SignToolPkcs7_Encode(&Cat, cVerbosity); 3602 if (rcExit2 == RTEXITCODE_SUCCESS) 3603 rcExit2 = SignToolPkcs7_WriteSignatureToFile(&Cat, ValueUnion.psz, cVerbosity); 3604 SignToolPkcs7_Delete(&Cat); 3605 } 3423 3606 } 3424 if (rcExit2 != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS) 3425 rcExit = rcExit2; 3426 rcExit2 = RTEXITCODE_SUCCESS; 3427 } 3428 break; 3429 3430 default: 3431 return RTGetOptPrintError(ch, &ValueUnion); 3432 } 3433 if (rcExit2 != RTEXITCODE_SUCCESS) 3434 { 3435 rcExit = rcExit2; 3436 break; 3437 } 3438 } 3439 3440 if (hAddCerts != NIL_RTCRSTORE) 3441 RTCrStoreRelease(hAddCerts); 3442 return rcExit; 3443 } 3444 3445 #endif /*!IPRT_IN_BUILD_TOOL */ 3446 3447 3448 /********************************************************************************************************************************* 3449 * The 'sign-cat' command. * 3450 *********************************************************************************************************************************/ 3451 #ifndef IPRT_IN_BUILD_TOOL 3452 3453 static RTEXITCODE HelpSignCat(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel) 3454 { 3455 RT_NOREF_PV(enmLevel); 3456 3457 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT, 3458 "sign-cat [-v|--verbose] " 3459 "[--type sha1|sha256] " 3460 "[--append] " 3461 OPT_CERT_KEY_SYNOPSIS("--") 3462 "[--add-cert <file>] " 3463 OPT_CERT_KEY_SYNOPSIS("--timestamp-") 3464 "[--timestamp-type old|new] " 3465 "[--timestamp-date <fake-isots>] " 3466 "[--timestamp-year <fake-year>] " 3467 "[--replace-existing|-r] " 3468 "<exe>\n"); 3469 if (enmLevel == RTSIGNTOOLHELP_FULL) 3470 RTStrmWrappedPrintf(pStrm, 0, 3471 "Sign a catalog file.\n" 3472 "\n" 3473 "The --timestamp-override option can take a partial or full ISO timestamp. It is merged " 3474 "with the current time if partial.\n" 3475 "\n"); 3476 return RTEXITCODE_SUCCESS; 3477 } 3478 3479 3480 static RTEXITCODE HandleSignCat(int cArgs, char **papszArgs) 3481 { 3482 /* 3483 * Parse arguments. 3484 */ 3485 static const RTGETOPTDEF s_aOptions[] = 3486 { 3487 { "--append", 'a', RTGETOPT_REQ_NOTHING }, 3488 { "/as", 'a', RTGETOPT_REQ_NOTHING }, 3489 { "--type", 't', RTGETOPT_REQ_STRING }, 3490 { "/fd", 't', RTGETOPT_REQ_STRING }, 3491 { "--add-cert", OPT_ADD_CERT, RTGETOPT_REQ_STRING }, 3492 { "/ac", OPT_ADD_CERT, RTGETOPT_REQ_STRING }, 3493 { "--no-signing-time", OPT_NO_SIGNING_TIME, RTGETOPT_REQ_NOTHING }, 3494 OPT_CERT_KEY_GETOPTDEF_ENTRIES("--", 1000), 3495 OPT_CERT_KEY_GETOPTDEF_COMPAT_ENTRIES( 1000), 3496 OPT_CERT_KEY_GETOPTDEF_ENTRIES("--timestamp-", 1020), 3497 { "--timestamp-type", OPT_TIMESTAMP_TYPE, RTGETOPT_REQ_STRING }, 3498 { "--timestamp-override", OPT_TIMESTAMP_OVERRIDE, RTGETOPT_REQ_STRING }, 3499 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }, 3500 { "/v", 'v', RTGETOPT_REQ_NOTHING }, 3501 { "/debug", 'v', RTGETOPT_REQ_NOTHING }, 3502 }; 3503 3504 unsigned cVerbosity = 0; 3505 RTDIGESTTYPE enmSigType = RTDIGESTTYPE_SHA1; 3506 bool fReplaceExisting = true; 3507 bool fNoSigningTime = false; 3508 SignToolKeyPair SigningCertKey("signing", true); 3509 RTCRSTORE hAddCerts = NIL_RTCRSTORE; /* leaked if returning directly (--help, --version) */ 3510 bool fTimestampTypeOld = true; 3511 SignToolKeyPair TimestampCertKey("timestamp"); 3512 RTTIMESPEC SigningTime; 3513 RTTimeNow(&SigningTime); 3514 3515 RTGETOPTSTATE GetState; 3516 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST); 3517 AssertRCReturn(rc, RTEXITCODE_FAILURE); 3518 3519 RTEXITCODE rcExit = RTEXITCODE_SUCCESS; 3520 RTGETOPTUNION ValueUnion; 3521 int ch; 3522 while ((ch = RTGetOpt(&GetState, &ValueUnion))) 3523 { 3524 RTEXITCODE rcExit2 = RTEXITCODE_SUCCESS; 3525 switch (ch) 3526 { 3527 OPT_CERT_KEY_SWITCH_CASES(SigningCertKey, 1000, ch, ValueUnion, rcExit2); 3528 OPT_CERT_KEY_SWITCH_CASES(TimestampCertKey, 1020, ch, ValueUnion, rcExit2); 3529 case 't': rcExit2 = HandleOptSignatureType(&enmSigType, ValueUnion.psz); break; 3530 case 'a': fReplaceExisting = false; break; 3531 case OPT_NO_SIGNING_TIME: fNoSigningTime = true; break; 3532 case OPT_ADD_CERT: rcExit2 = HandleOptAddCert(&hAddCerts, ValueUnion.psz); break; 3533 case OPT_TIMESTAMP_TYPE: rcExit2 = HandleOptTimestampType(&fTimestampTypeOld, ValueUnion.psz); break; 3534 case OPT_TIMESTAMP_OVERRIDE: rcExit2 = HandleOptTimestampOverride(&SigningTime, ValueUnion.psz); break; 3535 case 'v': cVerbosity++; break; 3536 case 'V': return HandleVersion(cArgs, papszArgs); 3537 case 'h': return HelpSignExe(g_pStdOut, RTSIGNTOOLHELP_FULL); 3538 3539 case VINF_GETOPT_NOT_OPTION: 3540 /* Do final certificate and key option processing (first file only). */ 3541 rcExit2 = SigningCertKey.finalizeOptions(cVerbosity); 3542 if (rcExit2 == RTEXITCODE_SUCCESS) 3543 rcExit2 = TimestampCertKey.finalizeOptions(cVerbosity); 3544 if (rcExit2 == RTEXITCODE_SUCCESS) 3545 { 3546 /* Do the work: */ 3547 SIGNTOOLPKCS7 Cat; 3548 rcExit2 = SignToolPkcs7_InitFromFile(&Cat, ValueUnion.psz, cVerbosity); 3549 if (rcExit2 == RTEXITCODE_SUCCESS) 3550 { 3551 rcExit2 = SignToolPkcs7_AddOrReplaceCatSignature(&Cat, cVerbosity, enmSigType, fReplaceExisting, 3552 fNoSigningTime, &SigningCertKey, hAddCerts, 3553 fTimestampTypeOld, SigningTime, &TimestampCertKey); 3554 if (rcExit2 == RTEXITCODE_SUCCESS) 3555 rcExit2 = SignToolPkcs7_Encode(&Cat, cVerbosity); 3556 if (rcExit2 == RTEXITCODE_SUCCESS) 3557 rcExit2 = SignToolPkcs7_WriteSignatureToFile(&Cat, ValueUnion.psz, cVerbosity); 3558 SignToolPkcs7_Delete(&Cat); 3559 } 3607 else 3608 rcExit2 = RTEXITCODE_FAILURE; 3560 3609 if (rcExit2 != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS) 3561 3610 rcExit = rcExit2; … … 5159 5208 #ifndef IPRT_IN_BUILD_TOOL 5160 5209 { "add-timestamp-exe-signature", HandleAddTimestampExeSignature, HelpAddTimestampExeSignature }, 5161 { "sign-exe", HandleSignExe, HelpSignExe }, 5162 { "sign-cat", HandleSignCat, HelpSignCat }, 5210 { "sign", HandleSign, HelpSign }, 5163 5211 #endif 5164 5212 #ifndef IPRT_IN_BUILD_TOOL
Note:
See TracChangeset
for help on using the changeset viewer.