Milán Major

netscape-revival

1 branch
Code

security/cmd/verify.c

#include "sec.h"

#if defined(__sun) && !defined(SVR4)
extern int fread(char *, size_t, size_t, FILE*);
extern int fprintf(FILE *, char *, ...);
extern int fclose(FILE*);
extern int getopt(int, char**, char*);
extern char *optarg;
#endif

static PRArenaPool *arena;

SECStatus SEC_ATOBFile(SECItem *result, FILE *inFile)
{
    unsigned char *buf;
    unsigned count, rem, part, bufSize, newSize;
    ATOBContext *cx;
    int nb;
    SECStatus rv;

    cx = ATOB_NewContext();
    if (!cx) {
	return PORT_GetError();
    }

    buf = 0;
    count = 0;
    bufSize = 0;
    rv = ATOB_Begin(cx);
    if (rv) goto loser;
    for (;;) {
	char ibuf[4096];

	if (feof(inFile)) break;
	nb = fread(ibuf, 1, sizeof(ibuf), inFile);
	if (nb != sizeof(ibuf)) {
	    if (nb == 0) {
		if (ferror(inFile)) {
		    PORT_SetError(SEC_ERROR_IO);
		    rv = SECFailure;
		    goto loser;
		}
		/* eof */
		break;
	    }
	}

	/* Over grow buffer to accomodate new data */
	newSize = count + nb + 128;
	if (buf) {
	    buf = (unsigned char*) PORT_Realloc(buf, newSize);
	} else {
	    buf = (unsigned char*) PORT_Alloc(newSize);
	}
	if (!buf) goto loser;
	bufSize = newSize;

	rv = ATOB_Update(cx, buf + count, &part, bufSize-count, ibuf, nb);
	if (rv) goto loser;
	count += part;
    }
    rv = ATOB_End(cx, buf+count, &rem, bufSize-count);
    if (rv) goto loser;
    count += rem;

    /* Trim buffer down to fit data actually decoded */
    buf = (unsigned char*) PORT_Realloc(buf, count);
    if (!buf) goto loser;
    result->data = buf;
    result->len = count;
    rv = 0;
    goto done;

  loser:
    PORT_Free(buf);

  done:
    ATOB_DestroyContext(cx);
    return rv;
}

static SECKEYPublicKey *ReadCert(char *fileName)
{
    char *typeTag;
    int rv;
    SECItem certder, rawder;
    FILE *fp;
    CERTCertificate *c;
    SECTypedData td;
    CERTSignedData sd;

    fp = fopen(fileName, "r");
    if (!fp) return 0;

    /* Get data. It might be wrapped and it might not be... */
    rv = DER_Read(&rawder, fp);
    if (rv) return 0;
    PORT_Memset(&td, 0, sizeof(td));
    rv = DER_Decode(arena, &td, SECTypedDataTemplate, &rawder);
    if (rv) {
	/* data was not wrapped, maybe */
	certder = rawder;
    } else {
	/* Data is wrapped ... */
	if ((td.type.len != sizeof(SEC_CT_CERTIFICATE)-1) ||
	    PORT_Memcmp(td.type.data, SEC_CT_CERTIFICATE,
		      sizeof(SEC_CT_CERTIFICATE)-1)) {
	    /* Then again, maybe not... */
	    certder = rawder;
	} else
	    certder = td.data;
    }

    PORT_Memset(&sd, 0, sizeof(sd));
    rv = DER_Decode(arena, &sd, CERTSignedDataTemplate, &certder);
    if (rv) return 0;
    c = SEC_NewCertificate();
    if (!c) return 0;
    rv = DER_Decode(arena, c, CERTCertificateTemplate, &sd.data);
    if (rv) return 0;
    return CERT_ExtractPublicKey(&c->subjectPublicKeyInfo);
}

static SECKEYPublicKey *ReadCertRequest(char *fileName)
{
    char *typeTag;
    int rv;
    SECItem certder;
    FILE *fp;
    CERTCertificateRequest *cr;
    CERTSignedData sd;
    SECTypedData td;
    SECItem rawder;

    fp = fopen(fileName, "r");
    if (!fp) return 0;

    /* Get data. It might be wrapped and it might not be... */
    rv = DER_Read(&rawder, fp);
    if (rv) return 0;
    PORT_Memset(&td, 0, sizeof(td));
    rv = DER_Decode(arena, &td, SECTypedDataTemplate, &rawder);
    if (rv) {
	/* data was not wrapped, maybe */
	certder = rawder;
    } else {
	/* Data is wrapped ... */
	if ((td.type.len != sizeof(SEC_CT_CERTIFICATE_REQUEST)-1) ||
	    PORT_Memcmp(td.type.data, SEC_CT_CERTIFICATE_REQUEST,
		      sizeof(SEC_CT_CERTIFICATE_REQUEST)-1)) {
	    /* Then again, maybe not... */
	    certder = rawder;
	} else
	    certder = td.data;
    }

    PORT_Memset(&sd, 0, sizeof(sd));
    rv = DER_Decode(arena, &sd, CERTSignedDataTemplate, &certder);
    if (rv) return 0;
    cr = (CERTCertificateRequest*) PORT_ZAlloc(sizeof(CERTCertificateRequest));
    if (!cr) return 0;
    rv = DER_Decode(arena, cr, CERTCertificateRequestTemplate, &sd.data);
    if (rv) return 0;
    return CERT_ExtractPublicKey(&cr->subjectPublicKeyInfo);
}

static void Usage(char *progName)
{
    fprintf(stderr,
	    "Usage: %s [-k keyfile] [-c certfile] [-r certreqfile] [-q] [-d] [-i input] [-s sig-file]\n",
	    progName);
    fprintf(stderr, "%-20s Specify the file that contains the key pair\n",
	    "-k keyfile");
    fprintf(stderr, "%-20s Specify the file that contains a certificate\n",
	    "-c certfile");
    fprintf(stderr, "%-20s Specify the file that contains a certificate request\n",
	    "-r certreqfile");
    fprintf(stderr, "%-20s Define an input file to use (default is stdin)\n",
	    "-i input");
    fprintf(stderr, "%-20s Define a signature file to use\n",
	    "-s sig-file");
    fprintf(stderr, "%-20s Expect DER signed input\n",
	    "-d");
    fprintf(stderr, "%-20s Ascii decode signature input using RFC1113\n",
	    "-a");
    fprintf(stderr, "%-20s Be quiet; No messages\n",
	    "-q");
    fprintf(stderr, "A signature file must be specified if -d is not used\n");
    fprintf(stderr, "One of -k or -c must be used\n");
    fprintf(stderr, "Can't mix ascii and der yet\n");
    exit(-1);
}

int main(int argc, char **argv)
{
    char *progName;
    int o, rv, der, quiet, ascii;
    SECKEYPrivateKey *k;
    SECKEYPublicKey *pubKey;
    FILE *inFile, *sigFile;

    progName = strrchr(argv[0], '/');
    progName = progName ? progName+1 : argv[0];

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (!arena) {
	fprintf(stderr, "%s: unable to allocate memory\n", progName);
	return -1;
    }
    
    /* Parse command line arguments */
    inFile = 0;
    sigFile = 0;
    k = 0;
    pubKey = 0;
    der = 0;
    quiet = 0;
    ascii = 0;
    while ((o = getopt(argc, argv, "c:r:k:i:s:daq")) != -1) {
	switch (o) {
	  case '?':
	    Usage(progName);
	    break;

	  case 'c':
	    pubKey = ReadCert(optarg);
	    break;

	  case 'r':
	    pubKey = ReadCertRequest(optarg);
	    break;

	  case 'k':
	    k = SEC_GetPrivateKey(optarg, stdin, stderr);
	    if (!k) {
		fprintf(stderr, "%s: bad key file\n", progName);
		return -1;
	    }
	    break;

	  case 'i':
	    inFile = fopen(optarg, "r");
	    if (!inFile) {
		fprintf(stderr, "%s: unable to open \"%s\" for reading\n",
			progName, optarg);
		return -1;
	    }
	    break;

	  case 's':
	    sigFile = fopen(optarg, "r");
	    if (!sigFile) {
		fprintf(stderr, "%s: unable to open \"%s\" for reading\n",
			progName, optarg);
		return -1;
	    }
	    break;

	  case 'd':
	    der = 1;
	    break;

	  case 'a':
	    ascii = 1;
	    break;

	  case 'q':
	    quiet = 1;
	    break;
	}
    }
    if (!k && !pubKey) {
	Usage(progName);
    }
    if (k && pubKey) {
	Usage(progName);
    }
    if (!inFile) inFile = stdin;

    if (k) {
	pubKey = SECKEY_ConvertToPublicKey(k);
    }

    if (der) {
	SECItem d;
	CERTSignedData sd;

	PORT_Memset(&sd, 0, sizeof(sd));
	do {
	    rv = DER_Read(&d, inFile);
	    if (rv) break;
	    rv = DER_Decode(arena, &sd, CERTSignedDataTemplate, &d);
	    if (rv) break;
	    DER_ConvertBitString(&sd.signature);
	    rv = VFY_VerifyData(sd.data.data, sd.data.len,
				pubKey, &sd.signature);
	} while (0);
    } else {
	char buf[4096];
	SECItem sig;

	if (!sigFile) {
	    Usage(progName);
	}

	/* Read it in, whatever it is */
	if (ascii) {
	    rv = SEC_ATOBFile(&sig, sigFile);
	    if (rv && !quiet) {
		fprintf(stderr, "%s: lossage: rv=%d error=%d errno=%d\n",
			progName, rv, PORT_GetError(), errno);
		return -1;
	    }
	} else {
	    rv = fread(buf, 1, sizeof(buf), sigFile);
	    if (rv == 0) {
		if (!quiet)
		    fprintf(stderr, "%s: bad signature file\n", progName);
		return -1;
	    }
	    sig.data = buf;
	    sig.len = rv;
	}
	rv = SEC_VerifyFile(inFile, pubKey, &sig);
    }
    if (!rv && !quiet) {
	fprintf(stderr, "%s: signature check passed\n", progName);
    } else
    if (rv && !quiet) {
	fprintf(stderr, "%s: signature check FAILED\n", progName);
	return -1;
    }

    return 0;
}